mirror of https://github.com/janet-lang/janet
Compare commits
1086 Commits
Author | SHA1 | Date |
---|---|---|
Calvin Rose | 7bae7d9efd | |
Calvin Rose | ae2c5820a1 | |
Calvin Rose | 60e0c8ea92 | |
Calvin Rose | 7d3acc0ed6 | |
Calvin Rose | 2637b33957 | |
Calvin Rose | 58ccb66659 | |
Calvin Rose | 634429cf61 | |
Calvin Rose | 03166a745a | |
Calvin Rose | ace60e1898 | |
Calvin Rose | 876b7f106f | |
Calvin Rose | 809b6589a1 | |
Calvin Rose | 02f53ca014 | |
Calvin Rose | 0b03ddb21b | |
Calvin Rose | ea5d4fd3af | |
Calvin Rose | e6b73f8cd1 | |
Calvin Rose | af232ef729 | |
Calvin Rose | 2e2f8abfc0 | |
Calvin Rose | 91a583db27 | |
znley | c1647a74c5 | |
Calvin Rose | 721f280966 | |
Calvin Rose | e914eaf055 | |
Calvin Rose | fe54013679 | |
Calvin Rose | fdaf2e1594 | |
Calvin Rose | 9946f3bdf4 | |
Calvin Rose | c747e8d16c | |
Calvin Rose | 3e402d397e | |
Calvin Rose | 0350834cd3 | |
Calvin Rose | 60e22d9703 | |
John W Higgins | ee7362e847 | |
Calvin Rose | 369f96b80e | |
Calvin Rose | 7c5ed04ab1 | |
Calvin Rose | 4779a445e0 | |
Calvin Rose | f0f1b7ce9e | |
Calvin Rose | 7c9157a0ed | |
Calvin Rose | 522a6cb435 | |
Gautham | d0d551d739 | |
Gautham | 71a123fef7 | |
Gautham | 3f40c8d7fb | |
Gautham | 983c2e5499 | |
Gautham | eebb4c3ade | |
Gautham | 50425eac72 | |
Gautham | 382ff77bbe | |
Gautham | bf680fb5d3 | |
Gautham | 4ed7db4f91 | |
Calvin Rose | bf19920d65 | |
Gautham | 174b5f6686 | |
Gautham | 4173645b81 | |
Gautham | af511f1f55 | |
Gautham | 83c6080380 | |
Calvin Rose | 2f0c789ea1 | |
Calvin Rose | a9b8f8e8a9 | |
Calvin Rose | f92f3eb6fa | |
Calvin Rose | 89e74dca3e | |
Calvin Rose | f2e86d2f8d | |
John W Higgins | 623da131e5 | |
John W Higgins | e89ec31ae5 | |
Calvin Rose | 68a6ed208e | |
Calvin Rose | c01b32c4f3 | |
Josef Pospíšil | ee11ff9da9 | |
Josef Pospíšil | ed56d5d6ff | |
Josef Pospíšil | b317ab755c | |
Josef Pospíšil | 9819994999 | |
Josef Pospíšil | e9dbaa81d2 | |
Josef Pospíšil | 9f9146ffae | |
Josef Pospíšil | 9d9732af97 | |
Calvin Rose | ebb8fa9787 | |
Calvin Rose | 9e6abbf4d4 | |
Calvin Rose | 6032a6d658 | |
Max Schillinger | c29ab22e6d | |
sogaiu | 592ac4904c | |
Calvin Rose | 03ae2ec153 | |
Calvin Rose | 3bc42d0d37 | |
Calvin Rose | 12630d3e54 | |
Calvin Rose | c9897f99c3 | |
Calvin Rose | e66dc14b3a | |
Calvin Rose | 7a2868c147 | |
Calvin Rose | 9e0daaee09 | |
Calvin Rose | c293c7de93 | |
Calvin Rose | 49eb5f8563 | |
amano.kenji | 674b375b2c | |
llmII | 7e94c091eb | |
sogaiu | 5885ccba61 | |
Calvin Rose | 431ecd3d1a | |
Calvin Rose | f6df8ff935 | |
Calvin Rose | 3fd70f0951 | |
Calvin Rose | bebb635d4f | |
sogaiu | 354896bc4b | |
Calvin Rose | 5ddefff27e | |
sogaiu | 91827eef4f | |
Calvin Rose | 9c14c09962 | |
Calvin Rose | e85a84171f | |
Calvin Rose | 3a4f86c3d7 | |
Calvin Rose | 5e75963312 | |
Calvin Rose | 184d9289b5 | |
Calvin Rose | b7ff9577c0 | |
sogaiu | 942a1aaac6 | |
Josef Pospíšil | 69f0fe004d | |
sogaiu | 2a04347a42 | |
Calvin Rose | 1394f1a5c0 | |
sogaiu | cf4d19a8ea | |
Calvin Rose | 23b0fe9f8e | |
Josef Pospíšil | 1ba718b15e | |
Calvin Rose | df5f79ff35 | |
Calvin Rose | 6d7e8528ea | |
Philip Nelson | 197bb73a62 | |
Calvin Rose | f91e599451 | |
Josef Pospíšil | 5b9aa9237c | |
Ian Henry | 61f38fab37 | |
Calvin Rose | 9142f38cbc | |
Calvin Rose | e8ed961572 | |
Calvin Rose | be11a2a1ad | |
Ian Henry | ea75086300 | |
Calvin Rose | 9eeefbd79a | |
sogaiu | c573a98363 | |
Calvin Rose | 11d7af3f95 | |
Calvin Rose | a10b4f61d8 | |
Calvin Rose | a0cb7514f1 | |
Calvin Rose | b066edc116 | |
Josef Pospíšil | 938f5a689e | |
Calvin Rose | 772f4c26e8 | |
Locria Cyber | 6b5d151beb | |
Calvin Rose | a9176a77e6 | |
Calvin Rose | 16f409c6a9 | |
Calvin Rose | 9593c930de | |
Calvin Rose | 56f33f514b | |
Calvin Rose | 1ccd544b94 | |
Calvin Rose | 93c83a2ee2 | |
Calvin Rose | f459e32ada | |
Ico Doornekamp | 9b640c8e9c | |
Calvin Rose | a3228f4997 | |
Calvin Rose | 715eb69d92 | |
Calvin Rose | df2d5cb3d3 | |
Calvin Rose | 3b189eab64 | |
Calvin Rose | 609b629c22 | |
Calvin Rose | e74365fe38 | |
Calvin Rose | 46b34833c2 | |
Vincent Lee | 045c80869d | |
Calvin Rose | 2ea2e72ddd | |
sogaiu | 1b17e12fd6 | |
Calvin Rose | cc5beda0d2 | |
Calvin Rose | a363fd926d | |
Calvin Rose | 21ebede529 | |
Calvin Rose | 15d67e9191 | |
Calvin Rose | b5996f5f02 | |
Andriamanitra | 83204dc293 | |
Calvin Rose | e3f4142d2a | |
Calvin Rose | f18ad36b1b | |
Calvin Rose | cb25a2ecd6 | |
Calvin Rose | 741a5036e8 | |
Calvin Rose | 549ee95f3d | |
Calvin Rose | 6ae81058aa | |
Calvin Rose | 267c603824 | |
Calvin Rose | a8f583a372 | |
Calvin Rose | 2b5d90f73a | |
Calvin Rose | 4139e426fe | |
Calvin Rose | a775a89e01 | |
Calvin Rose | 990f6352e0 | |
Calvin Rose | b344702304 | |
Calvin Rose | d497612bce | |
Calvin Rose | 2a3b101bd8 | |
Calvin Rose | 63e93af421 | |
Calvin Rose | ab055b3ebe | |
Calvin Rose | a9a013473f | |
Calvin Rose | 87de1e5766 | |
Calvin Rose | 894aaef267 | |
Calvin Rose | e209e54ffe | |
Calvin Rose | 7511eadaa7 | |
Calvin Rose | 6c4906605a | |
Calvin Rose | 8a9be9d837 | |
Calvin Rose | b72098cc71 | |
Calvin Rose | defe60e08b | |
Calvin Rose | 7f852b8af4 | |
Calvin Rose | d71c100ca7 | |
Calvin Rose | 5442c8e86d | |
Calvin Rose | cf4901e713 | |
Calvin Rose | 4b8c1ac2d2 | |
Calvin Rose | 555e0c0b85 | |
Calvin Rose | dc301305de | |
Calvin Rose | f1111c135b | |
Calvin Rose | 3905e92965 | |
Calvin Rose | 1418ada38f | |
Calvin Rose | 9256a66b76 | |
Calvin Rose | e8c013a778 | |
Calvin Rose | fea8242ea7 | |
Calvin Rose | 7bfb17c209 | |
Calvin Rose | e7e4341e70 | |
Calvin Rose | 6186be4443 | |
Calvin Rose | d07f01d7cb | |
Calvin Rose | 73291a30a0 | |
Calvin Rose | a3b129845b | |
Calvin Rose | 0ff8f58be8 | |
Calvin Rose | 66292beec9 | |
Calvin Rose | bf2af1051f | |
Calvin Rose | b6e3020d4c | |
Calvin Rose | 8f516a1e28 | |
Calvin Rose | 5f2e287efd | |
Calvin Rose | 8c0d65cf9f | |
Ian Henry | fa609a5079 | |
Calvin Rose | c708ff9708 | |
Calvin Rose | 2ea90334a3 | |
Calvin Rose | eea8aa555f | |
Calvin Rose | 51a75e1872 | |
Calvin Rose | af7ed4322e | |
Calvin Rose | 7cdd7cf6eb | |
Calvin Rose | 26aa622afc | |
Calvin Rose | 84ad161f1e | |
Calvin Rose | 6efb965dab | |
Calvin Rose | 8c90a12e0f | |
Calvin Rose | 2d54e88e74 | |
Calvin Rose | 16ea5323e0 | |
Calvin Rose | 7a23ce2367 | |
Calvin Rose | e05bc7eb54 | |
Calvin Rose | b3a6e25ce0 | |
Calvin Rose | b63d41102e | |
Calvin Rose | 964295b59d | |
Calvin Rose | d19db30f3d | |
Calvin Rose | d12464fc0e | |
Calvin Rose | a96971c8a7 | |
Calvin Rose | f6f769503a | |
Calvin Rose | 82917ac6e3 | |
Calvin Rose | a6ffafb1a2 | |
Calvin Rose | fb8c529f2e | |
Calvin Rose | 1ee98e1e66 | |
Calvin Rose | 81f35f5dd1 | |
Calvin Rose | 1b402347cd | |
Calvin Rose | 7599656784 | |
Calvin Rose | dccb60ba35 | |
Calvin Rose | ae642ceca0 | |
Calvin Rose | 471b6f9966 | |
Calvin Rose | 5dd18bac2c | |
Calvin Rose | 018f4e0891 | |
Calvin Rose | e85809a98a | |
Calvin Rose | e6e9bd8147 | |
Calvin Rose | 221645d2ce | |
Calvin Rose | 2f4a6214a2 | |
Calvin Rose | e00a461c26 | |
Calvin Rose | c31314be38 | |
Andriamanitra | ee142c4be0 | |
Andriamanitra | aeacc0b31b | |
Calvin Rose | 7b4c3bdbcc | |
Calvin Rose | 910b9cf1fd | |
Calvin Rose | b10aaceab0 | |
Calvin Rose | 169bd812c9 | |
Calvin Rose | 34767f1e13 | |
sogaiu | 4f642c0843 | |
Calvin Rose | 4e5889ed59 | |
Calvin Rose | a1b848ad76 | |
Calvin Rose | dbcc1fad3e | |
primo-ppcg | db366558e7 | |
sogaiu | a23c03fbd0 | |
Calvin Rose | ff18b92eb0 | |
Josef Pospíšil | 7f148522ab | |
Calvin Rose | 159c612924 | |
Calvin Rose | b95dfd4bdf | |
Calvin Rose | e69954af2f | |
primo-ppcg | a5ff26f602 | |
primo-ppcg | a7536268e1 | |
primo-ppcg | 541469371a | |
Calvin Rose | a13aeaf955 | |
primo-ppcg | 9cf674cdcb | |
Calvin Rose | 51c0cf97bc | |
primo-ppcg | 4cb1f616c5 | |
primo-ppcg | 645109048b | |
primo-ppcg | f969fb69e1 | |
Calvin Rose | bfb60fdb84 | |
primo-ppcg | 2f43cb843e | |
Calvin Rose | 874fd2aba7 | |
Calvin Rose | 33d1371186 | |
Calvin Rose | d2dd241e6b | |
Calvin Rose | 4ecadfabf4 | |
Calvin Rose | ffd79c6097 | |
primo-ppcg | 35a8d2a519 | |
Calvin Rose | 21eab7e9cc | |
Calvin Rose | d9605c2856 | |
Calvin Rose | 70a467d469 | |
primo-ppcg | 6e8979336d | |
Calvin Rose | ee01045db5 | |
Calvin Rose | b7f8224588 | |
Calvin Rose | ca4c1e4259 | |
Calvin Rose | 91712add3d | |
Calvin Rose | 7198dcb416 | |
Calvin Rose | 08e20e912d | |
Calvin Rose | f45571033c | |
Calvin Rose | 2ac36a0572 | |
Calvin Rose | 3df1d54847 | |
Calvin Rose | f3969b6066 | |
primo-ppcg | 6222f35bc8 | |
primo-ppcg | 2f178963c0 | |
primo-ppcg | 15760b0950 | |
Calvin Rose | 43a6a70e1e | |
Calvin Rose | cd36f1ef5f | |
primo-ppcg | cdd7083c86 | |
Calvin Rose | 8df7364319 | |
Calvin Rose | 63023722d1 | |
Calvin Rose | 79c12e5116 | |
primo-ppcg | 53e16944a1 | |
Calvin Rose | 7475362c85 | |
primo-ppcg | 9238b82cde | |
Calvin Rose | 7049f658ec | |
wooosh | 701913fb19 | |
primo-ppcg | 831f41a62b | |
Calvin Rose | 0ea1da80e7 | |
Calvin Rose | 06eea74b98 | |
primo-ppcg | c8c0e112bc | |
primo-ppcg | 7417e82c51 | |
Calvin Rose | ecc4d80a5a | |
Calvin Rose | 3df24c52f4 | |
primo-ppcg | 8a70fb95b5 | |
primo-ppcg | d8b45ecd61 | |
primo-ppcg | 61712bae9c | |
Calvin Rose | 4ff81a5a25 | |
Calvin Rose | 08f0e55d8f | |
Calvin Rose | 080b37cb31 | |
Calvin Rose | bbdcd035ba | |
sogaiu | f9233ef90b | |
Calvin Rose | cd3573a4d2 | |
Calvin Rose | 738fe24e6d | |
primo-ppcg | c2e55b5486 | |
Calvin Rose | 989f0726e3 | |
primo-ppcg | bdefd3ba1e | |
Calvin Rose | 4efcff33bd | |
Calvin Rose | 8183cc5a8d | |
Calvin Rose | f3bda1536d | |
Calvin Rose | 3b6371e03d | |
Calvin Rose | b5d3c87253 | |
Calvin Rose | f73b8c550a | |
Calvin Rose | 5437744126 | |
sogaiu | 5a5e70b001 | |
sogaiu | 348a5bc0a9 | |
Calvin Rose | 026c64fa01 | |
Calvin Rose | e38663c457 | |
Calvin Rose | 117c741c29 | |
Calvin Rose | 9bc5bec9f1 | |
Calvin Rose | a5f4e4d328 | |
Calvin Rose | db0abfde72 | |
Calvin Rose | edf263bcb5 | |
Calvin Rose | 60fba585e3 | |
Calvin Rose | ebb6fe5be3 | |
Calvin Rose | d91c95bf92 | |
primo-ppcg | 2007438424 | |
primo-ppcg | 81423635ad | |
Calvin Rose | 58d297364a | |
Calvin Rose | db902c90c4 | |
Calvin Rose | 42ccd0f790 | |
Michael Camilleri | 20ec6f574e | |
primo-ppcg | b3db367ae7 | |
primo-ppcg | 8a62c742e6 | |
Calvin Rose | b125cbeac9 | |
Calvin Rose | 3f7a2c2197 | |
Calvin Rose | f6248369fe | |
primo-ppcg | c83f3ec097 | |
Calvin Rose | 0cd00da354 | |
bakpakin | 4b7b285aa9 | |
Dmitry | d63379e777 | |
Calvin Rose | b219b146fa | |
Calvin Rose | ff90b81ec3 | |
Calvin Rose | 9120eaef79 | |
Michael Camilleri | 1ccd879916 | |
Michael Camilleri | f977ace7f8 | |
Calvin Rose | c3f4dc0c15 | |
sogaiu | 78eed9b11c | |
Calvin Rose | 3a4d56afca | |
Calvin Rose | 63bb93fc07 | |
Calvin Rose | 5a39a04a79 | |
Calvin Rose | 2fde34b519 | |
Calvin Rose | 1ef5c038db | |
Calvin Rose | e2459cfb47 | |
Calvin Rose | cfffc0bcf1 | |
Calvin Rose | 7272f43191 | |
primo-ppcg | 2a7ea27bb7 | |
primo-ppcg | 32c5b816ae | |
Chloe Kudryavtsev | e54ea7a1d8 | |
primo-ppcg | 1077efd03a | |
Chloe Kudryavtsev | f9ab91511d | |
primo-ppcg | 2c3ca2984e | |
primo-ppcg | 94722e566c | |
Christopher Chambers | 163f7ee85d | |
Christopher Chambers | 52d3470cbe | |
Calvin Rose | 0bd6e85c61 | |
Calvin Rose | e35c6b876f | |
Calvin Rose | 9a2897e741 | |
primo-ppcg | 70b2e8179d | |
primo-ppcg | 5317edc65d | |
Calvin Rose | 866d83579e | |
primo-ppcg | a238391b36 | |
Calvin Rose | 5e152d30db | |
Christopher Chambers | 57c954783d | |
primo-ppcg | b5407ac708 | |
primo-ppcg | 472ec730b5 | |
Calvin Rose | 8c819b1f91 | |
Calvin Rose | 528a516390 | |
Calvin Rose | 6509e37c84 | |
Calvin Rose | 649173f661 | |
Calvin Rose | 1efb0adb35 | |
Calvin Rose | 88a8e2c1df | |
Ico Doornekamp | bb4ff05d35 | |
Calvin Rose | dd3b601c87 | |
Calvin Rose | e22d101a62 | |
Calvin Rose | 4b3c813f5a | |
Calvin Rose | 67f375bea2 | |
Calvin Rose | 88ba99b87e | |
Christopher Chambers | 53447e9d0b | |
Calvin Rose | c4c86f8671 | |
Calvin Rose | 658941d26d | |
Calvin Rose | e4bf27b01c | |
Calvin Rose | 7d48b75f81 | |
Calvin Rose | 5f56bf836c | |
Ico Doornekamp | c0f5f97ddb | |
Calvin Rose | 15177ac2e9 | |
Calvin Rose | 8360bc93ac | |
Ico Doornekamp | e0ea844d50 | |
sogaiu | 9675411f35 | |
Calvin Rose | e97299fc65 | |
Calvin Rose | 26a113927e | |
Calvin Rose | d0aa7ef590 | |
Calvin Rose | 5de889419f | |
Calvin Rose | 0fcbda2da7 | |
Calvin Rose | 14e33c295f | |
Calvin Rose | 644ac8caf8 | |
Calvin Rose | 77189b6e66 | |
Calvin Rose | 4f8f7f66ee | |
Calvin Rose | b099bd97f2 | |
Calvin Rose | 961c6ea15a | |
Calvin Rose | 9c97d8f648 | |
Ico Doornekamp | ad7bf80611 | |
Ico Doornekamp | 40080b23ae | |
Calvin Rose | 7acb5c63e0 | |
Calvin Rose | fcca9bbab3 | |
Calvin Rose | dbb2187425 | |
Calvin Rose | 82e51f9e81 | |
Calvin Rose | 4782a76bca | |
Ico Doornekamp | d13788a4ed | |
Ico Doornekamp | e64a0175b1 | |
Calvin Rose | 4aca94154f | |
Calvin Rose | ac5f118dac | |
Charlotte Koch | a2812ec5eb | |
Calvin Rose | 70f13f1b62 | |
Calvin Rose | 77e62a25cb | |
Ico Doornekamp | 09345ec786 | |
primo-ppcg | bad73baf98 | |
primo-ppcg | 3602f5aa5d | |
primo-ppcg | 672b705faf | |
Ico Doornekamp | 64e3cdeb2b | |
Calvin Rose | 909c906080 | |
Calvin Rose | 71bde11e95 | |
Calvin Rose | fc20fbed92 | |
Calvin Rose | e6b7c85c37 | |
Ico Doornekamp | b3a92363f8 | |
Ico Doornekamp | e9f2d1aca7 | |
Ico Doornekamp | b4e3dbf331 | |
Calvin Rose | c3620786cf | |
Calvin Rose | 41943746e4 | |
Calvin Rose | 176e816b8c | |
Ico Doornekamp | 50a19bd870 | |
Calvin Rose | 57b751b994 | |
Calvin Rose | 77732a8f44 | |
Calvin Rose | c47c2e538d | |
Calvin Rose | cc5545277d | |
Ico Doornekamp | 63353b98cd | |
tionis | 4dfc869b8a | |
tionis | b4b1c7d80b | |
tionis | e53c03028f | |
Calvin Rose | 8680aef42f | |
Calvin Rose | c3fd71d643 | |
Ico Doornekamp | 30c47d685d | |
Ico Doornekamp | 80db682109 | |
Ico Doornekamp | e8e5f66f4c | |
Ico Doornekamp | aaf3d08bcd | |
Ico Doornekamp | 61132d6c40 | |
tionis | 9cc0645a1e | |
Calvin Rose | fc8c6a429e | |
Calvin Rose | 2f966883d9 | |
tionis | 320ba80ca1 | |
Calvin Rose | b621d4dd2e | |
tionis | 56d927c72d | |
tionis | 53afc2e50a | |
Ico Doornekamp | 89debac8f6 | |
Calvin Rose | f2197fa2d8 | |
Ico Doornekamp | a6a097c111 | |
Ico Doornekamp | c3e28bc924 | |
Ico Doornekamp | 8d78fb1f6b | |
Calvin Rose | 148917d4ca | |
Calvin Rose | d8cf9bf942 | |
Calvin Rose | d6f5a060ed | |
Calvin Rose | 692b6ef8ac | |
Ico Doornekamp | ac5f1fe1be | |
tionis | 0f35acade1 | |
tionis | 56d72ec4c5 | |
tionis | 71d51c160d | |
tionis | 0b58e505ee | |
Ico Doornekamp | 2a6c615bec | |
Ico Doornekamp | ab8c5a0b5f | |
Ico Doornekamp | 68c35feaea | |
Ico Doornekamp | 88d0c2ca0f | |
Calvin Rose | 398833ebe3 | |
Calvin Rose | 358f5a03bf | |
Calvin Rose | fba1fdabe4 | |
Calvin Rose | d42afd21e5 | |
Calvin Rose | 20ada86761 | |
Calvin Rose | 3b353f1855 | |
Calvin Rose | 1467ab4f93 | |
Calvin Rose | 7e65c2bdad | |
Calvin Rose | 84a4e3e98a | |
Calvin Rose | bcbeedb001 | |
Calvin Rose | e04b103b5d | |
Chloe Kudryavtsev | ac75b94679 | |
Ico Doornekamp | d3bb06cfd6 | |
Ico Doornekamp | 5cd729c4c1 | |
Calvin Rose | c9fd2bdf39 | |
Calvin Rose | e4be5992b3 | |
Calvin Rose | 2ac4988f1b | |
Calvin Rose | 19f14adb9e | |
Calvin Rose | 86de039492 | |
Calvin Rose | 2360164e4f | |
Calvin Rose | c93ddceadb | |
sogaiu | cd19dec44a | |
Chloe Kudryavtsev | 53ba9c800a | |
Calvin Rose | cabbaded68 | |
Calvin Rose | 9bb589f827 | |
Calvin Rose | c3a06686c2 | |
Calvin Rose | 7d57f87007 | |
Calvin Rose | 4cc4a9d38b | |
Calvin Rose | 02c7cd0194 | |
Calvin Rose | 696efcb9e2 | |
Calvin Rose | 6e9cde8ac1 | |
sogaiu | a9fae49671 | |
Calvin Rose | 440af9fd64 | |
Calvin Rose | 347721ae40 | |
Calvin Rose | daea91044c | |
Calvin Rose | 4ed3f2c662 | |
Calvin Rose | 3641c8f60a | |
Calvin Rose | e4b68cd940 | |
Calvin Rose | b8c936e2fe | |
Calvin Rose | 83cd519702 | |
Ian Henry | 54b54f85f3 | |
Ian Henry | ccd874fe4e | |
Ian Henry | 9dc7e8ed3a | |
Ian Henry | 485099fd6e | |
wackbyte | d359c6b43e | |
Calvin Rose | d9ed7a77f8 | |
wackbyte | 4238a4ca6a | |
Chloe Kudryavtsev | 0902a5a981 | |
Chloe Kudryavtsev | f3192303ab | |
Calvin Rose | bef5bd72c2 | |
ML | b6175e4296 | |
Calvin Rose | 3858b2e177 | |
Calvin Rose | 9a76e77981 | |
Calvin Rose | 8182d640cd | |
Calvin Rose | 1c6fda1a5c | |
Calvin Rose | c51db1cf2f | |
Ian Henry | 4e7930fc4c | |
Calvin Rose | 3563f8ccdb | |
Charlotte Koch | 575af763f6 | |
Charlotte Koch | 8b16b9b246 | |
Calvin Rose | 01aab66667 | |
Calvin Rose | aa5c987a94 | |
Calvin Rose | 75229332c8 | |
sogaiu | 9d5b1ba838 | |
Calvin Rose | f27b225b34 | |
sogaiu | 3c523d66e9 | |
Calvin Rose | 1144c27c54 | |
Ian Henry | b442b21d3f | |
Calvin Rose | 746ff5307d | |
Calvin Rose | ef85b24d8f | |
Calvin Rose | c55d93512b | |
Calvin Rose | 2e38f9ba61 | |
Calvin Rose | 1cadff8e58 | |
Calvin Rose | d1eba60ba8 | |
Calvin Rose | 057dccad8f | |
Calvin Rose | 4285200b4b | |
Calvin Rose | 73c2fbbc2a | |
Calvin Rose | 37b7e170fa | |
Calvin Rose | b032d94877 | |
Calvin Rose | 9476016741 | |
Calvin Rose | 7a1c9c7798 | |
Calvin Rose | c7fb7b4451 | |
Calvin Rose | 67c474fc7a | |
Calvin Rose | 4e8154cf8a | |
Calvin Rose | 9582d3c623 | |
Calvin Rose | 0079500713 | |
Calvin Rose | 55af6ce834 | |
Calvin Rose | 3e82fdc125 | |
Calvin Rose | 7344a6cfc0 | |
Calvin Rose | 0aded71343 | |
Calvin Rose | 7663b1e703 | |
Calvin Rose | 282546c03f | |
Calvin Rose | f4bc89d1c0 | |
Jona Ekenberg | fa277c3797 | |
Jona Ekenberg | c0c8ab25e6 | |
Jona Ekenberg | b685bf3026 | |
Jona Ekenberg | ce31db09e4 | |
Jona Ekenberg | 624a6cf619 | |
Jona Ekenberg | 587aa87d28 | |
Jona Ekenberg | 88813c4f87 | |
Calvin Rose | dacbe29771 | |
Calvin Rose | 244833cfa1 | |
Calvin Rose | 05e7f974e3 | |
Calvin Rose | 0dbef65a73 | |
Calvin Rose | 9106228787 | |
Calvin Rose | 6ae3bdb25c | |
sogaiu | 310bcec260 | |
Calvin Rose | 8c4cc4e671 | |
Calvin Rose | c6eaaa83ed | |
sogaiu | 8f598d6f96 | |
Calvin Rose | 20bc323d17 | |
Calvin Rose | 8b0bcf4db9 | |
Calvin Rose | 8955e6f536 | |
Calvin Rose | f8ddea6452 | |
Calvin Rose | 987e04086d | |
Calvin Rose | 85f2acbf52 | |
alectroemel | 1acf4c3ab7 | |
Calvin Rose | 07a3158fba | |
bakpakin | 2f8bed9d82 | |
bakpakin | a490937cd9 | |
bakpakin | 8ee5942481 | |
bakpakin | 93b469885a | |
Calvin Rose | d8d1de2dcb | |
Ian Henry | ab224514f0 | |
Calvin Rose | 75179de8da | |
Calvin Rose | c28df14e6b | |
Calvin Rose | b73855b193 | |
Calvin Rose | 2093ab2baa | |
Calvin Rose | a0f40042cb | |
Daisuke Fujimura (fd0) | 3254c2c477 | |
Calvin Rose | 0a8eb9e3ba | |
sogaiu | 70e0c6f9ef | |
Calvin Rose | a8a78d4525 | |
Calvin Rose | 57e6ee963d | |
Calvin Rose | ce6bfb8420 | |
Calvin Rose | f0672bdc59 | |
Charlotte Koch | 23de953fbd | |
Calvin Rose | 03c496bdd8 | |
Calvin Rose | d5ee6cf521 | |
sogaiu | fb7981e053 | |
harryvederci | 846123ecab | |
Calvin Rose | 373cb444fe | |
Calvin Rose | 90f212df92 | |
Calvin Rose | 12286e4246 | |
Calvin Rose | aa60c1f36a | |
Calvin Rose | c731f01067 | |
Calvin Rose | 6c9c1cdb30 | |
Calvin Rose | 9ba2b40e87 | |
Calvin Rose | 7a3d055012 | |
Calvin Rose | 0824f45e29 | |
Charlotte Koch | 4debe3446c | |
Calvin Rose | 07fe9bcdf6 | |
Calvin Rose | 6a557a73f5 | |
Calvin Rose | 8d1cfe0c56 | |
Calvin Rose | a3a42eebea | |
Calvin Rose | 76be8006a4 | |
Calvin Rose | bfcfd58259 | |
Calvin Rose | 914a4360e7 | |
Calvin Rose | 8c31874eeb | |
Calvin Rose | ef7afeb2ea | |
LGTM Migrator | 4067f883a2 | |
Calvin Rose | c8974fffbe | |
Calvin Rose | b75fb8dc9e | |
Calvin Rose | 57356781a9 | |
Calvin Rose | e43eab5fd6 | |
Calvin Rose | 894cd0e022 | |
Calvin Rose | db2c63fffc | |
Calvin Rose | 60e0f32f1a | |
Calvin Rose | e731996a68 | |
Calvin Rose | 2f69cd4209 | |
Calvin Rose | fd59de25c5 | |
Calvin Rose | af12c3d41a | |
Calvin Rose | 54b52bbeb5 | |
Calvin Rose | 1174c68d9a | |
Calvin Rose | 448ea7167f | |
Calvin Rose | 6b27008c99 | |
Calvin Rose | 725c785882 | |
Calvin Rose | ab068cff67 | |
bakpakin | 9dc03adfda | |
bakpakin | 49f9e4eddf | |
bakpakin | 43c47ac44c | |
Calvin Rose | 1cebe64664 | |
Calvin Rose | f33c381043 | |
Calvin Rose | 3479841c77 | |
Calvin Rose | 6a899968a9 | |
Calvin Rose | bb8405a36e | |
bakpakin | c7bc711f63 | |
bakpakin | e326071c35 | |
Locria Cyber | ad6a669381 | |
Locria Cyber | e4c9dafc9a | |
Calvin Rose | dfc0aefd87 | |
Calvin Rose | 356b39c6f5 | |
Calvin Rose | 8da7bb6b68 | |
Autumn! | 9341081a4d | |
Calvin Rose | 324a086eb4 | |
Ashok Gautham | ed595f52c2 | |
Calvin Rose | 64ad0023bb | |
Autumn! | fe5f661d15 | |
Calvin Rose | ff26e3a8ba | |
Calvin Rose | 14657a762c | |
Calvin Rose | 4754fa3902 | |
Calvin Rose | f302f87337 | |
Calvin Rose | 94dbcde292 | |
Josef Pospíšil | 4336a174b1 | |
Techcable | 0adb13ed71 | |
Techcable | e78a3d1c19 | |
Techcable | c099ec05ee | |
Techcable | a20612478e | |
Techcable | f778e8bbd1 | |
Techcable | 7203c046f9 | |
Techcable | 754b61c593 | |
Techcable | 927e9e4e4d | |
Techcable | 699f9622d7 | |
Techcable | 765eb84c33 | |
Calvin Rose | 03ba1f7021 | |
Calvin Rose | 1f7f20788c | |
Calvin Rose | c59dd29190 | |
Calvin Rose | 99f63a41a3 | |
Calvin Rose | a575f5df36 | |
Techcable | 12a1849090 | |
Calvin Rose | 0817e627ee | |
Calvin Rose | 14d90239a7 | |
Calvin Rose | f5d11dc656 | |
Calvin Rose | 6dcf5bf077 | |
Calvin Rose | ac2082e9b3 | |
Techcable | dbac495bee | |
Calvin Rose | fe5ccb163e | |
Calvin Rose | 1aea5ee007 | |
Calvin Rose | 13cd9f8067 | |
bakpakin | 34496ecaf0 | |
bakpakin | c043b1d949 | |
bakpakin | 9a6d2a7b32 | |
bakpakin | f8a9efa8e4 | |
Calvin Rose | 5b2169e0d1 | |
Calvin Rose | 2c927ea768 | |
Calvin Rose | f4bbcdcbc8 | |
Calvin Rose | 79c375b1af | |
Calvin Rose | f443a3b3a1 | |
Calvin Rose | 684d2d63f4 | |
Calvin Rose | 1900d8f843 | |
Calvin Rose | 3c2af95d21 | |
Calvin Rose | b35414ea0f | |
Calvin Rose | fb5b056f7b | |
bakpakin | 7248c1dfdb | |
bakpakin | 4c7ea9e893 | |
bakpakin | c7801ce277 | |
Calvin Rose | f741a8e3ff | |
bakpakin | 6a92e8b609 | |
bakpakin | 9da91a8217 | |
bakpakin | 69853c8e5c | |
Autumn! | 1f41b6c138 | |
Calvin Rose | e001efa9fd | |
Calvin Rose | 435e64d4cf | |
Calvin Rose | f296c8f5fb | |
Calvin Rose | 8d0e6ed32f | |
Calvin Rose | b6a36afffe | |
Stephen Hassard | e422abc269 | |
Calvin Rose | 221d71d07b | |
Calvin Rose | 9f35f0837e | |
Josef Pospíšil | 515891b035 | |
Josef Pospíšil | 94a506876f | |
Calvin Rose | 9bde57854a | |
Calvin Rose | f456369941 | |
Calvin Rose | 8f0a1ffe5d | |
Calvin Rose | e4bafc621a | |
Calvin Rose | cfa39ab3b0 | |
Calvin Rose | 47e91bfd89 | |
Calvin Rose | eecc388ebd | |
Calvin Rose | 0a15a5ee56 | |
Calvin Rose | cfaae47cea | |
Calvin Rose | c1a0352592 | |
Calvin Rose | 965f45aa3f | |
Calvin Rose | 6ea27fe836 | |
Calvin Rose | 0dccc22b38 | |
Calvin Rose | cbe833962b | |
Calvin Rose | b5720f6f10 | |
Calvin Rose | 56b4e0b0ec | |
Calvin Rose | e316ccb1e0 | |
Calvin Rose | a6f93efd39 | |
Calvin Rose | 20511cf608 | |
Calvin Rose | 1a1dd39367 | |
Calvin Rose | 589981bdcb | |
Calvin Rose | 89546776b2 | |
Calvin Rose | f0d7b3cd12 | |
Calvin Rose | e37be627e0 | |
Calvin Rose | d803561582 | |
Calvin Rose | a1aab4008f | |
Calvin Rose | a1172529bf | |
Calvin Rose | 1d905bf07f | |
Calvin Rose | eed678a14b | |
Calvin Rose | b1bdffbc34 | |
Calvin Rose | cff718f37d | |
Calvin Rose | 40e9430278 | |
Calvin Rose | 62fc55fc74 | |
Calvin Rose | 80729353c8 | |
Calvin Rose | 105ba5e124 | |
Calvin Rose | ad1b50d1f5 | |
Calvin Rose | 1905437abe | |
Calvin Rose | 87fc339c45 | |
Calvin Rose | 3af7d61d3e | |
Calvin Rose | a45ef7a856 | |
Calvin Rose | 299998055d | |
Calvin Rose | c9586d39ed | |
Calvin Rose | 2e9f67f4e4 | |
Calvin Rose | e318170fea | |
Calvin Rose | 73c4289792 | |
Calvin Rose | ea45d7ee47 | |
Calvin Rose | 6d970725e7 | |
Calvin Rose | 458c2c6d88 | |
Calvin Rose | 0cc53a8964 | |
Calvin Rose | 0bc96304a9 | |
Calvin Rose | c75b088ff8 | |
Calvin Rose | 181f0341f5 | |
Calvin Rose | 33bb08d53b | |
Calvin Rose | 6d188f6e44 | |
Calvin Rose | c3648331f1 | |
Calvin Rose | a5b66029d3 | |
Calvin Rose | 49bfe80191 | |
Calvin Rose | a5def77bfe | |
Calvin Rose | 9ecb5b4791 | |
Calvin Rose | 1cc48a370a | |
Calvin Rose | f1ec8d1e11 | |
Calvin Rose | 55c34cd84f | |
masukomi | aca52d1e36 | |
Calvin Rose | 6f90df26a5 | |
Calvin Rose | 9d9cb378ff | |
Calvin Rose | f92aac14aa | |
Calvin Rose | 3f27d78ab5 | |
Calvin Rose | 282d1ba22f | |
Calvin Rose | 94c19575b1 | |
Calvin Rose | e3e485285b | |
Calvin Rose | 986e36720e | |
Calvin Rose | 74348ab6c2 | |
Calvin Rose | 8d1ad99f42 | |
Calvin Rose | e69bbff195 | |
Calvin Rose | c9f33bbde0 | |
Calvin Rose | 9c9f9d4fa6 | |
Calvin Rose | 2f64a6b0cb | |
Calvin Rose | dfa78ad3c6 | |
Calvin Rose | 677ae46f0c | |
Calvin Rose | 6ada2a458f | |
Calvin Rose | 8145f3b68d | |
Calvin Rose | 48289acee6 | |
Calvin Rose | e5a989c6f9 | |
Calvin Rose | 4c56704935 | |
naveen | 9cda44f443 | |
Calvin Rose | 431451bac2 | |
Calvin Rose | 395ca7feea | |
bakpakin | e0b7533c39 | |
bakpakin | 5b2a402930 | |
bakpakin | 85129a1873 | |
Calvin Rose | 487d333024 | |
Calvin Rose | fe7d35171f | |
Calvin Rose | b3aed13567 | |
Calvin Rose | a9d4d2bfa3 | |
rick2600 | 1ff521683f | |
Calvin Rose | 0395a03b6b | |
rick2600 | 7fda7709ff | |
Calvin Rose | 65a9200cff | |
John Gabriele | 473eec26c1 | |
Calvin Rose | 9fa945ad93 | |
Calvin Rose | a895219d2f | |
bakpakin | 427f7c362e | |
Calvin Rose | 73f5c41fae | |
Calvin Rose | b4ec168401 | |
Calvin Rose | 726d35c766 | |
Calvin Rose | 6db796e10c | |
Calvin Rose | c38d9134cd | |
John Gabriele | 471204b163 | |
Calvin Rose | 7f23bfa66d | |
John Gabriele | 9287b26042 | |
Calvin Rose | e22936fbf8 | |
Calvin Rose | 04ace9fc16 | |
Calvin Rose | 8466b333fb | |
John Gabriele | 96602612ba | |
John Gabriele | 690b98bff9 | |
John Gabriele | 8329131bfe | |
Calvin Rose | 9986aab326 | |
Calvin Rose | 0b105bc535 | |
Calvin Rose | 51ac9c9506 | |
Calvin Rose | 0310176696 | |
Calvin Rose | 84a7a2bc3e | |
Calvin Rose | 1e66a7e555 | |
John Gabriele | 2bffb9d682 | |
John Gabriele | 811125a760 | |
John Gabriele | 0dd91082a1 | |
John Gabriele | c80587868e | |
John Gabriele | 8c52dc86c7 | |
John Gabriele | be24592bc3 | |
Calvin Rose | 0d1a5c621d | |
Calvin Rose | 8a3eff3b65 | |
John Gabriele | b1050b884d | |
John Gabriele | 181d883a1d | |
Calvin Rose | e01b65fd3d | |
Calvin Rose | bbd74b5ae2 | |
rick2600 | d5a5c49357 | |
Calvin Rose | a964b164a6 | |
Calvin Rose | 1aac0489d7 | |
Calvin Rose | e474755887 | |
sogaiu | bf9a60f70d | |
Calvin Rose | a2ba0913d3 | |
Calvin Rose | f74df41fff | |
Calvin Rose | 2a950e4ce9 | |
Calvin Rose | f05e5f908e | |
Jason Pepas | 43139b43b1 | |
Calvin Rose | 5811b47aad | |
Techcable | 54e3db4d8c | |
Calvin Rose | 7491421c31 | |
Calvin Rose | 9d0da74347 | |
Jason Pepas | e9870b293f | |
Jason Pepas | ab910d060b | |
Calvin Rose | b60ef68ac6 | |
Jason Pepas | c9986936ed | |
Calvin Rose | d77be46644 | |
Calvin Rose | 3715d7a184 | |
Calvin Rose | 1c96c7163a | |
Calvin Rose | 9f733b25db | |
Calvin Rose | 1419a33b64 | |
Jason Pepas | f270739f9f | |
jgart | e51a391286 | |
Calvin Rose | c815185574 | |
Calvin Rose | 8045e29a52 | |
Ian Shehadeh | bbb3e16fd1 | |
Jona Ekenberg | 3cd1657387 | |
Calvin Rose | d7ea122cf7 | |
Ian Shehadeh | 6aea7c7f70 | |
Calvin Rose | 56ba1d9cd3 | |
Calvin Rose | 408b03ae0d | |
Calvin Rose | d94fd746af | |
John Gabriele | dbd1316d1e | |
Ian Shehadeh | 75845c0283 | |
Ian Shehadeh | 88db9751d7 | |
Calvin Rose | 6f645c4cb7 | |
Calvin Rose | 4e31d85349 | |
Calvin Rose | de542a81c0 | |
Calvin Rose | 461576e7a2 | |
Calvin Rose | 21bd62b1ce | |
paulsnar | 838cd1157c | |
Calvin Rose | 2f068b91d8 | |
Calvin Rose | aba87bf1bd | |
Calvin Rose | e64da8ede4 | |
Calvin Rose | a9f38dfce4 | |
Calvin Rose | a097537a03 | |
Calvin Rose | 66e0b53cf6 | |
paulsnar | 06f2e81dd5 | |
Calvin Rose | 40ae2e812f | |
Calvin Rose | 06f613e40b | |
Calvin Rose | 61c8c1e8d2 | |
Michael Camilleri | ee924ee310 | |
Michael Camilleri | fad0ce3ced | |
Michael Camilleri | d396180939 | |
Calvin Rose | 0d089abe67 | |
Michael Camilleri | ed5c1dfc3c | |
Calvin Rose | 6b949a7375 | |
Calvin Rose | 3028e2908f | |
Calvin Rose | 578803b01f | |
Calvin Rose | 46738825c0 | |
Calvin Rose | 56357699cb | |
Alberto González Palomo | fe8e718183 | |
Michael Camilleri | 1eb34989d4 | |
Michael Camilleri | 2f3b4c8bfb | |
Ian Shehadeh | 6412768000 | |
Ian Shehadeh | 82688b9a44 | |
Ian Shehadeh | 651e12cfe4 | |
Ian Shehadeh | 4118d581af | |
Ian Shehadeh | 62608bec03 | |
Ian Shehadeh | 71cffc973d | |
Ian Shehadeh | a8e49d084b | |
Ian Shehadeh | db631097b1 | |
Ian Shehadeh | 0d31674166 | |
Ian Shehadeh | cb5af974a4 | |
Calvin Rose | f2f421a0a2 | |
Michael Camilleri | 413c46e2ee | |
Calvin Rose | 3b412d51f0 | |
Ian Shehadeh | 4931e2aee2 | |
Calvin Rose | ffadf673cf | |
Calvin Rose | 5b5a7e5a24 | |
Calvin Rose | ab53208f47 | |
Calvin Rose | 7c407705e8 | |
Ian Shehadeh | 60378ff941 | |
Michael Camilleri | 30a0c77d19 | |
Calvin Rose | 07ec89276b | |
Calvin Rose | a37dc1af9d | |
Calvin Rose | 03458df140 | |
Calvin Rose | 164eb9659e | |
Calvin Rose | 99cfbaa63b | |
Ian Shehadeh | 8d8a6534e3 | |
Ian Shehadeh | 938c5013c9 | |
Michael Camilleri | ea9d5ec793 | |
Michael Camilleri | ec65f038a8 | |
Calvin Rose | 199ec36d40 | |
sogaiu | 1326ded048 | |
Michael Camilleri | 8347439644 | |
Calvin Rose | cddc2a8280 | |
Michael Camilleri | 97a8938407 | |
Michael Camilleri | 939d1dcae9 | |
Calvin Rose | 9d5cc5c11f | |
Calvin Rose | d998f24d26 | |
Calvin Rose | d543f8857b | |
Calvin Rose | c48a942d22 | |
Calvin Rose | e1602618c3 | |
Calvin Rose | 36be240623 | |
Josef Pospíšil | 04e499c97f | |
Josef Pospíšil | f586a8a9dc | |
Josef Pospíšil | 5112ed77d6 | |
Michael Camilleri | bf29a54272 | |
Calvin Rose | 6d9286a202 | |
Calvin Rose | 92fdd07ca3 | |
Calvin Rose | 1c937ad960 | |
Calvin Rose | f9891a5c04 | |
Calvin Rose | e8ad311d84 | |
Calvin Rose | 545c09e202 | |
Calvin Rose | 4dc281a05f | |
Calvin Rose | 3a0af8caad | |
Calvin Rose | 8ff2fecb26 | |
Calvin Rose | 1855c6aed5 | |
Calvin Rose | d4c6643311 | |
Calvin Rose | e8c738002b | |
Calvin Rose | 309c3aaeb8 | |
Calvin Rose | 1f8bcadb3b | |
Calvin Rose | 6f4af5fef8 | |
Calvin Rose | 868cdb9f8b | |
Calvin Rose | 2f76a429ef | |
Grazfather | a69799aa42 | |
Grazfather | 139bef2142 | |
Calvin Rose | 8ba142bcf4 | |
Calvin Rose | c49e4966f6 | |
Calvin Rose | 516fa4e49d | |
Michael Camilleri | 6bf9f89429 | |
Calvin Rose | a0ddfcb109 | |
Calvin Rose | 3df7921fdc | |
Calvin Rose | 6172a9ca2d | |
Calvin Rose | 4a40e57cf0 | |
Calvin Rose | cdedda4ca1 | |
Josef Pospíšil | e6babd84f7 | |
Calvin Rose | 868ec1a7e3 | |
Calvin Rose | e08394c870 | |
Calvin Rose | a99500aebf | |
Calvin Rose | aa5095c23b | |
Calvin Rose | 9e0f36e5a7 | |
Calvin Rose | d481d079ba | |
Calvin Rose | bc9ec7ac4a | |
Calvin Rose | 6f7e81067c | |
Calvin Rose | af946f398e | |
Calvin Rose | c7ca26e9c7 | |
Calvin Rose | ef7129f45d | |
Calvin Rose | a20bdd334a | |
Andrew Chambers | 2ef49a92cc | |
Calvin Rose | 75f56b68c6 | |
jgart | d34d319d89 | |
Calvin Rose | 6660c1da38 | |
Michael Camilleri | 4e263b8c39 | |
Calvin Rose | 3cb604df02 | |
Calvin Rose | af9dc7a69e | |
Calvin Rose | 1247e69c78 | |
Andrew Chambers | aab0e4315d | |
Andrew Chambers | 14f6517733 | |
Andrew Chambers | 5d75effb37 | |
Calvin Rose | ab4f18954b | |
Michael Camilleri | e1460c65e8 | |
Calvin Rose | 425a0fcf07 | |
Calvin Rose | 7205ee5e0a | |
Calvin Rose | 72c5db8910 | |
bakpakin | 3067f4be3a | |
Calvin Rose | 2aa1ccdd76 | |
Calvin Rose | 0284df503f | |
Calvin Rose | 2833a983d8 | |
Calvin Rose | 39c6be7cb7 | |
sogaiu | fdc94c1353 | |
Calvin Rose | 9cc4e48124 | |
Calvin Rose | 34c7f15d6d | |
Calvin Rose | 899a9b025e | |
Calvin Rose | deb4315383 | |
Calvin Rose | 9a06660fdb | |
Calvin Rose | 5c35d24e13 | |
Calvin Rose | 03f99752a7 | |
Calvin Rose | fd37567c18 | |
Calvin Rose | 6e38bf1578 | |
Calvin Rose | 8b2d278840 | |
Calvin Rose | 06aa0a124d | |
Calvin Rose | eb4595158d | |
Calvin Rose | 32103441f1 | |
Michael Camilleri | 7ed0aa6630 | |
Calvin Rose | f690229f31 | |
Michael Camilleri | f3bab72a86 | |
Michael Camilleri | 2bd63c2d27 | |
Calvin Rose | 545d9e85e9 | |
Calvin Rose | 21a4ab4ec7 | |
Calvin Rose | 66fbbeb5ec | |
Calvin Rose | 55879c7b6d | |
Calvin Rose | fab65d6c40 | |
Calvin Rose | 4d983e54b5 |
|
@ -1,4 +1,4 @@
|
|||
image: freebsd/12.x
|
||||
image: freebsd/14.x
|
||||
sources:
|
||||
- https://git.sr.ht/~bakpakin/janet
|
||||
packages:
|
||||
|
@ -9,3 +9,4 @@ tasks:
|
|||
gmake
|
||||
gmake test
|
||||
sudo gmake install
|
||||
sudo gmake uninstall
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
image: openbsd/latest
|
||||
image: openbsd/7.4
|
||||
sources:
|
||||
- https://git.sr.ht/~bakpakin/janet
|
||||
packages:
|
||||
|
@ -11,9 +11,10 @@ tasks:
|
|||
gmake test
|
||||
doas gmake install
|
||||
gmake test-install
|
||||
doas gmake uninstall
|
||||
- meson_min: |
|
||||
cd janet
|
||||
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dtyped_array=false -Dreduced_os=true
|
||||
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
|
||||
cd build_meson_min
|
||||
ninja
|
||||
- meson_prf: |
|
||||
|
@ -29,4 +30,3 @@ tasks:
|
|||
ninja
|
||||
ninja test
|
||||
doas ninja install
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
*.janet linguist-language=Clojure
|
||||
|
||||
*.janet linguist-language=Janet
|
||||
*.janet text eol=lf
|
||||
*.c text eol=lf
|
||||
*.h text eol=lf
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/sh
|
||||
set -eux
|
||||
|
||||
COSMO_DIR="/sc/cosmocc"
|
||||
|
||||
# build x86_64
|
||||
X86_64_CC="/sc/cosmocc/bin/x86_64-unknown-cosmo-cc"
|
||||
X86_64_AR="/sc/cosmocc/bin/x86_64-unknown-cosmo-ar"
|
||||
mkdir -p /sc/cosmocc/x86_64
|
||||
make -j CC="$X86_64_CC" AR="$X86_64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
|
||||
cp build/janet /sc/cosmocc/x86_64/janet
|
||||
make clean
|
||||
|
||||
# build aarch64
|
||||
AARCH64_CC="/sc/cosmocc/bin/aarch64-unknown-cosmo-cc"
|
||||
AARCH64_AR="/sc/cosmocc/bin/aarch64-unknown-cosmo-ar"
|
||||
mkdir -p /sc/cosmocc/aarch64
|
||||
make -j CC="$AARCH64_CC" AR="$AARCH64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
|
||||
cp build/janet /sc/cosmocc/aarch64/janet
|
||||
make clean
|
||||
|
||||
# fat binary
|
||||
apefat () {
|
||||
OUTPUT="$1"
|
||||
OLDNAME_X86_64="$(basename -- "$2")"
|
||||
OLDNAME_AARCH64="$(basename -- "$3")"
|
||||
TARG_FOLD="$(dirname "$OUTPUT")"
|
||||
"$COSMO_DIR/bin/apelink" -l "$COSMO_DIR/bin/ape-x86_64.elf" \
|
||||
-l "$COSMO_DIR/bin/ape-aarch64.elf" \
|
||||
-M "$COSMO_DIR/bin/ape-m1.c" \
|
||||
-o "$OUTPUT" \
|
||||
"$2" \
|
||||
"$3"
|
||||
cp "$2" "$TARG_FOLD/$OLDNAME_X86_64.x86_64"
|
||||
cp "$3" "$TARG_FOLD/$OLDNAME_AARCH64.aarch64"
|
||||
}
|
||||
|
||||
apefat /sc/cosmocc/janet.com /sc/cosmocc/x86_64/janet /sc/cosmocc/aarch64/janet
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
sudo apt update
|
||||
sudo apt-get install -y ca-certificates libssl-dev\
|
||||
qemu qemu-utils qemu-user-static\
|
||||
texinfo groff\
|
||||
cmake ninja-build bison zip\
|
||||
pkg-config build-essential autoconf re2c
|
||||
|
||||
# download cosmocc
|
||||
cd /sc
|
||||
wget https://github.com/jart/cosmopolitan/releases/download/3.3.3/cosmocc-3.3.3.zip
|
||||
mkdir -p cosmocc
|
||||
cd cosmocc
|
||||
unzip ../cosmocc-3.3.3.zip
|
||||
|
||||
# register
|
||||
cd /sc/cosmocc
|
||||
sudo cp ./bin/ape-x86_64.elf /usr/bin/ape
|
||||
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
|
|
@ -0,0 +1,41 @@
|
|||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: "2 7 * * 4"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ cpp ]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
|
@ -5,9 +5,14 @@ on:
|
|||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
release:
|
||||
permissions:
|
||||
contents: write # for softprops/action-gh-release to create GitHub release
|
||||
name: Build release binaries
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
|
@ -33,3 +38,52 @@ jobs:
|
|||
build/janet.h
|
||||
build/c/janet.c
|
||||
build/c/shell.c
|
||||
|
||||
release-windows:
|
||||
permissions:
|
||||
contents: write # for softprops/action-gh-release to create GitHub release
|
||||
name: Build release binaries for windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Setup MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Build the project
|
||||
shell: cmd
|
||||
run: build_win all
|
||||
- name: Draft the release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
./dist/*.zip
|
||||
./*.zip
|
||||
./*.msi
|
||||
|
||||
release-cosmo:
|
||||
permissions:
|
||||
contents: write # for softprops/action-gh-release to create GitHub release
|
||||
name: Build release binaries for Cosmo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: create build folder
|
||||
run: |
|
||||
sudo mkdir -p /sc
|
||||
sudo chmod -R 0777 /sc
|
||||
- name: setup Cosmopolitan Libc
|
||||
run: bash ./.github/cosmo/setup
|
||||
- name: Set the version
|
||||
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
|
||||
- name: Set the platform
|
||||
run: echo "platform=cosmo" >> $GITHUB_ENV
|
||||
- name: build Janet APE binary
|
||||
run: bash ./.github/cosmo/build
|
||||
- name: push binary to github
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
/sc/cosmocc/janet.com
|
||||
|
|
|
@ -2,6 +2,9 @@ name: Test
|
|||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
test-posix:
|
||||
|
@ -32,3 +35,57 @@ jobs:
|
|||
- name: Test the project
|
||||
shell: cmd
|
||||
run: build_win test
|
||||
|
||||
test-mingw:
|
||||
name: Build on Windows with Mingw (no test yet)
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Setup Mingw
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: UCRT64
|
||||
update: true
|
||||
install: >-
|
||||
base-devel
|
||||
git
|
||||
gcc
|
||||
- name: Build the project
|
||||
shell: cmd
|
||||
run: make -j4 CC=gcc JANET_NO_AMALG=1
|
||||
|
||||
test-mingw-linux:
|
||||
name: Build and test with Mingw on Linux + Wine
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Setup Mingw and wine
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install libstdc++6:i386 libgcc-s1:i386
|
||||
sudo apt-get install gcc-mingw-w64-x86-64-win32 wine wine32 wine64
|
||||
- name: Compile the project
|
||||
run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine
|
||||
- name: Test the project
|
||||
run: make test UNAME=MINGW RUN=wine VERBOSE=1
|
||||
|
||||
test-arm-linux:
|
||||
name: Build and test ARM32 cross compilation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Setup qemu and cross compiler
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-arm-linux-gnueabi qemu-user
|
||||
- name: Compile the project
|
||||
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc
|
||||
- name: Test the project
|
||||
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test VERBOSE=1
|
||||
|
|
|
@ -34,7 +34,11 @@ local
|
|||
|
||||
# Common test files I use.
|
||||
temp.janet
|
||||
temp.c
|
||||
temp*janet
|
||||
temp*.c
|
||||
scratch.janet
|
||||
scratch.c
|
||||
|
||||
# Emscripten
|
||||
*.bc
|
||||
|
@ -44,6 +48,7 @@ janet.wasm
|
|||
# Generated files
|
||||
*.gen.h
|
||||
*.gen.c
|
||||
*.tmp
|
||||
|
||||
# Generate test files
|
||||
*.out
|
||||
|
@ -56,6 +61,7 @@ xxd.exe
|
|||
# VSCode
|
||||
.vs
|
||||
.clangd
|
||||
.cache
|
||||
|
||||
# Swap files
|
||||
*.swp
|
||||
|
@ -67,10 +73,13 @@ tags
|
|||
vgcore.*
|
||||
*.out.*
|
||||
|
||||
# Wix artifacts
|
||||
# WiX artifacts
|
||||
*.msi
|
||||
*.wixpdb
|
||||
|
||||
# Makefile config
|
||||
/config.mk
|
||||
|
||||
# Created by https://www.gitignore.io/api/c
|
||||
|
||||
### C ###
|
||||
|
@ -118,6 +127,9 @@ vgcore.*
|
|||
*.idb
|
||||
*.pdb
|
||||
|
||||
# GGov
|
||||
*.gcov
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
|
@ -126,6 +138,9 @@ Module.symvers
|
|||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
# Coverage files
|
||||
*.cov
|
||||
|
||||
# End of https://www.gitignore.io/api/c
|
||||
|
||||
# Created by https://www.gitignore.io/api/cmake
|
||||
|
|
244
CHANGELOG.md
244
CHANGELOG.md
|
@ -1,6 +1,250 @@
|
|||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## Unreleased - ???
|
||||
- Add extra optional `env` argument to `eval` and `eval-string`.
|
||||
- Allow naming function literals with a keyword. This allows better stacktraces for macros without
|
||||
accidentally adding new bindings.
|
||||
- Add macros `ev/with-lock`, `ev/with-rlock`, and `ev/with-wlock` for using mutexes and rwlocks.
|
||||
- Add `with-env`
|
||||
- Add *module-make-env* dynamic binding
|
||||
- Add buffer/format-at
|
||||
- Add long form command line options for readable CLI usage
|
||||
- Fix bug with `net/accept-loop` that would sometimes miss connections.
|
||||
|
||||
## 1.34.0 - 2024-03-22
|
||||
- Add a new (split) PEG special by @ianthehenry
|
||||
- Add buffer/push-* sized int and float by @pnelson
|
||||
- Documentation improvements: @amano-kenji, @llmII, @MaxGyver83, @pepe, @sogaiu.
|
||||
- Expose _exit to skip certain cleanup with os/exit.
|
||||
- Swap set / body order for each by @sogaiu.
|
||||
- Abort on assert failure instead of exit.
|
||||
- Fix: os/proc-wait by @llmII.
|
||||
- Fix macex1 to keep syntax location for all tuples.
|
||||
- Restore if-let tail calls.
|
||||
- Don't try and resume fibers that can't be resumed.
|
||||
- Register stream on unmarshal.
|
||||
- Fix asm roundtrip issue.
|
||||
|
||||
## 1.33.0 - 2024-01-07
|
||||
- Add more + and * keywords to default-peg-grammar by @sogaiu.
|
||||
- Use libc strlen in janet_buffer_push_cstring by @williewillus.
|
||||
- Be a bit safer with reference counting.
|
||||
- Add support for atomic loads in Janet's atomic abstraction.
|
||||
- Fix poll event loop CPU usage issue.
|
||||
- Add ipv6, shared, and cryptorand options to meson.
|
||||
- Add more ipv6 feature detection.
|
||||
- Fix loop for forever loop.
|
||||
- Cleaned up unused NetStateConnect, fixed janet_async_end() ev refcount by @zevv.
|
||||
- Fix warnings w/ MSVC and format.
|
||||
- Fix marshal_one_env w/ JANET_MARSHAL_UNSAFE.
|
||||
- Fix `(default)`.
|
||||
- Fix cannot marshal fiber with c stackframe, in a dynamic way that is fairly conservative.
|
||||
- Fix typo for SIGALARM in os/proc-kill.
|
||||
- Prevent bytecode optimization from remove mk* instructions.
|
||||
- Fix arity typo in peg.c by @pepe.
|
||||
- Update Makefile for MinGW.
|
||||
- Fix canceling waiting fiber.
|
||||
- Add a new (sub) PEG special by @ianthehenry.
|
||||
- Fix if net/server's handler has incorrect arity.
|
||||
- Fix macex raising on ().
|
||||
|
||||
## 1.32.1 - 2023-10-15
|
||||
- Fix return value from C function `janet_dobytes` when called on Janet functions that yield to event loop.
|
||||
- Change C API for event loop interaction - get rid of JanetListener and instead use `janet_async_start` and `janet_async_end`.
|
||||
- Rework event loop to make fewer system calls on kqueue and epoll.
|
||||
- Expose atomic refcount abstraction in janet.h
|
||||
- Add `array/weak` for weak references in arrays
|
||||
- Add support for weak tables via `table/weak`, `table/weak-keys`, and `table/weak-values`.
|
||||
- Fix compiler bug with using the result of `(break x)` expression in some contexts.
|
||||
- Rework internal event loop code to be better behaved on Windows
|
||||
- Update meson build to work better on windows
|
||||
|
||||
## 1.31.0 - 2023-09-17
|
||||
- Report line and column when using `janet_dobytes`
|
||||
- Add `:unless` loop modifier
|
||||
- Allow calling `reverse` on generators.
|
||||
- Improve performance of a number of core functions including `partition`, `mean`, `keys`, `values`, `pairs`, `interleave`.
|
||||
- Add `lengthable?`
|
||||
- Add `os/sigaction`
|
||||
- Change `every?` and `any?` to behave like the functional versions of the `and` and `or` macros.
|
||||
- Fix bug with garbage collecting threaded abstract types.
|
||||
- Add `:signal` to the `sandbox` function to allow intercepting signals.
|
||||
|
||||
## 1.30.0 - 2023-08-05
|
||||
- Change indexing of `array/remove` to start from -1 at the end instead of -2.
|
||||
- Add new string escape sequences `\\a`, `\\b`, `\\?`, and `\\'`.
|
||||
- Fix bug with marshalling channels
|
||||
- Add `div` for floored division
|
||||
- Make `div` and `mod` variadic
|
||||
- Support `bnot` for integer types.
|
||||
- Define `(mod x 0)` as `x`
|
||||
- Add `ffi/pointer-cfunction` to convert pointers to cfunctions
|
||||
|
||||
## 1.29.1 - 2023-06-19
|
||||
- Add support for passing booleans to PEGs for "always" and "never" matching.
|
||||
- Allow dictionary types for `take` and `drop`
|
||||
- Fix bug with closing channels while other fibers were waiting on them - `ev/take`, `ev/give`, and `ev/select` will now return the correct (documented) value when another fiber closes the channel.
|
||||
- Add `ffi/calling-conventions` to show all available calling conventions for FFI.
|
||||
- Add `net/setsockopt`
|
||||
- Add `signal` argument to `os/proc-kill` to send signals besides `SIGKILL` on Posix.
|
||||
- Add `source` argument to `os/clock` to get different time sources.
|
||||
- Various combinator functions now are variadic like `map`
|
||||
- Add `file/lines` to iterate over lines in a file lazily.
|
||||
- Reorganize test suite to be sorted by module rather than pseudo-randomly.
|
||||
- Add `*task-id*`
|
||||
- Add `env` argument to `fiber/new`.
|
||||
- Add `JANET_NO_AMALG` flag to Makefile to properly incremental builds
|
||||
- Optimize bytecode compiler to generate fewer instructions and improve loops.
|
||||
- Fix bug with `ev/gather` and hung fibers.
|
||||
- Add `os/isatty`
|
||||
- Add `has-key?` and `has-value?`
|
||||
- Make imperative arithmetic macros variadic
|
||||
- `ev/connect` now yields to the event loop instead of blocking while waiting for an ACK.
|
||||
|
||||
## 1.28.0 - 2023-05-13
|
||||
- Various bug fixes
|
||||
- Make nested short-fn's behave a bit more predictably (it is still not recommended to nest short-fns).
|
||||
- Add `os/strftime` for date formatting.
|
||||
- Fix `ev/select` on threaded channels sometimes live-locking.
|
||||
- Support the `NO_COLOR` environment variable to turn off VT100 color codes in repl (and in scripts).
|
||||
See http://no-color.org/
|
||||
- Disallow using `(splice x)` in contexts where it doesn't make sense rather than silently coercing to `x`.
|
||||
Instead, raise a compiler error.
|
||||
- Change the names of `:user8` and `:user9` sigals to `:interrupt` and `:await`
|
||||
- Change the names of `:user8` and `:user9` fiber statuses to `:interrupted` and `:suspended`.
|
||||
- Add `ev/all-tasks` to see all currently suspended fibers.
|
||||
- Add `keep-syntax` and `keep-syntax!` functions to make writing macros easier.
|
||||
|
||||
## 1.27.0 - 2023-03-05
|
||||
- Change semantics around bracket tuples to no longer be equal to regular tuples.
|
||||
- Add `index` argument to `ffi/write` for symmetry with `ffi/read`.
|
||||
- Add `buffer/push-at`
|
||||
- Add `ffi/pointer-buffer` to convert pointers to buffers the cannot be reallocated. This
|
||||
allows easier manipulation of FFI memory, memory mapped files, and buffer memory shared between threads.
|
||||
- Calling `ev/cancel` on a fiber waiting on `ev/gather` will correctly
|
||||
cancel the child fibers.
|
||||
- Add `(sandbox ...)` function to core for permission based security. Also add `janet_sandbox` to C API.
|
||||
The sandbox allows limiting access to the file system, network, ffi, and OS resources at runtime.
|
||||
- Add `(.locals)` function to debugger to see currently bound local symbols.
|
||||
- Track symbol -> slot mapping so debugger can get symbolic information. This exposes local bindings
|
||||
in `debug/stack` and `disasm`.
|
||||
- Add `os/compiler` to detect what host compiler was used to compile the interpreter
|
||||
- Add support for mingw and cygwin builds (mingw support also added in jpm).
|
||||
|
||||
## 1.26.0 - 2023-01-07
|
||||
- Add `ffi/malloc` and `ffi/free`. Useful as tools of last resort.
|
||||
- Add `ffi/jitfn` to allow calling function pointers generated at runtime from machine code.
|
||||
Bring your own assembler, though.
|
||||
- Channels can now be marshalled. Pending state is not saved, only items in the channel.
|
||||
- Use the new `.length` function pointer on abstract types for lengths. Adding
|
||||
a `length` method will still work as well.
|
||||
- Support byte views on abstract types with the `.bytes` function pointer.
|
||||
- Add the `u` format specifier to printf family functions.
|
||||
- Allow printing 64 integer types in `printf` and `string/format` family functions.
|
||||
- Allow importing modules from custom directories more easily with the `@` prefix
|
||||
to module paths. For example, if there is a dynamic binding :custom-modules that
|
||||
is a file system path to a directory of modules, import from that directory with
|
||||
`(import @custom-modules/mymod)`.
|
||||
- Fix error message bug in FFI library.
|
||||
|
||||
## 1.25.1 - 2022-10-29
|
||||
- Add `memcmp` function to core library.
|
||||
- Fix bug in `os/open` with `:rw` permissions not correct on Linux.
|
||||
- Support config.mk for more easily configuring the Makefile.
|
||||
|
||||
## 1.25.0 - 2022-10-10
|
||||
- Windows FFI fixes.
|
||||
- Fix PEG `if-not` combinator with captures in the condition
|
||||
- Fix bug with `os/date` with nil first argument
|
||||
- Fix bug with `net/accept` on Linux that could leak file descriptors to subprocesses
|
||||
- Reduce number of hash collisions from pointer hashing
|
||||
- Add optional parameter to `marshal` to skip cycle checking code
|
||||
|
||||
## 1.24.1 - 2022-08-24
|
||||
- Fix FFI bug on Linux/Posix
|
||||
- Improve parse error messages for bad delimiters.
|
||||
- Add optional `name` parameter to the `short-fn` macro.
|
||||
|
||||
## 1.24.0 - 2022-08-14
|
||||
- Add FFI support to 64-bit windows compiled with MSVC
|
||||
- Don't process shared object names passed to dlopen.
|
||||
- Add better support for windows console in the default shell.c for auto-completion and
|
||||
other shell-like input features.
|
||||
- Improve default error message from `assert`.
|
||||
- Add the `tabseq` macro for simpler table comprehensions.
|
||||
- Allow setting `(dyn :task-id)` in fibers to improve context in supervisor messages. Prior to
|
||||
this change, supervisor messages over threaded channels would be from ambiguous threads/fibers.
|
||||
|
||||
## 1.23.0 - 2022-06-20
|
||||
- Add experimental `ffi/` module for interfacing with dynamic libraries and raw function pointers. Only available
|
||||
on 64 bit linux, mac, and bsd systems.
|
||||
- Allow using `&named` in function prototypes for named arguments. This is a more ergonomic
|
||||
variant of `&keys` that isn't as redundant, more self documenting, and allows extension to
|
||||
things like default arguments.
|
||||
- Add `delay` macro for lazy evaluate-and-save thunks.
|
||||
- Remove pthread.h from janet.h for easier includes.
|
||||
- Add `debugger` - an easy to use debugger function that just takes a fiber.
|
||||
- `dofile` will now start a debugger on errors if the environment it is passed has `:debug` set.
|
||||
- Add `debugger-on-status` function, which can be passed to `run-context` to start a debugger on
|
||||
abnormal fiber signals.
|
||||
- Allow running scripts with the `-d` flag to use the built-in debugger on errors and breakpoints.
|
||||
- Add mutexes (locks) and reader-writer locks to ev module for thread coordination.
|
||||
- Add `parse-all` as a generalization of the `parse` function.
|
||||
- Add `os/cpu-count` to get the number of available processors on a machine
|
||||
|
||||
## 1.22.0 - 2022-05-09
|
||||
- Prohibit negative size argument to `table/new`.
|
||||
- Add `module/value`.
|
||||
- Remove `file/popen`. Use `os/spawn` with the `:pipe` options instead.
|
||||
- Fix bug in peg `thru` and `to` combinators.
|
||||
- Fix printing issue in `doc` macro.
|
||||
- Numerous updates to function docstrings
|
||||
- Add `defdyn` aliases for various dynamic bindings used in core.
|
||||
- Install `janet.h` symlink to make Janet native libraries and applications
|
||||
easier to build without `jpm`.
|
||||
|
||||
## 1.21.2 - 2022-04-01
|
||||
- C functions `janet_dobytes` and `janet_dostring` will now enter the event loop if it is enabled.
|
||||
- Fix hashing regression - hash of negative 0 must be the same as positive 0 since they are equal.
|
||||
- The `flycheck` function no longer pollutes the module/cache
|
||||
- Fix quasiquote bug in compiler
|
||||
- Disallow use of `cancel` and `resume` on fibers scheduled or created with `ev/go`, as well as the root
|
||||
fiber.
|
||||
|
||||
## 1.20.0 - 2022-1-27
|
||||
- Add `:missing-symbol` hook to `compile` that will act as a catch-all macro for undefined symbols.
|
||||
- Add `:redef` dynamic binding that will allow users to redefine top-level bindings with late binding. This
|
||||
is intended for development use.
|
||||
- Fix a bug with reading from a stream returned by `os/open` on Windows and Linux.
|
||||
- Add `:ppc64` as a detectable OS type.
|
||||
- Add `& more` support for destructuring in the match macro.
|
||||
- Add `& more` support for destructuring in all binding forms (`def`).
|
||||
|
||||
## 1.19.2 - 2021-12-06
|
||||
- Fix bug with missing status lines in some stack traces.
|
||||
- Update hash function to have better statistical properties.
|
||||
|
||||
## 1.19.1 - 2021-12-04
|
||||
- Add an optional `prefix` parameter to `debug/stacktrace` to allow printing prettier error messages.
|
||||
- Remove appveyor for CI pipeline
|
||||
- Fixed a bug that prevented sending threaded abstracts over threaded channels.
|
||||
- Fix bug in the `map` function with arity at least 3.
|
||||
|
||||
## 1.19.0 - 2021-11-27
|
||||
- Add `math/log-gamma` to replace `math/gamma`, and change `math/gamma` to be the expected gamma function.
|
||||
- Fix leaking file-descriptors in os/spawn and os/execute.
|
||||
- Ctrl-C will now raise SIGINT.
|
||||
- Allow quoted literals in the `match` macro to behave as expected in patterns.
|
||||
- Fix windows net related bug for TCP servers.
|
||||
- Allow evaluating ev streams with dofile.
|
||||
- Fix `ev` related bug with operations on already closed file descriptors.
|
||||
- Add struct and table agnostic `getproto` function.
|
||||
- Add a number of functions related to structs.
|
||||
- Add prototypes to structs. Structs can now inherit from other structs, just like tables.
|
||||
- Create a struct with a prototype with `struct/with-proto`.
|
||||
- Deadlocked channels will no longer exit early - instead they will hang, which is more intuitive.
|
||||
|
||||
## 1.18.1 - 2021-10-16
|
||||
- Fix some documentation typos
|
||||
- Fix - Set pipes passed to subprocess to blocking mode.
|
||||
|
|
|
@ -43,7 +43,7 @@ For changes to the VM and Core code, you will probably need to know C. Janet is
|
|||
a subset of C99 that works with Microsoft Visual C++. This means most of C99 but with the following
|
||||
omissions.
|
||||
|
||||
* No `restrict`
|
||||
* No `restrict`
|
||||
* Certain functions in the standard library are not always available
|
||||
|
||||
In practice, this means programming for both MSVC on one hand and everything else on the other.
|
||||
|
@ -64,6 +64,23 @@ ensure a consistent code style for C.
|
|||
All janet code in the project should be formatted similar to the code in core.janet.
|
||||
The auto formatting from janet.vim will work well.
|
||||
|
||||
## Typo Fixing and One-Line changes
|
||||
|
||||
Typo fixes are welcome, as are simple one line fixes. Do not open many separate pull requests for each
|
||||
individual typo fix. This is incredibly annoying to deal with as someone needs to review each PR, run
|
||||
CI, and merge. Instead, accumulate batches of typo fixes into a single PR. If there are objections to
|
||||
specific changes, these can be addressed in the review process before the final merge, if the changes
|
||||
are accepted.
|
||||
|
||||
Similarly, low effort and bad faith changes are annoying to developers and such issues may be closed
|
||||
immediately without response.
|
||||
|
||||
## Contributions from Automated Tools
|
||||
|
||||
People making changes found or generated by automated tools MUST note this when opening an issue
|
||||
or creating a pull request. This can help give context to developers if the change/issue is
|
||||
confusing or nonsensical.
|
||||
|
||||
## Suggesting Changes
|
||||
|
||||
To suggest changes, open an issue on GitHub. Check GitHub for other issues
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2021 Calvin Rose and contributors
|
||||
Copyright (c) 2023 Calvin Rose and contributors
|
||||
|
||||
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
|
||||
|
|
136
Makefile
136
Makefile
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2021 Calvin Rose
|
||||
# Copyright (c) 2023 Calvin Rose
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
|
@ -21,65 +21,98 @@
|
|||
################################
|
||||
##### Set global variables #####
|
||||
################################
|
||||
|
||||
sinclude config.mk
|
||||
PREFIX?=/usr/local
|
||||
|
||||
JANETCONF_HEADER?=src/conf/janetconf.h
|
||||
INCLUDEDIR?=$(PREFIX)/include
|
||||
BINDIR?=$(PREFIX)/bin
|
||||
LIBDIR?=$(PREFIX)/lib
|
||||
JANET_BUILD?="\"$(shell git log --pretty=format:'%h' -n 1 2> /dev/null || echo local)\""
|
||||
CLIBS=-lm -lpthread
|
||||
JANET_TARGET=build/janet
|
||||
JANET_BOOT=build/janet_boot
|
||||
JANET_IMPORT_LIB=build/janet.lib
|
||||
JANET_LIBRARY_IMPORT_LIB=build/libjanet.lib
|
||||
JANET_LIBRARY=build/libjanet.so
|
||||
JANET_STATIC_LIBRARY=build/libjanet.a
|
||||
JANET_PATH?=$(LIBDIR)/janet
|
||||
JANET_MANPATH?=$(PREFIX)/share/man/man1/
|
||||
JANET_PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig
|
||||
JANET_DIST_DIR?=janet-dist
|
||||
JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)'
|
||||
JANET_TARGET_OBJECTS=build/janet.o build/shell.o
|
||||
JPM_TAG?=master
|
||||
HAS_SHARED?=1
|
||||
DEBUGGER=gdb
|
||||
SONAME_SETTER=-Wl,-soname,
|
||||
|
||||
# For cross compilation
|
||||
HOSTCC?=$(CC)
|
||||
HOSTAR?=$(AR)
|
||||
CFLAGS?=-O2
|
||||
# Symbols are (optionally) removed later, keep -g as default!
|
||||
CFLAGS?=-O2 -g
|
||||
LDFLAGS?=-rdynamic
|
||||
LIBJANET_LDFLAGS?=$(LD_FLAGS)
|
||||
RUN:=$(RUN)
|
||||
|
||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
|
||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 -g $(COMMON_CFLAGS)
|
||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 $(COMMON_CFLAGS) -g
|
||||
BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS)
|
||||
|
||||
# Disable amalgamated build
|
||||
ifeq ($(JANET_NO_AMALG), 1)
|
||||
JANET_TARGET_OBJECTS+=$(patsubst src/%.c,build/%.bin.o,$(JANET_CORE_SOURCES))
|
||||
JANET_BOOT_FLAGS+=image-only
|
||||
endif
|
||||
|
||||
# For installation
|
||||
LDCONFIG:=ldconfig "$(LIBDIR)"
|
||||
|
||||
# Check OS
|
||||
UNAME:=$(shell uname -s)
|
||||
UNAME?=$(shell uname -s)
|
||||
ifeq ($(UNAME), Darwin)
|
||||
CLIBS:=$(CLIBS) -ldl
|
||||
SONAME_SETTER:=-Wl,-install_name,
|
||||
JANET_LIBRARY=build/libjanet.dylib
|
||||
LDCONFIG:=true
|
||||
else ifeq ($(UNAME), Linux)
|
||||
CLIBS:=$(CLIBS) -lrt -ldl
|
||||
endif
|
||||
|
||||
# For other unix likes, add flags here!
|
||||
ifeq ($(UNAME), Haiku)
|
||||
LDCONFIG:=true
|
||||
LDFLAGS=-Wl,--export-dynamic
|
||||
endif
|
||||
# For Android (termux)
|
||||
ifeq ($(UNAME), Linux) # uname on Darwin doesn't recognise -o
|
||||
ifeq ($(shell uname -o), Android)
|
||||
CLIBS:=$(CLIBS) -landroid-spawn
|
||||
endif
|
||||
endif
|
||||
|
||||
$(shell mkdir -p build/core build/c build/boot)
|
||||
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
|
||||
# Mingw
|
||||
ifeq ($(findstring MINGW,$(UNAME)), MINGW)
|
||||
CLIBS:=-lws2_32 -lpsapi -lwsock32
|
||||
LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB)
|
||||
LIBJANET_LDFLAGS:=-Wl,--out-implib,$(JANET_LIBRARY_IMPORT_LIB)
|
||||
JANET_TARGET:=$(JANET_TARGET).exe
|
||||
JANET_BOOT:=$(JANET_BOOT).exe
|
||||
endif
|
||||
|
||||
|
||||
$(shell mkdir -p build/core build/c build/boot build/mainclient)
|
||||
all: $(JANET_TARGET) $(JANET_STATIC_LIBRARY) build/janet.h
|
||||
ifeq ($(HAS_SHARED), 1)
|
||||
all: $(JANET_LIBRARY)
|
||||
endif
|
||||
|
||||
######################
|
||||
##### Name Files #####
|
||||
######################
|
||||
|
||||
JANET_HEADERS=src/include/janet.h src/conf/janetconf.h
|
||||
JANET_HEADERS=src/include/janet.h $(JANETCONF_HEADER)
|
||||
|
||||
JANET_LOCAL_HEADERS=src/core/features.h \
|
||||
src/core/util.h \
|
||||
|
@ -104,6 +137,7 @@ JANET_CORE_SOURCES=src/core/abstract.c \
|
|||
src/core/debug.c \
|
||||
src/core/emit.c \
|
||||
src/core/ev.c \
|
||||
src/core/ffi.c \
|
||||
src/core/fiber.c \
|
||||
src/core/gc.c \
|
||||
src/core/inttypes.c \
|
||||
|
@ -150,42 +184,53 @@ $(JANET_BOOT_OBJECTS): $(JANET_BOOT_HEADERS)
|
|||
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
|
||||
$(CC) $(BOOT_CFLAGS) -o $@ -c $<
|
||||
|
||||
build/janet_boot: $(JANET_BOOT_OBJECTS)
|
||||
$(JANET_BOOT): $(JANET_BOOT_OBJECTS)
|
||||
$(CC) $(BOOT_CFLAGS) -o $@ $(JANET_BOOT_OBJECTS) $(CLIBS)
|
||||
|
||||
# Now the reason we bootstrap in the first place
|
||||
build/c/janet.c: build/janet_boot src/boot/boot.janet
|
||||
build/janet_boot . JANET_PATH '$(JANET_PATH)' > $@
|
||||
build/c/janet.c: $(JANET_BOOT) src/boot/boot.janet
|
||||
$(RUN) $(JANET_BOOT) $(JANET_BOOT_FLAGS) > $@
|
||||
cksum $@
|
||||
|
||||
##################
|
||||
##### Quicky #####
|
||||
##################
|
||||
|
||||
build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
|
||||
$(HOSTCC) $(BUILD_CFLAGS) -o $@ -c $<
|
||||
|
||||
########################
|
||||
##### Amalgamation #####
|
||||
########################
|
||||
|
||||
SONAME=libjanet.so.1.18
|
||||
ifeq ($(UNAME), Darwin)
|
||||
SONAME=libjanet.1.34.dylib
|
||||
else
|
||||
SONAME=libjanet.so.1.34
|
||||
endif
|
||||
|
||||
build/c/shell.c: src/mainclient/shell.c
|
||||
cp $< $@
|
||||
|
||||
build/janet.h: $(JANET_TARGET) src/include/janet.h src/conf/janetconf.h
|
||||
./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h src/conf/janetconf.h $@
|
||||
build/janet.h: $(JANET_TARGET) src/include/janet.h $(JANETCONF_HEADER)
|
||||
$(RUN) ./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h $(JANETCONF_HEADER) $@
|
||||
|
||||
build/janetconf.h: src/conf/janetconf.h
|
||||
build/janetconf.h: $(JANETCONF_HEADER)
|
||||
cp $< $@
|
||||
|
||||
build/janet.o: build/c/janet.c src/conf/janetconf.h src/include/janet.h
|
||||
build/janet.o: build/c/janet.c $(JANETCONF_HEADER) src/include/janet.h
|
||||
$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
|
||||
|
||||
build/shell.o: build/c/shell.c src/conf/janetconf.h src/include/janet.h
|
||||
build/shell.o: build/c/shell.c $(JANETCONF_HEADER) src/include/janet.h
|
||||
$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
|
||||
|
||||
$(JANET_TARGET): build/janet.o build/shell.o
|
||||
$(JANET_TARGET): $(JANET_TARGET_OBJECTS)
|
||||
$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS)
|
||||
|
||||
$(JANET_LIBRARY): build/janet.o build/shell.o
|
||||
$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
|
||||
$(JANET_LIBRARY): $(JANET_TARGET_OBJECTS)
|
||||
$(HOSTCC) $(LIBJANET_LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
|
||||
|
||||
$(JANET_STATIC_LIBRARY): build/janet.o build/shell.o
|
||||
$(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
|
||||
$(HOSTAR) rcs $@ $^
|
||||
|
||||
###################
|
||||
|
@ -197,19 +242,19 @@ $(JANET_STATIC_LIBRARY): build/janet.o build/shell.o
|
|||
TEST_SCRIPTS=$(wildcard test/suite*.janet)
|
||||
|
||||
repl: $(JANET_TARGET)
|
||||
./$(JANET_TARGET)
|
||||
$(RUN) ./$(JANET_TARGET)
|
||||
|
||||
debug: $(JANET_TARGET)
|
||||
$(DEBUGGER) ./$(JANET_TARGET)
|
||||
|
||||
VALGRIND_COMMAND=valgrind --leak-check=full
|
||||
VALGRIND_COMMAND=valgrind --leak-check=full --quiet
|
||||
|
||||
valgrind: $(JANET_TARGET)
|
||||
$(VALGRIND_COMMAND) ./$(JANET_TARGET)
|
||||
|
||||
test: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
for f in test/suite*.janet; do ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
|
||||
for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
|
||||
|
||||
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
|
@ -226,19 +271,25 @@ dist: build/janet-dist.tar.gz
|
|||
|
||||
build/janet-%.tar.gz: $(JANET_TARGET) \
|
||||
build/janet.h \
|
||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
|
||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_STATIC_LIBRARY) \
|
||||
README.md build/c/janet.c build/c/shell.c
|
||||
mkdir -p build/$(JANET_DIST_DIR)/bin
|
||||
cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/
|
||||
strip -x -S 'build/$(JANET_DIST_DIR)/bin/janet'
|
||||
mkdir -p build/$(JANET_DIST_DIR)/include
|
||||
cp build/janet.h build/$(JANET_DIST_DIR)/include/
|
||||
mkdir -p build/$(JANET_DIST_DIR)/lib/
|
||||
cp $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
|
||||
cp $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
|
||||
cp $(JANET_LIBRARY) build/$(JANET_DIST_DIR)/lib/ || true
|
||||
mkdir -p build/$(JANET_DIST_DIR)/man/man1/
|
||||
cp janet.1 build/$(JANET_DIST_DIR)/man/man1/janet.1
|
||||
mkdir -p build/$(JANET_DIST_DIR)/src/
|
||||
cp build/c/janet.c build/c/shell.c build/$(JANET_DIST_DIR)/src/
|
||||
cp CONTRIBUTING.md LICENSE README.md build/$(JANET_DIST_DIR)/
|
||||
cd build && tar -czvf ../$@ ./$(JANET_DIST_DIR)
|
||||
ifeq ($(HAS_SHARED), 1)
|
||||
build/janet-%.tar.gz: $(JANET_LIBRARY)
|
||||
endif
|
||||
|
||||
#########################
|
||||
##### Documentation #####
|
||||
|
@ -247,7 +298,7 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
|
|||
docs: build/doc.html
|
||||
|
||||
build/doc.html: $(JANET_TARGET) tools/gendoc.janet
|
||||
$(JANET_TARGET) tools/gendoc.janet > build/doc.html
|
||||
$(RUN) $(JANET_TARGET) tools/gendoc.janet > build/doc.html
|
||||
|
||||
########################
|
||||
##### Installation #####
|
||||
|
@ -263,7 +314,7 @@ build/janet.pc: $(JANET_TARGET)
|
|||
echo "Name: janet" >> $@
|
||||
echo "Url: https://janet-lang.org" >> $@
|
||||
echo "Description: Library for the Janet programming language." >> $@
|
||||
$(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@
|
||||
$(RUN) $(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@
|
||||
echo 'Cflags: -I$${includedir}' >> $@
|
||||
echo 'Libs: -L$${libdir} -ljanet' >> $@
|
||||
echo 'Libs.private: $(CLIBS)' >> $@
|
||||
|
@ -271,19 +322,29 @@ build/janet.pc: $(JANET_TARGET)
|
|||
install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h
|
||||
mkdir -p '$(DESTDIR)$(BINDIR)'
|
||||
cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
|
||||
strip -x -S '$(DESTDIR)$(BINDIR)/janet'
|
||||
mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
|
||||
cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
|
||||
ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h'
|
||||
mkdir -p '$(DESTDIR)$(JANET_PATH)'
|
||||
mkdir -p '$(DESTDIR)$(LIBDIR)'
|
||||
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')'
|
||||
if test $(UNAME) = Darwin ; then \
|
||||
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.$(shell $(JANET_TARGET) -e '(print janet/version)').dylib' ; \
|
||||
ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.dylib' ; \
|
||||
ln -sf libjanet.$(shell $(JANET_TARGET) -e '(print janet/version)').dylib $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
|
||||
else \
|
||||
cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')' ; \
|
||||
ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.so' ; \
|
||||
ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
|
||||
fi
|
||||
cp $(JANET_STATIC_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.a'
|
||||
ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.so'
|
||||
ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(DESTDIR)$(LIBDIR)/$(SONAME)
|
||||
mkdir -p '$(DESTDIR)$(JANET_MANPATH)'
|
||||
cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
|
||||
mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
|
||||
cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
|
||||
[ -z '$(DESTDIR)' ] && $(LDCONFIG) || true
|
||||
cp '$(JANET_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
|
||||
cp '$(JANET_LIBRARY_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
|
||||
[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
|
||||
|
||||
install-jpm-git: $(JANET_TARGET)
|
||||
mkdir -p build
|
||||
|
@ -295,11 +356,12 @@ install-jpm-git: $(JANET_TARGET)
|
|||
JANET_HEADERPATH='$(INCLUDEDIR)/janet' \
|
||||
JANET_BINPATH='$(BINDIR)' \
|
||||
JANET_LIBPATH='$(LIBDIR)' \
|
||||
../../$(JANET_TARGET) ./bootstrap.janet
|
||||
$(RUN) ../../$(JANET_TARGET) ./bootstrap.janet
|
||||
|
||||
uninstall:
|
||||
-rm '$(DESTDIR)$(BINDIR)/janet'
|
||||
-rm -rf '$(DESTDIR)$(INCLUDEDIR)/janet'
|
||||
-rm -rf '$(DESTDIR)$(INCLUDEDIR)/janet.h'
|
||||
-rm -rf '$(DESTDIR)$(LIBDIR)'/libjanet.*
|
||||
-rm '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
|
||||
-rm '$(DESTDIR)$(JANET_MANPATH)/janet.1'
|
||||
|
@ -310,14 +372,14 @@ uninstall:
|
|||
#################
|
||||
|
||||
format:
|
||||
tools/format.sh
|
||||
sh tools/format.sh
|
||||
|
||||
grammar: build/janet.tmLanguage
|
||||
build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
|
||||
$(JANET_TARGET) $< > $@
|
||||
$(RUN) $(JANET_TARGET) $< > $@
|
||||
|
||||
compile-commands:
|
||||
# Requires pip install copmiledb
|
||||
# Requires pip install compiledb
|
||||
compiledb make
|
||||
|
||||
clean:
|
||||
|
|
244
README.md
244
README.md
|
@ -1,64 +1,136 @@
|
|||
[![Join the chat](https://badges.gitter.im/janet-language/community.svg)](https://gitter.im/janet-language/community)
|
||||
|
||||
[![Appveyor Status](https://ci.appveyor.com/api/projects/status/bjraxrxexmt3sxyv/branch/master?svg=true)](https://ci.appveyor.com/project/bakpakin/janet/branch/master)
|
||||
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/commits/freebsd.yml.svg)](https://builds.sr.ht/~bakpakin/janet/commits/freebsd.yml?)
|
||||
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/commits/openbsd.yml.svg)](https://builds.sr.ht/~bakpakin/janet/commits/openbsd.yml?)
|
||||
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml.svg)](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml?)
|
||||
[![builds.sr.ht status](https://builds.sr.ht/~bakpakin/janet/commits/master/openbsd.yml.svg)](https://builds.sr.ht/~bakpakin/janet/commits/master/openbsd.yml?)
|
||||
[![Actions Status](https://github.com/janet-lang/janet/actions/workflows/test.yml/badge.svg)](https://github.com/janet-lang/janet/actions/workflows/test.yml)
|
||||
|
||||
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">
|
||||
|
||||
**Janet** is a functional and imperative programming language and bytecode interpreter. It is a
|
||||
lisp-like language, but lists are replaced
|
||||
by other data structures (arrays, tables (hash table), struct (immutable hash table), tuples).
|
||||
The language also supports bridging to native code written in C, meta-programming with macros, and bytecode assembly.
|
||||
**Janet** is a programming language for system scripting, expressive automation, and
|
||||
extending programs written in C or C++ with user scripting capabilities.
|
||||
|
||||
Janet makes a good system scripting language, or a language to embed in other programs.
|
||||
It's like Lua and GNU Guile in that regard. It has more built-in functionality and a richer core language than
|
||||
Lua, but smaller than GNU Guile or Python. However, it is much easier to embed and port than Python or Guile.
|
||||
|
||||
There is a REPL for trying out the language, as well as the ability
|
||||
to run script files. This client program is separate from the core runtime, so
|
||||
Janet can be embedded in other programs. Try Janet in your browser at
|
||||
[https://janet-lang.org](https://janet-lang.org).
|
||||
<https://janet-lang.org>.
|
||||
|
||||
If you'd like to financially support the ongoing development of Janet, consider
|
||||
[sponsoring its primary author](https://github.com/sponsors/bakpakin) through GitHub.
|
||||
|
||||
<br>
|
||||
|
||||
## Use Cases
|
||||
## Examples
|
||||
|
||||
Janet makes a good system scripting language, or a language to embed in other programs.
|
||||
It's like Lua and Guile in that regard. It has more built-in functionality and a richer core language than
|
||||
Lua, but smaller than GNU Guile or Python.
|
||||
See the examples directory for all provided example programs.
|
||||
|
||||
## Features
|
||||
### Game of Life
|
||||
|
||||
* Configurable at build time - turn features on or off for a smaller or more featureful build
|
||||
* Minimal setup - one binary and you are good to go!
|
||||
```janet
|
||||
# John Conway's Game of Life
|
||||
|
||||
(def- window
|
||||
(seq [x :range [-1 2]
|
||||
y :range [-1 2]
|
||||
:when (not (and (zero? x) (zero? y)))]
|
||||
[x y]))
|
||||
|
||||
(defn- neighbors
|
||||
[[x y]]
|
||||
(map (fn [[x1 y1]] [(+ x x1) (+ y y1)]) window))
|
||||
|
||||
(defn tick
|
||||
"Get the next state in the Game Of Life."
|
||||
[state]
|
||||
(def cell-set (frequencies state))
|
||||
(def neighbor-set (frequencies (mapcat neighbors state)))
|
||||
(seq [coord :keys neighbor-set
|
||||
:let [count (get neighbor-set coord)]
|
||||
:when (or (= count 3) (and (get cell-set coord) (= count 2)))]
|
||||
coord))
|
||||
|
||||
(defn draw
|
||||
"Draw cells in the game of life from (x1, y1) to (x2, y2)"
|
||||
[state x1 y1 x2 y2]
|
||||
(def cellset @{})
|
||||
(each cell state (put cellset cell true))
|
||||
(loop [x :range [x1 (+ 1 x2)]
|
||||
:after (print)
|
||||
y :range [y1 (+ 1 y2)]]
|
||||
(file/write stdout (if (get cellset [x y]) "X " ". ")))
|
||||
(print))
|
||||
|
||||
# Print the first 20 generations of a glider
|
||||
(var *state* '[(0 0) (-1 0) (1 0) (1 1) (0 2)])
|
||||
(for i 0 20
|
||||
(print "generation " i)
|
||||
(draw *state* -7 -7 7 7)
|
||||
(set *state* (tick *state*)))
|
||||
```
|
||||
|
||||
### TCP Echo Server
|
||||
|
||||
```janet
|
||||
# A simple TCP echo server using the built-in socket networking and event loop.
|
||||
|
||||
(defn handler
|
||||
"Simple handler for connections."
|
||||
[stream]
|
||||
(defer (:close stream)
|
||||
(def id (gensym))
|
||||
(def b @"")
|
||||
(print "Connection " id "!")
|
||||
(while (:read stream 1024 b)
|
||||
(printf " %v -> %v" id b)
|
||||
(:write stream b)
|
||||
(buffer/clear b))
|
||||
(printf "Done %v!" id)
|
||||
(ev/sleep 0.5)))
|
||||
|
||||
(net/server "127.0.0.1" "8000" handler)
|
||||
```
|
||||
|
||||
### Windows FFI Hello, World!
|
||||
|
||||
```janet
|
||||
# Use the FFI to popup a Windows message box - no C required
|
||||
|
||||
(ffi/context "user32.dll")
|
||||
|
||||
(ffi/defbind MessageBoxA :int
|
||||
[w :ptr text :string cap :string typ :int])
|
||||
|
||||
(MessageBoxA nil "Hello, World!" "Test" 0)
|
||||
```
|
||||
|
||||
## Language Features
|
||||
|
||||
* 600+ functions and macros in the core library
|
||||
* Built-in socket networking, threading, subprocesses, and file system functions.
|
||||
* Parsing Expression Grammars (PEG) engine as a more robust Regex alternative
|
||||
* Macros and compile-time computation
|
||||
* Per-thread event loop for efficient IO (epoll/IOCP/kqueue)
|
||||
* First-class green threads (continuations) as well as OS threads
|
||||
* Erlang-style supervision trees that integrate with the event loop
|
||||
* First-class closures
|
||||
* Garbage collection
|
||||
* First-class green threads (continuations)
|
||||
* Distributed as janet.c and janet.h for embedding into a larger program.
|
||||
* Python-style generators (implemented as a plain macro)
|
||||
* Mutable and immutable arrays (array/tuple)
|
||||
* Mutable and immutable hashtables (table/struct)
|
||||
* Mutable and immutable strings (buffer/string)
|
||||
* Macros
|
||||
* Multithreading
|
||||
* Per-thread event loop for efficient evented IO
|
||||
* Byte code interpreter with an assembly interface, as well as bytecode verification
|
||||
* Tail call Optimization
|
||||
* Direct interop with C via abstract types and C functions
|
||||
* Dynamically load C libraries
|
||||
* Functional and imperative standard library
|
||||
* Lexical scoping
|
||||
* Imperative programming as well as functional
|
||||
* REPL
|
||||
* Parsing Expression Grammars built into the core library
|
||||
* 400+ functions and macros in the core library
|
||||
* Embedding Janet in other programs
|
||||
* Interactive environment with detailed stack traces
|
||||
* Tail recursion
|
||||
* Interface with C functions and dynamically load plugins ("natives").
|
||||
* Built-in C FFI for when the native bindings are too much work
|
||||
* REPL development with debugger and inspectable runtime
|
||||
|
||||
## Documentation
|
||||
|
||||
* For a quick tutorial, see [the introduction](https://janet-lang.org/docs/index.html) for more details.
|
||||
* For the full API for all functions in the core library, see [the core API doc](https://janet-lang.org/api/index.html)
|
||||
* For the full API for all functions in the core library, see [the core API doc](https://janet-lang.org/api/index.html).
|
||||
|
||||
Documentation is also available locally in the REPL.
|
||||
Use the `(doc symbol-name)` macro to get API
|
||||
|
@ -66,7 +138,7 @@ documentation for symbols in the core library. For example,
|
|||
```
|
||||
(doc apply)
|
||||
```
|
||||
Shows documentation for the `apply` function.
|
||||
shows documentation for the `apply` function.
|
||||
|
||||
To get a list of all bindings in the default
|
||||
environment, use the `(all-bindings)` function. You
|
||||
|
@ -85,11 +157,13 @@ the SourceHut mirror is actively maintained.
|
|||
|
||||
The Makefile is non-portable and requires GNU-flavored make.
|
||||
|
||||
```
|
||||
```sh
|
||||
cd somewhere/my/projects/janet
|
||||
make
|
||||
make test
|
||||
make repl
|
||||
make install
|
||||
make install-jpm-git
|
||||
```
|
||||
|
||||
Find out more about the available make targets by running `make help`.
|
||||
|
@ -99,42 +173,45 @@ Find out more about the available make targets by running `make help`.
|
|||
32-bit Haiku build instructions are the same as the UNIX-like build instructions,
|
||||
but you need to specify an alternative compiler, such as `gcc-x86`.
|
||||
|
||||
```
|
||||
```sh
|
||||
cd somewhere/my/projects/janet
|
||||
make CC=gcc-x86
|
||||
make test
|
||||
make repl
|
||||
make install
|
||||
make install-jpm-git
|
||||
```
|
||||
|
||||
### FreeBSD
|
||||
|
||||
FreeBSD build instructions are the same as the UNIX-like build instructions,
|
||||
but you need `gmake` to compile. Alternatively, install directly from
|
||||
packages, using `pkg install lang/janet`.
|
||||
but you need `gmake` to compile. Alternatively, install the package directly with `pkg install lang/janet`.
|
||||
|
||||
```
|
||||
```sh
|
||||
cd somewhere/my/projects/janet
|
||||
gmake
|
||||
gmake test
|
||||
gmake repl
|
||||
gmake install
|
||||
gmake install-jpm-git
|
||||
```
|
||||
|
||||
### NetBSD
|
||||
|
||||
NetBSD build instructions are the same as the FreeBSD build instructions.
|
||||
Alternatively, install directly from packages, using `pkgin install janet`.
|
||||
Alternatively, install the package directly with `pkgin install janet`.
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#)
|
||||
2. Run a Visual Studio Command Prompt (cl.exe and link.exe need to be on the PATH) and cd to the directory with janet.
|
||||
3. Run `build_win` to compile janet.
|
||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#).
|
||||
2. Run a Visual Studio Command Prompt (`cl.exe` and `link.exe` need to be on your PATH) and `cd` to the directory with Janet.
|
||||
3. Run `build_win` to compile Janet.
|
||||
4. Run `build_win test` to make sure everything is working.
|
||||
|
||||
To build an `.msi` installer executable, in addition to the above steps, you will have to:
|
||||
|
||||
5. Install, or otherwise add to your PATH the [WiX 3.11 Toolset](https://github.com/wixtoolset/wix3/releases)
|
||||
6. run `build_win dist`
|
||||
5. Install, or otherwise add to your PATH the [WiX 3.11 Toolset](https://github.com/wixtoolset/wix3/releases).
|
||||
6. Run `build_win dist`.
|
||||
|
||||
Now you should have an `.msi`. You can run `build_win install` to install the `.msi`, or execute the file itself.
|
||||
|
||||
|
@ -170,9 +247,9 @@ ninja -C build install
|
|||
|
||||
Janet can be hacked on with pretty much any environment you like, but for IDE
|
||||
lovers, [Gnome Builder](https://wiki.gnome.org/Apps/Builder) is probably the
|
||||
best option, as it has excellent meson integration. It also offers code completion
|
||||
best option, as it has excellent Meson integration. It also offers code completion
|
||||
for Janet's C API right out of the box, which is very useful for exploring. VSCode, Vim,
|
||||
Emacs, and Atom will have syntax packages for the Janet language, though.
|
||||
Emacs, and Atom each have syntax packages for the Janet language, though.
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -181,8 +258,8 @@ to try out the language, you don't need to install anything. You can also move t
|
|||
|
||||
## Usage
|
||||
|
||||
A REPL is launched when the binary is invoked with no arguments. Pass the -h flag
|
||||
to display the usage information. Individual scripts can be run with `./janet myscript.janet`
|
||||
A REPL is launched when the binary is invoked with no arguments. Pass the `-h` flag
|
||||
to display the usage information. Individual scripts can be run with `./janet myscript.janet`.
|
||||
|
||||
If you are looking to explore, you can print a list of all available macros, functions, and constants
|
||||
by entering the command `(all-bindings)` into the REPL.
|
||||
|
@ -197,20 +274,26 @@ Hello, World!
|
|||
nil
|
||||
janet:3:> (os/exit)
|
||||
$ janet -h
|
||||
usage: build/janet [options] script args...
|
||||
usage: janet [options] script args...
|
||||
Options are:
|
||||
-h : Show this help
|
||||
-v : Print the version string
|
||||
-s : Use raw stdin instead of getline like functionality
|
||||
-e code : Execute a string of janet
|
||||
-E code arguments... : Evaluate an expression as a short-fn with arguments
|
||||
-d : Set the debug flag in the REPL
|
||||
-r : Enter the REPL after running all scripts
|
||||
-R : Disables loading profile.janet when JANET_PROFILE is present
|
||||
-p : Keep on executing if there is a top-level error (persistent)
|
||||
-q : Hide prompt, logo, and REPL output (quiet)
|
||||
-q : Hide logo (quiet)
|
||||
-k : Compile scripts but do not execute (flycheck)
|
||||
-m syspath : Set system path for loading global modules
|
||||
-c source output : Compile janet source code into an image
|
||||
-i : Load the script argument as an image file instead of source code
|
||||
-n : Disable ANSI color output in the REPL
|
||||
-l path : Execute code in a file before running the main script
|
||||
-l lib : Use a module before processing more arguments
|
||||
-w level : Set the lint warning level - default is "normal"
|
||||
-x level : Set the lint error level - default is "none"
|
||||
-- : Stop handling options
|
||||
```
|
||||
|
||||
|
@ -221,8 +304,8 @@ If installed, you can also run `man janet` to get usage information.
|
|||
Janet can be embedded in a host program very easily. The normal build
|
||||
will create a file `build/janet.c`, which is a single C file
|
||||
that contains all the source to Janet. This file, along with
|
||||
`src/include/janet.h` and `src/conf/janetconf.h` can be dragged into any C
|
||||
project and compiled into the project. Janet should be compiled with `-std=c99`
|
||||
`src/include/janet.h` and `src/conf/janetconf.h`, can be dragged into any C
|
||||
project and compiled into it. Janet should be compiled with `-std=c99`
|
||||
on most compilers, and will need to be linked to the math library, `-lm`, and
|
||||
the dynamic linker, `-ldl`, if one wants to be able to load dynamic modules. If
|
||||
there is no need for dynamic modules, add the define
|
||||
|
@ -230,26 +313,35 @@ there is no need for dynamic modules, add the define
|
|||
|
||||
See the [Embedding Section](https://janet-lang.org/capi/embedding.html) on the website for more information.
|
||||
|
||||
## Examples
|
||||
|
||||
See the examples directory for some example janet code.
|
||||
|
||||
## Discussion
|
||||
|
||||
Feel free to ask questions and join the discussion on the [Janet Gitter Channel](https://gitter.im/janet-language/community).
|
||||
Gitter provides Matrix and irc bridges as well.
|
||||
Feel free to ask questions and join the discussion on the [Janet Zulip Instance](https://janet.zulipchat.com/)
|
||||
|
||||
## FAQ
|
||||
|
||||
### How fast is it?
|
||||
|
||||
It is about the same speed as most interpreted languages without a JIT compiler. Tight, critical
|
||||
loops should probably be written in C or C++ . Programs tend to be a bit faster than
|
||||
they would be in a language like Python due to the discouragement of slow Object-Oriented abstraction
|
||||
with lots of hash-table lookups, and making late-binding explicit. All values are boxed in an 8-byte
|
||||
representation by default and allocated on the heap, with the exception of numbers, nils and booleans. The
|
||||
PEG engine is a specialized interpreter that can efficiently process string and buffer data.
|
||||
|
||||
The GC is simple and stop-the-world, but GC knobs are exposed in the core library and separate threads
|
||||
have isolated heaps and garbage collectors. Data that is shared between threads is reference counted.
|
||||
|
||||
YMMV.
|
||||
|
||||
### Where is (favorite feature from other language)?
|
||||
|
||||
It may exist, it may not. If you want to propose major language features, go ahead and open an issue, but
|
||||
they will likely by closed as "will not implement". Often, such features make one usecase simpler at the expense
|
||||
It may exist, it may not. If you want to propose a major language feature, go ahead and open an issue, but
|
||||
it will likely be closed as "will not implement". Often, such features make one usecase simpler at the expense
|
||||
of 5 others by making the language more complicated.
|
||||
|
||||
### Is there a language spec?
|
||||
|
||||
There is not currently a spec besides the documentation at https://janet-lang.org.
|
||||
There is not currently a spec besides the documentation at <https://janet-lang.org>.
|
||||
|
||||
### Is this Scheme/Common Lisp? Where are the cons cells?
|
||||
|
||||
|
@ -257,21 +349,21 @@ Nope. There are no cons cells here.
|
|||
|
||||
### Is this a Clojure port?
|
||||
|
||||
No. It's similar to Clojure superficially because I like Lisps and I like the asthetics.
|
||||
Internally, Janet is not at all like Clojure.
|
||||
No. It's similar to Clojure superficially because I like Lisps and I like the aesthetics.
|
||||
Internally, Janet is not at all like Clojure, Scheme, or Common Lisp.
|
||||
|
||||
### Are the immutable data structures (tuples and structs) implemented as hash tries?
|
||||
|
||||
No. They are immutable arrays and hash tables. Don't try and use them like Clojure's vectors
|
||||
and maps, instead they work well as table keys or other identifiers.
|
||||
|
||||
### Can I do Object Oriented programming with Janet?
|
||||
### Can I do object-oriented programming with Janet?
|
||||
|
||||
To some extent, yes. However, it is not the recommended method of abstraction, and performance may suffer.
|
||||
That said, tables can be used to make mutable objects with inheritance and polymorphism, where object
|
||||
methods are implemeted with keywords.
|
||||
methods are implemented with keywords.
|
||||
|
||||
```
|
||||
```clj
|
||||
(def Car @{:honk (fn [self msg] (print "car " self " goes " msg)) })
|
||||
(def my-car (table/setproto @{} Car))
|
||||
(:honk my-car "Beep!")
|
||||
|
@ -282,17 +374,25 @@ methods are implemeted with keywords.
|
|||
Usually, one of a few reasons:
|
||||
- Often, it already exists in a different form and the Clojure port would be redundant.
|
||||
- Clojure programs often generate a lot of garbage and rely on the JVM to clean it up.
|
||||
Janet does not run on the JVM, and has a more primitive garbage collector.
|
||||
- We want to keep the Janet core small. With Lisps, usually a feature can be added as a library
|
||||
without feeling "bolted on", especially when compared to ALGOL like languages. Adding features
|
||||
to the core also makes it a bit more difficult keep Janet maximally portable.
|
||||
Janet does not run on the JVM and has a more primitive garbage collector.
|
||||
- We want to keep the Janet core small. With Lisps, a feature can usually be added as a library
|
||||
without feeling "bolted on", especially when compared to ALGOL-like languages. Adding features
|
||||
to the core also makes it a bit more difficult to keep Janet maximally portable.
|
||||
|
||||
### Can I bind to Rust/Zig/Go/Java/Nim/C++/D/Pascal/Fortran/Odin/Jai/(Some new "Systems" Programming Language)?
|
||||
|
||||
Probably, if that language has a good interface with C. But the programmer may need to do
|
||||
some extra work to map Janet's internal memory model to that of the bound language. Janet
|
||||
also uses `setjmp`/`longjmp` for non-local returns internally. This
|
||||
approach is out of favor with many programmers now and doesn't always play well with other languages
|
||||
that have exceptions or stack-unwinding.
|
||||
|
||||
### Why is my terminal spitting out junk when I run the REPL?
|
||||
|
||||
Make sure your terminal supports ANSI escape codes. Most modern terminals will
|
||||
support these, but some older terminals, Windows consoles, or embedded terminals
|
||||
will not. If your terminal does not support ANSI escape codes, run the REPL with
|
||||
the `-n` flag, which disables color output. You can also try the `-s` if further issues
|
||||
the `-n` flag, which disables color output. You can also try the `-s` flag if further issues
|
||||
ensue.
|
||||
|
||||
## Why is it called "Janet"?
|
||||
|
|
51
appveyor.yml
51
appveyor.yml
|
@ -1,51 +0,0 @@
|
|||
version: build-{build}
|
||||
clone_folder: c:\projects\janet
|
||||
image:
|
||||
- Visual Studio 2019
|
||||
configuration:
|
||||
- Release
|
||||
platform:
|
||||
- x64
|
||||
- x86
|
||||
environment:
|
||||
matrix:
|
||||
- arch: Win64
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
# skip unsupported combinations
|
||||
init:
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %platform%
|
||||
|
||||
install:
|
||||
- set JANET_BUILD=%appveyor_repo_commit:~0,7%
|
||||
- build_win all
|
||||
- set janet_outname=%appveyor_repo_tag_name%
|
||||
- if "%janet_outname%"=="" set /P janet_outname=<build\version.txt
|
||||
build: off
|
||||
|
||||
artifacts:
|
||||
- name: janet.c
|
||||
path: dist\janet.c
|
||||
type: File
|
||||
- name: janet.h
|
||||
path: dist\janet.h
|
||||
type: File
|
||||
- name: shell.c
|
||||
path: dist\shell.c
|
||||
type: File
|
||||
- name: "janet-$(janet_outname)-windows-%platform%"
|
||||
path: dist
|
||||
type: Zip
|
||||
- path: "janet-$(janet_outname)-windows-%platform%-installer.msi"
|
||||
type: File
|
||||
|
||||
deploy:
|
||||
description: 'The Janet Programming Language.'
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: lwEXy09qhj2jSH9s1C/KvCkAUqJSma8phFR+0kbsfUc3rVxpNK5uD3z9Md0SjYRx
|
||||
artifact: /(janet|shell).*/
|
||||
draft: true
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
|
@ -41,32 +41,32 @@ if not exist build\boot mkdir build\boot
|
|||
@rem Build the bootstrap interpreter
|
||||
for %%f in (src\core\*.c) do (
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
)
|
||||
for %%f in (src\boot\*.c) do (
|
||||
%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
)
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
build\janet_boot . > build\c\janet.c
|
||||
|
||||
@rem Build the sources
|
||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
%JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
@rem Build the resources
|
||||
rc /nologo /fobuild\janet_win.res janet_win.rc
|
||||
|
||||
@rem Link everything to main client
|
||||
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
@rem Build static library (libjanet.a)
|
||||
@rem Build static library (libjanet.lib)
|
||||
%JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@if not errorlevel 0 goto :BUILDFAIL
|
||||
|
||||
echo === Successfully built janet.exe for Windows ===
|
||||
echo === Run 'build_win test' to run tests. ==
|
||||
|
@ -91,14 +91,16 @@ exit /b 0
|
|||
:CLEAN
|
||||
del *.exe *.lib *.exp
|
||||
rd /s /q build
|
||||
rd /s /q dist
|
||||
if exist dist (
|
||||
rd /s /q dist
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
@rem Run tests
|
||||
:TEST
|
||||
for %%f in (test/suite*.janet) do (
|
||||
janet.exe test\%%f
|
||||
@if errorlevel 1 goto TESTFAIL
|
||||
@if not errorlevel 0 goto TESTFAIL
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
|
@ -117,6 +119,7 @@ copy README.md dist\README.md
|
|||
|
||||
copy janet.lib dist\janet.lib
|
||||
copy janet.exp dist\janet.exp
|
||||
copy janet.def dist\janet.def
|
||||
|
||||
janet.exe tools\patch-header.janet src\include\janet.h src\conf\janetconf.h build\janet.h
|
||||
copy build\janet.h dist\janet.h
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
(defn dowork [name n]
|
||||
(print name " starting work...")
|
||||
(os/execute [(dyn :executable) "-e" (string "(os/sleep " n ")")])
|
||||
(os/execute [(dyn :executable) "-e" (string "(os/sleep " n ")")] :p)
|
||||
(print name " finished work!"))
|
||||
|
||||
# Will be done in parallel
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
(defn sleep
|
||||
"Sleep the entire thread, not just a single fiber."
|
||||
[n]
|
||||
(os/sleep (* 0.1 n)))
|
||||
|
||||
(defn work [lock n]
|
||||
(ev/acquire-lock lock)
|
||||
(print "working " n "...")
|
||||
(sleep n)
|
||||
(print "done working...")
|
||||
(ev/release-lock lock))
|
||||
|
||||
(defn reader
|
||||
[rwlock n]
|
||||
(ev/acquire-rlock rwlock)
|
||||
(print "reading " n "...")
|
||||
(sleep n)
|
||||
(print "done reading " n "...")
|
||||
(ev/release-rlock rwlock))
|
||||
|
||||
(defn writer
|
||||
[rwlock n]
|
||||
(ev/acquire-wlock rwlock)
|
||||
(print "writing " n "...")
|
||||
(sleep n)
|
||||
(print "done writing...")
|
||||
(ev/release-wlock rwlock))
|
||||
|
||||
(defn test-lock
|
||||
[]
|
||||
(def lock (ev/lock))
|
||||
(for i 3 7
|
||||
(ev/spawn-thread
|
||||
(work lock i))))
|
||||
|
||||
(defn test-rwlock
|
||||
[]
|
||||
(def rwlock (ev/rwlock))
|
||||
(for i 0 20
|
||||
(if (> 0.1 (math/random))
|
||||
(ev/spawn-thread (writer rwlock i))
|
||||
(ev/spawn-thread (reader rwlock i)))))
|
||||
|
||||
(test-rwlock)
|
||||
(test-lock)
|
|
@ -0,0 +1,71 @@
|
|||
# :lazy true needed for jpm quickbin
|
||||
# lazily loads library on first function use
|
||||
# so the `main` function
|
||||
# can be marshalled.
|
||||
(ffi/context "/usr/lib/libgtk-3.so" :lazy true)
|
||||
|
||||
(ffi/defbind
|
||||
gtk-application-new :ptr
|
||||
"Add docstrings as needed."
|
||||
[title :string flags :uint])
|
||||
|
||||
(ffi/defbind
|
||||
g-signal-connect-data :ulong
|
||||
[a :ptr b :ptr c :ptr d :ptr e :ptr f :int])
|
||||
|
||||
(ffi/defbind
|
||||
g-application-run :int
|
||||
[app :ptr argc :int argv :ptr])
|
||||
|
||||
(ffi/defbind
|
||||
gtk-application-window-new :ptr
|
||||
[a :ptr])
|
||||
|
||||
(ffi/defbind
|
||||
gtk-button-new-with-label :ptr
|
||||
[a :ptr])
|
||||
|
||||
(ffi/defbind
|
||||
gtk-container-add :void
|
||||
[a :ptr b :ptr])
|
||||
|
||||
(ffi/defbind
|
||||
gtk-widget-show-all :void
|
||||
[a :ptr])
|
||||
|
||||
(ffi/defbind
|
||||
gtk-button-set-label :void
|
||||
[a :ptr b :ptr])
|
||||
|
||||
(def cb (delay (ffi/trampoline :default)))
|
||||
|
||||
(defn ffi/array
|
||||
``Convert a janet array to a buffer that can be passed to FFI functions.
|
||||
For example, to create an array of type `char *` (array of c strings), one
|
||||
could use `(ffi/array ["hello" "world"] :ptr)`. One needs to be careful that
|
||||
array elements are not garbage collected though - the GC can't follow references
|
||||
inside an arbitrary byte buffer.``
|
||||
[arr ctype &opt buf]
|
||||
(default buf @"")
|
||||
(each el arr
|
||||
(ffi/write ctype el buf))
|
||||
buf)
|
||||
|
||||
(defn on-active
|
||||
[app]
|
||||
(def window (gtk-application-window-new app))
|
||||
(def btn (gtk-button-new-with-label "Click Me!"))
|
||||
(g-signal-connect-data btn "clicked" (cb)
|
||||
(fn [btn] (gtk-button-set-label btn "Hello World"))
|
||||
nil 1)
|
||||
(gtk-container-add window btn)
|
||||
(gtk-widget-show-all window))
|
||||
|
||||
(defn main
|
||||
[&]
|
||||
(def app (gtk-application-new "org.janet-lang.example.HelloApp" 0))
|
||||
(g-signal-connect-data app "activate" (cb) on-active nil 1)
|
||||
# manually build an array with ffi/write
|
||||
# - we are responsible for preventing gc when the arg array is used
|
||||
(def argv (ffi/array (dyn *args*) :string))
|
||||
(g-application-run app (length (dyn *args*)) argv))
|
|
@ -0,0 +1,205 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define EXPORTER __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORTER
|
||||
#endif
|
||||
|
||||
/* Structs */
|
||||
|
||||
typedef struct {
|
||||
int a, b;
|
||||
float c, d;
|
||||
} Split;
|
||||
|
||||
typedef struct {
|
||||
float c, d;
|
||||
int a, b;
|
||||
} SplitFlip;
|
||||
|
||||
typedef struct {
|
||||
int u, v, w, x, y, z;
|
||||
} SixInts;
|
||||
|
||||
typedef struct {
|
||||
int a;
|
||||
int b;
|
||||
} intint;
|
||||
|
||||
typedef struct {
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
} intintint;
|
||||
|
||||
typedef struct {
|
||||
int64_t a;
|
||||
int64_t b;
|
||||
int64_t c;
|
||||
} big;
|
||||
|
||||
/* Functions */
|
||||
|
||||
EXPORTER
|
||||
int int_fn(int a, int b) {
|
||||
return (a << 2) + b;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
double my_fn(int64_t a, int64_t b, const char *x) {
|
||||
return (double)(a + b) + 0.5 + strlen(x);
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
double double_fn(double x, double y, double z) {
|
||||
return (x + y) * z * 3;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
double double_many(double x, double y, double z, double w, double a, double b) {
|
||||
return x + y + z + w + a + b;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
double double_lots(
|
||||
double a,
|
||||
double b,
|
||||
double c,
|
||||
double d,
|
||||
double e,
|
||||
double f,
|
||||
double g,
|
||||
double h,
|
||||
double i,
|
||||
double j) {
|
||||
return i + j;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
double double_lots_2(
|
||||
double a,
|
||||
double b,
|
||||
double c,
|
||||
double d,
|
||||
double e,
|
||||
double f,
|
||||
double g,
|
||||
double h,
|
||||
double i,
|
||||
double j) {
|
||||
return a +
|
||||
10.0 * b +
|
||||
100.0 * c +
|
||||
1000.0 * d +
|
||||
10000.0 * e +
|
||||
100000.0 * f +
|
||||
1000000.0 * g +
|
||||
10000000.0 * h +
|
||||
100000000.0 * i +
|
||||
1000000000.0 * j;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
double float_fn(float x, float y, float z) {
|
||||
return (x + y) * z;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
int intint_fn(double x, intint ii) {
|
||||
printf("double: %g\n", x);
|
||||
return ii.a + ii.b;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
int intintint_fn(double x, intintint iii) {
|
||||
printf("double: %g\n", x);
|
||||
return iii.a + iii.b + iii.c;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
intint return_struct(int i) {
|
||||
intint ret;
|
||||
ret.a = i;
|
||||
ret.b = i * i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
big struct_big(int i, double d) {
|
||||
big ret;
|
||||
ret.a = i;
|
||||
ret.b = (int64_t) d;
|
||||
ret.c = ret.a + ret.b + 1000;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
void void_fn(void) {
|
||||
printf("void fn ran\n");
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
void void_fn_2(double y) {
|
||||
printf("y = %f\n", y);
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
void void_ret_fn(int x) {
|
||||
printf("void fn ran: %d\n", x);
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
int intintint_fn_2(intintint iii, int i) {
|
||||
fprintf(stderr, "iii.a = %d, iii.b = %d, iii.c = %d, i = %d\n", iii.a, iii.b, iii.c, i);
|
||||
return i * (iii.a + iii.b + iii.c);
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
float split_fn(Split s) {
|
||||
return s.a * s.c + s.b * s.d;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
float split_flip_fn(SplitFlip s) {
|
||||
return s.a * s.c + s.b * s.d;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
Split split_ret_fn(int x, float y) {
|
||||
Split ret;
|
||||
ret.a = x;
|
||||
ret.b = x;
|
||||
ret.c = y;
|
||||
ret.d = y;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
SplitFlip split_flip_ret_fn(int x, float y) {
|
||||
SplitFlip ret;
|
||||
ret.a = x;
|
||||
ret.b = x;
|
||||
ret.c = y;
|
||||
ret.d = y;
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
SixInts sixints_fn(void) {
|
||||
return (SixInts) {
|
||||
6666, 1111, 2222, 3333, 4444, 5555
|
||||
};
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
int sixints_fn_2(int x, SixInts s) {
|
||||
return x + s.u + s.v + s.w + s.x + s.y + s.z;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
int sixints_fn_3(SixInts s, int x) {
|
||||
return x + s.u + s.v + s.w + s.x + s.y + s.z;
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
#
|
||||
# Simple FFI test script that tests against a simple shared object
|
||||
#
|
||||
|
||||
(def is-windows (= :windows (os/which)))
|
||||
(def ffi/loc (string "examples/ffi/so." (if is-windows "dll" "so")))
|
||||
(def ffi/source-loc "examples/ffi/so.c")
|
||||
|
||||
(if is-windows
|
||||
(os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
|
||||
(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px))
|
||||
|
||||
(ffi/context ffi/loc)
|
||||
|
||||
(def intintint (ffi/struct :int :int :int))
|
||||
(def big (ffi/struct :s64 :s64 :s64))
|
||||
(def split (ffi/struct :int :int :float :float))
|
||||
(def split-flip (ffi/struct :float :float :int :int))
|
||||
(def six-ints (ffi/struct :int :int :int :int :int :int))
|
||||
|
||||
(ffi/defbind int-fn :int [a :int b :int])
|
||||
(ffi/defbind double-fn :double [a :double b :double c :double])
|
||||
(ffi/defbind double-many :double
|
||||
[x :double y :double z :double w :double a :double b :double])
|
||||
(ffi/defbind double-lots :double
|
||||
[a :double b :double c :double d :double e :double f :double g :double h :double i :double j :double])
|
||||
(ffi/defbind float-fn :double
|
||||
[x :float y :float z :float])
|
||||
(ffi/defbind intint-fn :int
|
||||
[x :double ii [:int :int]])
|
||||
(ffi/defbind return-struct [:int :int]
|
||||
[i :int])
|
||||
(ffi/defbind intintint-fn :int
|
||||
[x :double iii intintint])
|
||||
(ffi/defbind struct-big big
|
||||
[i :int d :double])
|
||||
(ffi/defbind void-fn :void [])
|
||||
(ffi/defbind double-lots-2 :double
|
||||
[a :double
|
||||
b :double
|
||||
c :double
|
||||
d :double
|
||||
e :double
|
||||
f :double
|
||||
g :double
|
||||
h :double
|
||||
i :double
|
||||
j :double])
|
||||
(ffi/defbind void-fn-2 :void [y :double])
|
||||
(ffi/defbind intintint-fn-2 :int [iii intintint i :int])
|
||||
(ffi/defbind split-fn :float [s split])
|
||||
(ffi/defbind split-flip-fn :float [s split-flip])
|
||||
(ffi/defbind split-ret-fn split [x :int y :float])
|
||||
(ffi/defbind split-flip-ret-fn split-flip [x :int y :float])
|
||||
(ffi/defbind sixints-fn six-ints [])
|
||||
(ffi/defbind sixints-fn-2 :int [x :int s six-ints])
|
||||
(ffi/defbind sixints-fn-3 :int [s six-ints x :int])
|
||||
(ffi/defbind-alias int-fn int-fn-aliased :int [a :int b :int])
|
||||
|
||||
#
|
||||
# Struct reading and writing
|
||||
#
|
||||
|
||||
(defn check-round-trip
|
||||
[t value]
|
||||
(def buf (ffi/write t value))
|
||||
(def same-value (ffi/read t buf))
|
||||
(assert (deep= value same-value)
|
||||
(string/format "round trip %j (got %j)" value same-value)))
|
||||
|
||||
(check-round-trip :bool true)
|
||||
(check-round-trip :bool false)
|
||||
(check-round-trip :void nil)
|
||||
(check-round-trip :void nil)
|
||||
(check-round-trip :s8 10)
|
||||
(check-round-trip :s8 0)
|
||||
(check-round-trip :s8 -10)
|
||||
(check-round-trip :u8 10)
|
||||
(check-round-trip :u8 0)
|
||||
(check-round-trip :s16 10)
|
||||
(check-round-trip :s16 0)
|
||||
(check-round-trip :s16 -12312)
|
||||
(check-round-trip :u16 10)
|
||||
(check-round-trip :u16 0)
|
||||
(check-round-trip :u32 0)
|
||||
(check-round-trip :u32 10)
|
||||
(check-round-trip :u32 0xFFFF7777)
|
||||
(check-round-trip :s32 0x7FFF7777)
|
||||
(check-round-trip :s32 0)
|
||||
(check-round-trip :s32 -1234567)
|
||||
|
||||
(def s (ffi/struct :s8 :s8 :s8 :float))
|
||||
(check-round-trip s [1 3 5 123.5])
|
||||
(check-round-trip s [-1 -3 -5 -123.5])
|
||||
|
||||
#
|
||||
# Call functions
|
||||
#
|
||||
|
||||
(tracev (sixints-fn))
|
||||
(tracev (sixints-fn-2 100 [1 2 3 4 5 6]))
|
||||
(tracev (sixints-fn-3 [1 2 3 4 5 6] 200))
|
||||
(tracev (split-ret-fn 10 12))
|
||||
(tracev (split-flip-ret-fn 10 12))
|
||||
(tracev (split-flip-ret-fn 12 10))
|
||||
(tracev (intintint-fn-2 [10 20 30] 3))
|
||||
(tracev (split-fn [5 6 1.2 3.4]))
|
||||
(tracev (void-fn-2 10.3))
|
||||
(tracev (double-many 1 2 3 4 5 6))
|
||||
(tracev (string/format "%.17g" (double-many 1 2 3 4 5 6)))
|
||||
(tracev (type (double-many 1 2 3 4 5 6)))
|
||||
(tracev (double-lots-2 0 1 2 3 4 5 6 7 8 9))
|
||||
(tracev (void-fn))
|
||||
(tracev (int-fn 10 20))
|
||||
(tracev (double-fn 1.5 2.5 3.5))
|
||||
(tracev (double-lots 1 2 3 4 5 6 7 8 9 10))
|
||||
(tracev (float-fn 8 4 17))
|
||||
(tracev (intint-fn 123.456 [10 20]))
|
||||
(tracev (intintint-fn 123.456 [10 20 30]))
|
||||
(tracev (return-struct 42))
|
||||
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
|
||||
(tracev (struct-big 11 99.5))
|
||||
(tracev (int-fn-aliased 10 20))
|
||||
|
||||
(assert (= [10 10 12 12] (split-ret-fn 10 12)))
|
||||
(assert (= [12 12 10 10] (split-flip-ret-fn 10 12)))
|
||||
(assert (= 183 (intintint-fn-2 [10 20 31] 3)))
|
||||
(assert (= 264 (math/round (* 10 (split-fn [5 6 1.2 3.4])))))
|
||||
(assert (= 9876543210 (double-lots-2 0 1 2 3 4 5 6 7 8 9)))
|
||||
(assert (= 60 (int-fn 10 20)))
|
||||
(assert (= 42 (double-fn 1.5 2.5 3.5)))
|
||||
(assert (= 21 (math/round (double-many 1 2 3 4 5 6.01))))
|
||||
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
|
||||
(assert (= 204 (float-fn 8 4 17)))
|
||||
|
||||
(print "Done.")
|
|
@ -0,0 +1,7 @@
|
|||
(ffi/context "user32.dll")
|
||||
|
||||
(ffi/defbind MessageBoxA :int
|
||||
[w :ptr text :string cap :string typ :int])
|
||||
|
||||
(MessageBoxA nil "Hello, World!" "Test" 0)
|
||||
|
Binary file not shown.
|
@ -0,0 +1,17 @@
|
|||
BITS 64
|
||||
|
||||
;;;
|
||||
;;; Code
|
||||
;;;
|
||||
mov rax, 1 ; write(
|
||||
mov rdi, 1 ; STDOUT_FILENO,
|
||||
lea rsi, [rel msg] ; msg,
|
||||
mov rdx, msglen ; sizeof(msg)
|
||||
syscall ; );
|
||||
ret ; return;
|
||||
|
||||
;;;
|
||||
;;; Constants
|
||||
;;;
|
||||
msg: db "Hello, world!", 10
|
||||
msglen: equ $ - msg
|
|
@ -0,0 +1,13 @@
|
|||
###
|
||||
### Relies on NASM being installed to assemble code.
|
||||
### Only works on x86-64 Linux.
|
||||
###
|
||||
### Before running, compile hello.nasm to hello.bin with
|
||||
### $ nasm hello.nasm -o hello.bin
|
||||
|
||||
(def bin (slurp "hello.bin"))
|
||||
(def f (ffi/jitfn bin))
|
||||
(def signature (ffi/signature :default :void))
|
||||
(ffi/call f signature)
|
||||
(print "called a jitted function with FFI!")
|
||||
(print "machine code: " (describe (string/slice f)))
|
|
@ -0,0 +1,2 @@
|
|||
(while (not (empty? (def line (getline))))
|
||||
(prin "line: " line))
|
|
@ -0,0 +1,30 @@
|
|||
(defn init-db [c]
|
||||
(def res @{:clients @{}})
|
||||
(var i 0)
|
||||
(repeat c
|
||||
(def n (string "client" i))
|
||||
(put-in res [:clients n] @{:name n :projects @{}})
|
||||
(++ i)
|
||||
(repeat c
|
||||
(def pn (string "project" i))
|
||||
(put-in res [:clients n :projects pn] @{:name pn})
|
||||
(++ i)
|
||||
(repeat c
|
||||
(def tn (string "task" i))
|
||||
(put-in res [:clients n :projects pn :tasks tn] @{:name pn})
|
||||
(++ i))))
|
||||
res)
|
||||
|
||||
(loop [c :range [30 80 1]]
|
||||
(var s (os/clock))
|
||||
(print "Marshal DB with " c " clients, "
|
||||
(* c c) " projects and "
|
||||
(* c c c) " tasks. "
|
||||
"Total " (+ (* c c c) (* c c) c) " tables")
|
||||
(def buf (marshal (init-db c) @{} @""))
|
||||
(print "Buffer is " (length buf) " bytes")
|
||||
(print "Duration " (- (os/clock) s))
|
||||
(set s (os/clock))
|
||||
(gccollect)
|
||||
(print "Collected garbage in " (- (os/clock) s)))
|
||||
|
|
@ -76,9 +76,16 @@ void num_array_put(void *p, Janet key, Janet value) {
|
|||
}
|
||||
}
|
||||
|
||||
static Janet num_array_length(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
num_array *array = (num_array *)janet_getabstract(argv, 0, &num_array_type);
|
||||
return janet_wrap_number(array->size);
|
||||
}
|
||||
|
||||
static const JanetMethod methods[] = {
|
||||
{"scale", num_array_scale},
|
||||
{"sum", num_array_sum},
|
||||
{"length", num_array_length},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -109,6 +116,11 @@ static const JanetReg cfuns[] = {
|
|||
"(numarray/scale numarray factor)\n\n"
|
||||
"scale numarray by factor"
|
||||
},
|
||||
{
|
||||
"sum", num_array_sum,
|
||||
"(numarray/sum numarray)\n\n"
|
||||
"sums numarray"
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(import build/numarray)
|
||||
(import /build/numarray)
|
||||
|
||||
(def a (numarray/new 30))
|
||||
(print (get a 20))
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Switch to python
|
||||
|
||||
(print "running in Janet")
|
||||
(os/posix-exec ["python"] :p)
|
||||
(print "will not print")
|
|
@ -0,0 +1,41 @@
|
|||
###
|
||||
### Usage: janet examples/sigaction.janet 1|2|3|4 &
|
||||
###
|
||||
### Then at shell: kill -s SIGTERM $!
|
||||
###
|
||||
|
||||
(defn action
|
||||
[]
|
||||
(print "Handled SIGTERM!")
|
||||
(flush)
|
||||
(os/exit 1))
|
||||
|
||||
(defn main1
|
||||
[]
|
||||
(os/sigaction :term action true)
|
||||
(forever))
|
||||
|
||||
(defn main2
|
||||
[]
|
||||
(os/sigaction :term action)
|
||||
(forever))
|
||||
|
||||
(defn main3
|
||||
[]
|
||||
(os/sigaction :term action true)
|
||||
(forever (ev/sleep math/inf)))
|
||||
|
||||
(defn main4
|
||||
[]
|
||||
(os/sigaction :term action)
|
||||
(forever (ev/sleep math/inf)))
|
||||
|
||||
(defn main
|
||||
[& args]
|
||||
(def which (scan-number (get args 1 "1")))
|
||||
(case which
|
||||
1 (main1) # should work
|
||||
2 (main2) # will not work
|
||||
3 (main3) # should work
|
||||
4 (main4) # should work
|
||||
(error "bad main")))
|
|
@ -1,10 +1,10 @@
|
|||
# An example of using Janet's extensible module system
|
||||
# to import files from URL. To try this, run `janet -l examples/urlloader.janet`
|
||||
# from the repl, and then:
|
||||
# An example of using Janet's extensible module system to import files from
|
||||
# URL. To try this, run `janet -l ./examples/urlloader.janet` from the command
|
||||
# line, and then at the REPL type:
|
||||
#
|
||||
# (import https://raw.githubusercontent.com/janet-lang/janet/master/examples/colors.janet :as c)
|
||||
#
|
||||
# This will import a file using curl. You can then try
|
||||
# This will import a file using curl. You can then try:
|
||||
#
|
||||
# (print (c/color :green "Hello!"))
|
||||
#
|
||||
|
@ -13,9 +13,9 @@
|
|||
|
||||
(defn- load-url
|
||||
[url args]
|
||||
(def f (file/popen (string "curl " url)))
|
||||
(def res (dofile f :source url ;args))
|
||||
(try (file/close f) ([err] nil))
|
||||
(def p (os/spawn ["curl" url "-s"] :p {:out :pipe}))
|
||||
(def res (dofile (p :out) :source url ;args))
|
||||
(:wait p)
|
||||
res)
|
||||
|
||||
(defn- check-http-url
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
(def weak-k (table/weak-keys 10))
|
||||
(def weak-v (table/weak-values 10))
|
||||
(def weak-kv (table/weak 10))
|
||||
|
||||
(put weak-kv (gensym) 10)
|
||||
(put weak-kv :hello :world)
|
||||
(put weak-k :abc123zz77asda :stuff)
|
||||
(put weak-k true :abc123zz77asda)
|
||||
(put weak-k :zyzzyz false)
|
||||
(put weak-v (gensym) 10)
|
||||
(put weak-v 20 (gensym))
|
||||
(print "before gc")
|
||||
(tracev weak-k)
|
||||
(tracev weak-v)
|
||||
(tracev weak-kv)
|
||||
(gccollect)
|
||||
(print "after gc")
|
||||
(tracev weak-k)
|
||||
(tracev weak-v)
|
||||
(tracev weak-kv)
|
17
janet.1
17
janet.1
|
@ -164,10 +164,15 @@ Execute a string of Janet source. Source code is executed in the order it is enc
|
|||
arguments are executed before later ones.
|
||||
|
||||
.TP
|
||||
.BR \-E\ code arguments
|
||||
.BR \-E\ code\ arguments...
|
||||
Execute a single Janet expression as a Janet short-fn, passing the remaining command line arguments to the expression. This allows
|
||||
more concise one-liners with command line arguments.
|
||||
|
||||
Example: janet -E '(print $0)' 12 is equivalent to '((short-fn (print $0)) 12)', which is in turn equivalent to
|
||||
`((fn [k] (print k)) 12)`
|
||||
|
||||
See docs for the `short-fn` function for more details.
|
||||
|
||||
.TP
|
||||
.BR \-d
|
||||
Enable debug mode. On all terminating signals as well the debug signal, this will
|
||||
|
@ -178,6 +183,10 @@ default repl.
|
|||
.BR \-n
|
||||
Disable ANSI colors in the repl. Has no effect if no repl is run.
|
||||
|
||||
.TP
|
||||
.BR \-N
|
||||
Enable ANSI colors in the repl. Has no effect if no repl is run.
|
||||
|
||||
.TP
|
||||
.BR \-r
|
||||
Open a REPL (Read Eval Print Loop) after executing all sources. By default, if Janet is called with no
|
||||
|
@ -263,5 +272,11 @@ This variable does nothing in the default configuration of Janet, as PRF is disa
|
|||
cannot be defined for this variable to have an effect.
|
||||
.RE
|
||||
|
||||
.B NO_COLOR
|
||||
.RS
|
||||
Turn off color by default in the repl and in the error handler of scripts. This can be changed at runtime
|
||||
via dynamic bindings *err-color* and *pretty-format*, or via the command line parameters -n and -N.
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
Written by Calvin Rose <calsrose@gmail.com>
|
||||
|
|
120
meson.build
120
meson.build
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2021 Calvin Rose and contributors
|
||||
# Copyright (c) 2023 Calvin Rose and contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
|
@ -20,7 +20,7 @@
|
|||
|
||||
project('janet', 'c',
|
||||
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
|
||||
version : '1.18.1')
|
||||
version : '1.34.0')
|
||||
|
||||
# Global settings
|
||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
|
||||
|
@ -61,6 +61,7 @@ conf.set('JANET_NO_SOURCEMAPS', not get_option('sourcemaps'))
|
|||
conf.set('JANET_NO_ASSEMBLER', not get_option('assembler'))
|
||||
conf.set('JANET_NO_PEG', not get_option('peg'))
|
||||
conf.set('JANET_NO_NET', not get_option('net'))
|
||||
conf.set('JANET_NO_IPV6', not get_option('ipv6'))
|
||||
conf.set('JANET_NO_EV', not get_option('ev') or get_option('single_threaded'))
|
||||
conf.set('JANET_REDUCED_OS', get_option('reduced_os'))
|
||||
conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
|
||||
|
@ -76,6 +77,9 @@ conf.set('JANET_SIMPLE_GETLINE', get_option('simple_getline'))
|
|||
conf.set('JANET_EV_NO_EPOLL', not get_option('epoll'))
|
||||
conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
|
||||
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
|
||||
conf.set('JANET_NO_FFI', not get_option('ffi'))
|
||||
conf.set('JANET_NO_FFI_JIT', not get_option('ffi_jit'))
|
||||
conf.set('JANET_NO_CRYPTORAND', not get_option('cryptorand'))
|
||||
if get_option('os_name') != ''
|
||||
conf.set('JANET_OS_NAME', get_option('os_name'))
|
||||
endif
|
||||
|
@ -116,6 +120,7 @@ core_src = [
|
|||
'src/core/debug.c',
|
||||
'src/core/emit.c',
|
||||
'src/core/ev.c',
|
||||
'src/core/ffi.c',
|
||||
'src/core/fiber.c',
|
||||
'src/core/gc.c',
|
||||
'src/core/inttypes.c',
|
||||
|
@ -166,7 +171,7 @@ janet_boot = executable('janet-boot', core_src, boot_src,
|
|||
|
||||
# Build janet.c
|
||||
janetc = custom_target('janetc',
|
||||
input : [janet_boot],
|
||||
input : [janet_boot, 'src/boot/boot.janet'],
|
||||
output : 'janet.c',
|
||||
capture : true,
|
||||
command : [
|
||||
|
@ -179,25 +184,41 @@ if not get_option('single_threaded')
|
|||
janet_dependencies += thread_dep
|
||||
endif
|
||||
|
||||
libjanet = library('janet', janetc,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
version: meson.project_version(),
|
||||
soversion: version_parts[0] + '.' + version_parts[1],
|
||||
install : true)
|
||||
|
||||
# Allow building with no shared library
|
||||
if cc.has_argument('-fvisibility=hidden')
|
||||
lib_cflags = ['-fvisibility=hidden']
|
||||
else
|
||||
lib_cflags = []
|
||||
endif
|
||||
if get_option('shared')
|
||||
libjanet = library('janet', janetc,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
version: meson.project_version(),
|
||||
soversion: version_parts[0] + '.' + version_parts[1],
|
||||
c_args : lib_cflags,
|
||||
install : true)
|
||||
# Extra c flags - adding -fvisibility=hidden matches the Makefile and
|
||||
# shaves off about 10k on linux x64, likely similar on other platforms.
|
||||
if cc.has_argument('-fvisibility=hidden')
|
||||
extra_cflags = ['-fvisibility=hidden']
|
||||
if cc.has_argument('-fvisibility=hidden')
|
||||
extra_cflags = ['-fvisibility=hidden', '-DJANET_DLL_IMPORT']
|
||||
else
|
||||
extra_cflags = ['-DJANET_DLL_IMPORT']
|
||||
endif
|
||||
janet_mainclient = executable('janet', mainclient_src,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
link_with: [libjanet],
|
||||
c_args : extra_cflags,
|
||||
install : true)
|
||||
else
|
||||
extra_cflags = []
|
||||
# No shared library
|
||||
janet_mainclient = executable('janet', mainclient_src, janetc,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
c_args : lib_cflags,
|
||||
install : true)
|
||||
endif
|
||||
janet_mainclient = executable('janet', janetc, mainclient_src,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
c_args : extra_cflags,
|
||||
install : true)
|
||||
|
||||
if meson.is_cross_build()
|
||||
native_cc = meson.get_compiler('c', native: true)
|
||||
|
@ -224,17 +245,34 @@ docs = custom_target('docs',
|
|||
|
||||
# Tests
|
||||
test_files = [
|
||||
'test/suite0000.janet',
|
||||
'test/suite0001.janet',
|
||||
'test/suite0002.janet',
|
||||
'test/suite0003.janet',
|
||||
'test/suite0004.janet',
|
||||
'test/suite0005.janet',
|
||||
'test/suite0006.janet',
|
||||
'test/suite0007.janet',
|
||||
'test/suite0008.janet',
|
||||
'test/suite0009.janet',
|
||||
'test/suite0010.janet'
|
||||
'test/suite-array.janet',
|
||||
'test/suite-asm.janet',
|
||||
'test/suite-boot.janet',
|
||||
'test/suite-buffer.janet',
|
||||
'test/suite-capi.janet',
|
||||
'test/suite-cfuns.janet',
|
||||
'test/suite-compile.janet',
|
||||
'test/suite-corelib.janet',
|
||||
'test/suite-debug.janet',
|
||||
'test/suite-ev.janet',
|
||||
'test/suite-ffi.janet',
|
||||
'test/suite-inttypes.janet',
|
||||
'test/suite-io.janet',
|
||||
'test/suite-marsh.janet',
|
||||
'test/suite-math.janet',
|
||||
'test/suite-os.janet',
|
||||
'test/suite-parse.janet',
|
||||
'test/suite-peg.janet',
|
||||
'test/suite-pp.janet',
|
||||
'test/suite-specials.janet',
|
||||
'test/suite-string.janet',
|
||||
'test/suite-strtod.janet',
|
||||
'test/suite-struct.janet',
|
||||
'test/suite-symcache.janet',
|
||||
'test/suite-table.janet',
|
||||
'test/suite-unknown.janet',
|
||||
'test/suite-value.janet',
|
||||
'test/suite-vm.janet'
|
||||
]
|
||||
foreach t : test_files
|
||||
test(t, janet_nativeclient, args : files([t]), workdir : meson.current_source_dir())
|
||||
|
@ -244,14 +282,15 @@ endforeach
|
|||
run_target('repl', command : [janet_nativeclient])
|
||||
|
||||
# For use as meson subproject (wrap)
|
||||
janet_dep = declare_dependency(include_directories : incdir,
|
||||
link_with : libjanet)
|
||||
|
||||
if get_option('shared')
|
||||
janet_dep = declare_dependency(include_directories : incdir,
|
||||
link_with : libjanet)
|
||||
# pkgconfig
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(libjanet,
|
||||
subdirs: 'janet',
|
||||
description: 'Library for the Janet programming language.')
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(libjanet,
|
||||
subdirs: 'janet',
|
||||
description: 'Library for the Janet programming language.')
|
||||
endif
|
||||
|
||||
# Installation
|
||||
install_man('janet.1')
|
||||
|
@ -261,5 +300,12 @@ patched_janet = custom_target('patched-janeth',
|
|||
install : true,
|
||||
install_dir : join_paths(get_option('includedir'), 'janet'),
|
||||
build_by_default : true,
|
||||
output : ['janet.h'],
|
||||
output : ['janet_' + meson.project_version() + '.h'],
|
||||
command : [janet_nativeclient, '@INPUT@', '@OUTPUT@'])
|
||||
|
||||
# Create a version of the janet.h header that matches what jpm often expects
|
||||
if meson.version().version_compare('>=0.61')
|
||||
install_symlink('janet.h', pointing_to: 'janet/janet_' + meson.project_version() + '.h', install_dir: get_option('includedir'))
|
||||
install_symlink('janet.h', pointing_to: 'janet_' + meson.project_version() + '.h', install_dir: join_paths(get_option('includedir'), 'janet'))
|
||||
endif
|
||||
|
||||
|
|
|
@ -11,14 +11,17 @@ option('peg', type : 'boolean', value : true)
|
|||
option('int_types', type : 'boolean', value : true)
|
||||
option('prf', type : 'boolean', value : false)
|
||||
option('net', type : 'boolean', value : true)
|
||||
option('ipv6', type : 'boolean', value : true)
|
||||
option('ev', type : 'boolean', value : true)
|
||||
option('processes', type : 'boolean', value : true)
|
||||
option('umask', type : 'boolean', value : true)
|
||||
option('realpath', type : 'boolean', value : true)
|
||||
option('simple_getline', type : 'boolean', value : false)
|
||||
option('epoll', type : 'boolean', value : false)
|
||||
option('kqueue', type : 'boolean', value : false)
|
||||
option('interpreter_interrupt', type : 'boolean', value : false)
|
||||
option('epoll', type : 'boolean', value : true)
|
||||
option('kqueue', type : 'boolean', value : true)
|
||||
option('interpreter_interrupt', type : 'boolean', value : true)
|
||||
option('ffi', type : 'boolean', value : true)
|
||||
option('ffi_jit', type : 'boolean', value : true)
|
||||
|
||||
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
|
||||
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)
|
||||
|
@ -27,3 +30,5 @@ option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7f
|
|||
|
||||
option('arch_name', type : 'string', value: '')
|
||||
option('os_name', type : 'string', value: '')
|
||||
option('shared', type : 'boolean', value: true)
|
||||
option('cryptorand', type : 'boolean', value: true)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
2407
src/boot/boot.janet
2407
src/boot/boot.janet
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -70,6 +70,5 @@ int system_test() {
|
|||
|
||||
assert(janet_equals(tuple1, tuple2));
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
#define JANETCONF_H
|
||||
|
||||
#define JANET_VERSION_MAJOR 1
|
||||
#define JANET_VERSION_MINOR 18
|
||||
#define JANET_VERSION_PATCH 1
|
||||
#define JANET_VERSION_MINOR 34
|
||||
#define JANET_VERSION_PATCH 0
|
||||
#define JANET_VERSION_EXTRA ""
|
||||
#define JANET_VERSION "1.18.1"
|
||||
#define JANET_VERSION "1.34.0"
|
||||
|
||||
/* #define JANET_BUILD "local" */
|
||||
|
||||
|
@ -33,6 +33,8 @@
|
|||
/* #define JANET_NO_SYMLINKS */
|
||||
/* #define JANET_NO_UMASK */
|
||||
/* #define JANET_NO_THREADS */
|
||||
/* #define JANET_NO_FFI */
|
||||
/* #define JANET_NO_FFI_JIT */
|
||||
|
||||
/* Other settings */
|
||||
/* #define JANET_DEBUG */
|
||||
|
@ -50,6 +52,9 @@
|
|||
/* #define JANET_EV_NO_EPOLL */
|
||||
/* #define JANET_EV_NO_KQUEUE */
|
||||
/* #define JANET_NO_INTERPRETER_INTERRUPT */
|
||||
/* #define JANET_NO_IPV6 */
|
||||
/* #define JANET_NO_CRYPTORAND */
|
||||
/* #define JANET_USE_STDATOMIC */
|
||||
|
||||
/* Custom vm allocator support */
|
||||
/* #include <mimalloc.h> */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -23,14 +23,16 @@
|
|||
#ifndef JANET_AMALG
|
||||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#include "gc.h"
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
#ifdef JANET_EV
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Create new userdata */
|
||||
void *janet_abstract_begin(const JanetAbstractType *atype, size_t size) {
|
||||
|
@ -63,8 +65,8 @@ void *janet_abstract_begin_threaded(const JanetAbstractType *atype, size_t size)
|
|||
}
|
||||
janet_vm.next_collection += size + sizeof(JanetAbstractHead);
|
||||
header->gc.flags = JANET_MEMORY_THREADED_ABSTRACT;
|
||||
header->gc.next = NULL; /* Clear memory for address sanitizers */
|
||||
header->gc.refcount = 1;
|
||||
header->gc.data.next = NULL; /* Clear memory for address sanitizers */
|
||||
header->gc.data.refcount = 1;
|
||||
header->size = size;
|
||||
header->type = atype;
|
||||
void *abstract = (void *) & (header->data);
|
||||
|
@ -85,12 +87,12 @@ void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size) {
|
|||
|
||||
#ifdef JANET_WINDOWS
|
||||
|
||||
static int32_t janet_incref(JanetAbstractHead *ab) {
|
||||
return InterlockedIncrement(&ab->gc.refcount);
|
||||
size_t janet_os_mutex_size(void) {
|
||||
return sizeof(CRITICAL_SECTION);
|
||||
}
|
||||
|
||||
static int32_t janet_decref(JanetAbstractHead *ab) {
|
||||
return InterlockedDecrement(&ab->gc.refcount);
|
||||
size_t janet_os_rwlock_size(void) {
|
||||
return sizeof(void *);
|
||||
}
|
||||
|
||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
|
||||
|
@ -106,43 +108,97 @@ void janet_os_mutex_lock(JanetOSMutex *mutex) {
|
|||
}
|
||||
|
||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
|
||||
/* error handling? May want to keep counter */
|
||||
LeaveCriticalSection((CRITICAL_SECTION *) mutex);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {
|
||||
InitializeSRWLock((PSRWLOCK) rwlock);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_deinit(JanetOSRWLock *rwlock) {
|
||||
/* no op? */
|
||||
(void) rwlock;
|
||||
}
|
||||
|
||||
void janet_os_rwlock_rlock(JanetOSRWLock *rwlock) {
|
||||
AcquireSRWLockShared((PSRWLOCK) rwlock);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_wlock(JanetOSRWLock *rwlock) {
|
||||
AcquireSRWLockExclusive((PSRWLOCK) rwlock);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_runlock(JanetOSRWLock *rwlock) {
|
||||
ReleaseSRWLockShared((PSRWLOCK) rwlock);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
|
||||
ReleaseSRWLockExclusive((PSRWLOCK) rwlock);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int32_t janet_incref(JanetAbstractHead *ab) {
|
||||
return __atomic_add_fetch(&ab->gc.refcount, 1, __ATOMIC_RELAXED);
|
||||
size_t janet_os_mutex_size(void) {
|
||||
return sizeof(pthread_mutex_t);
|
||||
}
|
||||
|
||||
static int32_t janet_decref(JanetAbstractHead *ab) {
|
||||
return __atomic_add_fetch(&ab->gc.refcount, -1, __ATOMIC_RELAXED);
|
||||
size_t janet_os_rwlock_size(void) {
|
||||
return sizeof(pthread_rwlock_t);
|
||||
}
|
||||
|
||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
|
||||
pthread_mutex_init(mutex, NULL);
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init((pthread_mutex_t *) mutex, &attr);
|
||||
}
|
||||
|
||||
void janet_os_mutex_deinit(JanetOSMutex *mutex) {
|
||||
pthread_mutex_destroy(mutex);
|
||||
pthread_mutex_destroy((pthread_mutex_t *) mutex);
|
||||
}
|
||||
|
||||
void janet_os_mutex_lock(JanetOSMutex *mutex) {
|
||||
pthread_mutex_lock(mutex);
|
||||
pthread_mutex_lock((pthread_mutex_t *) mutex);
|
||||
}
|
||||
|
||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
|
||||
pthread_mutex_unlock(mutex);
|
||||
int ret = pthread_mutex_unlock((pthread_mutex_t *) mutex);
|
||||
if (ret) janet_panic("cannot release lock");
|
||||
}
|
||||
|
||||
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {
|
||||
pthread_rwlock_init((pthread_rwlock_t *) rwlock, NULL);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_deinit(JanetOSRWLock *rwlock) {
|
||||
pthread_rwlock_destroy((pthread_rwlock_t *) rwlock);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_rlock(JanetOSRWLock *rwlock) {
|
||||
pthread_rwlock_rdlock((pthread_rwlock_t *) rwlock);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_wlock(JanetOSRWLock *rwlock) {
|
||||
pthread_rwlock_wrlock((pthread_rwlock_t *) rwlock);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_runlock(JanetOSRWLock *rwlock) {
|
||||
pthread_rwlock_unlock((pthread_rwlock_t *) rwlock);
|
||||
}
|
||||
|
||||
void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
|
||||
pthread_rwlock_unlock((pthread_rwlock_t *) rwlock);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int32_t janet_abstract_incref(void *abst) {
|
||||
return janet_incref(janet_abstract_head(abst));
|
||||
return janet_atomic_inc(&janet_abstract_head(abst)->gc.data.refcount);
|
||||
}
|
||||
|
||||
int32_t janet_abstract_decref(void *abst) {
|
||||
return janet_decref(janet_abstract_head(abst));
|
||||
return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -30,9 +30,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
/* Creates a new array */
|
||||
JanetArray *janet_array(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
|
||||
static void janet_array_impl(JanetArray *array, int32_t capacity) {
|
||||
Janet *data = NULL;
|
||||
if (capacity > 0) {
|
||||
janet_vm.next_collection += capacity * sizeof(Janet);
|
||||
|
@ -44,6 +42,19 @@ JanetArray *janet_array(int32_t capacity) {
|
|||
array->count = 0;
|
||||
array->capacity = capacity;
|
||||
array->data = data;
|
||||
}
|
||||
|
||||
/* Creates a new array */
|
||||
JanetArray *janet_array(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
|
||||
janet_array_impl(array, capacity);
|
||||
return array;
|
||||
}
|
||||
|
||||
/* Creates a new array with weak references */
|
||||
JanetArray *janet_array_weak(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY_WEAK, sizeof(JanetArray));
|
||||
janet_array_impl(array, capacity);
|
||||
return array;
|
||||
}
|
||||
|
||||
|
@ -125,18 +136,27 @@ Janet janet_array_peek(JanetArray *array) {
|
|||
JANET_CORE_FN(cfun_array_new,
|
||||
"(array/new capacity)",
|
||||
"Creates a new empty array with a pre-allocated capacity. The same as "
|
||||
"(array) but can be more efficient if the maximum size of an array is known.") {
|
||||
"`(array)` but can be more efficient if the maximum size of an array is known.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
JanetArray *array = janet_array(cap);
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_array_weak,
|
||||
"(array/weak capacity)",
|
||||
"Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
JanetArray *array = janet_array_weak(cap);
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_array_new_filled,
|
||||
"(array/new-filled count &opt value)",
|
||||
"Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") {
|
||||
janet_arity(argc, 1, 2);
|
||||
int32_t count = janet_getinteger(argv, 0);
|
||||
int32_t count = janet_getnat(argv, 0);
|
||||
Janet x = (argc == 2) ? argv[1] : janet_wrap_nil();
|
||||
JanetArray *array = janet_array(count);
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
|
@ -177,8 +197,8 @@ JANET_CORE_FN(cfun_array_peek,
|
|||
}
|
||||
|
||||
JANET_CORE_FN(cfun_array_push,
|
||||
"(array/push arr x)",
|
||||
"Insert an element in the end of an array. Modifies the input array and returns it.") {
|
||||
"(array/push arr & xs)",
|
||||
"Push all the elements of xs to the end of an array. Modifies the input array and returns it.") {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
if (INT32_MAX - argc + 1 <= array->count) {
|
||||
|
@ -194,7 +214,7 @@ JANET_CORE_FN(cfun_array_push,
|
|||
JANET_CORE_FN(cfun_array_ensure,
|
||||
"(array/ensure arr capacity growth)",
|
||||
"Ensures that the memory backing the array is large enough for `capacity` "
|
||||
"items at the given rate of growth. Capacity and growth must be integers. "
|
||||
"items at the given rate of growth. `capacity` and `growth` must be integers. "
|
||||
"If the backing capacity is already enough, then this function does nothing. "
|
||||
"Otherwise, the backing memory will be reallocated so that there is enough space.") {
|
||||
janet_fixarity(argc, 3);
|
||||
|
@ -211,7 +231,7 @@ JANET_CORE_FN(cfun_array_slice,
|
|||
"Takes a slice of array or tuple from `start` to `end`. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the "
|
||||
"end of the array. By default, `start` is 0 and `end` is the length of the array. "
|
||||
"Note that index -1 is synonymous with index `(length arrtup)` to allow a full "
|
||||
"Note that if the range is negative, it is taken as (start, end] to allow a full "
|
||||
"negative slice range. Returns a new array.") {
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
|
@ -259,8 +279,8 @@ JANET_CORE_FN(cfun_array_insert,
|
|||
"(array/insert arr at & xs)",
|
||||
"Insert all `xs` into array `arr` at index `at`. `at` should be an integer between "
|
||||
"0 and the length of the array. A negative value for `at` will index backwards from "
|
||||
"the end of the array, such that inserting at -1 appends to the array. "
|
||||
"Returns the array.") {
|
||||
"the end of the array, inserting after the index such that inserting at -1 appends to "
|
||||
"the array. Returns the array.") {
|
||||
size_t chunksize, restsize;
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
|
@ -297,7 +317,7 @@ JANET_CORE_FN(cfun_array_remove,
|
|||
int32_t at = janet_getinteger(argv, 1);
|
||||
int32_t n = 1;
|
||||
if (at < 0) {
|
||||
at = array->count + at + 1;
|
||||
at = array->count + at;
|
||||
}
|
||||
if (at < 0 || at > array->count)
|
||||
janet_panicf("removal index %d out of range [0,%d]", at, array->count);
|
||||
|
@ -352,6 +372,7 @@ JANET_CORE_FN(cfun_array_clear,
|
|||
void janet_lib_array(JanetTable *env) {
|
||||
JanetRegExt array_cfuns[] = {
|
||||
JANET_CORE_REG("array/new", cfun_array_new),
|
||||
JANET_CORE_REG("array/weak", cfun_array_weak),
|
||||
JANET_CORE_REG("array/new-filled", cfun_array_new_filled),
|
||||
JANET_CORE_REG("array/fill", cfun_array_fill),
|
||||
JANET_CORE_REG("array/pop", cfun_array_pop),
|
||||
|
|
118
src/core/asm.c
118
src/core/asm.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -75,6 +75,7 @@ static const JanetInstructionDef janet_ops[] = {
|
|||
{"cmp", JOP_COMPARE},
|
||||
{"cncl", JOP_CANCEL},
|
||||
{"div", JOP_DIVIDE},
|
||||
{"divf", JOP_DIVIDE_FLOOR},
|
||||
{"divim", JOP_DIVIDE_IMMEDIATE},
|
||||
{"eq", JOP_EQUALS},
|
||||
{"eqim", JOP_EQUALS_IMMEDIATE},
|
||||
|
@ -137,6 +138,7 @@ static const JanetInstructionDef janet_ops[] = {
|
|||
{"sru", JOP_SHIFT_RIGHT_UNSIGNED},
|
||||
{"sruim", JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
|
||||
{"sub", JOP_SUBTRACT},
|
||||
{"subim", JOP_SUBTRACT_IMMEDIATE},
|
||||
{"tcall", JOP_TAILCALL},
|
||||
{"tchck", JOP_TYPECHECK}
|
||||
};
|
||||
|
@ -187,7 +189,11 @@ static void janet_asm_longjmp(JanetAssembler *a) {
|
|||
|
||||
/* Throw some kind of assembly error */
|
||||
static void janet_asm_error(JanetAssembler *a, const char *message) {
|
||||
a->errmessage = janet_formatc("%s, instruction %d", message, a->errindex);
|
||||
if (a->errindex < 0) {
|
||||
a->errmessage = janet_formatc("%s", message);
|
||||
} else {
|
||||
a->errmessage = janet_formatc("%s, instruction %d", message, a->errindex);
|
||||
}
|
||||
janet_asm_longjmp(a);
|
||||
}
|
||||
#define janet_asm_assert(a, c, m) do { if (!(c)) janet_asm_error((a), (m)); } while (0)
|
||||
|
@ -516,6 +522,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
|||
#endif
|
||||
if (NULL != a.parent) {
|
||||
janet_asm_deinit(&a);
|
||||
a.parent->errmessage = a.errmessage;
|
||||
janet_asm_longjmp(a.parent);
|
||||
}
|
||||
result.funcdef = NULL;
|
||||
|
@ -553,6 +560,13 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
|||
x = janet_get1(s, janet_ckeywordv("vararg"));
|
||||
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
|
||||
|
||||
/* Initialize slotcount */
|
||||
def->slotcount = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG) + def->arity;
|
||||
|
||||
/* Check structarg */
|
||||
x = janet_get1(s, janet_ckeywordv("structarg"));
|
||||
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
|
||||
/* Check source */
|
||||
x = janet_get1(s, janet_ckeywordv("source"));
|
||||
if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x);
|
||||
|
@ -597,6 +611,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
|||
|
||||
/* Parse sub funcdefs */
|
||||
x = janet_get1(s, janet_ckeywordv("closures"));
|
||||
if (janet_checktype(x, JANET_NIL)) {
|
||||
x = janet_get1(s, janet_ckeywordv("defs"));
|
||||
}
|
||||
if (janet_indexed_view(x, &arr, &count)) {
|
||||
int32_t i;
|
||||
for (i = 0; i < count; i++) {
|
||||
|
@ -709,16 +726,70 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
|||
}
|
||||
}
|
||||
|
||||
/* Set symbolmap */
|
||||
def->symbolmap = NULL;
|
||||
def->symbolmap_length = 0;
|
||||
x = janet_get1(s, janet_ckeywordv("symbolmap"));
|
||||
if (janet_indexed_view(x, &arr, &count)) {
|
||||
def->symbolmap_length = count;
|
||||
def->symbolmap = janet_malloc(sizeof(JanetSymbolMap) * (size_t)count);
|
||||
if (NULL == def->symbolmap) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
const Janet *tup;
|
||||
Janet entry = arr[i];
|
||||
JanetSymbolMap ss;
|
||||
if (!janet_checktype(entry, JANET_TUPLE)) {
|
||||
janet_asm_error(&a, "expected tuple");
|
||||
}
|
||||
tup = janet_unwrap_tuple(entry);
|
||||
if (janet_keyeq(tup[0], "upvalue")) {
|
||||
ss.birth_pc = UINT32_MAX;
|
||||
} else if (!janet_checkint(tup[0])) {
|
||||
janet_asm_error(&a, "expected integer");
|
||||
} else {
|
||||
ss.birth_pc = janet_unwrap_integer(tup[0]);
|
||||
}
|
||||
if (!janet_checkint(tup[1])) {
|
||||
janet_asm_error(&a, "expected integer");
|
||||
}
|
||||
if (!janet_checkint(tup[2])) {
|
||||
janet_asm_error(&a, "expected integer");
|
||||
}
|
||||
if (!janet_checktype(tup[3], JANET_SYMBOL)) {
|
||||
janet_asm_error(&a, "expected symbol");
|
||||
}
|
||||
ss.death_pc = janet_unwrap_integer(tup[1]);
|
||||
ss.slot_index = janet_unwrap_integer(tup[2]);
|
||||
ss.symbol = janet_unwrap_symbol(tup[3]);
|
||||
def->symbolmap[i] = ss;
|
||||
}
|
||||
}
|
||||
if (def->symbolmap_length) def->flags |= JANET_FUNCDEF_FLAG_HASSYMBOLMAP;
|
||||
|
||||
/* Set environments */
|
||||
def->environments =
|
||||
janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
|
||||
if (NULL == def->environments) {
|
||||
x = janet_get1(s, janet_ckeywordv("environments"));
|
||||
if (janet_indexed_view(x, &arr, &count)) {
|
||||
def->environments_length = count;
|
||||
if (def->environments_length) {
|
||||
def->environments = janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
|
||||
}
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
if (!janet_checkint(arr[i])) {
|
||||
janet_asm_error(&a, "expected integer");
|
||||
}
|
||||
def->environments[i] = janet_unwrap_integer(arr[i]);
|
||||
}
|
||||
}
|
||||
if (def->environments_length && NULL == def->environments) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* Verify the func def */
|
||||
if (janet_verify(def)) {
|
||||
janet_asm_error(&a, "invalid assembly");
|
||||
int verify_status = janet_verify(def);
|
||||
if (verify_status) {
|
||||
janet_asm_errorv(&a, janet_formatc("invalid assembly (%d)", verify_status));
|
||||
}
|
||||
|
||||
/* Add final flags */
|
||||
|
@ -861,6 +932,29 @@ static Janet janet_disasm_slotcount(JanetFuncDef *def) {
|
|||
return janet_wrap_integer(def->slotcount);
|
||||
}
|
||||
|
||||
static Janet janet_disasm_symbolslots(JanetFuncDef *def) {
|
||||
if (def->symbolmap == NULL) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
JanetArray *symbolslots = janet_array(def->symbolmap_length);
|
||||
Janet upvaluekw = janet_ckeywordv("upvalue");
|
||||
for (int32_t i = 0; i < def->symbolmap_length; i++) {
|
||||
JanetSymbolMap ss = def->symbolmap[i];
|
||||
Janet *t = janet_tuple_begin(4);
|
||||
if (ss.birth_pc == UINT32_MAX) {
|
||||
t[0] = upvaluekw;
|
||||
} else {
|
||||
t[0] = janet_wrap_integer(ss.birth_pc);
|
||||
}
|
||||
t[1] = janet_wrap_integer(ss.death_pc);
|
||||
t[2] = janet_wrap_integer(ss.slot_index);
|
||||
t[3] = janet_wrap_symbol(ss.symbol);
|
||||
symbolslots->data[i] = janet_wrap_tuple(janet_tuple_end(t));
|
||||
}
|
||||
symbolslots->count = def->symbolmap_length;
|
||||
return janet_wrap_array(symbolslots);
|
||||
}
|
||||
|
||||
static Janet janet_disasm_bytecode(JanetFuncDef *def) {
|
||||
JanetArray *bcode = janet_array(def->bytecode_length);
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
|
@ -884,6 +978,10 @@ static Janet janet_disasm_vararg(JanetFuncDef *def) {
|
|||
return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_VARARG);
|
||||
}
|
||||
|
||||
static Janet janet_disasm_structarg(JanetFuncDef *def) {
|
||||
return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_STRUCTARG);
|
||||
}
|
||||
|
||||
static Janet janet_disasm_constants(JanetFuncDef *def) {
|
||||
JanetArray *constants = janet_array(def->constants_length);
|
||||
for (int32_t i = 0; i < def->constants_length; i++) {
|
||||
|
@ -933,8 +1031,10 @@ Janet janet_disasm(JanetFuncDef *def) {
|
|||
janet_table_put(ret, janet_ckeywordv("bytecode"), janet_disasm_bytecode(def));
|
||||
janet_table_put(ret, janet_ckeywordv("source"), janet_disasm_source(def));
|
||||
janet_table_put(ret, janet_ckeywordv("vararg"), janet_disasm_vararg(def));
|
||||
janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def));
|
||||
janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def));
|
||||
janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def));
|
||||
janet_table_put(ret, janet_ckeywordv("symbolmap"), janet_disasm_symbolslots(def));
|
||||
janet_table_put(ret, janet_ckeywordv("constants"), janet_disasm_constants(def));
|
||||
janet_table_put(ret, janet_ckeywordv("sourcemap"), janet_disasm_sourcemap(def));
|
||||
janet_table_put(ret, janet_ckeywordv("environments"), janet_disasm_environments(def));
|
||||
|
@ -952,7 +1052,7 @@ JANET_CORE_FN(cfun_asm,
|
|||
JanetAssembleResult res;
|
||||
res = janet_asm(argv[0], 0);
|
||||
if (res.status != JANET_ASSEMBLE_OK) {
|
||||
janet_panics(res.error);
|
||||
janet_panics(res.error ? res.error : janet_cstring("invalid assembly"));
|
||||
}
|
||||
return janet_wrap_function(janet_thunk(res.funcdef));
|
||||
}
|
||||
|
@ -971,6 +1071,7 @@ JANET_CORE_FN(cfun_disasm,
|
|||
"* :source - name of source file that this function was compiled from.\n"
|
||||
"* :name - name of function.\n"
|
||||
"* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n"
|
||||
"* :symbolmap - all symbols and their slots.\n"
|
||||
"* :constants - an array of constants referenced by this function.\n"
|
||||
"* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n"
|
||||
"* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n"
|
||||
|
@ -986,6 +1087,7 @@ JANET_CORE_FN(cfun_disasm,
|
|||
if (!janet_cstrcmp(kw, "source")) return janet_disasm_source(f->def);
|
||||
if (!janet_cstrcmp(kw, "name")) return janet_disasm_name(f->def);
|
||||
if (!janet_cstrcmp(kw, "vararg")) return janet_disasm_vararg(f->def);
|
||||
if (!janet_cstrcmp(kw, "structarg")) return janet_disasm_structarg(f->def);
|
||||
if (!janet_cstrcmp(kw, "slotcount")) return janet_disasm_slotcount(f->def);
|
||||
if (!janet_cstrcmp(kw, "constants")) return janet_disasm_constants(f->def);
|
||||
if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -28,8 +28,15 @@
|
|||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Allow for managed buffers that cannot realloc/free their backing memory */
|
||||
static void janet_buffer_can_realloc(JanetBuffer *buffer) {
|
||||
if (buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC) {
|
||||
janet_panic("buffer cannot reallocate foreign memory");
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize a buffer */
|
||||
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
|
||||
static JanetBuffer *janet_buffer_init_impl(JanetBuffer *buffer, int32_t capacity) {
|
||||
uint8_t *data = NULL;
|
||||
if (capacity < 4) capacity = 4;
|
||||
janet_gcpressure(capacity);
|
||||
|
@ -43,15 +50,37 @@ JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
|
|||
return buffer;
|
||||
}
|
||||
|
||||
/* Initialize a buffer */
|
||||
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
|
||||
janet_buffer_init_impl(buffer, capacity);
|
||||
buffer->gc.data.next = NULL;
|
||||
buffer->gc.flags = JANET_MEM_DISABLED;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Initialize an unmanaged buffer */
|
||||
JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t count) {
|
||||
if (count < 0) janet_panic("count < 0");
|
||||
if (capacity < count) janet_panic("capacity < count");
|
||||
JanetBuffer *buffer = janet_gcalloc(JANET_MEMORY_BUFFER, sizeof(JanetBuffer));
|
||||
buffer->gc.flags |= JANET_BUFFER_FLAG_NO_REALLOC;
|
||||
buffer->capacity = capacity;
|
||||
buffer->count = count;
|
||||
buffer->data = (uint8_t *) memory;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Deinitialize a buffer (free data memory) */
|
||||
void janet_buffer_deinit(JanetBuffer *buffer) {
|
||||
janet_free(buffer->data);
|
||||
if (!(buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
|
||||
janet_free(buffer->data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize a buffer */
|
||||
JanetBuffer *janet_buffer(int32_t capacity) {
|
||||
JanetBuffer *buffer = janet_gcalloc(JANET_MEMORY_BUFFER, sizeof(JanetBuffer));
|
||||
return janet_buffer_init(buffer, capacity);
|
||||
return janet_buffer_init_impl(buffer, capacity);
|
||||
}
|
||||
|
||||
/* Ensure that the buffer has enough internal capacity */
|
||||
|
@ -59,6 +88,7 @@ void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth)
|
|||
uint8_t *new_data;
|
||||
uint8_t *old = buffer->data;
|
||||
if (capacity <= buffer->capacity) return;
|
||||
janet_buffer_can_realloc(buffer);
|
||||
int64_t big_capacity = ((int64_t) capacity) * growth;
|
||||
capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity;
|
||||
janet_gcpressure(capacity - buffer->capacity);
|
||||
|
@ -91,6 +121,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
|
|||
}
|
||||
int32_t new_size = buffer->count + n;
|
||||
if (new_size > buffer->capacity) {
|
||||
janet_buffer_can_realloc(buffer);
|
||||
int32_t new_capacity = (new_size > (INT32_MAX / 2)) ? INT32_MAX : (new_size * 2);
|
||||
uint8_t *new_data = janet_realloc(buffer->data, new_capacity * sizeof(uint8_t));
|
||||
janet_gcpressure(new_capacity - buffer->capacity);
|
||||
|
@ -104,8 +135,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
|
|||
|
||||
/* Push a cstring to buffer */
|
||||
void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) {
|
||||
int32_t len = 0;
|
||||
while (cstring[len]) ++len;
|
||||
int32_t len = (int32_t) strlen(cstring);
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len);
|
||||
}
|
||||
|
||||
|
@ -164,7 +194,7 @@ void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x) {
|
|||
|
||||
JANET_CORE_FN(cfun_buffer_new,
|
||||
"(buffer/new capacity)",
|
||||
"Creates a new, empty buffer with enough backing memory for capacity bytes. "
|
||||
"Creates a new, empty buffer with enough backing memory for `capacity` bytes. "
|
||||
"Returns a new buffer of length 0.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
|
@ -174,21 +204,36 @@ JANET_CORE_FN(cfun_buffer_new,
|
|||
|
||||
JANET_CORE_FN(cfun_buffer_new_filled,
|
||||
"(buffer/new-filled count &opt byte)",
|
||||
"Creates a new buffer of length count filled with byte. By default, byte is 0. "
|
||||
"Creates a new buffer of length `count` filled with `byte`. By default, `byte` is 0. "
|
||||
"Returns the new buffer.") {
|
||||
janet_arity(argc, 1, 2);
|
||||
int32_t count = janet_getinteger(argv, 0);
|
||||
if (count < 0) count = 0;
|
||||
int32_t byte = 0;
|
||||
if (argc == 2) {
|
||||
byte = janet_getinteger(argv, 1) & 0xFF;
|
||||
}
|
||||
JanetBuffer *buffer = janet_buffer(count);
|
||||
if (buffer->data)
|
||||
if (buffer->data && count > 0)
|
||||
memset(buffer->data, byte, count);
|
||||
buffer->count = count;
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_frombytes,
|
||||
"(buffer/from-bytes & byte-vals)",
|
||||
"Creates a buffer from integer parameters with byte values. All integers "
|
||||
"will be coerced to the range of 1 byte 0-255.") {
|
||||
int32_t i;
|
||||
JanetBuffer *buffer = janet_buffer(argc);
|
||||
for (i = 0; i < argc; i++) {
|
||||
int32_t c = janet_getinteger(argv, i);
|
||||
buffer->data[i] = c & 0xFF;
|
||||
}
|
||||
buffer->count = argc;
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_fill,
|
||||
"(buffer/fill buffer &opt byte)",
|
||||
"Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. "
|
||||
|
@ -211,6 +256,7 @@ JANET_CORE_FN(cfun_buffer_trim,
|
|||
"modified buffer.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
janet_buffer_can_realloc(buffer);
|
||||
if (buffer->count < buffer->capacity) {
|
||||
int32_t newcap = buffer->count > 4 ? buffer->count : 4;
|
||||
uint8_t *newData = janet_realloc(buffer->data, newcap);
|
||||
|
@ -274,17 +320,145 @@ JANET_CORE_FN(cfun_buffer_chars,
|
|||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push,
|
||||
"(buffer/push buffer & xs)",
|
||||
"Push both individual bytes and byte sequences to a buffer. For each x in xs, "
|
||||
"push the byte if x is an integer, otherwise push the bytesequence to the buffer. "
|
||||
"Thus, this function behaves like both `buffer/push-string` and `buffer/push-byte`. "
|
||||
"Returns the modified buffer. "
|
||||
"Will throw an error if the buffer overflows.") {
|
||||
int32_t i;
|
||||
janet_arity(argc, 1, -1);
|
||||
static int should_reverse_bytes(const Janet *argv, int32_t argc) {
|
||||
JanetKeyword order_kw = janet_getkeyword(argv, argc);
|
||||
if (!janet_cstrcmp(order_kw, "le")) {
|
||||
#if JANET_BIG_ENDIAN
|
||||
return 1;
|
||||
#endif
|
||||
} else if (!janet_cstrcmp(order_kw, "be")) {
|
||||
#if JANET_LITTLE_ENDIAN
|
||||
return 1;
|
||||
#endif
|
||||
} else if (!janet_cstrcmp(order_kw, "native")) {
|
||||
return 0;
|
||||
} else {
|
||||
janet_panicf("expected endianness :le, :be or :native, got %v", argv[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reverse_u32(uint8_t bytes[4]) {
|
||||
uint8_t temp;
|
||||
temp = bytes[3];
|
||||
bytes[3] = bytes[0];
|
||||
bytes[0] = temp;
|
||||
temp = bytes[2];
|
||||
bytes[2] = bytes[1];
|
||||
bytes[1] = temp;
|
||||
}
|
||||
|
||||
static void reverse_u64(uint8_t bytes[8]) {
|
||||
uint8_t temp;
|
||||
temp = bytes[7];
|
||||
bytes[7] = bytes[0];
|
||||
bytes[0] = temp;
|
||||
temp = bytes[6];
|
||||
bytes[6] = bytes[1];
|
||||
bytes[1] = temp;
|
||||
temp = bytes[5];
|
||||
bytes[5] = bytes[2];
|
||||
bytes[2] = temp;
|
||||
temp = bytes[4];
|
||||
bytes[4] = bytes[3];
|
||||
bytes[3] = temp;
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_uint16,
|
||||
"(buffer/push-uint16 buffer order data)",
|
||||
"Push a 16 bit unsigned integer data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
for (i = 1; i < argc; i++) {
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
union {
|
||||
uint16_t data;
|
||||
uint8_t bytes[2];
|
||||
} u;
|
||||
u.data = (uint16_t) janet_getinteger(argv, 2);
|
||||
if (reverse) {
|
||||
uint8_t temp = u.bytes[1];
|
||||
u.bytes[1] = u.bytes[0];
|
||||
u.bytes[0] = temp;
|
||||
}
|
||||
janet_buffer_push_u16(buffer, *(uint16_t *) u.bytes);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_uint32,
|
||||
"(buffer/push-uint32 buffer order data)",
|
||||
"Push a 32 bit unsigned integer data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
union {
|
||||
uint32_t data;
|
||||
uint8_t bytes[4];
|
||||
} u;
|
||||
u.data = (uint32_t) janet_getinteger(argv, 2);
|
||||
if (reverse)
|
||||
reverse_u32(u.bytes);
|
||||
janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_uint64,
|
||||
"(buffer/push-uint64 buffer order data)",
|
||||
"Push a 64 bit unsigned integer data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
union {
|
||||
uint64_t data;
|
||||
uint8_t bytes[8];
|
||||
} u;
|
||||
u.data = (uint64_t) janet_getuinteger64(argv, 2);
|
||||
if (reverse)
|
||||
reverse_u64(u.bytes);
|
||||
janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_float32,
|
||||
"(buffer/push-float32 buffer order data)",
|
||||
"Push the underlying bytes of a 32 bit float data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
union {
|
||||
float data;
|
||||
uint8_t bytes[4];
|
||||
} u;
|
||||
u.data = (float) janet_getnumber(argv, 2);
|
||||
if (reverse)
|
||||
reverse_u32(u.bytes);
|
||||
janet_buffer_push_u32(buffer, *(uint32_t *) u.bytes);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_float64,
|
||||
"(buffer/push-float64 buffer order data)",
|
||||
"Push the underlying bytes of a 64 bit float data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
union {
|
||||
double data;
|
||||
uint8_t bytes[8];
|
||||
} u;
|
||||
u.data = janet_getnumber(argv, 2);
|
||||
if (reverse)
|
||||
reverse_u64(u.bytes);
|
||||
janet_buffer_push_u64(buffer, *(uint64_t *) u.bytes);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offset, int32_t argc) {
|
||||
for (int32_t i = argc_offset; i < argc; i++) {
|
||||
if (janet_checktype(argv[i], JANET_NUMBER)) {
|
||||
janet_buffer_push_u8(buffer, (uint8_t)(janet_getinteger(argv, i) & 0xFF));
|
||||
} else {
|
||||
|
@ -296,9 +470,39 @@ JANET_CORE_FN(cfun_buffer_push,
|
|||
janet_buffer_push_bytes(buffer, view.bytes, view.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_at,
|
||||
"(buffer/push-at buffer index & xs)",
|
||||
"Same as buffer/push, but copies the new data into the buffer "
|
||||
" at index `index`.") {
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int32_t index = janet_getinteger(argv, 1);
|
||||
int32_t old_count = buffer->count;
|
||||
if (index < 0 || index > old_count) {
|
||||
janet_panicf("index out of range [0, %d)", old_count);
|
||||
}
|
||||
buffer->count = index;
|
||||
buffer_push_impl(buffer, argv, 2, argc);
|
||||
if (buffer->count < old_count) {
|
||||
buffer->count = old_count;
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push,
|
||||
"(buffer/push buffer & xs)",
|
||||
"Push both individual bytes and byte sequences to a buffer. For each x in xs, "
|
||||
"push the byte if x is an integer, otherwise push the bytesequence to the buffer. "
|
||||
"Thus, this function behaves like both `buffer/push-string` and `buffer/push-byte`. "
|
||||
"Returns the modified buffer. "
|
||||
"Will throw an error if the buffer overflows.") {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
buffer_push_impl(buffer, argv, 1, argc);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_clear,
|
||||
"(buffer/clear buffer)",
|
||||
|
@ -312,7 +516,7 @@ JANET_CORE_FN(cfun_buffer_clear,
|
|||
|
||||
JANET_CORE_FN(cfun_buffer_popn,
|
||||
"(buffer/popn buffer n)",
|
||||
"Removes the last n bytes from the buffer. Returns the modified buffer.") {
|
||||
"Removes the last `n` bytes from the buffer. Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int32_t n = janet_getinteger(argv, 1);
|
||||
|
@ -327,9 +531,9 @@ JANET_CORE_FN(cfun_buffer_popn,
|
|||
|
||||
JANET_CORE_FN(cfun_buffer_slice,
|
||||
"(buffer/slice bytes &opt start end)",
|
||||
"Takes a slice of a byte sequence from start to end. The range is half open, "
|
||||
"Takes a slice of a byte sequence from `start` to `end`. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the end of the "
|
||||
"end of the array. By default, start is 0 and end is the length of the buffer. "
|
||||
"end of the array. By default, `start` is 0 and `end` is the length of the buffer. "
|
||||
"Returns a new buffer.") {
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
|
@ -399,22 +603,24 @@ JANET_CORE_FN(cfun_buffer_bittoggle,
|
|||
|
||||
JANET_CORE_FN(cfun_buffer_blit,
|
||||
"(buffer/blit dest src &opt dest-start src-start src-end)",
|
||||
"Insert the contents of src into dest. Can optionally take indices that "
|
||||
"indicate which part of src to copy into which part of dest. Indices can be "
|
||||
"negative to index from the end of src or dest. Returns dest.") {
|
||||
"Insert the contents of `src` into `dest`. Can optionally take indices that "
|
||||
"indicate which part of `src` to copy into which part of `dest`. Indices can be "
|
||||
"negative in order to index from the end of `src` or `dest`. Returns `dest`.") {
|
||||
janet_arity(argc, 2, 5);
|
||||
JanetBuffer *dest = janet_getbuffer(argv, 0);
|
||||
JanetByteView src = janet_getbytes(argv, 1);
|
||||
int same_buf = src.bytes == dest->data;
|
||||
int32_t offset_dest = 0;
|
||||
int32_t offset_src = 0;
|
||||
if (argc > 2)
|
||||
if (argc > 2 && !janet_checktype(argv[2], JANET_NIL))
|
||||
offset_dest = janet_gethalfrange(argv, 2, dest->count, "dest-start");
|
||||
if (argc > 3)
|
||||
if (argc > 3 && !janet_checktype(argv[3], JANET_NIL))
|
||||
offset_src = janet_gethalfrange(argv, 3, src.len, "src-start");
|
||||
int32_t length_src;
|
||||
if (argc > 4) {
|
||||
int32_t src_end = janet_gethalfrange(argv, 4, src.len, "src-end");
|
||||
int32_t src_end = src.len;
|
||||
if (!janet_checktype(argv[4], JANET_NIL))
|
||||
src_end = janet_gethalfrange(argv, 4, src.len, "src-end");
|
||||
length_src = src_end - offset_src;
|
||||
if (length_src < 0) length_src = 0;
|
||||
} else {
|
||||
|
@ -441,7 +647,7 @@ JANET_CORE_FN(cfun_buffer_blit,
|
|||
JANET_CORE_FN(cfun_buffer_format,
|
||||
"(buffer/format buffer format & args)",
|
||||
"Snprintf like functionality for printing values into a buffer. Returns "
|
||||
" the modified buffer.") {
|
||||
"the modified buffer.") {
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
const char *strfrmt = (const char *) janet_getstring(argv, 1);
|
||||
|
@ -449,16 +655,44 @@ JANET_CORE_FN(cfun_buffer_format,
|
|||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_format_at,
|
||||
"(buffer/format-at buffer at format & args)",
|
||||
"Snprintf like functionality for printing values into a buffer. Returns "
|
||||
"the modified buffer.") {
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int32_t at = janet_getinteger(argv, 1);
|
||||
if (at < 0) {
|
||||
at += buffer->count + 1;
|
||||
}
|
||||
if (at > buffer->count || at < 0) janet_panicf("expected index at to be in range [0, %d), got %d", buffer->count, at);
|
||||
int32_t oldcount = buffer->count;
|
||||
buffer->count = at;
|
||||
const char *strfrmt = (const char *) janet_getstring(argv, 2);
|
||||
janet_buffer_format(buffer, strfrmt, 2, argc, argv);
|
||||
if (buffer->count < oldcount) {
|
||||
buffer->count = oldcount;
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
void janet_lib_buffer(JanetTable *env) {
|
||||
JanetRegExt buffer_cfuns[] = {
|
||||
JANET_CORE_REG("buffer/new", cfun_buffer_new),
|
||||
JANET_CORE_REG("buffer/new-filled", cfun_buffer_new_filled),
|
||||
JANET_CORE_REG("buffer/from-bytes", cfun_buffer_frombytes),
|
||||
JANET_CORE_REG("buffer/fill", cfun_buffer_fill),
|
||||
JANET_CORE_REG("buffer/trim", cfun_buffer_trim),
|
||||
JANET_CORE_REG("buffer/push-byte", cfun_buffer_u8),
|
||||
JANET_CORE_REG("buffer/push-word", cfun_buffer_word),
|
||||
JANET_CORE_REG("buffer/push-string", cfun_buffer_chars),
|
||||
JANET_CORE_REG("buffer/push-uint16", cfun_buffer_push_uint16),
|
||||
JANET_CORE_REG("buffer/push-uint32", cfun_buffer_push_uint32),
|
||||
JANET_CORE_REG("buffer/push-uint64", cfun_buffer_push_uint64),
|
||||
JANET_CORE_REG("buffer/push-float32", cfun_buffer_push_float32),
|
||||
JANET_CORE_REG("buffer/push-float64", cfun_buffer_push_float64),
|
||||
JANET_CORE_REG("buffer/push", cfun_buffer_push),
|
||||
JANET_CORE_REG("buffer/push-at", cfun_buffer_push_at),
|
||||
JANET_CORE_REG("buffer/popn", cfun_buffer_popn),
|
||||
JANET_CORE_REG("buffer/clear", cfun_buffer_clear),
|
||||
JANET_CORE_REG("buffer/slice", cfun_buffer_slice),
|
||||
|
@ -468,6 +702,7 @@ void janet_lib_buffer(JanetTable *env) {
|
|||
JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle),
|
||||
JANET_CORE_REG("buffer/blit", cfun_buffer_blit),
|
||||
JANET_CORE_REG("buffer/format", cfun_buffer_format),
|
||||
JANET_CORE_REG("buffer/format-at", cfun_buffer_format_at),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, buffer_cfuns);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -25,6 +25,7 @@
|
|||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include "regalloc.h"
|
||||
#endif
|
||||
|
||||
/* Look up table for instructions */
|
||||
|
@ -36,11 +37,13 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
|||
JINT_0, /* JOP_RETURN_NIL, */
|
||||
JINT_SSI, /* JOP_ADD_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_ADD, */
|
||||
JINT_SSI, /* JOP_SUBTRACT_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_SUBTRACT, */
|
||||
JINT_SSI, /* JOP_MULTIPLY_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_MULTIPLY, */
|
||||
JINT_SSI, /* JOP_DIVIDE_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_DIVIDE, */
|
||||
JINT_SSS, /* JOP_DIVIDE_FLOOR */
|
||||
JINT_SSS, /* JOP_MODULO, */
|
||||
JINT_SSS, /* JOP_REMAINDER, */
|
||||
JINT_SSS, /* JOP_BAND, */
|
||||
|
@ -106,6 +109,294 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
|||
JINT_SSS /* JOP_CANCEL, */
|
||||
};
|
||||
|
||||
/* Remove all noops while preserving jumps and debugging information.
|
||||
* Useful as part of a filtering compiler pass. */
|
||||
void janet_bytecode_remove_noops(JanetFuncDef *def) {
|
||||
|
||||
/* Get an instruction rewrite map so we can rewrite jumps */
|
||||
uint32_t *pc_map = janet_smalloc(sizeof(uint32_t) * (1 + def->bytecode_length));
|
||||
uint32_t new_bytecode_length = 0;
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
uint32_t instr = def->bytecode[i];
|
||||
uint32_t opcode = instr & 0x7F;
|
||||
pc_map[i] = new_bytecode_length;
|
||||
if (opcode != JOP_NOOP) {
|
||||
new_bytecode_length++;
|
||||
}
|
||||
}
|
||||
pc_map[def->bytecode_length] = new_bytecode_length;
|
||||
|
||||
/* Linear scan rewrite bytecode and sourcemap. Also fix jumps. */
|
||||
int32_t j = 0;
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
uint32_t instr = def->bytecode[i];
|
||||
uint32_t opcode = instr & 0x7F;
|
||||
int32_t old_jump_target = 0;
|
||||
int32_t new_jump_target = 0;
|
||||
switch (opcode) {
|
||||
case JOP_NOOP:
|
||||
continue;
|
||||
case JOP_JUMP:
|
||||
/* relative pc is in DS field of instruction */
|
||||
old_jump_target = i + (((int32_t)instr) >> 8);
|
||||
new_jump_target = pc_map[old_jump_target];
|
||||
instr += (new_jump_target - old_jump_target + (i - j)) << 8;
|
||||
break;
|
||||
case JOP_JUMP_IF:
|
||||
case JOP_JUMP_IF_NIL:
|
||||
case JOP_JUMP_IF_NOT:
|
||||
case JOP_JUMP_IF_NOT_NIL:
|
||||
/* relative pc is in ES field of instruction */
|
||||
old_jump_target = i + (((int32_t)instr) >> 16);
|
||||
new_jump_target = pc_map[old_jump_target];
|
||||
instr += (new_jump_target - old_jump_target + (i - j)) << 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
def->bytecode[j] = instr;
|
||||
if (def->sourcemap != NULL) {
|
||||
def->sourcemap[j] = def->sourcemap[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
/* Rewrite symbolmap */
|
||||
for (int32_t i = 0; i < def->symbolmap_length; i++) {
|
||||
JanetSymbolMap *sm = def->symbolmap + i;
|
||||
/* Don't rewrite upvalue mappings */
|
||||
if (sm->birth_pc < UINT32_MAX) {
|
||||
sm->birth_pc = pc_map[sm->birth_pc];
|
||||
sm->death_pc = pc_map[sm->death_pc];
|
||||
}
|
||||
}
|
||||
|
||||
def->bytecode_length = new_bytecode_length;
|
||||
def->bytecode = janet_realloc(def->bytecode, def->bytecode_length * sizeof(uint32_t));
|
||||
janet_sfree(pc_map);
|
||||
}
|
||||
|
||||
/* Remove redundant loads, moves and other instructions if possible and convert them to
|
||||
* noops. Input is assumed valid bytecode. */
|
||||
void janet_bytecode_movopt(JanetFuncDef *def) {
|
||||
JanetcRegisterAllocator ra;
|
||||
int recur = 1;
|
||||
|
||||
/* Iterate this until no more instructions can be removed. */
|
||||
while (recur) {
|
||||
janetc_regalloc_init(&ra);
|
||||
|
||||
/* Look for slots that have writes but no reads (and aren't in the closure bitset). */
|
||||
if (def->closure_bitset != NULL) {
|
||||
for (int32_t i = 0; i < def->slotcount; i++) {
|
||||
int32_t index = i >> 5;
|
||||
uint32_t mask = 1U << (((uint32_t) i) & 31);
|
||||
if (def->closure_bitset[index] & mask) {
|
||||
janetc_regalloc_touch(&ra, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define AA ((instr >> 8) & 0xFF)
|
||||
#define BB ((instr >> 16) & 0xFF)
|
||||
#define CC (instr >> 24)
|
||||
#define DD (instr >> 8)
|
||||
#define EE (instr >> 16)
|
||||
|
||||
/* Check reads and writes */
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
uint32_t instr = def->bytecode[i];
|
||||
switch (instr & 0x7F) {
|
||||
|
||||
/* Group instructions my how they read from slots */
|
||||
|
||||
/* No reads or writes */
|
||||
default:
|
||||
janet_assert(0, "unhandled instruction");
|
||||
case JOP_JUMP:
|
||||
case JOP_NOOP:
|
||||
case JOP_RETURN_NIL:
|
||||
/* Write A */
|
||||
case JOP_LOAD_INTEGER:
|
||||
case JOP_LOAD_CONSTANT:
|
||||
case JOP_LOAD_UPVALUE:
|
||||
case JOP_CLOSURE:
|
||||
/* Write D */
|
||||
case JOP_LOAD_NIL:
|
||||
case JOP_LOAD_TRUE:
|
||||
case JOP_LOAD_FALSE:
|
||||
case JOP_LOAD_SELF:
|
||||
break;
|
||||
case JOP_MAKE_ARRAY:
|
||||
case JOP_MAKE_BUFFER:
|
||||
case JOP_MAKE_STRING:
|
||||
case JOP_MAKE_STRUCT:
|
||||
case JOP_MAKE_TABLE:
|
||||
case JOP_MAKE_TUPLE:
|
||||
case JOP_MAKE_BRACKET_TUPLE:
|
||||
/* Reads from the stack, don't remove */
|
||||
janetc_regalloc_touch(&ra, DD);
|
||||
break;
|
||||
|
||||
/* Read A */
|
||||
case JOP_ERROR:
|
||||
case JOP_TYPECHECK:
|
||||
case JOP_JUMP_IF:
|
||||
case JOP_JUMP_IF_NOT:
|
||||
case JOP_JUMP_IF_NIL:
|
||||
case JOP_JUMP_IF_NOT_NIL:
|
||||
case JOP_SET_UPVALUE:
|
||||
/* Write E, Read A */
|
||||
case JOP_MOVE_FAR:
|
||||
janetc_regalloc_touch(&ra, AA);
|
||||
break;
|
||||
|
||||
/* Read B */
|
||||
case JOP_SIGNAL:
|
||||
/* Write A, Read B */
|
||||
case JOP_ADD_IMMEDIATE:
|
||||
case JOP_SUBTRACT_IMMEDIATE:
|
||||
case JOP_MULTIPLY_IMMEDIATE:
|
||||
case JOP_DIVIDE_IMMEDIATE:
|
||||
case JOP_SHIFT_LEFT_IMMEDIATE:
|
||||
case JOP_SHIFT_RIGHT_IMMEDIATE:
|
||||
case JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE:
|
||||
case JOP_GREATER_THAN_IMMEDIATE:
|
||||
case JOP_LESS_THAN_IMMEDIATE:
|
||||
case JOP_EQUALS_IMMEDIATE:
|
||||
case JOP_NOT_EQUALS_IMMEDIATE:
|
||||
case JOP_GET_INDEX:
|
||||
janetc_regalloc_touch(&ra, BB);
|
||||
break;
|
||||
|
||||
/* Read D */
|
||||
case JOP_RETURN:
|
||||
case JOP_PUSH:
|
||||
case JOP_PUSH_ARRAY:
|
||||
case JOP_TAILCALL:
|
||||
janetc_regalloc_touch(&ra, DD);
|
||||
break;
|
||||
|
||||
/* Write A, Read E */
|
||||
case JOP_MOVE_NEAR:
|
||||
case JOP_LENGTH:
|
||||
case JOP_BNOT:
|
||||
case JOP_CALL:
|
||||
janetc_regalloc_touch(&ra, EE);
|
||||
break;
|
||||
|
||||
/* Read A, B */
|
||||
case JOP_PUT_INDEX:
|
||||
janetc_regalloc_touch(&ra, AA);
|
||||
janetc_regalloc_touch(&ra, BB);
|
||||
break;
|
||||
|
||||
/* Read A, E */
|
||||
case JOP_PUSH_2:
|
||||
janetc_regalloc_touch(&ra, AA);
|
||||
janetc_regalloc_touch(&ra, EE);
|
||||
break;
|
||||
|
||||
/* Read B, C */
|
||||
case JOP_PROPAGATE:
|
||||
/* Write A, Read B and C */
|
||||
case JOP_BAND:
|
||||
case JOP_BOR:
|
||||
case JOP_BXOR:
|
||||
case JOP_ADD:
|
||||
case JOP_SUBTRACT:
|
||||
case JOP_MULTIPLY:
|
||||
case JOP_DIVIDE:
|
||||
case JOP_DIVIDE_FLOOR:
|
||||
case JOP_MODULO:
|
||||
case JOP_REMAINDER:
|
||||
case JOP_SHIFT_LEFT:
|
||||
case JOP_SHIFT_RIGHT:
|
||||
case JOP_SHIFT_RIGHT_UNSIGNED:
|
||||
case JOP_GREATER_THAN:
|
||||
case JOP_LESS_THAN:
|
||||
case JOP_EQUALS:
|
||||
case JOP_COMPARE:
|
||||
case JOP_IN:
|
||||
case JOP_GET:
|
||||
case JOP_GREATER_THAN_EQUAL:
|
||||
case JOP_LESS_THAN_EQUAL:
|
||||
case JOP_NOT_EQUALS:
|
||||
case JOP_CANCEL:
|
||||
case JOP_RESUME:
|
||||
case JOP_NEXT:
|
||||
janetc_regalloc_touch(&ra, BB);
|
||||
janetc_regalloc_touch(&ra, CC);
|
||||
break;
|
||||
|
||||
/* Read A, B, C */
|
||||
case JOP_PUT:
|
||||
case JOP_PUSH_3:
|
||||
janetc_regalloc_touch(&ra, AA);
|
||||
janetc_regalloc_touch(&ra, BB);
|
||||
janetc_regalloc_touch(&ra, CC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate and set noops on instructions that make writes that no one ever reads.
|
||||
* Only set noops for instructions with no side effects - moves, loads, etc. that can't
|
||||
* raise errors (outside of systemic errors like oom or stack overflow). */
|
||||
recur = 0;
|
||||
for (int32_t i = 0; i < def->bytecode_length; i++) {
|
||||
uint32_t instr = def->bytecode[i];
|
||||
switch (instr & 0x7F) {
|
||||
default:
|
||||
break;
|
||||
/* Write D */
|
||||
case JOP_LOAD_NIL:
|
||||
case JOP_LOAD_TRUE:
|
||||
case JOP_LOAD_FALSE:
|
||||
case JOP_LOAD_SELF:
|
||||
case JOP_MAKE_ARRAY:
|
||||
case JOP_MAKE_TUPLE:
|
||||
case JOP_MAKE_BRACKET_TUPLE: {
|
||||
if (!janetc_regalloc_check(&ra, DD)) {
|
||||
def->bytecode[i] = JOP_NOOP;
|
||||
recur = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* Write E, Read A */
|
||||
case JOP_MOVE_FAR: {
|
||||
if (!janetc_regalloc_check(&ra, EE)) {
|
||||
def->bytecode[i] = JOP_NOOP;
|
||||
recur = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* Write A, Read E */
|
||||
case JOP_MOVE_NEAR:
|
||||
/* Write A, Read B */
|
||||
case JOP_GET_INDEX:
|
||||
/* Write A */
|
||||
case JOP_LOAD_INTEGER:
|
||||
case JOP_LOAD_CONSTANT:
|
||||
case JOP_LOAD_UPVALUE:
|
||||
case JOP_CLOSURE: {
|
||||
if (!janetc_regalloc_check(&ra, AA)) {
|
||||
def->bytecode[i] = JOP_NOOP;
|
||||
recur = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
janetc_regalloc_deinit(&ra);
|
||||
#undef AA
|
||||
#undef BB
|
||||
#undef CC
|
||||
#undef DD
|
||||
#undef EE
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify some bytecode */
|
||||
int janet_verify(JanetFuncDef *def) {
|
||||
int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG);
|
||||
|
@ -218,6 +509,7 @@ JanetFuncDef *janet_funcdef_alloc(void) {
|
|||
def->closure_bitset = NULL;
|
||||
def->flags = 0;
|
||||
def->slotcount = 0;
|
||||
def->symbolmap = NULL;
|
||||
def->arity = 0;
|
||||
def->min_arity = 0;
|
||||
def->max_arity = INT32_MAX;
|
||||
|
@ -229,6 +521,7 @@ JanetFuncDef *janet_funcdef_alloc(void) {
|
|||
def->constants_length = 0;
|
||||
def->bytecode_length = 0;
|
||||
def->environments_length = 0;
|
||||
def->symbolmap_length = 0;
|
||||
return def;
|
||||
}
|
||||
|
||||
|
|
153
src/core/capi.c
153
src/core/capi.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -35,6 +35,13 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JANET_USE_STDATOMIC
|
||||
#include <stdatomic.h>
|
||||
/* We don't need stdatomic on most compilers since we use compiler builtins for atomic operations.
|
||||
* Some (TCC), explicitly require using stdatomic.h and don't have any exposed builtins (that I know of).
|
||||
* For TCC and similar compilers, one would need -std=c11 or similar then to get access. */
|
||||
#endif
|
||||
|
||||
JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
|
||||
#ifdef JANET_TOP_LEVEL_SIGNAL
|
||||
JANET_TOP_LEVEL_SIGNAL(msg);
|
||||
|
@ -209,12 +216,46 @@ const char *janet_optcstring(const Janet *argv, int32_t argc, int32_t n, const c
|
|||
#undef DEFINE_OPTLEN
|
||||
|
||||
const char *janet_getcstring(const Janet *argv, int32_t n) {
|
||||
const uint8_t *jstr = janet_getstring(argv, n);
|
||||
const char *cstr = (const char *)jstr;
|
||||
if (strlen(cstr) != (size_t) janet_string_length(jstr)) {
|
||||
janet_panic("string contains embedded 0s");
|
||||
if (!janet_checktype(argv[n], JANET_STRING)) {
|
||||
janet_panic_type(argv[n], n, JANET_TFLAG_STRING);
|
||||
}
|
||||
return janet_getcbytes(argv, n);
|
||||
}
|
||||
|
||||
const char *janet_getcbytes(const Janet *argv, int32_t n) {
|
||||
/* Ensure buffer 0-padded */
|
||||
if (janet_checktype(argv[n], JANET_BUFFER)) {
|
||||
JanetBuffer *b = janet_unwrap_buffer(argv[n]);
|
||||
if ((b->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC) && b->count == b->capacity) {
|
||||
/* Make a copy with janet_smalloc in the rare case we have a buffer that
|
||||
* cannot be realloced and pushing a 0 byte would panic. */
|
||||
char *new_string = janet_smalloc(b->count + 1);
|
||||
memcpy(new_string, b->data, b->count);
|
||||
new_string[b->count] = 0;
|
||||
if (strlen(new_string) != (size_t) b->count) goto badzeros;
|
||||
return new_string;
|
||||
} else {
|
||||
/* Ensure trailing 0 */
|
||||
janet_buffer_push_u8(b, 0);
|
||||
b->count--;
|
||||
if (strlen((char *)b->data) != (size_t) b->count) goto badzeros;
|
||||
return (const char *) b->data;
|
||||
}
|
||||
}
|
||||
JanetByteView view = janet_getbytes(argv, n);
|
||||
const char *cstr = (const char *)view.bytes;
|
||||
if (strlen(cstr) != (size_t) view.len) goto badzeros;
|
||||
return cstr;
|
||||
|
||||
badzeros:
|
||||
janet_panic("bytes contain embedded 0s");
|
||||
}
|
||||
|
||||
const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt) {
|
||||
if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
|
||||
return dflt;
|
||||
}
|
||||
return janet_getcbytes(argv, n);
|
||||
}
|
||||
|
||||
int32_t janet_getnat(const Janet *argv, int32_t n) {
|
||||
|
@ -259,12 +300,36 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
|
|||
return janet_unwrap_integer(x);
|
||||
}
|
||||
|
||||
uint32_t janet_getuinteger(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
if (!janet_checkuint(x)) {
|
||||
janet_panicf("bad slot #%d, expected 32 bit signed integer, got %v", n, x);
|
||||
}
|
||||
return janet_unwrap_integer(x);
|
||||
}
|
||||
|
||||
int64_t janet_getinteger64(const Janet *argv, int32_t n) {
|
||||
#ifdef JANET_INT_TYPES
|
||||
return janet_unwrap_s64(argv[n]);
|
||||
#else
|
||||
Janet x = argv[n];
|
||||
if (!janet_checkint64(x)) {
|
||||
janet_panicf("bad slot #%d, expected 64 bit signed integer, got %v", n, x);
|
||||
}
|
||||
return (int64_t) janet_unwrap_number(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t janet_getuinteger64(const Janet *argv, int32_t n) {
|
||||
#ifdef JANET_INT_TYPES
|
||||
return janet_unwrap_u64(argv[n]);
|
||||
#else
|
||||
Janet x = argv[n];
|
||||
if (!janet_checkuint64(x)) {
|
||||
janet_panicf("bad slot #%d, expected 64 bit unsigned integer, got %v", n, x);
|
||||
}
|
||||
return (uint64_t) janet_unwrap_number(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t janet_getsize(const Janet *argv, int32_t n) {
|
||||
|
@ -280,16 +345,30 @@ int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const c
|
|||
int32_t not_raw = raw;
|
||||
if (not_raw < 0) not_raw += length + 1;
|
||||
if (not_raw < 0 || not_raw > length)
|
||||
janet_panicf("%s index %d out of range [%d,%d]", which, raw, -length - 1, length);
|
||||
janet_panicf("%s index %d out of range [%d,%d]", which, (int64_t) raw, -(int64_t)length - 1, (int64_t) length);
|
||||
return not_raw;
|
||||
}
|
||||
|
||||
int32_t janet_getstartrange(const Janet *argv, int32_t argc, int32_t n, int32_t length) {
|
||||
if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
|
||||
return 0;
|
||||
}
|
||||
return janet_gethalfrange(argv, n, length, "start");
|
||||
}
|
||||
|
||||
int32_t janet_getendrange(const Janet *argv, int32_t argc, int32_t n, int32_t length) {
|
||||
if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
|
||||
return length;
|
||||
}
|
||||
return janet_gethalfrange(argv, n, length, "end");
|
||||
}
|
||||
|
||||
int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which) {
|
||||
int32_t raw = janet_getinteger(argv, n);
|
||||
int32_t not_raw = raw;
|
||||
if (not_raw < 0) not_raw += length;
|
||||
if (not_raw < 0 || not_raw > length)
|
||||
janet_panicf("%s index %d out of range [%d,%d)", which, raw, -length, length);
|
||||
janet_panicf("%s index %d out of range [%d,%d)", which, (int64_t)raw, -(int64_t)length, (int64_t)length);
|
||||
return not_raw;
|
||||
}
|
||||
|
||||
|
@ -336,24 +415,10 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) {
|
|||
janet_arity(argc, 1, 3);
|
||||
JanetRange range;
|
||||
int32_t length = janet_length(argv[0]);
|
||||
if (argc == 1) {
|
||||
range.start = 0;
|
||||
range.end = length;
|
||||
} else if (argc == 2) {
|
||||
range.start = janet_checktype(argv[1], JANET_NIL)
|
||||
? 0
|
||||
: janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = length;
|
||||
} else {
|
||||
range.start = janet_checktype(argv[1], JANET_NIL)
|
||||
? 0
|
||||
: janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = janet_checktype(argv[2], JANET_NIL)
|
||||
? length
|
||||
: janet_gethalfrange(argv, 2, length, "end");
|
||||
if (range.end < range.start)
|
||||
range.end = range.start;
|
||||
}
|
||||
range.start = janet_getstartrange(argv, argc, 1, length);
|
||||
range.end = janet_getendrange(argv, argc, 2, length);
|
||||
if (range.end < range.start)
|
||||
range.end = range.start;
|
||||
return range;
|
||||
}
|
||||
|
||||
|
@ -433,9 +498,41 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA
|
|||
return janet_getabstract(argv, n, at);
|
||||
}
|
||||
|
||||
/* Atomic refcounts */
|
||||
|
||||
JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
|
||||
#ifdef JANET_WINDOWS
|
||||
return InterlockedIncrement(x);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_fetch_add_explicit(x, 1, memory_order_relaxed) + 1;
|
||||
#else
|
||||
return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED);
|
||||
#endif
|
||||
}
|
||||
|
||||
JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
|
||||
#ifdef JANET_WINDOWS
|
||||
return InterlockedDecrement(x);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_fetch_add_explicit(x, -1, memory_order_acq_rel) - 1;
|
||||
#else
|
||||
return __atomic_add_fetch(x, -1, __ATOMIC_ACQ_REL);
|
||||
#endif
|
||||
}
|
||||
|
||||
JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
|
||||
#ifdef JANET_WINDOWS
|
||||
return InterlockedOr(x, 0);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_load_explicit(x, memory_order_acquire);
|
||||
#else
|
||||
return __atomic_load_n(x, __ATOMIC_ACQUIRE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Some definitions for function-like macros */
|
||||
|
||||
JANET_API JanetStructHead *(janet_struct_head)(const JanetKV *st) {
|
||||
JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) {
|
||||
return janet_struct_head(st);
|
||||
}
|
||||
|
||||
|
@ -443,10 +540,10 @@ JANET_API JanetAbstractHead *(janet_abstract_head)(const void *abstract) {
|
|||
return janet_abstract_head(abstract);
|
||||
}
|
||||
|
||||
JANET_API JanetStringHead *(janet_string_head)(const uint8_t *s) {
|
||||
JANET_API JanetStringHead *(janet_string_head)(JanetString s) {
|
||||
return janet_string_head(s);
|
||||
}
|
||||
|
||||
JANET_API JanetTupleHead *(janet_tuple_head)(const Janet *tuple) {
|
||||
JANET_API JanetTupleHead *(janet_tuple_head)(JanetTuple tuple) {
|
||||
return janet_tuple_head(tuple);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -99,7 +99,7 @@ static JanetSlot opfunction(
|
|||
static int can_be_imm(Janet x, int8_t *out) {
|
||||
if (!janet_checkint(x)) return 0;
|
||||
int32_t integer = janet_unwrap_integer(x);
|
||||
if (integer > 127 || integer < -127) return 0;
|
||||
if (integer > INT8_MAX || integer < INT8_MIN) return 0;
|
||||
*out = (int8_t) integer;
|
||||
return 1;
|
||||
}
|
||||
|
@ -116,12 +116,11 @@ static JanetSlot opreduce(
|
|||
JanetSlot *args,
|
||||
int op,
|
||||
int opim,
|
||||
Janet nullary) {
|
||||
Janet nullary,
|
||||
Janet unary) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
int32_t i, len;
|
||||
int8_t imm = 0;
|
||||
int neg = opim < 0;
|
||||
if (opim < 0) opim = -opim;
|
||||
len = janet_v_count(args);
|
||||
JanetSlot t;
|
||||
if (len == 0) {
|
||||
|
@ -132,19 +131,19 @@ static JanetSlot opreduce(
|
|||
if (op == JOP_SUBTRACT) {
|
||||
janetc_emit_ssi(c, JOP_MULTIPLY_IMMEDIATE, t, args[0], -1, 1);
|
||||
} else {
|
||||
janetc_emit_sss(c, op, t, janetc_cslot(nullary), args[0], 1);
|
||||
janetc_emit_sss(c, op, t, janetc_cslot(unary), args[0], 1);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
t = janetc_gettarget(opts);
|
||||
if (opim && can_slot_be_imm(args[1], &imm)) {
|
||||
janetc_emit_ssi(c, opim, t, args[0], neg ? -imm : imm, 1);
|
||||
janetc_emit_ssi(c, opim, t, args[0], imm, 1);
|
||||
} else {
|
||||
janetc_emit_sss(c, op, t, args[0], args[1], 1);
|
||||
}
|
||||
for (i = 2; i < len; i++) {
|
||||
if (opim && can_slot_be_imm(args[i], &imm)) {
|
||||
janetc_emit_ssi(c, opim, t, t, neg ? -imm : imm, 1);
|
||||
janetc_emit_ssi(c, opim, t, t, imm, 1);
|
||||
} else {
|
||||
janetc_emit_sss(c, op, t, t, args[i], 1);
|
||||
}
|
||||
|
@ -155,7 +154,7 @@ static JanetSlot opreduce(
|
|||
/* Function optimizers */
|
||||
|
||||
static JanetSlot do_propagate(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_PROPAGATE, 0, janet_wrap_nil());
|
||||
return opreduce(opts, args, JOP_PROPAGATE, 0, janet_wrap_nil(), janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_error(JanetFopts opts, JanetSlot *args) {
|
||||
janetc_emit_s(opts.compiler, JOP_ERROR, args[0], 0);
|
||||
|
@ -172,7 +171,7 @@ static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) {
|
|||
return t;
|
||||
}
|
||||
static JanetSlot do_in(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil());
|
||||
return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil(), janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
|
||||
if (janet_v_count(args) == 3) {
|
||||
|
@ -192,20 +191,14 @@ static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
|
|||
c->buffer[label] |= (current - label) << 16;
|
||||
return t;
|
||||
} else {
|
||||
return opreduce(opts, args, JOP_GET, 0, janet_wrap_nil());
|
||||
return opreduce(opts, args, JOP_GET, 0, janet_wrap_nil(), janet_wrap_nil());
|
||||
}
|
||||
}
|
||||
static JanetSlot do_next(JanetFopts opts, JanetSlot *args) {
|
||||
return opfunction(opts, args, JOP_NEXT, janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_MODULO, 0, janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_REMAINDER, 0, janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil());
|
||||
return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil(), janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
|
||||
if (opts.flags & JANET_FOPTS_DROP) {
|
||||
|
@ -262,34 +255,43 @@ static JanetSlot do_apply(JanetFopts opts, JanetSlot *args) {
|
|||
/* Variadic operators specialization */
|
||||
|
||||
static JanetSlot do_add(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_ADD, JOP_ADD_IMMEDIATE, janet_wrap_integer(0));
|
||||
return opreduce(opts, args, JOP_ADD, JOP_ADD_IMMEDIATE, janet_wrap_integer(0), janet_wrap_integer(0));
|
||||
}
|
||||
static JanetSlot do_sub(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_SUBTRACT, -JOP_ADD_IMMEDIATE, janet_wrap_integer(0));
|
||||
return opreduce(opts, args, JOP_SUBTRACT, JOP_SUBTRACT_IMMEDIATE, janet_wrap_integer(0), janet_wrap_integer(0));
|
||||
}
|
||||
static JanetSlot do_mul(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_MULTIPLY, JOP_MULTIPLY_IMMEDIATE, janet_wrap_integer(1));
|
||||
return opreduce(opts, args, JOP_MULTIPLY, JOP_MULTIPLY_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
|
||||
}
|
||||
static JanetSlot do_div(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_DIVIDE, JOP_DIVIDE_IMMEDIATE, janet_wrap_integer(1));
|
||||
return opreduce(opts, args, JOP_DIVIDE, JOP_DIVIDE_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
|
||||
}
|
||||
static JanetSlot do_divf(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_DIVIDE_FLOOR, 0, janet_wrap_integer(1), janet_wrap_integer(1));
|
||||
}
|
||||
static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_MODULO, 0, janet_wrap_integer(0), janet_wrap_integer(1));
|
||||
}
|
||||
static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_REMAINDER, 0, janet_wrap_integer(0), janet_wrap_integer(1));
|
||||
}
|
||||
static JanetSlot do_band(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_BAND, 0, janet_wrap_integer(-1));
|
||||
return opreduce(opts, args, JOP_BAND, 0, janet_wrap_integer(-1), janet_wrap_integer(-1));
|
||||
}
|
||||
static JanetSlot do_bor(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_BOR, 0, janet_wrap_integer(0));
|
||||
return opreduce(opts, args, JOP_BOR, 0, janet_wrap_integer(0), janet_wrap_integer(0));
|
||||
}
|
||||
static JanetSlot do_bxor(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_BXOR, 0, janet_wrap_integer(0));
|
||||
return opreduce(opts, args, JOP_BXOR, 0, janet_wrap_integer(0), janet_wrap_integer(0));
|
||||
}
|
||||
static JanetSlot do_lshift(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_SHIFT_LEFT, JOP_SHIFT_LEFT_IMMEDIATE, janet_wrap_integer(1));
|
||||
return opreduce(opts, args, JOP_SHIFT_LEFT, JOP_SHIFT_LEFT_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
|
||||
}
|
||||
static JanetSlot do_rshift(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_SHIFT_RIGHT, JOP_SHIFT_RIGHT_IMMEDIATE, janet_wrap_integer(1));
|
||||
return opreduce(opts, args, JOP_SHIFT_RIGHT, JOP_SHIFT_RIGHT_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
|
||||
}
|
||||
static JanetSlot do_rshiftu(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_SHIFT_RIGHT_UNSIGNED, JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, janet_wrap_integer(1));
|
||||
return opreduce(opts, args, JOP_SHIFT_RIGHT_UNSIGNED, JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
|
||||
}
|
||||
static JanetSlot do_bnot(JanetFopts opts, JanetSlot *args) {
|
||||
return genericSS(opts, JOP_BNOT, args[0]);
|
||||
|
@ -383,10 +385,11 @@ static const JanetFunOptimizer optimizers[] = {
|
|||
{fixarity2, do_propagate},
|
||||
{arity2or3, do_get},
|
||||
{arity1or2, do_next},
|
||||
{fixarity2, do_modulo},
|
||||
{fixarity2, do_remainder},
|
||||
{NULL, do_modulo},
|
||||
{NULL, do_remainder},
|
||||
{fixarity2, do_cmp},
|
||||
{fixarity2, do_cancel},
|
||||
{NULL, do_divf}
|
||||
};
|
||||
|
||||
const JanetFunOptimizer *janetc_funopt(uint32_t flags) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -93,10 +93,14 @@ void janetc_freeslot(JanetCompiler *c, JanetSlot s) {
|
|||
/* Add a slot to a scope with a symbol associated with it (def or var). */
|
||||
void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s) {
|
||||
SymPair sp;
|
||||
int32_t cnt = janet_v_count(c->buffer);
|
||||
sp.sym = sym;
|
||||
sp.sym2 = sym;
|
||||
sp.slot = s;
|
||||
sp.keep = 0;
|
||||
sp.slot.flags |= JANET_SLOT_NAMED;
|
||||
sp.birth_pc = cnt ? cnt - 1 : 0;
|
||||
sp.death_pc = UINT32_MAX;
|
||||
janet_v_push(c->scope->syms, sp);
|
||||
}
|
||||
|
||||
|
@ -159,21 +163,27 @@ void janetc_popscope(JanetCompiler *c) {
|
|||
if (oldscope->flags & JANET_SCOPE_CLOSURE) {
|
||||
newscope->flags |= JANET_SCOPE_CLOSURE;
|
||||
}
|
||||
if (newscope->ra.max < oldscope->ra.max)
|
||||
if (newscope->ra.max < oldscope->ra.max) {
|
||||
newscope->ra.max = oldscope->ra.max;
|
||||
|
||||
/* Keep upvalue slots */
|
||||
for (int32_t i = 0; i < janet_v_count(oldscope->syms); i++) {
|
||||
SymPair pair = oldscope->syms[i];
|
||||
if (pair.keep) {
|
||||
/* The variable should not be lexically accessible */
|
||||
pair.sym = NULL;
|
||||
janet_v_push(newscope->syms, pair);
|
||||
janetc_regalloc_touch(&newscope->ra, pair.slot.index);
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep upvalue slots and symbols for debugging. */
|
||||
for (int32_t i = 0; i < janet_v_count(oldscope->syms); i++) {
|
||||
SymPair pair = oldscope->syms[i];
|
||||
/* The variable should not be lexically accessible */
|
||||
pair.sym = NULL;
|
||||
if (pair.death_pc == UINT32_MAX) {
|
||||
pair.death_pc = (uint32_t) janet_v_count(c->buffer);
|
||||
}
|
||||
if (pair.keep) {
|
||||
/* The variable should also not be included in the locals */
|
||||
pair.sym2 = NULL;
|
||||
janetc_regalloc_touch(&newscope->ra, pair.slot.index);
|
||||
}
|
||||
janet_v_push(newscope->syms, pair);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the old scope */
|
||||
janet_v_free(oldscope->consts);
|
||||
janet_v_free(oldscope->syms);
|
||||
|
@ -197,6 +207,39 @@ void janetc_popscope_keepslot(JanetCompiler *c, JanetSlot retslot) {
|
|||
}
|
||||
}
|
||||
|
||||
static int lookup_missing(
|
||||
JanetCompiler *c,
|
||||
const uint8_t *sym,
|
||||
JanetFunction *handler,
|
||||
JanetBinding *out) {
|
||||
int32_t minar = handler->def->min_arity;
|
||||
int32_t maxar = handler->def->max_arity;
|
||||
if (minar > 1 || maxar < 1) {
|
||||
janetc_error(c, janet_cstring("missing symbol lookup handler must take 1 argument"));
|
||||
return 0;
|
||||
}
|
||||
Janet args[1] = { janet_wrap_symbol(sym) };
|
||||
JanetFiber *fiberp = janet_fiber(handler, 64, 1, args);
|
||||
if (NULL == fiberp) {
|
||||
janetc_error(c, janet_cstring("failed to call missing symbol lookup handler"));
|
||||
return 0;
|
||||
}
|
||||
fiberp->env = c->env;
|
||||
int lock = janet_gclock();
|
||||
Janet tempOut;
|
||||
JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &tempOut);
|
||||
janet_gcunlock(lock);
|
||||
if (status != JANET_SIGNAL_OK) {
|
||||
janetc_error(c, janet_formatc("(lookup) %V", tempOut));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert return value as entry. */
|
||||
/* Alternative could use janet_resolve_ext(c->env, sym) to read result from environment. */
|
||||
*out = janet_binding_from_entry(tempOut);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Allow searching for symbols. Return information about the symbol */
|
||||
JanetSlot janetc_resolve(
|
||||
JanetCompiler *c,
|
||||
|
@ -230,6 +273,21 @@ JanetSlot janetc_resolve(
|
|||
/* Symbol not found - check for global */
|
||||
{
|
||||
JanetBinding binding = janet_resolve_ext(c->env, sym);
|
||||
if (binding.type == JANET_BINDING_NONE) {
|
||||
Janet handler = janet_table_get(c->env, janet_ckeywordv("missing-symbol"));
|
||||
switch (janet_type(handler)) {
|
||||
case JANET_NIL:
|
||||
break;
|
||||
case JANET_FUNCTION:
|
||||
if (!lookup_missing(c, sym, janet_unwrap_function(handler), &binding))
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
break;
|
||||
default:
|
||||
janetc_error(c, janet_formatc("invalid lookup handler %V", handler));
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
}
|
||||
|
||||
switch (binding.type) {
|
||||
default:
|
||||
case JANET_BINDING_NONE:
|
||||
|
@ -239,6 +297,12 @@ JanetSlot janetc_resolve(
|
|||
case JANET_BINDING_MACRO: /* Macro should function like defs when not in calling pos */
|
||||
ret = janetc_cslot(binding.value);
|
||||
break;
|
||||
case JANET_BINDING_DYNAMIC_DEF:
|
||||
case JANET_BINDING_DYNAMIC_MACRO:
|
||||
ret = janetc_cslot(binding.value);
|
||||
ret.flags |= JANET_SLOT_REF | JANET_SLOT_NAMED | JANET_SLOTTYPE_ANY;
|
||||
ret.flags &= ~JANET_SLOT_CONSTANT;
|
||||
break;
|
||||
case JANET_BINDING_VAR: {
|
||||
ret = janetc_cslot(binding.value);
|
||||
ret.flags |= JANET_SLOT_REF | JANET_SLOT_NAMED | JANET_SLOT_MUTABLE | JANET_SLOTTYPE_ANY;
|
||||
|
@ -280,6 +344,7 @@ found:
|
|||
}
|
||||
|
||||
/* non-local scope needs to expose its environment */
|
||||
JanetScope *original_scope = scope;
|
||||
pair->keep = 1;
|
||||
while (scope && !(scope->flags & JANET_SCOPE_FUNCTION))
|
||||
scope = scope->parent;
|
||||
|
@ -301,7 +366,7 @@ found:
|
|||
/* Check if scope already has env. If so, break */
|
||||
len = janet_v_count(scope->envs);
|
||||
for (j = 0; j < len; j++) {
|
||||
if (scope->envs[j] == envindex) {
|
||||
if (scope->envs[j].envindex == envindex) {
|
||||
scopefound = 1;
|
||||
envindex = j;
|
||||
break;
|
||||
|
@ -310,7 +375,10 @@ found:
|
|||
/* Add the environment if it is not already referenced */
|
||||
if (!scopefound) {
|
||||
len = janet_v_count(scope->envs);
|
||||
janet_v_push(scope->envs, envindex);
|
||||
JanetEnvRef ref;
|
||||
ref.envindex = envindex;
|
||||
ref.scope = original_scope;
|
||||
janet_v_push(scope->envs, ref);
|
||||
envindex = len;
|
||||
}
|
||||
}
|
||||
|
@ -354,6 +422,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len) {
|
|||
int32_t i;
|
||||
JanetSlot *ret = NULL;
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
|
||||
for (i = 0; i < len; i++) {
|
||||
janet_v_push(ret, janetc_value(subopts, vals[i]));
|
||||
}
|
||||
|
@ -364,6 +433,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len) {
|
|||
JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
|
||||
JanetSlot *ret = NULL;
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
|
||||
const JanetKV *kvs = NULL;
|
||||
int32_t cap = 0, len = 0;
|
||||
janet_dictionary_view(ds, &kvs, &len, &cap);
|
||||
|
@ -651,7 +721,7 @@ static int macroexpand1(
|
|||
}
|
||||
Janet macroval;
|
||||
JanetBindingType btype = janet_resolve(c->env, name, ¯oval);
|
||||
if (btype != JANET_BINDING_MACRO ||
|
||||
if (!(btype == JANET_BINDING_MACRO || btype == JANET_BINDING_DYNAMIC_MACRO) ||
|
||||
!janet_checktype(macroval, JANET_FUNCTION))
|
||||
return 0;
|
||||
|
||||
|
@ -676,12 +746,14 @@ static int macroexpand1(
|
|||
int lock = janet_gclock();
|
||||
Janet mf_kw = janet_ckeywordv("macro-form");
|
||||
janet_table_put(c->env, mf_kw, x);
|
||||
Janet ml_kw = janet_ckeywordv("macro-lints");
|
||||
if (c->lints) {
|
||||
janet_table_put(c->env, ml_kw, janet_wrap_array(c->lints));
|
||||
}
|
||||
Janet tempOut;
|
||||
JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &tempOut);
|
||||
janet_table_put(c->env, mf_kw, janet_wrap_nil());
|
||||
if (c->lints) {
|
||||
janet_table_put(c->env, janet_ckeywordv("macro-lints"), janet_wrap_array(c->lints));
|
||||
}
|
||||
janet_table_put(c->env, ml_kw, janet_wrap_nil());
|
||||
janet_gcunlock(lock);
|
||||
if (status != JANET_SIGNAL_OK) {
|
||||
const uint8_t *es = janet_formatc("(macro) %V", tempOut);
|
||||
|
@ -814,7 +886,10 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
|
|||
|
||||
/* Copy envs */
|
||||
def->environments_length = janet_v_count(scope->envs);
|
||||
def->environments = janet_v_flatten(scope->envs);
|
||||
def->environments = janet_malloc(sizeof(int32_t) * def->environments_length);
|
||||
for (int32_t i = 0; i < def->environments_length; i++) {
|
||||
def->environments[i] = scope->envs[i].envindex;
|
||||
}
|
||||
|
||||
def->constants_length = janet_v_count(scope->consts);
|
||||
def->constants = janet_v_flatten(scope->consts);
|
||||
|
@ -859,7 +934,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
|
|||
int32_t slotchunks = (def->slotcount + 31) >> 5;
|
||||
/* numchunks is min of slotchunks and scope->ua.count */
|
||||
int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks;
|
||||
uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks);
|
||||
uint32_t *chunks = janet_calloc(slotchunks, sizeof(uint32_t));
|
||||
if (NULL == chunks) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -869,9 +944,66 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
|
|||
def->closure_bitset = chunks;
|
||||
}
|
||||
|
||||
/* Capture symbol to local mapping */
|
||||
JanetSymbolMap *locals = NULL;
|
||||
|
||||
/* Symbol -> upvalue mapping */
|
||||
JanetScope *top = c->scope;
|
||||
while (top->parent) top = top->parent;
|
||||
for (JanetScope *s = top; s != NULL; s = s->child) {
|
||||
for (int32_t j = 0; j < janet_v_count(scope->envs); j++) {
|
||||
JanetEnvRef ref = scope->envs[j];
|
||||
JanetScope *upscope = ref.scope;
|
||||
if (upscope != s) continue;
|
||||
for (int32_t i = 0; i < janet_v_count(upscope->syms); i++) {
|
||||
SymPair pair = upscope->syms[i];
|
||||
if (pair.sym2) {
|
||||
JanetSymbolMap jsm;
|
||||
jsm.birth_pc = UINT32_MAX;
|
||||
jsm.death_pc = j;
|
||||
jsm.slot_index = pair.slot.index;
|
||||
jsm.symbol = pair.sym2;
|
||||
janet_v_push(locals, jsm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Symbol -> slot mapping */
|
||||
for (int32_t i = 0; i < janet_v_count(scope->syms); i++) {
|
||||
SymPair pair = scope->syms[i];
|
||||
if (pair.sym2) {
|
||||
JanetSymbolMap jsm;
|
||||
if (pair.death_pc == UINT32_MAX) {
|
||||
jsm.death_pc = def->bytecode_length;
|
||||
} else {
|
||||
jsm.death_pc = pair.death_pc - scope->bytecode_start;
|
||||
}
|
||||
/* Handle birth_pc == 0 correctly */
|
||||
if ((uint32_t) scope->bytecode_start > pair.birth_pc) {
|
||||
jsm.birth_pc = 0;
|
||||
} else {
|
||||
jsm.birth_pc = pair.birth_pc - scope->bytecode_start;
|
||||
}
|
||||
janet_assert(jsm.birth_pc <= jsm.death_pc, "birth pc after death pc");
|
||||
janet_assert(jsm.birth_pc < (uint32_t) def->bytecode_length, "bad birth pc");
|
||||
janet_assert(jsm.death_pc <= (uint32_t) def->bytecode_length, "bad death pc");
|
||||
jsm.slot_index = pair.slot.index;
|
||||
jsm.symbol = pair.sym2;
|
||||
janet_v_push(locals, jsm);
|
||||
}
|
||||
}
|
||||
def->symbolmap_length = janet_v_count(locals);
|
||||
def->symbolmap = janet_v_flatten(locals);
|
||||
if (def->symbolmap_length) def->flags |= JANET_FUNCDEF_FLAG_HASSYMBOLMAP;
|
||||
|
||||
/* Pop the scope */
|
||||
janetc_popscope(c);
|
||||
|
||||
/* Do basic optimization */
|
||||
janet_bytecode_movopt(def);
|
||||
janet_bytecode_remove_noops(def);
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
@ -924,7 +1056,7 @@ JanetCompileResult janet_compile_lint(Janet source,
|
|||
|
||||
if (c.result.status == JANET_COMPILE_OK) {
|
||||
JanetFuncDef *def = janetc_pop_funcdef(&c);
|
||||
def->name = janet_cstring("_thunk");
|
||||
def->name = janet_cstring("thunk");
|
||||
janet_def_addflags(def);
|
||||
c.result.funcdef = def;
|
||||
} else {
|
||||
|
@ -942,7 +1074,7 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
|
|||
}
|
||||
|
||||
/* C Function for compiling */
|
||||
JANET_CORE_FN(cfun,
|
||||
JANET_CORE_FN(cfun_compile,
|
||||
"(compile ast &opt env source lints)",
|
||||
"Compiles an Abstract Syntax Tree (ast) into a function. "
|
||||
"Pair the compile function with parsing functionality to implement "
|
||||
|
@ -951,16 +1083,25 @@ JANET_CORE_FN(cfun,
|
|||
"If a `lints` array is given, linting messages will be appended to the array. "
|
||||
"Each message will be a tuple of the form `(level line col message)`.") {
|
||||
janet_arity(argc, 1, 4);
|
||||
JanetTable *env = argc > 1 ? janet_gettable(argv, 1) : janet_vm.fiber->env;
|
||||
JanetTable *env = (argc > 1 && !janet_checktype(argv[1], JANET_NIL))
|
||||
? janet_gettable(argv, 1) : janet_vm.fiber->env;
|
||||
if (NULL == env) {
|
||||
env = janet_table(0);
|
||||
janet_vm.fiber->env = env;
|
||||
}
|
||||
const uint8_t *source = NULL;
|
||||
if (argc >= 3) {
|
||||
source = janet_getstring(argv, 2);
|
||||
Janet x = argv[2];
|
||||
if (janet_checktype(x, JANET_STRING)) {
|
||||
source = janet_unwrap_string(x);
|
||||
} else if (janet_checktype(x, JANET_KEYWORD)) {
|
||||
source = janet_unwrap_keyword(x);
|
||||
} else if (!janet_checktype(x, JANET_NIL)) {
|
||||
janet_panic_type(x, 2, JANET_TFLAG_STRING | JANET_TFLAG_KEYWORD);
|
||||
}
|
||||
}
|
||||
JanetArray *lints = (argc >= 4) ? janet_getarray(argv, 3) : NULL;
|
||||
JanetArray *lints = (argc >= 4 && !janet_checktype(argv[3], JANET_NIL))
|
||||
? janet_getarray(argv, 3) : NULL;
|
||||
JanetCompileResult res = janet_compile_lint(argv[0], env, source, lints);
|
||||
if (res.status == JANET_COMPILE_OK) {
|
||||
return janet_wrap_function(janet_thunk(res.funcdef));
|
||||
|
@ -982,7 +1123,7 @@ JANET_CORE_FN(cfun,
|
|||
|
||||
void janet_lib_compile(JanetTable *env) {
|
||||
JanetRegExt cfuns[] = {
|
||||
JANET_CORE_REG("compile", cfun),
|
||||
JANET_CORE_REG("compile", cfun_compile),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, cfuns);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -69,6 +69,7 @@ typedef enum {
|
|||
#define JANET_FUN_REMAINDER 30
|
||||
#define JANET_FUN_CMP 31
|
||||
#define JANET_FUN_CANCEL 32
|
||||
#define JANET_FUN_DIVIDE_FLOOR 33
|
||||
|
||||
/* Compiler typedefs */
|
||||
typedef struct JanetCompiler JanetCompiler;
|
||||
|
@ -111,13 +112,21 @@ struct JanetSlot {
|
|||
typedef struct SymPair {
|
||||
JanetSlot slot;
|
||||
const uint8_t *sym;
|
||||
const uint8_t *sym2;
|
||||
int keep;
|
||||
uint32_t birth_pc;
|
||||
uint32_t death_pc;
|
||||
} SymPair;
|
||||
|
||||
typedef struct JanetEnvRef {
|
||||
int32_t envindex;
|
||||
JanetScope *scope;
|
||||
} JanetEnvRef;
|
||||
|
||||
/* A lexical scope during compilation */
|
||||
struct JanetScope {
|
||||
|
||||
/* For debugging */
|
||||
/* For debugging the compiler */
|
||||
const char *name;
|
||||
|
||||
/* Scopes are doubly linked list */
|
||||
|
@ -133,7 +142,7 @@ struct JanetScope {
|
|||
/* FuncDefs */
|
||||
JanetFuncDef **defs;
|
||||
|
||||
/* Regsiter allocator */
|
||||
/* Register allocator */
|
||||
JanetcRegisterAllocator ra;
|
||||
|
||||
/* Upvalue allocator */
|
||||
|
@ -142,7 +151,7 @@ struct JanetScope {
|
|||
/* Referenced closure environments. The values at each index correspond
|
||||
* to which index to get the environment from in the parent. The environment
|
||||
* that corresponds to the direct parent's stack will always have value 0. */
|
||||
int32_t *envs;
|
||||
JanetEnvRef *envs;
|
||||
|
||||
int32_t bytecode_start;
|
||||
int flags;
|
||||
|
@ -179,6 +188,7 @@ struct JanetCompiler {
|
|||
#define JANET_FOPTS_TAIL 0x10000
|
||||
#define JANET_FOPTS_HINT 0x20000
|
||||
#define JANET_FOPTS_DROP 0x40000
|
||||
#define JANET_FOPTS_ACCEPT_SPLICE 0x80000
|
||||
|
||||
/* Options for compiling a single form */
|
||||
struct JanetFopts {
|
||||
|
@ -227,7 +237,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len);
|
|||
/* Get a bunch of slots for function arguments */
|
||||
JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds);
|
||||
|
||||
/* Push slots load via janetc_toslots. */
|
||||
/* Push slots loaded via janetc_toslots. */
|
||||
int32_t janetc_pushslots(JanetCompiler *c, JanetSlot *slots);
|
||||
|
||||
/* Free slots loaded via janetc_toslots */
|
||||
|
@ -258,4 +268,8 @@ JanetSlot janetc_cslot(Janet x);
|
|||
/* Search for a symbol */
|
||||
JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym);
|
||||
|
||||
/* Bytecode optimization */
|
||||
void janet_bytecode_movopt(JanetFuncDef *def);
|
||||
void janet_bytecode_remove_noops(JanetFuncDef *def);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -42,52 +42,8 @@ extern size_t janet_core_image_size;
|
|||
#define JDOC(x) NULL
|
||||
#endif
|
||||
|
||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
|
||||
* with native code. */
|
||||
#if defined(JANET_NO_DYNAMIC_MODULES)
|
||||
typedef int Clib;
|
||||
#define load_clib(name) ((void) name, 0)
|
||||
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
|
||||
#define error_clib() "dynamic libraries not supported"
|
||||
#elif defined(JANET_WINDOWS)
|
||||
#include <windows.h>
|
||||
typedef HINSTANCE Clib;
|
||||
#define load_clib(name) LoadLibrary((name))
|
||||
#define symbol_clib(lib, sym) GetProcAddress((lib), (sym))
|
||||
static char error_clib_buf[256];
|
||||
static char *error_clib(void) {
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
error_clib_buf, sizeof(error_clib_buf), NULL);
|
||||
error_clib_buf[strlen(error_clib_buf) - 1] = '\0';
|
||||
return error_clib_buf;
|
||||
}
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
typedef void *Clib;
|
||||
#define load_clib(name) dlopen((name), RTLD_NOW)
|
||||
#define symbol_clib(lib, sym) dlsym((lib), (sym))
|
||||
#define error_clib() dlerror()
|
||||
#endif
|
||||
|
||||
static char *get_processed_name(const char *name) {
|
||||
if (name[0] == '.') return (char *) name;
|
||||
const char *c;
|
||||
for (c = name; *c; c++) {
|
||||
if (*c == '/') return (char *) name;
|
||||
}
|
||||
size_t l = (size_t)(c - name);
|
||||
char *ret = janet_malloc(l + 3);
|
||||
if (NULL == ret) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
ret[0] = '.';
|
||||
ret[1] = '/';
|
||||
memcpy(ret + 2, name, l + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JanetModule janet_native(const char *name, const uint8_t **error) {
|
||||
janet_sandbox_assert(JANET_SANDBOX_DYNAMIC_MODULES);
|
||||
char *processed_name = get_processed_name(name);
|
||||
Clib lib = load_clib(processed_name);
|
||||
JanetModule init;
|
||||
|
@ -113,15 +69,15 @@ JanetModule janet_native(const char *name, const uint8_t **error) {
|
|||
host.minor < modconf.minor ||
|
||||
host.bits != modconf.bits) {
|
||||
char errbuf[128];
|
||||
sprintf(errbuf, "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
|
||||
host.major,
|
||||
host.minor,
|
||||
host.patch,
|
||||
host.bits,
|
||||
modconf.major,
|
||||
modconf.minor,
|
||||
modconf.patch,
|
||||
modconf.bits);
|
||||
snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
|
||||
host.major,
|
||||
host.minor,
|
||||
host.patch,
|
||||
host.bits,
|
||||
modconf.major,
|
||||
modconf.minor,
|
||||
modconf.patch,
|
||||
modconf.bits);
|
||||
*error = janet_cstring(errbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -154,11 +110,14 @@ JANET_CORE_FN(janet_core_expand_path,
|
|||
"(module/expand-path path template)",
|
||||
"Expands a path template as found in `module/paths` for `module/find`. "
|
||||
"This takes in a path (the argument to require) and a template string, "
|
||||
"to expand the path to a path that can be "
|
||||
"used for importing files. The replacements are as follows:\n\n"
|
||||
"* :all: -- the value of path verbatim\n\n"
|
||||
"* :cur: -- the current file, or (dyn :current-file)\n\n"
|
||||
"* :dir: -- the directory containing the current file\n\n"
|
||||
"to expand the path to a path that can be used for importing files. "
|
||||
"The replacements are as follows:\n\n"
|
||||
"* :all: -- the value of path verbatim.\n\n"
|
||||
"* :@all: -- Same as :all:, but if `path` starts with the @ character, "
|
||||
"the first path segment is replaced with a dynamic binding "
|
||||
"`(dyn <first path segment as keyword>)`.\n\n"
|
||||
"* :cur: -- the directory portion, if any, of (dyn :current-file)\n\n"
|
||||
"* :dir: -- the directory portion, if any, of the path argument\n\n"
|
||||
"* :name: -- the name component of path, with extension if given\n\n"
|
||||
"* :native: -- the extension used to load natives, .so or .dll\n\n"
|
||||
"* :sys: -- the system path, or (dyn :syspath)") {
|
||||
|
@ -202,6 +161,21 @@ JANET_CORE_FN(janet_core_expand_path,
|
|||
if (strncmp(template + i, ":all:", 5) == 0) {
|
||||
janet_buffer_push_cstring(out, input);
|
||||
i += 4;
|
||||
} else if (strncmp(template + i, ":@all:", 6) == 0) {
|
||||
if (input[0] == '@') {
|
||||
const char *p = input;
|
||||
while (*p && !is_path_sep(*p)) p++;
|
||||
size_t len = p - input - 1;
|
||||
char *str = janet_smalloc(len + 1);
|
||||
memcpy(str, input + 1, len);
|
||||
str[len] = '\0';
|
||||
janet_formatb(out, "%V", janet_dyn(str));
|
||||
janet_sfree(str);
|
||||
janet_buffer_push_cstring(out, p);
|
||||
} else {
|
||||
janet_buffer_push_cstring(out, input);
|
||||
}
|
||||
i += 5;
|
||||
} else if (strncmp(template + i, ":cur:", 5) == 0) {
|
||||
janet_buffer_push_bytes(out, (const uint8_t *)curdir, curlen);
|
||||
i += 4;
|
||||
|
@ -339,7 +313,10 @@ JANET_CORE_FN(janet_core_native,
|
|||
|
||||
JANET_CORE_FN(janet_core_describe,
|
||||
"(describe x)",
|
||||
"Returns a string that is a human-readable description of a value x.") {
|
||||
"Returns a string that is a human-readable description of `x`. "
|
||||
"For recursive data structures, the string returned contains a "
|
||||
"pointer value from which the identity of `x` "
|
||||
"can be determined.") {
|
||||
JanetBuffer *b = janet_buffer(0);
|
||||
for (int32_t i = 0; i < argc; ++i)
|
||||
janet_description_b(b, argv[i]);
|
||||
|
@ -449,6 +426,36 @@ JANET_CORE_FN(janet_core_slice,
|
|||
}
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_range,
|
||||
"(range & args)",
|
||||
"Create an array of values [start, end) with a given step. "
|
||||
"With one argument, returns a range [0, end). With two arguments, returns "
|
||||
"a range [start, end). With three, returns a range with optional step size.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
int32_t start = 0, stop = 0, step = 1, count = 0;
|
||||
if (argc == 3) {
|
||||
start = janet_getinteger(argv, 0);
|
||||
stop = janet_getinteger(argv, 1);
|
||||
step = janet_getinteger(argv, 2);
|
||||
count = (step > 0) ? (stop - start - 1) / step + 1 :
|
||||
((step < 0) ? (stop - start + 1) / step + 1 : 0);
|
||||
} else if (argc == 2) {
|
||||
start = janet_getinteger(argv, 0);
|
||||
stop = janet_getinteger(argv, 1);
|
||||
count = stop - start;
|
||||
} else {
|
||||
stop = janet_getinteger(argv, 0);
|
||||
count = stop;
|
||||
}
|
||||
count = (count > 0) ? count : 0;
|
||||
JanetArray *array = janet_array(count);
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
array->data[i] = janet_wrap_number(start + i * step);
|
||||
}
|
||||
array->count = count;
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_table,
|
||||
"(table & kvs)",
|
||||
"Creates a new table from a variadic number of keys and values. "
|
||||
|
@ -465,6 +472,25 @@ JANET_CORE_FN(janet_core_table,
|
|||
return janet_wrap_table(table);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_getproto,
|
||||
"(getproto x)",
|
||||
"Get the prototype of a table or struct. Will return nil if `x` has no prototype.") {
|
||||
janet_fixarity(argc, 1);
|
||||
if (janet_checktype(argv[0], JANET_TABLE)) {
|
||||
JanetTable *t = janet_unwrap_table(argv[0]);
|
||||
return t->proto
|
||||
? janet_wrap_table(t->proto)
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
if (janet_checktype(argv[0], JANET_STRUCT)) {
|
||||
JanetStruct st = janet_unwrap_struct(argv[0]);
|
||||
return janet_struct_proto(st)
|
||||
? janet_wrap_struct(janet_struct_proto(st))
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
janet_panicf("expected struct or table, got %v", argv[0]);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_struct,
|
||||
"(struct & kvs)",
|
||||
"Create a new struct from a sequence of key value pairs. "
|
||||
|
@ -472,8 +498,9 @@ JANET_CORE_FN(janet_core_struct,
|
|||
"an odd number of elements, an error will be thrown. Returns the "
|
||||
"new struct.") {
|
||||
int32_t i;
|
||||
if (argc & 1)
|
||||
if (argc & 1) {
|
||||
janet_panic("expected even number of arguments");
|
||||
}
|
||||
JanetKV *st = janet_struct_begin(argc >> 1);
|
||||
for (i = 0; i < argc; i += 2) {
|
||||
janet_struct_put(st, argv[i], argv[i + 1]);
|
||||
|
@ -632,31 +659,137 @@ ret_false:
|
|||
return janet_wrap_false();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_bytes,
|
||||
"(bytes? x)",
|
||||
"Check if x is a string, symbol, keyword, or buffer.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_BYTES));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_indexed,
|
||||
"(indexed? x)",
|
||||
"Check if x is an array or tuple.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_INDEXED));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_dictionary,
|
||||
"(dictionary? x)",
|
||||
"Check if x is a table or struct.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_DICTIONARY));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_lengthable,
|
||||
"(lengthable? x)",
|
||||
"Check if x is a bytes, indexed, or dictionary.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_LENGTHABLE));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_signal,
|
||||
"(signal what x)",
|
||||
"Raise a signal with payload x. ") {
|
||||
janet_arity(argc, 1, 2);
|
||||
int sig;
|
||||
Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
|
||||
if (janet_checkint(argv[0])) {
|
||||
int32_t s = janet_unwrap_integer(argv[0]);
|
||||
if (s < 0 || s > 9) {
|
||||
janet_panicf("expected user signal between 0 and 9, got %d", s);
|
||||
}
|
||||
sig = JANET_SIGNAL_USER0 + s;
|
||||
janet_signalv(JANET_SIGNAL_USER0 + s, payload);
|
||||
} else {
|
||||
JanetKeyword kw = janet_getkeyword(argv, 0);
|
||||
if (!janet_cstrcmp(kw, "yield")) {
|
||||
sig = JANET_SIGNAL_YIELD;
|
||||
} else if (!janet_cstrcmp(kw, "error")) {
|
||||
sig = JANET_SIGNAL_ERROR;
|
||||
} else if (!janet_cstrcmp(kw, "debug")) {
|
||||
sig = JANET_SIGNAL_DEBUG;
|
||||
} else {
|
||||
janet_panicf("unknown signal, expected :yield, :error, or :debug, got %v", argv[0]);
|
||||
for (unsigned i = 0; i < sizeof(janet_signal_names) / sizeof(char *); i++) {
|
||||
if (!janet_cstrcmp(kw, janet_signal_names[i])) {
|
||||
janet_signalv((JanetSignal) i, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
|
||||
janet_signalv(sig, payload);
|
||||
janet_panicf("unknown signal %v", argv[0]);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_memcmp,
|
||||
"(memcmp a b &opt len offset-a offset-b)",
|
||||
"Compare memory. Takes two byte sequences `a` and `b`, and "
|
||||
"return 0 if they have identical contents, a negative integer if a is less than b, "
|
||||
"and a positive integer if a is greater than b. Optionally take a length and offsets "
|
||||
"to compare slices of the bytes sequences.") {
|
||||
janet_arity(argc, 2, 5);
|
||||
JanetByteView a = janet_getbytes(argv, 0);
|
||||
JanetByteView b = janet_getbytes(argv, 1);
|
||||
int32_t len = janet_optnat(argv, argc, 2, a.len < b.len ? a.len : b.len);
|
||||
int32_t offset_a = janet_optnat(argv, argc, 3, 0);
|
||||
int32_t offset_b = janet_optnat(argv, argc, 4, 0);
|
||||
if (offset_a + len > a.len) janet_panicf("invalid offset-a: %d", offset_a);
|
||||
if (offset_b + len > b.len) janet_panicf("invalid offset-b: %d", offset_b);
|
||||
return janet_wrap_integer(memcmp(a.bytes + offset_a, b.bytes + offset_b, (size_t) len));
|
||||
}
|
||||
|
||||
typedef struct SandboxOption {
|
||||
const char *name;
|
||||
uint32_t flag;
|
||||
} SandboxOption;
|
||||
|
||||
static const SandboxOption sandbox_options[] = {
|
||||
{"all", JANET_SANDBOX_ALL},
|
||||
{"env", JANET_SANDBOX_ENV},
|
||||
{"ffi", JANET_SANDBOX_FFI},
|
||||
{"ffi-define", JANET_SANDBOX_FFI_DEFINE},
|
||||
{"ffi-jit", JANET_SANDBOX_FFI_JIT},
|
||||
{"ffi-use", JANET_SANDBOX_FFI_USE},
|
||||
{"fs", JANET_SANDBOX_FS},
|
||||
{"fs-read", JANET_SANDBOX_FS_READ},
|
||||
{"fs-temp", JANET_SANDBOX_FS_TEMP},
|
||||
{"fs-write", JANET_SANDBOX_FS_WRITE},
|
||||
{"hrtime", JANET_SANDBOX_HRTIME},
|
||||
{"modules", JANET_SANDBOX_DYNAMIC_MODULES},
|
||||
{"net", JANET_SANDBOX_NET},
|
||||
{"net-connect", JANET_SANDBOX_NET_CONNECT},
|
||||
{"net-listen", JANET_SANDBOX_NET_LISTEN},
|
||||
{"sandbox", JANET_SANDBOX_SANDBOX},
|
||||
{"signal", JANET_SANDBOX_SIGNAL},
|
||||
{"subprocess", JANET_SANDBOX_SUBPROCESS},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
JANET_CORE_FN(janet_core_sandbox,
|
||||
"(sandbox & forbidden-capabilities)",
|
||||
"Disable feature sets to prevent the interpreter from using certain system resources. "
|
||||
"Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n"
|
||||
"* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
|
||||
"* :env - disallow reading and write env variables\n"
|
||||
"* :ffi - disallow FFI (recommended if disabling anything else)\n"
|
||||
"* :ffi-define - disallow loading new FFI modules and binding new functions\n"
|
||||
"* :ffi-jit - disallow calling `ffi/jitfn`\n"
|
||||
"* :ffi-use - disallow using any previously bound FFI functions and memory-unsafe functions.\n"
|
||||
"* :fs - disallow access to the file system\n"
|
||||
"* :fs-read - disallow read access to the file system\n"
|
||||
"* :fs-temp - disallow creating temporary files\n"
|
||||
"* :fs-write - disallow write access to the file system\n"
|
||||
"* :hrtime - disallow high-resolution timers\n"
|
||||
"* :modules - disallow load dynamic modules (natives)\n"
|
||||
"* :net - disallow network access\n"
|
||||
"* :net-connect - disallow making outbound network connections\n"
|
||||
"* :net-listen - disallow accepting inbound network connections\n"
|
||||
"* :sandbox - disallow calling this function\n"
|
||||
"* :signal - disallow adding or removing signal handlers\n"
|
||||
"* :subprocess - disallow running subprocesses") {
|
||||
uint32_t flags = 0;
|
||||
for (int32_t i = 0; i < argc; i++) {
|
||||
JanetKeyword kw = janet_getkeyword(argv, i);
|
||||
const SandboxOption *opt = sandbox_options;
|
||||
while (opt->name != NULL) {
|
||||
if (janet_cstrcmp(kw, opt->name) == 0) {
|
||||
flags |= opt->flag;
|
||||
break;
|
||||
}
|
||||
opt++;
|
||||
}
|
||||
if (opt->name == NULL) janet_panicf("unknown capability %v", argv[i]);
|
||||
}
|
||||
janet_sandbox(flags);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
|
@ -912,14 +1045,6 @@ static const uint32_t next_asm[] = {
|
|||
JOP_NEXT | (1 << 24),
|
||||
JOP_RETURN
|
||||
};
|
||||
static const uint32_t modulo_asm[] = {
|
||||
JOP_MODULO | (1 << 24),
|
||||
JOP_RETURN
|
||||
};
|
||||
static const uint32_t remainder_asm[] = {
|
||||
JOP_REMAINDER | (1 << 24),
|
||||
JOP_RETURN
|
||||
};
|
||||
static const uint32_t cmp_asm[] = {
|
||||
JOP_COMPARE | (1 << 24),
|
||||
JOP_RETURN
|
||||
|
@ -958,8 +1083,16 @@ static void janet_load_libs(JanetTable *env) {
|
|||
JANET_CORE_REG("module/expand-path", janet_core_expand_path),
|
||||
JANET_CORE_REG("int?", janet_core_check_int),
|
||||
JANET_CORE_REG("nat?", janet_core_check_nat),
|
||||
JANET_CORE_REG("bytes?", janet_core_is_bytes),
|
||||
JANET_CORE_REG("indexed?", janet_core_is_indexed),
|
||||
JANET_CORE_REG("dictionary?", janet_core_is_dictionary),
|
||||
JANET_CORE_REG("lengthable?", janet_core_is_lengthable),
|
||||
JANET_CORE_REG("slice", janet_core_slice),
|
||||
JANET_CORE_REG("range", janet_core_range),
|
||||
JANET_CORE_REG("signal", janet_core_signal),
|
||||
JANET_CORE_REG("memcmp", janet_core_memcmp),
|
||||
JANET_CORE_REG("getproto", janet_core_getproto),
|
||||
JANET_CORE_REG("sandbox", janet_core_sandbox),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, corelib_cfuns);
|
||||
|
@ -969,6 +1102,7 @@ static void janet_load_libs(JanetTable *env) {
|
|||
janet_lib_tuple(env);
|
||||
janet_lib_buffer(env);
|
||||
janet_lib_table(env);
|
||||
janet_lib_struct(env);
|
||||
janet_lib_fiber(env);
|
||||
janet_lib_os(env);
|
||||
janet_lib_parse(env);
|
||||
|
@ -991,20 +1125,15 @@ static void janet_load_libs(JanetTable *env) {
|
|||
#ifdef JANET_NET
|
||||
janet_lib_net(env);
|
||||
#endif
|
||||
#ifdef JANET_FFI
|
||||
janet_lib_ffi(env);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
|
||||
JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
JanetTable *env = (NULL != replacements) ? replacements : janet_table(0);
|
||||
janet_quick_asm(env, JANET_FUN_MODULO,
|
||||
"mod", 2, 2, 2, 2, modulo_asm, sizeof(modulo_asm),
|
||||
JDOC("(mod dividend divisor)\n\n"
|
||||
"Returns the modulo of dividend / divisor."));
|
||||
janet_quick_asm(env, JANET_FUN_REMAINDER,
|
||||
"%", 2, 2, 2, 2, remainder_asm, sizeof(remainder_asm),
|
||||
JDOC("(% dividend divisor)\n\n"
|
||||
"Returns the remainder of dividend / divisor."));
|
||||
janet_quick_asm(env, JANET_FUN_CMP,
|
||||
"cmp", 2, 2, 2, 2, cmp_asm, sizeof(cmp_asm),
|
||||
JDOC("(cmp x y)\n\n"
|
||||
|
@ -1015,17 +1144,20 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
|||
JDOC("(next ds &opt key)\n\n"
|
||||
"Gets the next key in a data structure. Can be used to iterate through "
|
||||
"the keys of a data structure in an unspecified order. Keys are guaranteed "
|
||||
"to be seen only once per iteration if they data structure is not mutated "
|
||||
"to be seen only once per iteration if the data structure is not mutated "
|
||||
"during iteration. If key is nil, next returns the first key. If next "
|
||||
"returns nil, there are no more keys to iterate through."));
|
||||
janet_quick_asm(env, JANET_FUN_PROP,
|
||||
"propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm),
|
||||
JDOC("(propagate x fiber)\n\n"
|
||||
"Propagate a signal from a fiber to the current fiber. The resulting "
|
||||
"stack trace from the current fiber will include frames from fiber. If "
|
||||
"fiber is in a state that can be resumed, resuming the current fiber will "
|
||||
"first resume fiber. This function can be used to re-raise an error without "
|
||||
"losing the original stack trace."));
|
||||
"Propagate a signal from a fiber to the current fiber and "
|
||||
"set the last value of the current fiber to `x`. The signal "
|
||||
"value is then available as the status of the current fiber. "
|
||||
"The resulting stack trace from the current fiber will include "
|
||||
"frames from fiber. If fiber is in a state that can be resumed, "
|
||||
"resuming the current fiber will first resume `fiber`. "
|
||||
"This function can be used to re-raise an error without losing "
|
||||
"the original stack trace."));
|
||||
janet_quick_asm(env, JANET_FUN_DEBUG,
|
||||
"debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm),
|
||||
JDOC("(debug &opt x)\n\n"
|
||||
|
@ -1103,6 +1235,18 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
|||
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
templatize_varop(env, JANET_FUN_DIVIDE_FLOOR, "div", 1, 1, JOP_DIVIDE_FLOOR,
|
||||
JDOC("(div & xs)\n\n"
|
||||
"Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
templatize_varop(env, JANET_FUN_MODULO, "mod", 0, 1, JOP_MODULO,
|
||||
JDOC("(mod & xs)\n\n"
|
||||
"Returns the result of applying the modulo operator on the first value of xs with each remaining value. "
|
||||
"`(mod x 0)` is defined to be `x`."));
|
||||
templatize_varop(env, JANET_FUN_REMAINDER, "%", 0, 1, JOP_REMAINDER,
|
||||
JDOC("(% & xs)\n\n"
|
||||
"Returns the remainder of dividing the first value of xs by each remaining value."));
|
||||
templatize_varop(env, JANET_FUN_BAND, "band", -1, -1, JOP_BAND,
|
||||
JDOC("(band & xs)\n\n"
|
||||
"Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -86,7 +86,7 @@ void janet_debug_find(
|
|||
}
|
||||
}
|
||||
}
|
||||
current = current->next;
|
||||
current = current->data.next;
|
||||
}
|
||||
if (best_def) {
|
||||
*def_out = best_def;
|
||||
|
@ -96,15 +96,19 @@ void janet_debug_find(
|
|||
}
|
||||
}
|
||||
|
||||
void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
||||
const char *prefix = janet_checktype(err, JANET_NIL) ? NULL : "";
|
||||
janet_stacktrace_ext(fiber, err, prefix);
|
||||
}
|
||||
|
||||
/* Error reporting. This can be emulated from within Janet, but for
|
||||
* consitency with the top level code it is defined once. */
|
||||
void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
||||
void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
|
||||
|
||||
int32_t fi;
|
||||
const char *errstr = (const char *)janet_to_string(err);
|
||||
JanetFiber **fibers = NULL;
|
||||
|
||||
/* Don't print error line if it is nil. */
|
||||
int wrote_error = janet_checktype(err, JANET_NIL);
|
||||
int wrote_error = !prefix;
|
||||
|
||||
int print_color = janet_truthy(janet_dyn("err-color"));
|
||||
if (print_color) janet_eprintf("\x1b[31m");
|
||||
|
@ -126,11 +130,10 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
|||
/* Print prelude to stack frame */
|
||||
if (!wrote_error) {
|
||||
JanetFiberStatus status = janet_fiber_status(fiber);
|
||||
const char *prefix = status == JANET_STATUS_ERROR ? "" : "status ";
|
||||
janet_eprintf("%s%s: %s\n",
|
||||
prefix,
|
||||
prefix ? prefix : "",
|
||||
janet_status_names[status],
|
||||
errstr);
|
||||
errstr ? errstr : janet_status_names[status]);
|
||||
wrote_error = 1;
|
||||
}
|
||||
|
||||
|
@ -161,7 +164,7 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
|||
}
|
||||
}
|
||||
if (frame->flags & JANET_STACKFRAME_TAILCALL)
|
||||
janet_eprintf(" (tailcall)");
|
||||
janet_eprintf(" (tail call)");
|
||||
if (frame->func && frame->pc) {
|
||||
int32_t off = (int32_t)(frame->pc - def->bytecode);
|
||||
if (def->sourcemap) {
|
||||
|
@ -177,6 +180,11 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
|||
}
|
||||
}
|
||||
janet_eprintf("\n");
|
||||
/* Print fiber points optionally. Clutters traces but provides info
|
||||
if (i <= 0 && fi > 0) {
|
||||
janet_eprintf(" in parent fiber\n");
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,6 +319,7 @@ static Janet doframe(JanetStackFrame *frame) {
|
|||
if (frame->func && frame->pc) {
|
||||
Janet *stack = (Janet *)frame + JANET_FRAME_SIZE;
|
||||
JanetArray *slots;
|
||||
janet_assert(def != NULL, "def != NULL");
|
||||
off = (int32_t)(frame->pc - def->bytecode);
|
||||
janet_table_put(t, janet_ckeywordv("pc"), janet_wrap_integer(off));
|
||||
if (def->sourcemap) {
|
||||
|
@ -326,6 +335,27 @@ static Janet doframe(JanetStackFrame *frame) {
|
|||
safe_memcpy(slots->data, stack, sizeof(Janet) * def->slotcount);
|
||||
slots->count = def->slotcount;
|
||||
janet_table_put(t, janet_ckeywordv("slots"), janet_wrap_array(slots));
|
||||
/* Add local bindings */
|
||||
if (def->symbolmap) {
|
||||
JanetTable *local_bindings = janet_table(0);
|
||||
for (int32_t i = def->symbolmap_length - 1; i >= 0; i--) {
|
||||
JanetSymbolMap jsm = def->symbolmap[i];
|
||||
Janet value = janet_wrap_nil();
|
||||
uint32_t pc = (uint32_t)(frame->pc - def->bytecode);
|
||||
if (jsm.birth_pc == UINT32_MAX) {
|
||||
JanetFuncEnv *env = frame->func->envs[jsm.death_pc];
|
||||
if (env->offset > 0) {
|
||||
value = env->as.fiber->data[env->offset + jsm.slot_index];
|
||||
} else {
|
||||
value = env->as.values[jsm.slot_index];
|
||||
}
|
||||
} else if (pc >= jsm.birth_pc && pc < jsm.death_pc) {
|
||||
value = stack[jsm.slot_index];
|
||||
}
|
||||
janet_table_put(local_bindings, janet_wrap_symbol(jsm.symbol), value);
|
||||
}
|
||||
janet_table_put(t, janet_ckeywordv("locals"), janet_wrap_table(local_bindings));
|
||||
}
|
||||
}
|
||||
return janet_wrap_table(t);
|
||||
}
|
||||
|
@ -337,9 +367,9 @@ JANET_CORE_FN(cfun_debug_stack,
|
|||
"stack frame is the first table in the array, and the bottom-most stack frame "
|
||||
"is the last value. Each stack frame contains some of the following attributes:\n\n"
|
||||
"* :c - true if the stack frame is a c function invocation\n\n"
|
||||
"* :column - the current source column of the stack frame\n\n"
|
||||
"* :source-column - the current source column of the stack frame\n\n"
|
||||
"* :function - the function that the stack frame represents\n\n"
|
||||
"* :line - the current source line of the stack frame\n\n"
|
||||
"* :source-line - the current source line of the stack frame\n\n"
|
||||
"* :name - the human-friendly name of the function\n\n"
|
||||
"* :pc - integer indicating the location of the program counter\n\n"
|
||||
"* :source - string with the file path or other identifier for the source code\n\n"
|
||||
|
@ -361,14 +391,15 @@ JANET_CORE_FN(cfun_debug_stack,
|
|||
}
|
||||
|
||||
JANET_CORE_FN(cfun_debug_stacktrace,
|
||||
"(debug/stacktrace fiber &opt err)",
|
||||
"(debug/stacktrace fiber &opt err prefix)",
|
||||
"Prints a nice looking stacktrace for a fiber. Can optionally provide "
|
||||
"an error value to print the stack trace with. If `err` is nil or not "
|
||||
"an error value to print the stack trace with. If `prefix` is nil or not "
|
||||
"provided, will skip the error line. Returns the fiber.") {
|
||||
janet_arity(argc, 1, 2);
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
Janet x = argc == 1 ? janet_wrap_nil() : argv[1];
|
||||
janet_stacktrace(fiber, x);
|
||||
const char *prefix = janet_optcstring(argv, argc, 2, NULL);
|
||||
janet_stacktrace_ext(fiber, x, prefix);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -26,6 +26,7 @@
|
|||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
#include "regalloc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Get a register */
|
||||
|
@ -128,7 +129,8 @@ static void janetc_movenear(JanetCompiler *c,
|
|||
((uint32_t)(src.envindex) << 16) |
|
||||
((uint32_t)(dest) << 8) |
|
||||
JOP_LOAD_UPVALUE);
|
||||
} else if (src.index > 0xFF || src.index != dest) {
|
||||
} else if (src.index != dest) {
|
||||
janet_assert(src.index >= 0, "bad slot");
|
||||
janetc_emit(c,
|
||||
((uint32_t)(src.index) << 16) |
|
||||
((uint32_t)(dest) << 8) |
|
||||
|
@ -155,6 +157,7 @@ static void janetc_moveback(JanetCompiler *c,
|
|||
((uint32_t)(src) << 8) |
|
||||
JOP_SET_UPVALUE);
|
||||
} else if (dest.index != src) {
|
||||
janet_assert(dest.index >= 0, "bad slot");
|
||||
janetc_emit(c,
|
||||
((uint32_t)(dest.index) << 16) |
|
||||
((uint32_t)(src) << 8) |
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
1539
src/core/ev.c
1539
src/core/ev.c
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -26,9 +26,10 @@
|
|||
#define JANET_FEATURES_H_defined
|
||||
|
||||
#if defined(__NetBSD__) || defined(__APPLE__) || defined(__OpenBSD__) \
|
||||
|| defined(__bsdi__) || defined(__DragonFly__)
|
||||
|| defined(__bsdi__) || defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
/* Use BSD source on any BSD systems, include OSX */
|
||||
# define _BSD_SOURCE
|
||||
# define _POSIX_C_SOURCE 200809L
|
||||
#else
|
||||
/* Use POSIX feature flags */
|
||||
# ifndef _POSIX_C_SOURCE
|
||||
|
@ -36,13 +37,31 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define _DARWIN_C_SOURCE
|
||||
#endif
|
||||
|
||||
/* Needed for sched.h for cpu count */
|
||||
#ifdef __linux__
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
/* Needed for realpath on linux */
|
||||
#if !defined(_XOPEN_SOURCE) && (defined(__linux__) || defined(__EMSCRIPTEN__))
|
||||
#define _XOPEN_SOURCE 500
|
||||
/* needed for inet_pton and InitializeSRWLock */
|
||||
#ifdef __MINGW32__
|
||||
#define _WIN32_WINNT _WIN32_WINNT_VISTA
|
||||
#endif
|
||||
|
||||
/* Needed for realpath on linux, as well as pthread rwlocks. */
|
||||
#ifndef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
#if _XOPEN_SOURCE < 600
|
||||
#undef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
|
||||
/* Needed for timegm and other extensions when building with -std=c99.
|
||||
|
@ -52,4 +71,11 @@
|
|||
#define _NETBSD_SOURCE
|
||||
#endif
|
||||
|
||||
/* Needed for several things when building with -std=c99. */
|
||||
#if !__BSD_VISIBLE && (defined(__DragonFly__) || defined(__FreeBSD__))
|
||||
#define __BSD_VISIBLE 1
|
||||
#endif
|
||||
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -39,8 +39,10 @@ static void fiber_reset(JanetFiber *fiber) {
|
|||
fiber->env = NULL;
|
||||
fiber->last_value = janet_wrap_nil();
|
||||
#ifdef JANET_EV
|
||||
fiber->waiting = NULL;
|
||||
fiber->sched_id = 0;
|
||||
fiber->ev_callback = NULL;
|
||||
fiber->ev_state = NULL;
|
||||
fiber->ev_stream = NULL;
|
||||
fiber->supervisor_channel = NULL;
|
||||
#endif
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_NEW);
|
||||
|
@ -81,10 +83,10 @@ JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t
|
|||
}
|
||||
fiber->stacktop = newstacktop;
|
||||
}
|
||||
/* Don't panic on failure since we use this to implement janet_pcall */
|
||||
if (janet_fiber_funcframe(fiber, callee)) return NULL;
|
||||
janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
|
||||
#ifdef JANET_EV
|
||||
fiber->waiting = NULL;
|
||||
fiber->supervisor_channel = NULL;
|
||||
#endif
|
||||
return fiber;
|
||||
|
@ -237,8 +239,8 @@ int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) {
|
|||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead)
|
||||
: janet_wrap_tuple(janet_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead));
|
||||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,8 +370,8 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
|
|||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead)
|
||||
: janet_wrap_tuple(janet_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead));
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead));
|
||||
}
|
||||
stacksize = tuplehead - fiber->stackstart + 1;
|
||||
} else {
|
||||
|
@ -477,10 +479,10 @@ JANET_CORE_FN(cfun_fiber_setenv,
|
|||
}
|
||||
|
||||
JANET_CORE_FN(cfun_fiber_new,
|
||||
"(fiber/new func &opt sigmask)",
|
||||
"(fiber/new func &opt sigmask env)",
|
||||
"Create a new fiber with function body func. Can optionally "
|
||||
"take a set of signals to block from the current parent fiber "
|
||||
"when called. The mask is specified as a keyword where each character "
|
||||
"take a set of signals `sigmask` to capture from child fibers, "
|
||||
"and an environment table `env`. The mask is specified as a keyword where each character "
|
||||
"is used to indicate a signal to block. If the ev module is enabled, and "
|
||||
"this fiber is used as an argument to `ev/go`, these \"blocked\" signals "
|
||||
"will result in messages being sent to the supervisor channel. "
|
||||
|
@ -495,19 +497,25 @@ JANET_CORE_FN(cfun_fiber_new,
|
|||
"* :t - block termination signals: error + user[0-4]\n"
|
||||
"* :u - block user signals\n"
|
||||
"* :y - block yield signals\n"
|
||||
"* :w - block await signals (user9)\n"
|
||||
"* :r - block interrupt signals (user8)\n"
|
||||
"* :0-9 - block a specific user signal\n\n"
|
||||
"The sigmask argument also can take environment flags. If any mutually "
|
||||
"exclusive flags are present, the last flag takes precedence.\n\n"
|
||||
"* :i - inherit the environment from the current fiber\n"
|
||||
"* :p - the environment table's prototype is the current environment table") {
|
||||
janet_arity(argc, 1, 2);
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetFunction *func = janet_getfunction(argv, 0);
|
||||
JanetFiber *fiber;
|
||||
if (func->def->min_arity > 1) {
|
||||
janet_panicf("fiber function must accept 0 or 1 arguments");
|
||||
}
|
||||
fiber = janet_fiber(func, 64, func->def->min_arity, NULL);
|
||||
if (argc == 2) {
|
||||
janet_assert(fiber != NULL, "bad fiber arity check");
|
||||
if (argc == 3 && !janet_checktype(argv[2], JANET_NIL)) {
|
||||
fiber->env = janet_gettable(argv, 2);
|
||||
}
|
||||
if (argc >= 2) {
|
||||
int32_t i;
|
||||
JanetByteView view = janet_getbytes(argv, 1);
|
||||
fiber->flags = JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
|
||||
|
@ -518,7 +526,7 @@ JANET_CORE_FN(cfun_fiber_new,
|
|||
} else {
|
||||
switch (view.bytes[i]) {
|
||||
default:
|
||||
janet_panicf("invalid flag %c, expected a, t, d, e, u, y, i, or p", view.bytes[i]);
|
||||
janet_panicf("invalid flag %c, expected a, t, d, e, u, y, w, r, i, or p", view.bytes[i]);
|
||||
break;
|
||||
case 'a':
|
||||
fiber->flags |=
|
||||
|
@ -548,6 +556,12 @@ JANET_CORE_FN(cfun_fiber_new,
|
|||
case 'y':
|
||||
fiber->flags |= JANET_FIBER_MASK_YIELD;
|
||||
break;
|
||||
case 'w':
|
||||
fiber->flags |= JANET_FIBER_MASK_USER9;
|
||||
break;
|
||||
case 'r':
|
||||
fiber->flags |= JANET_FIBER_MASK_USER8;
|
||||
break;
|
||||
case 'i':
|
||||
if (!janet_vm.fiber->env) {
|
||||
janet_vm.fiber->env = janet_table(0);
|
||||
|
@ -575,7 +589,9 @@ JANET_CORE_FN(cfun_fiber_status,
|
|||
"* :error - the fiber has errored out\n"
|
||||
"* :debug - the fiber is suspended in debug mode\n"
|
||||
"* :pending - the fiber has been yielded\n"
|
||||
"* :user(0-9) - the fiber is suspended by a user signal\n"
|
||||
"* :user(0-7) - the fiber is suspended by a user signal\n"
|
||||
"* :interrupted - the fiber was interrupted\n"
|
||||
"* :suspended - the fiber is waiting to be resumed by the scheduler\n"
|
||||
"* :alive - the fiber is currently running and cannot be resumed\n"
|
||||
"* :new - the fiber has just been created and not yet run") {
|
||||
janet_fixarity(argc, 1);
|
||||
|
@ -625,11 +641,7 @@ JANET_CORE_FN(cfun_fiber_setmaxstack,
|
|||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_fiber_can_resume,
|
||||
"(fiber/can-resume? fiber)",
|
||||
"Check if a fiber is finished and cannot be resumed.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
int janet_fiber_can_resume(JanetFiber *fiber) {
|
||||
JanetFiberStatus s = janet_fiber_status(fiber);
|
||||
int isFinished = s == JANET_STATUS_DEAD ||
|
||||
s == JANET_STATUS_ERROR ||
|
||||
|
@ -638,11 +650,19 @@ JANET_CORE_FN(cfun_fiber_can_resume,
|
|||
s == JANET_STATUS_USER2 ||
|
||||
s == JANET_STATUS_USER3 ||
|
||||
s == JANET_STATUS_USER4;
|
||||
return janet_wrap_boolean(!isFinished);
|
||||
return !isFinished;
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_fiber_can_resume,
|
||||
"(fiber/can-resume? fiber)",
|
||||
"Check if a fiber is finished and cannot be resumed.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
return janet_wrap_boolean(janet_fiber_can_resume(fiber));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_fiber_last_value,
|
||||
"(fiber/last-value)",
|
||||
"(fiber/last-value fiber)",
|
||||
"Get the last value returned or signaled from the fiber.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -56,6 +56,13 @@
|
|||
#define JANET_FIBER_DID_LONGJUMP 0x8000000
|
||||
#define JANET_FIBER_FLAG_MASK 0xF000000
|
||||
|
||||
#define JANET_FIBER_EV_FLAG_CANCELED 0x10000
|
||||
#define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000
|
||||
#define JANET_FIBER_FLAG_ROOT 0x40000
|
||||
#define JANET_FIBER_EV_FLAG_IN_FLIGHT 0x1
|
||||
|
||||
/* used only on windows, should otherwise be unset */
|
||||
|
||||
#define janet_fiber_set_status(f, s) do {\
|
||||
(f)->flags &= ~JANET_FIBER_STATUS_MASK;\
|
||||
(f)->flags |= (s) << JANET_FIBER_STATUS_OFFSET;\
|
||||
|
|
189
src/core/gc.c
189
src/core/gc.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -132,6 +132,24 @@ static void janet_mark_many(const Janet *values, int32_t n) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Mark a bunch of key values items in memory */
|
||||
static void janet_mark_keys(const JanetKV *kvs, int32_t n) {
|
||||
const JanetKV *end = kvs + n;
|
||||
while (kvs < end) {
|
||||
janet_mark(kvs->key);
|
||||
kvs++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark a bunch of key values items in memory */
|
||||
static void janet_mark_values(const JanetKV *kvs, int32_t n) {
|
||||
const JanetKV *end = kvs + n;
|
||||
while (kvs < end) {
|
||||
janet_mark(kvs->value);
|
||||
kvs++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark a bunch of key values items in memory */
|
||||
static void janet_mark_kvs(const JanetKV *kvs, int32_t n) {
|
||||
const JanetKV *end = kvs + n;
|
||||
|
@ -146,7 +164,9 @@ static void janet_mark_array(JanetArray *array) {
|
|||
if (janet_gc_reachable(array))
|
||||
return;
|
||||
janet_gc_mark(array);
|
||||
janet_mark_many(array->data, array->count);
|
||||
if (janet_gc_type((JanetGCObject *) array) == JANET_MEMORY_ARRAY) {
|
||||
janet_mark_many(array->data, array->count);
|
||||
}
|
||||
}
|
||||
|
||||
static void janet_mark_table(JanetTable *table) {
|
||||
|
@ -154,7 +174,15 @@ recur: /* Manual tail recursion */
|
|||
if (janet_gc_reachable(table))
|
||||
return;
|
||||
janet_gc_mark(table);
|
||||
janet_mark_kvs(table->data, table->capacity);
|
||||
enum JanetMemoryType memtype = janet_gc_type(table);
|
||||
if (memtype == JANET_MEMORY_TABLE_WEAKK) {
|
||||
janet_mark_values(table->data, table->capacity);
|
||||
} else if (memtype == JANET_MEMORY_TABLE_WEAKV) {
|
||||
janet_mark_keys(table->data, table->capacity);
|
||||
} else if (memtype == JANET_MEMORY_TABLE) {
|
||||
janet_mark_kvs(table->data, table->capacity);
|
||||
}
|
||||
/* do nothing for JANET_MEMORY_TABLE_WEAKKV */
|
||||
if (table->proto) {
|
||||
table = table->proto;
|
||||
goto recur;
|
||||
|
@ -162,10 +190,13 @@ recur: /* Manual tail recursion */
|
|||
}
|
||||
|
||||
static void janet_mark_struct(const JanetKV *st) {
|
||||
recur:
|
||||
if (janet_gc_reachable(janet_struct_head(st)))
|
||||
return;
|
||||
janet_gc_mark(janet_struct_head(st));
|
||||
janet_mark_kvs(st, janet_struct_capacity(st));
|
||||
st = janet_struct_proto(st);
|
||||
if (st) goto recur;
|
||||
}
|
||||
|
||||
static void janet_mark_tuple(const Janet *tuple) {
|
||||
|
@ -206,6 +237,12 @@ static void janet_mark_funcdef(JanetFuncDef *def) {
|
|||
janet_mark_string(def->source);
|
||||
if (def->name)
|
||||
janet_mark_string(def->name);
|
||||
if (def->symbolmap) {
|
||||
for (int i = 0; i < def->symbolmap_length; i++) {
|
||||
janet_mark_string(def->symbolmap[i].symbol);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void janet_mark_function(JanetFunction *func) {
|
||||
|
@ -259,6 +296,12 @@ recur:
|
|||
if (fiber->supervisor_channel) {
|
||||
janet_mark_abstract(fiber->supervisor_channel);
|
||||
}
|
||||
if (fiber->ev_stream) {
|
||||
janet_mark_abstract(fiber->ev_stream);
|
||||
}
|
||||
if (fiber->ev_callback) {
|
||||
fiber->ev_callback(fiber, JANET_ASYNC_EVENT_MARK);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Explicit tail recursion */
|
||||
|
@ -283,9 +326,17 @@ static void janet_deinit_block(JanetGCObject *mem) {
|
|||
case JANET_MEMORY_TABLE:
|
||||
janet_free(((JanetTable *) mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_FIBER:
|
||||
janet_free(((JanetFiber *)mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_FIBER: {
|
||||
JanetFiber *f = (JanetFiber *)mem;
|
||||
#ifdef JANET_EV
|
||||
if (f->ev_state && !(f->flags & JANET_FIBER_EV_FLAG_IN_FLIGHT)) {
|
||||
janet_ev_dec_refcount();
|
||||
janet_free(f->ev_state);
|
||||
}
|
||||
#endif
|
||||
janet_free(f->data);
|
||||
}
|
||||
break;
|
||||
case JANET_MEMORY_BUFFER:
|
||||
janet_buffer_deinit((JanetBuffer *) mem);
|
||||
break;
|
||||
|
@ -311,19 +362,85 @@ static void janet_deinit_block(JanetGCObject *mem) {
|
|||
janet_free(def->bytecode);
|
||||
janet_free(def->sourcemap);
|
||||
janet_free(def->closure_bitset);
|
||||
janet_free(def->symbolmap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that a value x has been visited in the mark phase */
|
||||
static int janet_check_liveref(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
return 1;
|
||||
case JANET_ARRAY:
|
||||
case JANET_TABLE:
|
||||
case JANET_FUNCTION:
|
||||
case JANET_BUFFER:
|
||||
case JANET_FIBER:
|
||||
return janet_gc_reachable(janet_unwrap_pointer(x));
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
return janet_gc_reachable(janet_string_head(janet_unwrap_string(x)));
|
||||
case JANET_ABSTRACT:
|
||||
return janet_gc_reachable(janet_abstract_head(janet_unwrap_abstract(x)));
|
||||
case JANET_TUPLE:
|
||||
return janet_gc_reachable(janet_tuple_head(janet_unwrap_tuple(x)));
|
||||
case JANET_STRUCT:
|
||||
return janet_gc_reachable(janet_struct_head(janet_unwrap_struct(x)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over all allocated memory, and free memory that is not
|
||||
* marked as reachable. Flip the gc color flag for next sweep. */
|
||||
void janet_sweep() {
|
||||
JanetGCObject *previous = NULL;
|
||||
JanetGCObject *current = janet_vm.blocks;
|
||||
JanetGCObject *current = janet_vm.weak_blocks;
|
||||
JanetGCObject *next;
|
||||
|
||||
/* Sweep weak heap to drop weak refs */
|
||||
while (NULL != current) {
|
||||
next = current->next;
|
||||
next = current->data.next;
|
||||
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
|
||||
/* Check for dead references */
|
||||
enum JanetMemoryType type = janet_gc_type(current);
|
||||
if (type == JANET_MEMORY_ARRAY_WEAK) {
|
||||
JanetArray *array = (JanetArray *) current;
|
||||
for (uint32_t i = 0; i < (uint32_t) array->count; i++) {
|
||||
if (!janet_check_liveref(array->data[i])) {
|
||||
array->data[i] = janet_wrap_nil();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JanetTable *table = (JanetTable *) current;
|
||||
int check_values = (type == JANET_MEMORY_TABLE_WEAKV) || (type == JANET_MEMORY_TABLE_WEAKKV);
|
||||
int check_keys = (type == JANET_MEMORY_TABLE_WEAKK) || (type == JANET_MEMORY_TABLE_WEAKKV);
|
||||
JanetKV *end = table->data + table->capacity;
|
||||
JanetKV *kvs = table->data;
|
||||
while (kvs < end) {
|
||||
int drop = 0;
|
||||
if (check_keys && !janet_check_liveref(kvs->key)) drop = 1;
|
||||
if (check_values && !janet_check_liveref(kvs->value)) drop = 1;
|
||||
if (drop) {
|
||||
/* Inlined from janet_table_remove without search */
|
||||
table->count--;
|
||||
table->deleted++;
|
||||
kvs->key = janet_wrap_nil();
|
||||
kvs->value = janet_wrap_false();
|
||||
}
|
||||
kvs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
|
||||
/* Sweep weak heap to free blocks */
|
||||
previous = NULL;
|
||||
current = janet_vm.weak_blocks;
|
||||
while (NULL != current) {
|
||||
next = current->data.next;
|
||||
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
|
||||
previous = current;
|
||||
current->flags &= ~JANET_MEM_REACHABLE;
|
||||
|
@ -331,7 +448,28 @@ void janet_sweep() {
|
|||
janet_vm.block_count--;
|
||||
janet_deinit_block(current);
|
||||
if (NULL != previous) {
|
||||
previous->next = next;
|
||||
previous->data.next = next;
|
||||
} else {
|
||||
janet_vm.weak_blocks = next;
|
||||
}
|
||||
janet_free(current);
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
|
||||
/* Sweep main heap to free blocks */
|
||||
previous = NULL;
|
||||
current = janet_vm.blocks;
|
||||
while (NULL != current) {
|
||||
next = current->data.next;
|
||||
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
|
||||
previous = current;
|
||||
current->flags &= ~JANET_MEM_REACHABLE;
|
||||
} else {
|
||||
janet_vm.block_count--;
|
||||
janet_deinit_block(current);
|
||||
if (NULL != previous) {
|
||||
previous->data.next = next;
|
||||
} else {
|
||||
janet_vm.blocks = next;
|
||||
}
|
||||
|
@ -339,6 +477,7 @@ void janet_sweep() {
|
|||
}
|
||||
current = next;
|
||||
}
|
||||
|
||||
#ifdef JANET_EV
|
||||
/* Sweep threaded abstract types for references to decrement */
|
||||
JanetKV *items = janet_vm.threaded_abstracts.data;
|
||||
|
@ -360,14 +499,15 @@ void janet_sweep() {
|
|||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
/* Mark as tombstone in place */
|
||||
items[i].key = janet_wrap_nil();
|
||||
items[i].value = janet_wrap_false();
|
||||
janet_vm.threaded_abstracts.deleted++;
|
||||
janet_vm.threaded_abstracts.count--;
|
||||
/* Free memory */
|
||||
janet_free(janet_abstract_head(abst));
|
||||
}
|
||||
|
||||
/* Mark as tombstone in place */
|
||||
items[i].key = janet_wrap_nil();
|
||||
items[i].value = janet_wrap_false();
|
||||
janet_vm.threaded_abstracts.deleted++;
|
||||
janet_vm.threaded_abstracts.count--;
|
||||
}
|
||||
|
||||
/* Reset for next sweep */
|
||||
|
@ -395,8 +535,15 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
|
|||
|
||||
/* Prepend block to heap list */
|
||||
janet_vm.next_collection += size;
|
||||
mem->next = janet_vm.blocks;
|
||||
janet_vm.blocks = mem;
|
||||
if (type < JANET_MEMORY_TABLE_WEAKK) {
|
||||
/* normal heap */
|
||||
mem->data.next = janet_vm.blocks;
|
||||
janet_vm.blocks = mem;
|
||||
} else {
|
||||
/* weak heap */
|
||||
mem->data.next = janet_vm.weak_blocks;
|
||||
janet_vm.weak_blocks = mem;
|
||||
}
|
||||
janet_vm.block_count++;
|
||||
|
||||
return (void *)mem;
|
||||
|
@ -427,7 +574,8 @@ void janet_collect(void) {
|
|||
uint32_t i;
|
||||
if (janet_vm.gc_suspend) return;
|
||||
depth = JANET_RECURSION_GUARD;
|
||||
/* Try and prevent many major collections back to back.
|
||||
janet_vm.gc_mark_phase = 1;
|
||||
/* Try to prevent many major collections back to back.
|
||||
* A full collection will take O(janet_vm.block_count) time.
|
||||
* If we have a large heap, make sure our interval is not too
|
||||
* small so we won't make many collections over it. This is just a
|
||||
|
@ -446,6 +594,7 @@ void janet_collect(void) {
|
|||
Janet x = janet_vm.roots[--janet_vm.root_count];
|
||||
janet_mark(x);
|
||||
}
|
||||
janet_vm.gc_mark_phase = 0;
|
||||
janet_sweep();
|
||||
janet_vm.next_collection = 0;
|
||||
janet_free_all_scratch();
|
||||
|
@ -532,7 +681,7 @@ void janet_clear_memory(void) {
|
|||
JanetGCObject *current = janet_vm.blocks;
|
||||
while (NULL != current) {
|
||||
janet_deinit_block(current);
|
||||
JanetGCObject *next = current->next;
|
||||
JanetGCObject *next = current->data.next;
|
||||
janet_free(current);
|
||||
current = next;
|
||||
}
|
||||
|
@ -549,7 +698,9 @@ void janet_gcunlock(int handle) {
|
|||
janet_vm.gc_suspend = handle;
|
||||
}
|
||||
|
||||
/* Scratch memory API */
|
||||
/* Scratch memory API
|
||||
* Scratch memory allocations do not need to be free (but optionally can be), and will be automatically cleaned
|
||||
* up in the next call to janet_collect. */
|
||||
|
||||
void *janet_smalloc(size_t size) {
|
||||
JanetScratch *s = janet_malloc(sizeof(JanetScratch) + size);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -57,6 +57,10 @@ enum JanetMemoryType {
|
|||
JANET_MEMORY_FUNCENV,
|
||||
JANET_MEMORY_FUNCDEF,
|
||||
JANET_MEMORY_THREADED_ABSTRACT,
|
||||
JANET_MEMORY_TABLE_WEAKK,
|
||||
JANET_MEMORY_TABLE_WEAKV,
|
||||
JANET_MEMORY_TABLE_WEAKKV,
|
||||
JANET_MEMORY_ARRAY_WEAK
|
||||
};
|
||||
|
||||
/* To allocate collectable memory, one must call janet_alloc, initialize the memory,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose & contributors
|
||||
* Copyright (c) 2023 Calvin Rose & contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -73,13 +73,13 @@ static void *int64_unmarshal(JanetMarshalContext *ctx) {
|
|||
|
||||
static void it_s64_tostring(void *p, JanetBuffer *buffer) {
|
||||
char str[32];
|
||||
sprintf(str, "%" PRId64, *((int64_t *)p));
|
||||
snprintf(str, sizeof(str), "%" PRId64, *((int64_t *)p));
|
||||
janet_buffer_push_cstring(buffer, str);
|
||||
}
|
||||
|
||||
static void it_u64_tostring(void *p, JanetBuffer *buffer) {
|
||||
char str[32];
|
||||
sprintf(str, "%" PRIu64, *((uint64_t *)p));
|
||||
snprintf(str, sizeof(str), "%" PRIu64, *((uint64_t *)p));
|
||||
janet_buffer_push_cstring(buffer, str);
|
||||
}
|
||||
|
||||
|
@ -118,10 +118,9 @@ int64_t janet_unwrap_s64(Janet x) {
|
|||
default:
|
||||
break;
|
||||
case JANET_NUMBER : {
|
||||
double dbl = janet_unwrap_number(x);
|
||||
if (fabs(dbl) <= MAX_INT_IN_DBL)
|
||||
return (int64_t)dbl;
|
||||
break;
|
||||
double d = janet_unwrap_number(x);
|
||||
if (!janet_checkint64range(d)) break;
|
||||
return (int64_t) d;
|
||||
}
|
||||
case JANET_STRING: {
|
||||
int64_t value;
|
||||
|
@ -138,7 +137,7 @@ int64_t janet_unwrap_s64(Janet x) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
janet_panicf("bad s64 initializer: %t", x);
|
||||
janet_panicf("can not convert %t %q to 64 bit signed integer", x, x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -147,12 +146,9 @@ uint64_t janet_unwrap_u64(Janet x) {
|
|||
default:
|
||||
break;
|
||||
case JANET_NUMBER : {
|
||||
double dbl = janet_unwrap_number(x);
|
||||
/* Allow negative values to be cast to "wrap around".
|
||||
* This let's addition and subtraction work as expected. */
|
||||
if (fabs(dbl) <= MAX_INT_IN_DBL)
|
||||
return (uint64_t)dbl;
|
||||
break;
|
||||
double d = janet_unwrap_number(x);
|
||||
if (!janet_checkuint64range(d)) break;
|
||||
return (uint64_t) d;
|
||||
}
|
||||
case JANET_STRING: {
|
||||
uint64_t value;
|
||||
|
@ -169,7 +165,7 @@ uint64_t janet_unwrap_u64(Janet x) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
janet_panicf("bad u64 initializer: %t", x);
|
||||
janet_panicf("can not convert %t %q to a 64 bit unsigned integer", x, x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -207,6 +203,92 @@ JANET_CORE_FN(cfun_it_u64_new,
|
|||
return janet_wrap_u64(janet_unwrap_u64(argv[0]));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_to_number,
|
||||
"(int/to-number value)",
|
||||
"Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int32.") {
|
||||
janet_fixarity(argc, 1);
|
||||
if (janet_type(argv[0]) == JANET_ABSTRACT) {
|
||||
void *abst = janet_unwrap_abstract(argv[0]);
|
||||
|
||||
if (janet_abstract_type(abst) == &janet_s64_type) {
|
||||
int64_t value = *((int64_t *)abst);
|
||||
if (value > JANET_INTMAX_INT64) {
|
||||
janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
|
||||
}
|
||||
if (value < -JANET_INTMAX_INT64) {
|
||||
janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
|
||||
}
|
||||
return janet_wrap_number((double)value);
|
||||
}
|
||||
|
||||
if (janet_abstract_type(abst) == &janet_u64_type) {
|
||||
uint64_t value = *((uint64_t *)abst);
|
||||
if (value > JANET_INTMAX_INT64) {
|
||||
janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
|
||||
}
|
||||
|
||||
return janet_wrap_number((double)value);
|
||||
}
|
||||
}
|
||||
|
||||
janet_panicf("expected int/u64 or int/s64, got %q", argv[0]);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_to_bytes,
|
||||
"(int/to-bytes value &opt endianness buffer)",
|
||||
"Write the bytes of an `int/s64` or `int/u64` into a buffer.\n"
|
||||
"The `buffer` parameter specifies an existing buffer to write to, if unset a new buffer will be created.\n"
|
||||
"Returns the modified buffer.\n"
|
||||
"The `endianness` parameter indicates the byte order:\n"
|
||||
"- `nil` (unset): system byte order\n"
|
||||
"- `:le`: little-endian, least significant byte first\n"
|
||||
"- `:be`: big-endian, most significant byte first\n") {
|
||||
janet_arity(argc, 1, 3);
|
||||
if (janet_is_int(argv[0]) == JANET_INT_NONE) {
|
||||
janet_panicf("int/to-bytes: expected an int/s64 or int/u64, got %q", argv[0]);
|
||||
}
|
||||
|
||||
int reverse = 0;
|
||||
if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) {
|
||||
JanetKeyword endianness_kw = janet_getkeyword(argv, 1);
|
||||
if (!janet_cstrcmp(endianness_kw, "le")) {
|
||||
#if JANET_BIG_ENDIAN
|
||||
reverse = 1;
|
||||
#endif
|
||||
} else if (!janet_cstrcmp(endianness_kw, "be")) {
|
||||
#if JANET_LITTLE_ENDIAN
|
||||
reverse = 1;
|
||||
#endif
|
||||
} else {
|
||||
janet_panicf("int/to-bytes: expected endianness :le, :be or nil, got %v", argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
JanetBuffer *buffer = NULL;
|
||||
if (argc > 2 && !janet_checktype(argv[2], JANET_NIL)) {
|
||||
if (!janet_checktype(argv[2], JANET_BUFFER)) {
|
||||
janet_panicf("int/to-bytes: expected buffer or nil, got %q", argv[2]);
|
||||
}
|
||||
|
||||
buffer = janet_unwrap_buffer(argv[2]);
|
||||
janet_buffer_extra(buffer, 8);
|
||||
} else {
|
||||
buffer = janet_buffer(8);
|
||||
}
|
||||
|
||||
uint8_t *bytes = janet_unwrap_abstract(argv[0]);
|
||||
if (reverse) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
buffer->data[buffer->count + 7 - i] = bytes[i];
|
||||
}
|
||||
} else {
|
||||
memcpy(buffer->data + buffer->count, bytes, 8);
|
||||
}
|
||||
buffer->count += 8;
|
||||
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Code to support polymorphic comparison.
|
||||
* int/u64 and int/s64 support a "compare" method that allows
|
||||
|
@ -221,8 +303,8 @@ static int compare_double_double(double x, double y) {
|
|||
|
||||
static int compare_int64_double(int64_t x, double y) {
|
||||
if (isnan(y)) {
|
||||
return 0; // clojure and python do this
|
||||
} else if ((y > (- ((double) MAX_INT_IN_DBL))) && (y < ((double) MAX_INT_IN_DBL))) {
|
||||
return 0;
|
||||
} else if ((y > JANET_INTMIN_DOUBLE) && (y < JANET_INTMAX_DOUBLE)) {
|
||||
double dx = (double) x;
|
||||
return compare_double_double(dx, y);
|
||||
} else if (y > ((double) INT64_MAX)) {
|
||||
|
@ -237,10 +319,10 @@ static int compare_int64_double(int64_t x, double y) {
|
|||
|
||||
static int compare_uint64_double(uint64_t x, double y) {
|
||||
if (isnan(y)) {
|
||||
return 0; // clojure and python do this
|
||||
return 0;
|
||||
} else if (y < 0) {
|
||||
return 1;
|
||||
} else if ((y >= 0) && (y < ((double) MAX_INT_IN_DBL))) {
|
||||
} else if ((y >= 0) && (y < JANET_INTMAX_DOUBLE)) {
|
||||
double dx = (double) x;
|
||||
return compare_double_double(dx, y);
|
||||
} else if (y > ((double) UINT64_MAX)) {
|
||||
|
@ -253,8 +335,9 @@ static int compare_uint64_double(uint64_t x, double y) {
|
|||
|
||||
static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
if (janet_is_int(argv[0]) != JANET_INT_S64)
|
||||
if (janet_is_int(argv[0]) != JANET_INT_S64) {
|
||||
janet_panic("compare method requires int/s64 as first argument");
|
||||
}
|
||||
int64_t x = janet_unwrap_s64(argv[0]);
|
||||
switch (janet_type(argv[1])) {
|
||||
default:
|
||||
|
@ -269,7 +352,6 @@ static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
|
|||
int64_t y = *(int64_t *)abst;
|
||||
return janet_wrap_number((x < y) ? -1 : (x > y ? 1 : 0));
|
||||
} else if (janet_abstract_type(abst) == &janet_u64_type) {
|
||||
// comparing signed to unsigned -- be careful!
|
||||
uint64_t y = *(uint64_t *)abst;
|
||||
if (x < 0) {
|
||||
return janet_wrap_number(-1);
|
||||
|
@ -288,8 +370,9 @@ static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
|
|||
|
||||
static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
if (janet_is_int(argv[0]) != JANET_INT_U64) // is this needed?
|
||||
if (janet_is_int(argv[0]) != JANET_INT_U64) {
|
||||
janet_panic("compare method requires int/u64 as first argument");
|
||||
}
|
||||
uint64_t x = janet_unwrap_u64(argv[0]);
|
||||
switch (janet_type(argv[1])) {
|
||||
default:
|
||||
|
@ -304,7 +387,6 @@ static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
|
|||
uint64_t y = *(uint64_t *)abst;
|
||||
return janet_wrap_number((x < y) ? -1 : (x > y ? 1 : 0));
|
||||
} else if (janet_abstract_type(abst) == &janet_s64_type) {
|
||||
// comparing unsigned to signed -- be careful!
|
||||
int64_t y = *(int64_t *)abst;
|
||||
if (y < 0) {
|
||||
return janet_wrap_number(1);
|
||||
|
@ -321,25 +403,52 @@ static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
|
|||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/*
|
||||
* In C, signed arithmetic overflow is undefined behvior
|
||||
* but unsigned arithmetic overflow is twos complement
|
||||
*
|
||||
* Reference:
|
||||
* https://en.cppreference.com/w/cpp/language/ub
|
||||
* http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
|
||||
*
|
||||
* This means OPMETHOD & OPMETHODINVERT must always use
|
||||
* unsigned arithmetic internally, regardless of the true type.
|
||||
* This will not affect the end result (property of twos complement).
|
||||
*/
|
||||
#define OPMETHOD(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[0]); \
|
||||
for (int32_t i = 1; i < argc; i++) \
|
||||
*box oper##= janet_unwrap_##type(argv[i]); \
|
||||
/* This avoids undefined behavior. See above for why. */ \
|
||||
*box = (T) ((uint64_t) (*box)) oper ((uint64_t) janet_unwrap_##type(argv[i])); \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
|
||||
#define OPMETHODINVERT(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 2); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[1]); \
|
||||
*box oper##= janet_unwrap_##type(argv[0]); \
|
||||
/* This avoids undefined behavior. See above for why. */ \
|
||||
*box = (T) ((uint64_t) *box) oper ((uint64_t) janet_unwrap_##type(argv[0])); \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
|
||||
#define UNARYMETHOD(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 1); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = oper(janet_unwrap_##type(argv[0])); \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
|
||||
#define DIVZERO(name) DIVZERO_##name
|
||||
#define DIVZERO_div janet_panic("division by zero")
|
||||
#define DIVZERO_rem janet_panic("division by zero")
|
||||
#define DIVZERO_mod return janet_wrap_abstract(box)
|
||||
|
||||
#define DIVMETHOD(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
|
@ -347,19 +456,19 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
|||
*box = janet_unwrap_##type(argv[0]); \
|
||||
for (int32_t i = 1; i < argc; i++) { \
|
||||
T value = janet_unwrap_##type(argv[i]); \
|
||||
if (value == 0) janet_panic("division by zero"); \
|
||||
if (value == 0) DIVZERO(name); \
|
||||
*box oper##= value; \
|
||||
} \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
|
||||
#define DIVMETHODINVERT(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 2); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[1]); \
|
||||
T value = janet_unwrap_##type(argv[0]); \
|
||||
if (value == 0) janet_panic("division by zero"); \
|
||||
if (value == 0) DIVZERO(name); \
|
||||
*box oper##= value; \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
|
@ -371,7 +480,7 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
|||
*box = janet_unwrap_##type(argv[0]); \
|
||||
for (int32_t i = 1; i < argc; i++) { \
|
||||
T value = janet_unwrap_##type(argv[i]); \
|
||||
if (value == 0) janet_panic("division by zero"); \
|
||||
if (value == 0) DIVZERO(name); \
|
||||
if ((value == -1) && (*box == INT64_MIN)) janet_panic("INT64_MIN divided by -1"); \
|
||||
*box oper##= value; \
|
||||
} \
|
||||
|
@ -379,51 +488,95 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
|||
} \
|
||||
|
||||
#define DIVMETHODINVERT_SIGNED(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 2); \
|
||||
T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[1]); \
|
||||
T value = janet_unwrap_##type(argv[0]); \
|
||||
if (value == 0) janet_panic("division by zero"); \
|
||||
if (value == 0) DIVZERO(name); \
|
||||
if ((value == -1) && (*box == INT64_MIN)) janet_panic("INT64_MIN divided by -1"); \
|
||||
*box oper##= value; \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
|
||||
static Janet cfun_it_s64_divf(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
|
||||
int64_t op1 = janet_unwrap_s64(argv[0]);
|
||||
int64_t op2 = janet_unwrap_s64(argv[1]);
|
||||
if (op2 == 0) janet_panic("division by zero");
|
||||
int64_t x = op1 / op2;
|
||||
*box = x - (((op1 ^ op2) < 0) && (x * op2 != op1));
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
static Janet cfun_it_s64_divfi(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
|
||||
int64_t op2 = janet_unwrap_s64(argv[0]);
|
||||
int64_t op1 = janet_unwrap_s64(argv[1]);
|
||||
if (op2 == 0) janet_panic("division by zero");
|
||||
int64_t x = op1 / op2;
|
||||
*box = x - (((op1 ^ op2) < 0) && (x * op2 != op1));
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
static Janet cfun_it_s64_mod(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
|
||||
int64_t op1 = janet_unwrap_s64(argv[0]);
|
||||
int64_t op2 = janet_unwrap_s64(argv[1]);
|
||||
int64_t x = op1 % op2;
|
||||
*box = (op1 > 0)
|
||||
? ((op2 > 0) ? x : (0 == x ? x : x + op2))
|
||||
: ((op2 > 0) ? (0 == x ? x : x + op2) : x);
|
||||
if (op2 == 0) {
|
||||
*box = op1;
|
||||
} else {
|
||||
int64_t x = op1 % op2;
|
||||
*box = (((op1 ^ op2) < 0) && (x != 0)) ? x + op2 : x;
|
||||
}
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
static Janet cfun_it_s64_modi(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
|
||||
int64_t op2 = janet_unwrap_s64(argv[0]);
|
||||
int64_t op1 = janet_unwrap_s64(argv[1]);
|
||||
if (op2 == 0) {
|
||||
*box = op1;
|
||||
} else {
|
||||
int64_t x = op1 % op2;
|
||||
*box = (((op1 ^ op2) < 0) && (x != 0)) ? x + op2 : x;
|
||||
}
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
OPMETHOD(int64_t, s64, add, +)
|
||||
OPMETHOD(int64_t, s64, sub, -)
|
||||
OPMETHODINVERT(int64_t, s64, subi, -)
|
||||
OPMETHODINVERT(int64_t, s64, sub, -)
|
||||
OPMETHOD(int64_t, s64, mul, *)
|
||||
DIVMETHOD_SIGNED(int64_t, s64, div, /)
|
||||
DIVMETHOD_SIGNED(int64_t, s64, rem, %)
|
||||
DIVMETHODINVERT_SIGNED(int64_t, s64, divi, /)
|
||||
DIVMETHODINVERT_SIGNED(int64_t, s64, div, /)
|
||||
DIVMETHODINVERT_SIGNED(int64_t, s64, rem, %)
|
||||
OPMETHOD(int64_t, s64, and, &)
|
||||
OPMETHOD(int64_t, s64, or, |)
|
||||
OPMETHOD(int64_t, s64, xor, ^)
|
||||
UNARYMETHOD(int64_t, s64, not, ~)
|
||||
OPMETHOD(int64_t, s64, lshift, <<)
|
||||
OPMETHOD(int64_t, s64, rshift, >>)
|
||||
OPMETHOD(uint64_t, u64, add, +)
|
||||
OPMETHOD(uint64_t, u64, sub, -)
|
||||
OPMETHODINVERT(uint64_t, u64, subi, -)
|
||||
OPMETHODINVERT(uint64_t, u64, sub, -)
|
||||
OPMETHOD(uint64_t, u64, mul, *)
|
||||
DIVMETHOD(uint64_t, u64, div, /)
|
||||
DIVMETHOD(uint64_t, u64, rem, %)
|
||||
DIVMETHOD(uint64_t, u64, mod, %)
|
||||
DIVMETHODINVERT(uint64_t, u64, divi, /)
|
||||
DIVMETHODINVERT(uint64_t, u64, div, /)
|
||||
DIVMETHODINVERT(uint64_t, u64, rem, %)
|
||||
DIVMETHODINVERT(uint64_t, u64, mod, %)
|
||||
OPMETHOD(uint64_t, u64, and, &)
|
||||
OPMETHOD(uint64_t, u64, or, |)
|
||||
OPMETHOD(uint64_t, u64, xor, ^)
|
||||
UNARYMETHOD(uint64_t, u64, not, ~)
|
||||
OPMETHOD(uint64_t, u64, lshift, <<)
|
||||
OPMETHOD(uint64_t, u64, rshift, >>)
|
||||
|
||||
|
@ -432,7 +585,6 @@ OPMETHOD(uint64_t, u64, rshift, >>)
|
|||
#undef DIVMETHOD_SIGNED
|
||||
#undef COMPMETHOD
|
||||
|
||||
|
||||
static JanetMethod it_s64_methods[] = {
|
||||
{"+", cfun_it_s64_add},
|
||||
{"r+", cfun_it_s64_add},
|
||||
|
@ -442,20 +594,22 @@ static JanetMethod it_s64_methods[] = {
|
|||
{"r*", cfun_it_s64_mul},
|
||||
{"/", cfun_it_s64_div},
|
||||
{"r/", cfun_it_s64_divi},
|
||||
{"div", cfun_it_s64_divf},
|
||||
{"rdiv", cfun_it_s64_divfi},
|
||||
{"mod", cfun_it_s64_mod},
|
||||
{"rmod", cfun_it_s64_mod},
|
||||
{"rmod", cfun_it_s64_modi},
|
||||
{"%", cfun_it_s64_rem},
|
||||
{"r%", cfun_it_s64_rem},
|
||||
{"r%", cfun_it_s64_remi},
|
||||
{"&", cfun_it_s64_and},
|
||||
{"r&", cfun_it_s64_and},
|
||||
{"|", cfun_it_s64_or},
|
||||
{"r|", cfun_it_s64_or},
|
||||
{"^", cfun_it_s64_xor},
|
||||
{"r^", cfun_it_s64_xor},
|
||||
{"~", cfun_it_s64_not},
|
||||
{"<<", cfun_it_s64_lshift},
|
||||
{">>", cfun_it_s64_rshift},
|
||||
{"compare", cfun_it_s64_compare},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -468,20 +622,22 @@ static JanetMethod it_u64_methods[] = {
|
|||
{"r*", cfun_it_u64_mul},
|
||||
{"/", cfun_it_u64_div},
|
||||
{"r/", cfun_it_u64_divi},
|
||||
{"div", cfun_it_u64_div},
|
||||
{"rdiv", cfun_it_u64_divi},
|
||||
{"mod", cfun_it_u64_mod},
|
||||
{"rmod", cfun_it_u64_mod},
|
||||
{"%", cfun_it_u64_mod},
|
||||
{"r%", cfun_it_u64_mod},
|
||||
{"rmod", cfun_it_u64_modi},
|
||||
{"%", cfun_it_u64_rem},
|
||||
{"r%", cfun_it_u64_remi},
|
||||
{"&", cfun_it_u64_and},
|
||||
{"r&", cfun_it_u64_and},
|
||||
{"|", cfun_it_u64_or},
|
||||
{"r|", cfun_it_u64_or},
|
||||
{"^", cfun_it_u64_xor},
|
||||
{"r^", cfun_it_u64_xor},
|
||||
{"~", cfun_it_u64_not},
|
||||
{"<<", cfun_it_u64_lshift},
|
||||
{">>", cfun_it_u64_rshift},
|
||||
{"compare", cfun_it_u64_compare},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -514,6 +670,8 @@ void janet_lib_inttypes(JanetTable *env) {
|
|||
JanetRegExt it_cfuns[] = {
|
||||
JANET_CORE_REG("int/s64", cfun_it_s64_new),
|
||||
JANET_CORE_REG("int/u64", cfun_it_u64_new),
|
||||
JANET_CORE_REG("int/to-number", cfun_to_number),
|
||||
JANET_CORE_REG("int/to-bytes", cfun_to_bytes),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, it_cfuns);
|
||||
|
|
213
src/core/io.c
213
src/core/io.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -41,6 +41,11 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx);
|
|||
static void *io_file_unmarshal(JanetMarshalContext *ctx);
|
||||
static Janet io_file_next(void *p, Janet key);
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#define ftell _ftelli64
|
||||
#define fseek _fseeki64
|
||||
#endif
|
||||
|
||||
const JanetAbstractType janet_file_type = {
|
||||
"core/file",
|
||||
cfun_io_gc,
|
||||
|
@ -69,12 +74,15 @@ static int32_t checkflags(const uint8_t *str) {
|
|||
break;
|
||||
case 'w':
|
||||
flags |= JANET_FILE_WRITE;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
break;
|
||||
case 'a':
|
||||
flags |= JANET_FILE_APPEND;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS);
|
||||
break;
|
||||
case 'r':
|
||||
flags |= JANET_FILE_READ;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
|
||||
break;
|
||||
}
|
||||
for (i = 1; i < len; i++) {
|
||||
|
@ -84,6 +92,7 @@ static int32_t checkflags(const uint8_t *str) {
|
|||
break;
|
||||
case '+':
|
||||
if (flags & JANET_FILE_UPDATE) return -1;
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
|
||||
flags |= JANET_FILE_UPDATE;
|
||||
break;
|
||||
case 'b':
|
||||
|
@ -112,57 +121,22 @@ static void *makef(FILE *f, int32_t flags) {
|
|||
return iof;
|
||||
}
|
||||
|
||||
/* Open a process */
|
||||
#ifndef JANET_NO_PROCESSES
|
||||
JANET_CORE_FN(cfun_io_popen,
|
||||
"(file/popen command &opt mode) (DEPRECATED for os/spawn)",
|
||||
"Open a file that is backed by a process. The file must be opened in either "
|
||||
"the :r (read) or the :w (write) mode. In :r mode, the stdout of the "
|
||||
"process can be read from the file. In :w mode, the stdin of the process "
|
||||
"can be written to. Returns the new file.") {
|
||||
janet_arity(argc, 1, 2);
|
||||
const uint8_t *fname = janet_getstring(argv, 0);
|
||||
const uint8_t *fmode = NULL;
|
||||
int32_t flags;
|
||||
if (argc == 2) {
|
||||
fmode = janet_getkeyword(argv, 1);
|
||||
flags = JANET_FILE_PIPED | checkflags(fmode);
|
||||
if (flags & (JANET_FILE_UPDATE | JANET_FILE_BINARY | JANET_FILE_APPEND)) {
|
||||
janet_panicf("invalid popen file mode :%S, expected :r or :w", fmode);
|
||||
}
|
||||
fmode = (const uint8_t *)((fmode[0] == 'r') ? "r" : "w");
|
||||
} else {
|
||||
fmode = (const uint8_t *)"r";
|
||||
flags = JANET_FILE_PIPED | JANET_FILE_READ;
|
||||
}
|
||||
#ifdef JANET_WINDOWS
|
||||
#define popen _popen
|
||||
#endif
|
||||
FILE *f = popen((const char *)fname, (const char *)fmode);
|
||||
if (!f) {
|
||||
if (flags & JANET_FILE_NONIL)
|
||||
janet_panicf("failed to popen %s: %s", fname, strerror(errno));
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
return janet_makefile(f, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
JANET_CORE_FN(cfun_io_temp,
|
||||
"(file/temp)",
|
||||
"Open an anonymous temporary file that is removed on close. "
|
||||
"Raises an error on failure.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_TEMP);
|
||||
(void)argv;
|
||||
janet_fixarity(argc, 0);
|
||||
// XXX use mkostemp when we can to avoid CLOEXEC race.
|
||||
FILE *tmp = tmpfile();
|
||||
if (!tmp)
|
||||
janet_panicf("unable to create temporary file - %s", strerror(errno));
|
||||
janet_panicf("unable to create temporary file - %s", janet_strerror(errno));
|
||||
return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_fopen,
|
||||
"(file/open path &opt mode)",
|
||||
"(file/open path &opt mode buffer-size)",
|
||||
"Open a file. `path` is an absolute or relative path, and "
|
||||
"`mode` is a set of flags indicating the mode to open the file in. "
|
||||
"`mode` is a keyword where each character represents a flag. If the file "
|
||||
|
@ -174,8 +148,9 @@ JANET_CORE_FN(cfun_io_fopen,
|
|||
"Following one of the initial flags, 0 or more of the following flags can be appended:\n\n"
|
||||
"* b - open the file in binary mode (rather than text mode)\n\n"
|
||||
"* + - append to the file instead of overwriting it\n\n"
|
||||
"* n - error if the file cannot be opened instead of returning nil") {
|
||||
janet_arity(argc, 1, 2);
|
||||
"* n - error if the file cannot be opened instead of returning nil\n\n"
|
||||
"See fopen (<stdio.h>, C99) for further details.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
const uint8_t *fname = janet_getstring(argv, 0);
|
||||
const uint8_t *fmode;
|
||||
int32_t flags;
|
||||
|
@ -184,11 +159,21 @@ JANET_CORE_FN(cfun_io_fopen,
|
|||
flags = checkflags(fmode);
|
||||
} else {
|
||||
fmode = (const uint8_t *)"r";
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
|
||||
flags = JANET_FILE_READ;
|
||||
}
|
||||
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
||||
if (f != NULL) {
|
||||
size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
||||
if (bufsize != BUFSIZ) {
|
||||
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
|
||||
if (result) {
|
||||
janet_panic("failed to set buffer size for file");
|
||||
}
|
||||
}
|
||||
}
|
||||
return f ? janet_makefile(f, flags)
|
||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil())
|
||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
|
||||
|
@ -279,6 +264,13 @@ JANET_CORE_FN(cfun_io_fwrite,
|
|||
return argv[0];
|
||||
}
|
||||
|
||||
static void io_assert_writeable(JanetFile *iof) {
|
||||
if (iof->flags & JANET_FILE_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
|
||||
janet_panic("file is not writeable");
|
||||
}
|
||||
|
||||
/* Flush the bytes in the file */
|
||||
JANET_CORE_FN(cfun_io_fflush,
|
||||
"(file/flush f)",
|
||||
|
@ -286,17 +278,13 @@ JANET_CORE_FN(cfun_io_fflush,
|
|||
"buffered for efficiency reasons. Returns the file handle.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
||||
if (iof->flags & JANET_FILE_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
|
||||
janet_panic("file is not writeable");
|
||||
io_assert_writeable(iof);
|
||||
if (fflush(iof->file))
|
||||
janet_panic("could not flush file");
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#define pclose _pclose
|
||||
#define WEXITSTATUS(x) x
|
||||
#endif
|
||||
|
||||
|
@ -304,15 +292,9 @@ JANET_CORE_FN(cfun_io_fflush,
|
|||
int janet_file_close(JanetFile *file) {
|
||||
int ret = 0;
|
||||
if (!(file->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
|
||||
#ifndef JANET_NO_PROCESSES
|
||||
if (file->flags & JANET_FILE_PIPED) {
|
||||
ret = pclose(file->file);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ret = fclose(file->file);
|
||||
}
|
||||
ret = fclose(file->file);
|
||||
file->flags |= JANET_FILE_CLOSED;
|
||||
file->file = NULL; /* NULL derefence is easier to debug then other problems */
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
|
@ -331,30 +313,18 @@ JANET_CORE_FN(cfun_io_fclose,
|
|||
"(file/close f)",
|
||||
"Close a file and release all related resources. When you are "
|
||||
"done reading a file, close it to prevent a resource leak and let "
|
||||
"other processes read the file. If the file is the result of a file/popen "
|
||||
"call, close waits for and returns the process exit status.") {
|
||||
"other processes read the file.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
||||
if (iof->flags & JANET_FILE_CLOSED)
|
||||
return janet_wrap_nil();
|
||||
if (iof->flags & (JANET_FILE_NOT_CLOSEABLE))
|
||||
janet_panic("file not closable");
|
||||
if (iof->flags & JANET_FILE_PIPED) {
|
||||
#ifndef JANET_NO_PROCESSES
|
||||
int status = pclose(iof->file);
|
||||
iof->flags |= JANET_FILE_CLOSED;
|
||||
if (status == -1) janet_panic("could not close file");
|
||||
return janet_wrap_integer(WEXITSTATUS(status));
|
||||
#else
|
||||
return janet_wrap_nil();
|
||||
#endif
|
||||
} else {
|
||||
if (fclose(iof->file)) {
|
||||
iof->flags |= JANET_FILE_NOT_CLOSEABLE;
|
||||
janet_panic("could not close file");
|
||||
}
|
||||
iof->flags |= JANET_FILE_CLOSED;
|
||||
if (fclose(iof->file)) {
|
||||
iof->flags |= JANET_FILE_NOT_CLOSEABLE;
|
||||
janet_panic("could not close file");
|
||||
}
|
||||
iof->flags |= JANET_FILE_CLOSED;
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
|
@ -372,7 +342,7 @@ JANET_CORE_FN(cfun_io_fseek,
|
|||
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
||||
if (iof->flags & JANET_FILE_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
long int offset = 0;
|
||||
int64_t offset = 0;
|
||||
int whence = SEEK_CUR;
|
||||
if (argc >= 2) {
|
||||
const uint8_t *whence_sym = janet_getkeyword(argv, 1);
|
||||
|
@ -386,18 +356,31 @@ JANET_CORE_FN(cfun_io_fseek,
|
|||
janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]);
|
||||
}
|
||||
if (argc == 3) {
|
||||
offset = (long) janet_getinteger64(argv, 2);
|
||||
offset = (int64_t) janet_getinteger64(argv, 2);
|
||||
}
|
||||
}
|
||||
if (fseek(iof->file, offset, whence)) janet_panic("error seeking file");
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_ftell,
|
||||
"(file/tell f)",
|
||||
"Get the current value of the file position for file `f`.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
||||
if (iof->flags & JANET_FILE_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
int64_t pos = ftell(iof->file);
|
||||
if (pos == -1) janet_panic("error getting position in file");
|
||||
return janet_wrap_number((double)pos);
|
||||
}
|
||||
|
||||
static JanetMethod io_file_methods[] = {
|
||||
{"close", cfun_io_fclose},
|
||||
{"flush", cfun_io_fflush},
|
||||
{"read", cfun_io_fread},
|
||||
{"seek", cfun_io_fseek},
|
||||
{"tell", cfun_io_ftell},
|
||||
{"write", cfun_io_fwrite},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
@ -483,6 +466,19 @@ static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
|
|||
janet_buffer_push_u8(buf, '\n');
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
case JANET_FUNCTION: {
|
||||
/* Special case function */
|
||||
JanetFunction *fun = janet_unwrap_function(x);
|
||||
JanetBuffer *buf = janet_buffer(0);
|
||||
for (int32_t i = offset; i < argc; ++i) {
|
||||
janet_to_string_b(buf, argv[i]);
|
||||
}
|
||||
if (newline)
|
||||
janet_buffer_push_u8(buf, '\n');
|
||||
Janet args[1] = { janet_wrap_buffer(buf) };
|
||||
janet_call(fun, 1, args);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
case JANET_NIL:
|
||||
f = dflt_file;
|
||||
if (f == NULL) janet_panic("cannot print to nil");
|
||||
|
@ -492,6 +488,7 @@ static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
|
|||
if (janet_abstract_type(abstract) != &janet_file_type)
|
||||
return janet_wrap_nil();
|
||||
JanetFile *iofile = abstract;
|
||||
io_assert_writeable(iofile);
|
||||
f = iofile->file;
|
||||
break;
|
||||
}
|
||||
|
@ -522,7 +519,6 @@ static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
|
|||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
|
||||
static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
|
||||
int newline, const char *name, FILE *dflt_file) {
|
||||
Janet x = janet_dyn(name);
|
||||
|
@ -533,27 +529,27 @@ JANET_CORE_FN(cfun_io_print,
|
|||
"(print & xs)",
|
||||
"Print values to the console (standard out). Value are converted "
|
||||
"to strings if they are not already. After printing all values, a "
|
||||
"newline character is printed. Use the value of (dyn :out stdout) to determine "
|
||||
"what to push characters to. Expects (dyn :out stdout) to be either a core/file or "
|
||||
"newline character is printed. Use the value of `(dyn :out stdout)` to determine "
|
||||
"what to push characters to. Expects `(dyn :out stdout)` to be either a core/file or "
|
||||
"a buffer. Returns nil.") {
|
||||
return cfun_io_print_impl(argc, argv, 1, "out", stdout);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_prin,
|
||||
"(prin & xs)",
|
||||
"Same as print, but does not add trailing newline.") {
|
||||
"Same as `print`, but does not add trailing newline.") {
|
||||
return cfun_io_print_impl(argc, argv, 0, "out", stdout);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_eprint,
|
||||
"(eprint & xs)",
|
||||
"Same as print, but uses (dyn :err stderr) instead of (dyn :out stdout).") {
|
||||
"Same as `print`, but uses `(dyn :err stderr)` instead of `(dyn :out stdout)`.") {
|
||||
return cfun_io_print_impl(argc, argv, 1, "err", stderr);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_eprin,
|
||||
"(eprin & xs)",
|
||||
"Same as prin, but uses (dyn :err stderr) instead of (dyn :out stdout).") {
|
||||
"Same as `prin`, but uses `(dyn :err stderr)` instead of `(dyn :out stdout)`.") {
|
||||
return cfun_io_print_impl(argc, argv, 0, "err", stderr);
|
||||
}
|
||||
|
||||
|
@ -561,7 +557,7 @@ JANET_CORE_FN(cfun_io_xprint,
|
|||
"(xprint to & xs)",
|
||||
"Print to a file or other value explicitly (no dynamic bindings) with a trailing "
|
||||
"newline character. The value to print "
|
||||
"to is the first argument, and is otherwise the same as print. Returns nil.") {
|
||||
"to is the first argument, and is otherwise the same as `print`. Returns nil.") {
|
||||
janet_arity(argc, 1, -1);
|
||||
return cfun_io_print_impl_x(argc, argv, 1, NULL, 1, argv[0]);
|
||||
}
|
||||
|
@ -569,7 +565,7 @@ JANET_CORE_FN(cfun_io_xprint,
|
|||
JANET_CORE_FN(cfun_io_xprin,
|
||||
"(xprin to & xs)",
|
||||
"Print to a file or other value explicitly (no dynamic bindings). The value to print "
|
||||
"to is the first argument, and is otherwise the same as prin. Returns nil.") {
|
||||
"to is the first argument, and is otherwise the same as `prin`. Returns nil.") {
|
||||
janet_arity(argc, 1, -1);
|
||||
return cfun_io_print_impl_x(argc, argv, 0, NULL, 1, argv[0]);
|
||||
}
|
||||
|
@ -588,6 +584,16 @@ static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
|
|||
if (newline) janet_buffer_push_u8(buf, '\n');
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
case JANET_FUNCTION: {
|
||||
/* Special case function */
|
||||
JanetFunction *fun = janet_unwrap_function(x);
|
||||
JanetBuffer *buf = janet_buffer(0);
|
||||
janet_buffer_format(buf, fmt, offset, argc, argv);
|
||||
if (newline) janet_buffer_push_u8(buf, '\n');
|
||||
Janet args[1] = { janet_wrap_buffer(buf) };
|
||||
janet_call(fun, 1, args);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
case JANET_NIL:
|
||||
f = dflt_file;
|
||||
if (f == NULL) janet_panic("cannot print to nil");
|
||||
|
@ -597,6 +603,10 @@ static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
|
|||
if (janet_abstract_type(abstract) != &janet_file_type)
|
||||
return janet_wrap_nil();
|
||||
JanetFile *iofile = abstract;
|
||||
if (iofile->flags & JANET_FILE_CLOSED) {
|
||||
janet_panic("cannot print to closed file");
|
||||
}
|
||||
io_assert_writeable(iofile);
|
||||
f = iofile->file;
|
||||
break;
|
||||
}
|
||||
|
@ -627,38 +637,38 @@ static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, int newline,
|
|||
|
||||
JANET_CORE_FN(cfun_io_printf,
|
||||
"(printf fmt & xs)",
|
||||
"Prints output formatted as if with (string/format fmt ;xs) to (dyn :out stdout) with a trailing newline.") {
|
||||
"Prints output formatted as if with `(string/format fmt ;xs)` to `(dyn :out stdout)` with a trailing newline.") {
|
||||
return cfun_io_printf_impl(argc, argv, 1, "out", stdout);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_prinf,
|
||||
"(prinf fmt & xs)",
|
||||
"Like printf but with no trailing newline.") {
|
||||
"Like `printf` but with no trailing newline.") {
|
||||
return cfun_io_printf_impl(argc, argv, 0, "out", stdout);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_eprintf,
|
||||
"(eprintf fmt & xs)",
|
||||
"Prints output formatted as if with (string/format fmt ;xs) to (dyn :err stderr) with a trailing newline.") {
|
||||
"Prints output formatted as if with `(string/format fmt ;xs)` to `(dyn :err stderr)` with a trailing newline.") {
|
||||
return cfun_io_printf_impl(argc, argv, 1, "err", stderr);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_eprinf,
|
||||
"(eprinf fmt & xs)",
|
||||
"Like eprintf but with no trailing newline.") {
|
||||
"Like `eprintf` but with no trailing newline.") {
|
||||
return cfun_io_printf_impl(argc, argv, 0, "err", stderr);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_xprintf,
|
||||
"(xprintf to fmt & xs)",
|
||||
"Like printf but prints to an explicit file or value to. Returns nil.") {
|
||||
"Like `printf` but prints to an explicit file or value `to`. Returns nil.") {
|
||||
janet_arity(argc, 2, -1);
|
||||
return cfun_io_printf_impl_x(argc, argv, 1, NULL, 1, argv[0]);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_xprinf,
|
||||
"(xprinf to fmt & xs)",
|
||||
"Like prinf but prints to an explicit file or value to. Returns nil.") {
|
||||
"Like `prinf` but prints to an explicit file or value `to`. Returns nil.") {
|
||||
janet_arity(argc, 2, -1);
|
||||
return cfun_io_printf_impl_x(argc, argv, 0, NULL, 1, argv[0]);
|
||||
}
|
||||
|
@ -683,7 +693,7 @@ static void janet_flusher(const char *name, FILE *dflt_file) {
|
|||
|
||||
JANET_CORE_FN(cfun_io_flush,
|
||||
"(flush)",
|
||||
"Flush (dyn :out stdout) if it is a file, otherwise do nothing.") {
|
||||
"Flush `(dyn :out stdout)` if it is a file, otherwise do nothing.") {
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
janet_flusher("out", stdout);
|
||||
|
@ -692,7 +702,7 @@ JANET_CORE_FN(cfun_io_flush,
|
|||
|
||||
JANET_CORE_FN(cfun_io_eflush,
|
||||
"(eflush)",
|
||||
"Flush (dyn :err stderr) if it is a file, otherwise do nothing.") {
|
||||
"Flush `(dyn :err stderr)` if it is a file, otherwise do nothing.") {
|
||||
janet_fixarity(argc, 0);
|
||||
(void) argv;
|
||||
janet_flusher("err", stderr);
|
||||
|
@ -721,12 +731,23 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
|
|||
if (janet_abstract_type(abstract) != &janet_file_type)
|
||||
break;
|
||||
JanetFile *iofile = abstract;
|
||||
io_assert_writeable(iofile);
|
||||
f = iofile->file;
|
||||
}
|
||||
fwrite(buffer.data, buffer.count, 1, f);
|
||||
janet_buffer_deinit(&buffer);
|
||||
break;
|
||||
}
|
||||
case JANET_FUNCTION: {
|
||||
JanetFunction *fun = janet_unwrap_function(x);
|
||||
int32_t len = 0;
|
||||
while (format[len]) len++;
|
||||
JanetBuffer *buf = janet_buffer(len);
|
||||
janet_formatbv(buf, format, args);
|
||||
Janet args[1] = { janet_wrap_buffer(buf) };
|
||||
janet_call(fun, 1, args);
|
||||
break;
|
||||
}
|
||||
case JANET_BUFFER:
|
||||
janet_formatbv(janet_unwrap_buffer(x), format, args);
|
||||
break;
|
||||
|
@ -741,17 +762,17 @@ JanetFile *janet_getjfile(const Janet *argv, int32_t n) {
|
|||
return janet_getabstract(argv, n, &janet_file_type);
|
||||
}
|
||||
|
||||
FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) {
|
||||
FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags) {
|
||||
JanetFile *iof = janet_getabstract(argv, n, &janet_file_type);
|
||||
if (NULL != flags) *flags = iof->flags;
|
||||
return iof->file;
|
||||
}
|
||||
|
||||
JanetFile *janet_makejfile(FILE *f, int flags) {
|
||||
JanetFile *janet_makejfile(FILE *f, int32_t flags) {
|
||||
return makef(f, flags);
|
||||
}
|
||||
|
||||
Janet janet_makefile(FILE *f, int flags) {
|
||||
Janet janet_makefile(FILE *f, int32_t flags) {
|
||||
return janet_wrap_abstract(makef(f, flags));
|
||||
}
|
||||
|
||||
|
@ -759,7 +780,7 @@ JanetAbstract janet_checkfile(Janet j) {
|
|||
return janet_checkabstract(j, &janet_file_type);
|
||||
}
|
||||
|
||||
FILE *janet_unwrapfile(Janet j, int *flags) {
|
||||
FILE *janet_unwrapfile(Janet j, int32_t *flags) {
|
||||
JanetFile *iof = janet_unwrap_abstract(j);
|
||||
if (NULL != flags) *flags = iof->flags;
|
||||
return iof->file;
|
||||
|
@ -789,9 +810,7 @@ void janet_lib_io(JanetTable *env) {
|
|||
JANET_CORE_REG("file/write", cfun_io_fwrite),
|
||||
JANET_CORE_REG("file/flush", cfun_io_fflush),
|
||||
JANET_CORE_REG("file/seek", cfun_io_fseek),
|
||||
#ifndef JANET_NO_PROCESSES
|
||||
JANET_CORE_REG("file/popen", cfun_io_popen),
|
||||
#endif
|
||||
JANET_CORE_REG("file/tell", cfun_io_ftell),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, io_cfuns);
|
||||
|
|
244
src/core/marsh.c
244
src/core/marsh.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -37,6 +37,7 @@ typedef struct {
|
|||
JanetFuncEnv **seen_envs;
|
||||
JanetFuncDef **seen_defs;
|
||||
int32_t nextid;
|
||||
int maybe_cycles;
|
||||
} MarshalState;
|
||||
|
||||
/* Lead bytes in marshaling protocol */
|
||||
|
@ -64,8 +65,10 @@ enum {
|
|||
LB_FUNCDEF_REF, /* 220 */
|
||||
LB_UNSAFE_CFUNCTION, /* 221 */
|
||||
LB_UNSAFE_POINTER, /* 222 */
|
||||
LB_STRUCT_PROTO, /* 223 */
|
||||
#ifdef JANET_EV
|
||||
LB_THREADED_ABSTRACT/* 223 */
|
||||
LB_THREADED_ABSTRACT, /* 224 */
|
||||
LB_POINTER_BUFFER, /* 224 */
|
||||
#endif
|
||||
} LeadBytes;
|
||||
|
||||
|
@ -151,6 +154,10 @@ static void pushbytes(MarshalState *st, const uint8_t *bytes, int32_t len) {
|
|||
janet_buffer_push_bytes(st->buf, bytes, len);
|
||||
}
|
||||
|
||||
static void pushpointer(MarshalState *st, const void *ptr) {
|
||||
janet_buffer_push_bytes(st->buf, (const uint8_t *) &ptr, sizeof(ptr));
|
||||
}
|
||||
|
||||
/* Marshal a size_t onto the buffer */
|
||||
static void push64(MarshalState *st, uint64_t x) {
|
||||
if (x <= 0xF0) {
|
||||
|
@ -178,6 +185,19 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags);
|
|||
/* Prevent stack overflows */
|
||||
#define MARSH_STACKCHECK if ((flags & 0xFFFF) > JANET_RECURSION_GUARD) janet_panic("stack overflow")
|
||||
|
||||
/* Quick check if a fiber cannot be marshalled. This is will
|
||||
* have no false positives, but may have false negatives. */
|
||||
static int fiber_cannot_be_marshalled(JanetFiber *fiber) {
|
||||
if (janet_fiber_status(fiber) == JANET_STATUS_ALIVE) return 1;
|
||||
int32_t i = fiber->frame;
|
||||
while (i > 0) {
|
||||
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
if (!frame->func) return 1; /* has cfunction on stack */
|
||||
i = frame->prevframe;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Marshal a function env */
|
||||
static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
|
||||
MARSH_STACKCHECK;
|
||||
|
@ -190,7 +210,9 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
|
|||
}
|
||||
janet_env_valid(env);
|
||||
janet_v_push(st->seen_envs, env);
|
||||
if (env->offset > 0 && (JANET_STATUS_ALIVE == janet_fiber_status(env->as.fiber))) {
|
||||
|
||||
/* Special case for early detachment */
|
||||
if (env->offset > 0 && fiber_cannot_be_marshalled(env->as.fiber)) {
|
||||
pushint(st, 0);
|
||||
pushint(st, env->length);
|
||||
Janet *values = env->as.fiber->data + env->offset;
|
||||
|
@ -239,6 +261,7 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
|
|||
}
|
||||
/* Add to lookup */
|
||||
janet_v_push(st->seen_defs, def);
|
||||
|
||||
pushint(st, def->flags);
|
||||
pushint(st, def->slotcount);
|
||||
pushint(st, def->arity);
|
||||
|
@ -250,6 +273,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
|
|||
pushint(st, def->environments_length);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
|
||||
pushint(st, def->defs_length);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP)
|
||||
pushint(st, def->symbolmap_length);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASNAME)
|
||||
marshal_one(st, janet_wrap_string(def->name), flags);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCE)
|
||||
|
@ -257,7 +282,15 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
|
|||
|
||||
/* marshal constants */
|
||||
for (int32_t i = 0; i < def->constants_length; i++)
|
||||
marshal_one(st, def->constants[i], flags);
|
||||
marshal_one(st, def->constants[i], flags + 1);
|
||||
|
||||
/* Marshal symbol map, if needed */
|
||||
for (int32_t i = 0; i < def->symbolmap_length; i++) {
|
||||
pushint(st, (int32_t) def->symbolmap[i].birth_pc);
|
||||
pushint(st, (int32_t) def->symbolmap[i].death_pc);
|
||||
pushint(st, (int32_t) def->symbolmap[i].slot_index);
|
||||
marshal_one(st, janet_wrap_symbol(def->symbolmap[i].symbol), flags + 1);
|
||||
}
|
||||
|
||||
/* marshal the bytecode */
|
||||
janet_marshal_u32s(st, def->bytecode, def->bytecode_length);
|
||||
|
@ -268,7 +301,7 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
|
|||
|
||||
/* marshal the sub funcdefs if needed */
|
||||
for (int32_t i = 0; i < def->defs_length; i++)
|
||||
marshal_one_def(st, def->defs[i], flags);
|
||||
marshal_one_def(st, def->defs[i], flags + 1);
|
||||
|
||||
/* marshal source maps if needed */
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
|
||||
|
@ -310,7 +343,7 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
|
|||
while (i > 0) {
|
||||
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
if (frame->env) frame->flags |= JANET_STACKFRAME_HASENV;
|
||||
if (!frame->func) janet_panic("cannot marshal fiber with c stackframe");
|
||||
if (!frame->func) janet_panicf("cannot marshal fiber with c stackframe (%v)", janet_wrap_cfunction((JanetCFunction) frame->pc));
|
||||
pushint(st, frame->flags);
|
||||
pushint(st, frame->prevframe);
|
||||
int32_t pcdiff = (int32_t)(frame->pc - frame->func->def->bytecode);
|
||||
|
@ -345,6 +378,15 @@ void janet_marshal_int(JanetMarshalContext *ctx, int32_t value) {
|
|||
pushint(st, value);
|
||||
}
|
||||
|
||||
/* Only use in unsafe - don't marshal pointers otherwise */
|
||||
void janet_marshal_ptr(JanetMarshalContext *ctx, const void *ptr) {
|
||||
if (!(ctx->flags & JANET_MARSHAL_UNSAFE)) {
|
||||
janet_panic("can only marshal pointers in unsafe mode");
|
||||
}
|
||||
MarshalState *st = (MarshalState *)(ctx->m_state);
|
||||
pushpointer(st, ptr);
|
||||
}
|
||||
|
||||
void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value) {
|
||||
MarshalState *st = (MarshalState *)(ctx->m_state);
|
||||
pushbyte(st, value);
|
||||
|
@ -361,16 +403,27 @@ void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) {
|
|||
marshal_one(st, x, ctx->flags + 1);
|
||||
}
|
||||
|
||||
#ifdef JANET_MARSHAL_DEBUG
|
||||
#define MARK_SEEN() \
|
||||
do { if (st->maybe_cycles) { \
|
||||
Janet _check = janet_table_get(&st->seen, x); \
|
||||
if (!janet_checktype(_check, JANET_NIL)) janet_eprintf("double MARK_SEEN on %v\n", x); \
|
||||
janet_eprintf("made reference %d (%t) to %v\n", st->nextid, x, x); \
|
||||
janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); \
|
||||
} } while (0)
|
||||
#else
|
||||
#define MARK_SEEN() \
|
||||
do { if (st->maybe_cycles) { \
|
||||
janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); \
|
||||
} } while (0)
|
||||
#endif
|
||||
|
||||
void janet_marshal_abstract(JanetMarshalContext *ctx, void *abstract) {
|
||||
MarshalState *st = (MarshalState *)(ctx->m_state);
|
||||
janet_table_put(&st->seen,
|
||||
janet_wrap_abstract(abstract),
|
||||
janet_wrap_integer(st->nextid++));
|
||||
Janet x = janet_wrap_abstract(abstract);
|
||||
MARK_SEEN();
|
||||
}
|
||||
|
||||
#define MARK_SEEN() \
|
||||
janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++))
|
||||
|
||||
static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
|
||||
void *abstract = janet_unwrap_abstract(x);
|
||||
#ifdef JANET_EV
|
||||
|
@ -392,7 +445,7 @@ static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
|
|||
if (at->marshal) {
|
||||
pushbyte(st, LB_ABSTRACT);
|
||||
marshal_one(st, janet_csymbolv(at->name), flags + 1);
|
||||
JanetMarshalContext context = {st, NULL, flags, NULL, at};
|
||||
JanetMarshalContext context = {st, NULL, flags + 1, NULL, at};
|
||||
at->marshal(abstract, &context);
|
||||
} else {
|
||||
janet_panicf("cannot marshal %p", x);
|
||||
|
@ -427,11 +480,14 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
|
|||
|
||||
/* Check reference and registry value */
|
||||
{
|
||||
Janet check = janet_table_get(&st->seen, x);
|
||||
if (janet_checkint(check)) {
|
||||
pushbyte(st, LB_REFERENCE);
|
||||
pushint(st, janet_unwrap_integer(check));
|
||||
return;
|
||||
Janet check;
|
||||
if (st->maybe_cycles) {
|
||||
check = janet_table_get(&st->seen, x);
|
||||
if (janet_checkint(check)) {
|
||||
pushbyte(st, LB_REFERENCE);
|
||||
pushint(st, janet_unwrap_integer(check));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (st->rreg) {
|
||||
check = janet_table_get(st->rreg, x);
|
||||
|
@ -494,6 +550,16 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
|
|||
JanetBuffer *buffer = janet_unwrap_buffer(x);
|
||||
/* Record reference */
|
||||
MARK_SEEN();
|
||||
#ifdef JANET_EV
|
||||
if ((flags & JANET_MARSHAL_UNSAFE) &&
|
||||
(buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
|
||||
pushbyte(st, LB_POINTER_BUFFER);
|
||||
pushint(st, buffer->count);
|
||||
pushint(st, buffer->capacity);
|
||||
pushpointer(st, buffer->data);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
pushbyte(st, LB_BUFFER);
|
||||
pushint(st, buffer->count);
|
||||
pushbytes(st, buffer->data, buffer->count);
|
||||
|
@ -542,8 +608,10 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
|
|||
int32_t count;
|
||||
const JanetKV *struct_ = janet_unwrap_struct(x);
|
||||
count = janet_struct_length(struct_);
|
||||
pushbyte(st, LB_STRUCT);
|
||||
pushbyte(st, janet_struct_proto(struct_) ? LB_STRUCT_PROTO : LB_STRUCT);
|
||||
pushint(st, count);
|
||||
if (janet_struct_proto(struct_))
|
||||
marshal_one(st, janet_wrap_struct(janet_struct_proto(struct_)), flags + 1);
|
||||
for (int32_t i = 0; i < janet_struct_capacity(struct_); i++) {
|
||||
if (janet_checktype(struct_[i].key, JANET_NIL))
|
||||
continue;
|
||||
|
@ -587,8 +655,7 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
|
|||
if (!(flags & JANET_MARSHAL_UNSAFE)) goto no_registry;
|
||||
MARK_SEEN();
|
||||
pushbyte(st, LB_UNSAFE_POINTER);
|
||||
void *ptr = janet_unwrap_pointer(x);
|
||||
pushbytes(st, (uint8_t *) &ptr, sizeof(void *));
|
||||
pushpointer(st, janet_unwrap_pointer(x));
|
||||
return;
|
||||
}
|
||||
no_registry:
|
||||
|
@ -610,6 +677,7 @@ void janet_marshal(
|
|||
st.seen_defs = NULL;
|
||||
st.seen_envs = NULL;
|
||||
st.rreg = rreg;
|
||||
st.maybe_cycles = !(flags & JANET_MARSHAL_NO_CYCLES);
|
||||
janet_table_init(&st.seen, 0);
|
||||
marshal_one(&st, x, flags);
|
||||
janet_table_deinit(&st.seen);
|
||||
|
@ -694,9 +762,22 @@ static uint64_t read64(UnmarshalState *st, const uint8_t **atdata) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef JANET_MARSHAL_DEBUG
|
||||
static void dump_reference_table(UnmarshalState *st) {
|
||||
for (int32_t i = 0; i < janet_v_count(st->lookup); i++) {
|
||||
janet_eprintf(" reference %d (%t) = %v\n", i, st->lookup[i], st->lookup[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Assert a janet type */
|
||||
static void janet_asserttype(Janet x, JanetType t) {
|
||||
static void janet_asserttype(Janet x, JanetType t, UnmarshalState *st) {
|
||||
if (!janet_checktype(x, t)) {
|
||||
#ifdef JANET_MARSHAL_DEBUG
|
||||
dump_reference_table(st);
|
||||
#else
|
||||
(void) st;
|
||||
#endif
|
||||
janet_panicf("expected type %T, got %v", 1 << t, x);
|
||||
}
|
||||
}
|
||||
|
@ -748,7 +829,7 @@ static const uint8_t *unmarshal_one_env(
|
|||
Janet fiberv;
|
||||
/* On stack variant */
|
||||
data = unmarshal_one(st, data, &fiberv, flags);
|
||||
janet_asserttype(fiberv, JANET_FIBER);
|
||||
janet_asserttype(fiberv, JANET_FIBER, st);
|
||||
env->as.fiber = janet_unwrap_fiber(fiberv);
|
||||
/* Negative offset indicates untrusted input */
|
||||
env->offset = -offset;
|
||||
|
@ -814,6 +895,8 @@ static const uint8_t *unmarshal_one_def(
|
|||
def->constants = NULL;
|
||||
def->bytecode = NULL;
|
||||
def->sourcemap = NULL;
|
||||
def->symbolmap = NULL;
|
||||
def->symbolmap_length = 0;
|
||||
janet_v_push(st->lookup_defs, def);
|
||||
|
||||
/* Set default lengths to zero */
|
||||
|
@ -821,6 +904,7 @@ static const uint8_t *unmarshal_one_def(
|
|||
int32_t constants_length = 0;
|
||||
int32_t environments_length = 0;
|
||||
int32_t defs_length = 0;
|
||||
int32_t symbolmap_length = 0;
|
||||
|
||||
/* Read flags and other fixed values */
|
||||
def->flags = readint(st, &data);
|
||||
|
@ -836,18 +920,20 @@ static const uint8_t *unmarshal_one_def(
|
|||
environments_length = readnat(st, &data);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
|
||||
defs_length = readnat(st, &data);
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP)
|
||||
symbolmap_length = readnat(st, &data);
|
||||
|
||||
/* Check name and source (optional) */
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASNAME) {
|
||||
Janet x;
|
||||
data = unmarshal_one(st, data, &x, flags + 1);
|
||||
janet_asserttype(x, JANET_STRING);
|
||||
janet_asserttype(x, JANET_STRING, st);
|
||||
def->name = janet_unwrap_string(x);
|
||||
}
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCE) {
|
||||
Janet x;
|
||||
data = unmarshal_one(st, data, &x, flags + 1);
|
||||
janet_asserttype(x, JANET_STRING);
|
||||
janet_asserttype(x, JANET_STRING, st);
|
||||
def->source = janet_unwrap_string(x);
|
||||
}
|
||||
|
||||
|
@ -864,6 +950,27 @@ static const uint8_t *unmarshal_one_def(
|
|||
}
|
||||
def->constants_length = constants_length;
|
||||
|
||||
/* Unmarshal symbol map, if needed */
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP) {
|
||||
size_t size = sizeof(JanetSymbolMap) * symbolmap_length;
|
||||
def->symbolmap = janet_malloc(size);
|
||||
if (def->symbolmap == NULL) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
for (int32_t i = 0; i < symbolmap_length; i++) {
|
||||
def->symbolmap[i].birth_pc = (uint32_t) readint(st, &data);
|
||||
def->symbolmap[i].death_pc = (uint32_t) readint(st, &data);
|
||||
def->symbolmap[i].slot_index = (uint32_t) readint(st, &data);
|
||||
Janet value;
|
||||
data = unmarshal_one(st, data, &value, flags + 1);
|
||||
if (!janet_checktype(value, JANET_SYMBOL)) {
|
||||
janet_panicf("corrupted symbolmap when unmarshalling debug info, got %v", value);
|
||||
}
|
||||
def->symbolmap[i].symbol = janet_unwrap_symbol(value);
|
||||
}
|
||||
def->symbolmap_length = (uint32_t) symbolmap_length;
|
||||
}
|
||||
|
||||
/* Unmarshal bytecode */
|
||||
def->bytecode = janet_malloc(sizeof(uint32_t) * bytecode_length);
|
||||
if (!def->bytecode) {
|
||||
|
@ -956,9 +1063,11 @@ static const uint8_t *unmarshal_one_fiber(
|
|||
fiber->env = NULL;
|
||||
fiber->last_value = janet_wrap_nil();
|
||||
#ifdef JANET_EV
|
||||
fiber->waiting = NULL;
|
||||
fiber->sched_id = 0;
|
||||
fiber->supervisor_channel = NULL;
|
||||
fiber->ev_state = NULL;
|
||||
fiber->ev_callback = NULL;
|
||||
fiber->ev_stream = NULL;
|
||||
#endif
|
||||
|
||||
/* Push fiber to seen stack */
|
||||
|
@ -1007,7 +1116,7 @@ static const uint8_t *unmarshal_one_fiber(
|
|||
/* Get function */
|
||||
Janet funcv;
|
||||
data = unmarshal_one(st, data, &funcv, flags + 1);
|
||||
janet_asserttype(funcv, JANET_FUNCTION);
|
||||
janet_asserttype(funcv, JANET_FUNCTION, st);
|
||||
func = janet_unwrap_function(funcv);
|
||||
def = func->def;
|
||||
|
||||
|
@ -1053,7 +1162,7 @@ static const uint8_t *unmarshal_one_fiber(
|
|||
Janet envv;
|
||||
fiber_flags &= ~JANET_FIBER_FLAG_HASENV;
|
||||
data = unmarshal_one(st, data, &envv, flags + 1);
|
||||
janet_asserttype(envv, JANET_TABLE);
|
||||
janet_asserttype(envv, JANET_TABLE, st);
|
||||
fiber_env = janet_unwrap_table(envv);
|
||||
}
|
||||
|
||||
|
@ -1062,7 +1171,7 @@ static const uint8_t *unmarshal_one_fiber(
|
|||
Janet fiberv;
|
||||
fiber_flags &= ~JANET_FIBER_FLAG_HASCHILD;
|
||||
data = unmarshal_one(st, data, &fiberv, flags + 1);
|
||||
janet_asserttype(fiberv, JANET_FIBER);
|
||||
janet_asserttype(fiberv, JANET_FIBER, st);
|
||||
fiber->child = janet_unwrap_fiber(fiberv);
|
||||
}
|
||||
|
||||
|
@ -1106,6 +1215,18 @@ int64_t janet_unmarshal_int64(JanetMarshalContext *ctx) {
|
|||
return read64(st, &(ctx->data));
|
||||
}
|
||||
|
||||
void *janet_unmarshal_ptr(JanetMarshalContext *ctx) {
|
||||
if (!(ctx->flags & JANET_MARSHAL_UNSAFE)) {
|
||||
janet_panic("can only unmarshal pointers in unsafe mode");
|
||||
}
|
||||
UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
|
||||
void *ptr;
|
||||
MARSH_EOS(st, ctx->data + sizeof(void *) - 1);
|
||||
memcpy((char *) &ptr, ctx->data, sizeof(void *));
|
||||
ctx->data += sizeof(void *);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx) {
|
||||
UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
|
||||
MARSH_EOS(st, ctx->data);
|
||||
|
@ -1141,6 +1262,18 @@ void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
|
|||
return p;
|
||||
}
|
||||
|
||||
void *janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size) {
|
||||
#ifdef JANET_THREADS
|
||||
void *p = janet_abstract_threaded(ctx->at, size);
|
||||
janet_unmarshal_abstract_reuse(ctx, p);
|
||||
return p;
|
||||
#else
|
||||
(void) ctx;
|
||||
(void) size;
|
||||
janet_panic("threaded abstracts not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *data, Janet *out, int flags) {
|
||||
Janet key;
|
||||
data = unmarshal_one(st, data, &key, flags + 1);
|
||||
|
@ -1148,7 +1281,9 @@ static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *
|
|||
if (at == NULL) janet_panic("unknown abstract type");
|
||||
if (at->unmarshal) {
|
||||
JanetMarshalContext context = {NULL, st, flags, data, at};
|
||||
*out = janet_wrap_abstract(at->unmarshal(&context));
|
||||
void *abst = at->unmarshal(&context);
|
||||
janet_assert(abst != NULL, "null pointer abstract");
|
||||
*out = janet_wrap_abstract(abst);
|
||||
if (context.at != NULL) {
|
||||
janet_panic("janet_unmarshal_abstract not called");
|
||||
}
|
||||
|
@ -1249,7 +1384,7 @@ static const uint8_t *unmarshal_one(
|
|||
}
|
||||
case LB_FIBER: {
|
||||
JanetFiber *fiber;
|
||||
data = unmarshal_one_fiber(st, data + 1, &fiber, flags);
|
||||
data = unmarshal_one_fiber(st, data + 1, &fiber, flags + 1);
|
||||
*out = janet_wrap_fiber(fiber);
|
||||
return data;
|
||||
}
|
||||
|
@ -1264,6 +1399,9 @@ static const uint8_t *unmarshal_one(
|
|||
func = janet_gcalloc(JANET_MEMORY_FUNCTION, sizeof(JanetFunction) +
|
||||
len * sizeof(JanetFuncEnv));
|
||||
func->def = NULL;
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
func->envs[i] = NULL;
|
||||
}
|
||||
*out = janet_wrap_function(func);
|
||||
janet_v_push(st->lookup, *out);
|
||||
data = unmarshal_one_def(st, data, &def, flags + 1);
|
||||
|
@ -1281,6 +1419,7 @@ static const uint8_t *unmarshal_one(
|
|||
case LB_ARRAY:
|
||||
case LB_TUPLE:
|
||||
case LB_STRUCT:
|
||||
case LB_STRUCT_PROTO:
|
||||
case LB_TABLE:
|
||||
case LB_TABLE_PROTO:
|
||||
/* Things that open with integers */
|
||||
|
@ -1310,9 +1449,15 @@ static const uint8_t *unmarshal_one(
|
|||
}
|
||||
*out = janet_wrap_tuple(janet_tuple_end(tup));
|
||||
janet_v_push(st->lookup, *out);
|
||||
} else if (lead == LB_STRUCT) {
|
||||
} else if (lead == LB_STRUCT || lead == LB_STRUCT_PROTO) {
|
||||
/* Struct */
|
||||
JanetKV *struct_ = janet_struct_begin(len);
|
||||
if (lead == LB_STRUCT_PROTO) {
|
||||
Janet proto;
|
||||
data = unmarshal_one(st, data, &proto, flags + 1);
|
||||
janet_asserttype(proto, JANET_STRUCT, st);
|
||||
janet_struct_proto(struct_) = janet_unwrap_struct(proto);
|
||||
}
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
Janet key, value;
|
||||
data = unmarshal_one(st, data, &key, flags + 1);
|
||||
|
@ -1333,7 +1478,7 @@ static const uint8_t *unmarshal_one(
|
|||
if (lead == LB_TABLE_PROTO) {
|
||||
Janet proto;
|
||||
data = unmarshal_one(st, data, &proto, flags + 1);
|
||||
janet_asserttype(proto, JANET_TABLE);
|
||||
janet_asserttype(proto, JANET_TABLE, st);
|
||||
t->proto = janet_unwrap_table(proto);
|
||||
}
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
|
@ -1363,6 +1508,29 @@ static const uint8_t *unmarshal_one(
|
|||
janet_v_push(st->lookup, *out);
|
||||
return data;
|
||||
}
|
||||
#ifdef JANET_EV
|
||||
case LB_POINTER_BUFFER: {
|
||||
data++;
|
||||
int32_t count = readnat(st, &data);
|
||||
int32_t capacity = readnat(st, &data);
|
||||
MARSH_EOS(st, data + sizeof(void *));
|
||||
union {
|
||||
void *ptr;
|
||||
uint8_t bytes[sizeof(void *)];
|
||||
} u;
|
||||
if (!(flags & JANET_MARSHAL_UNSAFE)) {
|
||||
janet_panicf("unsafe flag not given, "
|
||||
"will not unmarshal raw pointer at index %d",
|
||||
(int)(data - st->start));
|
||||
}
|
||||
memcpy(u.bytes, data, sizeof(void *));
|
||||
data += sizeof(void *);
|
||||
JanetBuffer *buffer = janet_pointer_buffer_unsafe(u.ptr, capacity, count);
|
||||
*out = janet_wrap_buffer(buffer);
|
||||
janet_v_push(st->lookup, *out);
|
||||
return data;
|
||||
}
|
||||
#endif
|
||||
case LB_UNSAFE_CFUNCTION: {
|
||||
MARSH_EOS(st, data + sizeof(JanetCFunction));
|
||||
data++;
|
||||
|
@ -1461,16 +1629,17 @@ JANET_CORE_FN(cfun_env_lookup,
|
|||
}
|
||||
|
||||
JANET_CORE_FN(cfun_marshal,
|
||||
"(marshal x &opt reverse-lookup buffer)",
|
||||
"(marshal x &opt reverse-lookup buffer no-cycles)",
|
||||
"Marshal a value into a buffer and return the buffer. The buffer "
|
||||
"can then later be unmarshalled to reconstruct the initial value. "
|
||||
"Optionally, one can pass in a reverse lookup table to not marshal "
|
||||
"aliased values that are found in the table. Then a forward "
|
||||
"lookup table can be used to recover the original value when "
|
||||
"unmarshalling.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
janet_arity(argc, 1, 4);
|
||||
JanetBuffer *buffer;
|
||||
JanetTable *rreg = NULL;
|
||||
uint32_t flags = 0;
|
||||
if (argc > 1) {
|
||||
rreg = janet_gettable(argv, 1);
|
||||
}
|
||||
|
@ -1479,7 +1648,10 @@ JANET_CORE_FN(cfun_marshal,
|
|||
} else {
|
||||
buffer = janet_buffer(10);
|
||||
}
|
||||
janet_marshal(buffer, argv[0], rreg, 0);
|
||||
if (argc > 3 && janet_truthy(argv[3])) {
|
||||
flags |= JANET_MARSHAL_NO_CYCLES;
|
||||
}
|
||||
janet_marshal(buffer, argv[0], rreg, flags);
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
|
|
146
src/core/math.c
146
src/core/math.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -119,7 +119,7 @@ double janet_rng_double(JanetRNG *rng) {
|
|||
|
||||
JANET_CORE_FN(cfun_rng_make,
|
||||
"(math/rng &opt seed)",
|
||||
"Creates a Psuedo-Random number generator, with an optional seed. "
|
||||
"Creates a Pseudo-Random number generator, with an optional seed. "
|
||||
"The seed should be an unsigned 32 bit integer or a buffer. "
|
||||
"Do not use this for cryptography. Returns a core/rng abstract type."
|
||||
) {
|
||||
|
@ -150,8 +150,8 @@ JANET_CORE_FN(cfun_rng_uniform,
|
|||
|
||||
JANET_CORE_FN(cfun_rng_int,
|
||||
"(math/rng-int rng &opt max)",
|
||||
"Extract a random random integer in the range [0, max] from the RNG. If "
|
||||
"no max is given, the default is 2^31 - 1."
|
||||
"Extract a random integer in the range [0, max) for max > 0 from the RNG. "
|
||||
"If max is 0, return 0. If no max is given, the default is 2^31 - 1."
|
||||
) {
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type);
|
||||
|
@ -231,7 +231,7 @@ static Janet janet_rng_next(void *p, Janet key) {
|
|||
/* Get a random number */
|
||||
JANET_CORE_FN(janet_rand,
|
||||
"(math/random)",
|
||||
"Returns a uniformly distributed random number between 0 and 1") {
|
||||
"Returns a uniformly distributed random number between 0 and 1.") {
|
||||
(void) argv;
|
||||
janet_fixarity(argc, 0);
|
||||
return janet_wrap_number(janet_rng_double(&janet_vm.rng));
|
||||
|
@ -240,7 +240,7 @@ JANET_CORE_FN(janet_rand,
|
|||
/* Seed the random number generator */
|
||||
JANET_CORE_FN(janet_srand,
|
||||
"(math/seedrandom seed)",
|
||||
"Set the seed for the random number generator. seed should be "
|
||||
"Set the seed for the random number generator. `seed` should be "
|
||||
"an integer or a buffer."
|
||||
) {
|
||||
janet_fixarity(argc, 1);
|
||||
|
@ -254,42 +254,45 @@ JANET_CORE_FN(janet_srand,
|
|||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
#define JANET_DEFINE_MATHOP(name, fop, doc)\
|
||||
JANET_CORE_FN(janet_##name, "(math/" #name " x)", doc) {\
|
||||
#define JANET_DEFINE_NAMED_MATHOP(janet_name, fop, doc)\
|
||||
JANET_CORE_FN(janet_##fop, "(math/" janet_name " x)", doc) {\
|
||||
janet_fixarity(argc, 1); \
|
||||
double x = janet_getnumber(argv, 0); \
|
||||
return janet_wrap_number(fop(x)); \
|
||||
}
|
||||
|
||||
JANET_DEFINE_MATHOP(acos, acos, "Returns the arccosize of x.")
|
||||
JANET_DEFINE_MATHOP(asin, asin, "Returns the arcsin of x.")
|
||||
JANET_DEFINE_MATHOP(atan, atan, "Returns the arctangent of x.")
|
||||
JANET_DEFINE_MATHOP(cos, cos, "Returns the cosine of x.")
|
||||
JANET_DEFINE_MATHOP(cosh, cosh, "Returns the hyperbolic cosine of x.")
|
||||
JANET_DEFINE_MATHOP(acosh, acosh, "Returns the hyperbolic arccosine of x.")
|
||||
JANET_DEFINE_MATHOP(sin, sin, "Returns the sine of x.")
|
||||
JANET_DEFINE_MATHOP(sinh, sinh, "Returns the hyperbolic sine of x.")
|
||||
JANET_DEFINE_MATHOP(asinh, asinh, "Returns the hypberbolic arcsine of x.")
|
||||
JANET_DEFINE_MATHOP(tan, tan, "Returns the tangent of x.")
|
||||
JANET_DEFINE_MATHOP(tanh, tanh, "Returns the hyperbolic tangent of x.")
|
||||
JANET_DEFINE_MATHOP(atanh, atanh, "Returns the hyperbolic arctangent of x.")
|
||||
JANET_DEFINE_MATHOP(exp, exp, "Returns e to the power of x.")
|
||||
JANET_DEFINE_MATHOP(exp2, exp2, "Returns 2 to the power of x.")
|
||||
JANET_DEFINE_MATHOP(expm1, expm1, "Returns e to the power of x minus 1.")
|
||||
JANET_DEFINE_MATHOP(log, log, "Returns the natural logarithm of x.")
|
||||
JANET_DEFINE_MATHOP(log10, log10, "Returns the log base 10 of x.")
|
||||
JANET_DEFINE_MATHOP(log2, log2, "Returns the log base 2 of x.")
|
||||
JANET_DEFINE_MATHOP(sqrt, sqrt, "Returns the square root of x.")
|
||||
JANET_DEFINE_MATHOP(cbrt, cbrt, "Returns the cube root of x.")
|
||||
JANET_DEFINE_MATHOP(ceil, ceil, "Returns the smallest integer value number that is not less than x.")
|
||||
JANET_DEFINE_MATHOP(fabs, fabs, "Return the absolute value of x.")
|
||||
JANET_DEFINE_MATHOP(floor, floor, "Returns the largest integer value number that is not greater than x.")
|
||||
JANET_DEFINE_MATHOP(trunc, trunc, "Returns the integer between x and 0 nearest to x.")
|
||||
JANET_DEFINE_MATHOP(round, round, "Returns the integer nearest to x.")
|
||||
JANET_DEFINE_MATHOP(gamma, lgamma, "Returns gamma(x).")
|
||||
JANET_DEFINE_MATHOP(log1p, log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
|
||||
JANET_DEFINE_MATHOP(erf, erf, "Returns the error function of x.")
|
||||
JANET_DEFINE_MATHOP(erfc, erfc, "Returns the complementary error function of x.")
|
||||
#define JANET_DEFINE_MATHOP(fop, doc) JANET_DEFINE_NAMED_MATHOP(#fop, fop, doc)
|
||||
|
||||
JANET_DEFINE_MATHOP(acos, "Returns the arccosine of x.")
|
||||
JANET_DEFINE_MATHOP(asin, "Returns the arcsin of x.")
|
||||
JANET_DEFINE_MATHOP(atan, "Returns the arctangent of x.")
|
||||
JANET_DEFINE_MATHOP(cos, "Returns the cosine of x.")
|
||||
JANET_DEFINE_MATHOP(cosh, "Returns the hyperbolic cosine of x.")
|
||||
JANET_DEFINE_MATHOP(acosh, "Returns the hyperbolic arccosine of x.")
|
||||
JANET_DEFINE_MATHOP(sin, "Returns the sine of x.")
|
||||
JANET_DEFINE_MATHOP(sinh, "Returns the hyperbolic sine of x.")
|
||||
JANET_DEFINE_MATHOP(asinh, "Returns the hyperbolic arcsine of x.")
|
||||
JANET_DEFINE_MATHOP(tan, "Returns the tangent of x.")
|
||||
JANET_DEFINE_MATHOP(tanh, "Returns the hyperbolic tangent of x.")
|
||||
JANET_DEFINE_MATHOP(atanh, "Returns the hyperbolic arctangent of x.")
|
||||
JANET_DEFINE_MATHOP(exp, "Returns e to the power of x.")
|
||||
JANET_DEFINE_MATHOP(exp2, "Returns 2 to the power of x.")
|
||||
JANET_DEFINE_MATHOP(expm1, "Returns e to the power of x minus 1.")
|
||||
JANET_DEFINE_MATHOP(log, "Returns the natural logarithm of x.")
|
||||
JANET_DEFINE_MATHOP(log10, "Returns the log base 10 of x.")
|
||||
JANET_DEFINE_MATHOP(log2, "Returns the log base 2 of x.")
|
||||
JANET_DEFINE_MATHOP(sqrt, "Returns the square root of x.")
|
||||
JANET_DEFINE_MATHOP(cbrt, "Returns the cube root of x.")
|
||||
JANET_DEFINE_MATHOP(ceil, "Returns the smallest integer value number that is not less than x.")
|
||||
JANET_DEFINE_MATHOP(floor, "Returns the largest integer value number that is not greater than x.")
|
||||
JANET_DEFINE_MATHOP(trunc, "Returns the integer between x and 0 nearest to x.")
|
||||
JANET_DEFINE_MATHOP(round, "Returns the integer nearest to x.")
|
||||
JANET_DEFINE_MATHOP(log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
|
||||
JANET_DEFINE_MATHOP(erf, "Returns the error function of x.")
|
||||
JANET_DEFINE_MATHOP(erfc, "Returns the complementary error function of x.")
|
||||
JANET_DEFINE_NAMED_MATHOP("log-gamma", lgamma, "Returns log-gamma(x).")
|
||||
JANET_DEFINE_NAMED_MATHOP("abs", fabs, "Return the absolute value of x.")
|
||||
JANET_DEFINE_NAMED_MATHOP("gamma", tgamma, "Returns gamma(x).")
|
||||
|
||||
#define JANET_DEFINE_MATH2OP(name, fop, signature, doc)\
|
||||
JANET_CORE_FN(janet_##name, signature, doc) {\
|
||||
|
@ -302,13 +305,69 @@ JANET_CORE_FN(janet_##name, signature, doc) {\
|
|||
JANET_DEFINE_MATH2OP(atan2, atan2, "(math/atan2 y x)", "Returns the arctangent of y/x. Works even when x is 0.")
|
||||
JANET_DEFINE_MATH2OP(pow, pow, "(math/pow a x)", "Returns a to the power of x.")
|
||||
JANET_DEFINE_MATH2OP(hypot, hypot, "(math/hypot a b)", "Returns c from the equation c^2 = a^2 + b^2.")
|
||||
JANET_DEFINE_MATH2OP(nextafter, nextafter, "(math/next x y)", "Returns the next representable floating point vaue after x in the direction of y.")
|
||||
JANET_DEFINE_MATH2OP(nextafter, nextafter, "(math/next x y)", "Returns the next representable floating point value after x in the direction of y.")
|
||||
|
||||
JANET_CORE_FN(janet_not, "(not x)", "Returns the boolean inverse of x.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(!janet_truthy(argv[0]));
|
||||
}
|
||||
|
||||
static double janet_gcd(double x, double y) {
|
||||
if (isnan(x) || isnan(y)) {
|
||||
#ifdef NAN
|
||||
return NAN;
|
||||
#else
|
||||
return 0.0 / 0.0;
|
||||
#endif
|
||||
}
|
||||
if (isinf(x) || isinf(y)) return INFINITY;
|
||||
while (y != 0) {
|
||||
double temp = y;
|
||||
y = fmod(x, y);
|
||||
x = temp;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
static double janet_lcm(double x, double y) {
|
||||
return (x / janet_gcd(x, y)) * y;
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_gcd, "(math/gcd x y)",
|
||||
"Returns the greatest common divisor between x and y.") {
|
||||
janet_fixarity(argc, 2);
|
||||
double x = janet_getnumber(argv, 0);
|
||||
double y = janet_getnumber(argv, 1);
|
||||
return janet_wrap_number(janet_gcd(x, y));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_lcm, "(math/lcm x y)",
|
||||
"Returns the least common multiple of x and y.") {
|
||||
janet_fixarity(argc, 2);
|
||||
double x = janet_getnumber(argv, 0);
|
||||
double y = janet_getnumber(argv, 1);
|
||||
return janet_wrap_number(janet_lcm(x, y));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_frexp, "(math/frexp x)",
|
||||
"Returns a tuple of (mantissa, exponent) from number.") {
|
||||
janet_fixarity(argc, 1);
|
||||
double x = janet_getnumber(argv, 0);
|
||||
int exp;
|
||||
x = frexp(x, &exp);
|
||||
Janet *result = janet_tuple_begin(2);
|
||||
result[0] = janet_wrap_number(x);
|
||||
result[1] = janet_wrap_number((double) exp);
|
||||
return janet_wrap_tuple(janet_tuple_end(result));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_ldexp, "(math/ldexp m e)",
|
||||
"Creates a new number from a mantissa and an exponent.") {
|
||||
janet_fixarity(argc, 2);
|
||||
double x = janet_getnumber(argv, 0);
|
||||
int32_t y = janet_getinteger(argv, 1);
|
||||
return janet_wrap_number(ldexp(x, y));
|
||||
}
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_math(JanetTable *env) {
|
||||
|
@ -346,13 +405,18 @@ void janet_lib_math(JanetTable *env) {
|
|||
JANET_CORE_REG("math/hypot", janet_hypot),
|
||||
JANET_CORE_REG("math/exp2", janet_exp2),
|
||||
JANET_CORE_REG("math/log1p", janet_log1p),
|
||||
JANET_CORE_REG("math/gamma", janet_gamma),
|
||||
JANET_CORE_REG("math/gamma", janet_tgamma),
|
||||
JANET_CORE_REG("math/log-gamma", janet_lgamma),
|
||||
JANET_CORE_REG("math/erfc", janet_erfc),
|
||||
JANET_CORE_REG("math/erf", janet_erf),
|
||||
JANET_CORE_REG("math/expm1", janet_expm1),
|
||||
JANET_CORE_REG("math/trunc", janet_trunc),
|
||||
JANET_CORE_REG("math/round", janet_round),
|
||||
JANET_CORE_REG("math/next", janet_nextafter),
|
||||
JANET_CORE_REG("math/gcd", janet_cfun_gcd),
|
||||
JANET_CORE_REG("math/lcm", janet_cfun_lcm),
|
||||
JANET_CORE_REG("math/frexp", janet_cfun_frexp),
|
||||
JANET_CORE_REG("math/ldexp", janet_cfun_ldexp),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, math_cfuns);
|
||||
|
@ -369,11 +433,11 @@ void janet_lib_math(JanetTable *env) {
|
|||
JANET_CORE_DEF(env, "math/int32-min", janet_wrap_number(INT32_MIN),
|
||||
"The minimum contiguous integer representable by a 32 bit signed integer");
|
||||
JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX),
|
||||
"The maximum contiguous integer represtenable by a 32 bit signed integer");
|
||||
"The maximum contiguous integer representable by a 32 bit signed integer");
|
||||
JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
|
||||
"The minimum contiguous integer representable by a double (2^53)");
|
||||
JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
|
||||
"The maximum contiguous integer represtenable by a double (-(2^53))");
|
||||
"The maximum contiguous integer representable by a double (-(2^53))");
|
||||
#ifdef NAN
|
||||
JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
|
||||
#else
|
||||
|
|
332
src/core/net.c
332
src/core/net.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose and contributors.
|
||||
* Copyright (c) 2023 Calvin Rose and contributors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -24,6 +24,7 @@
|
|||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#include "fiber.h"
|
||||
#endif
|
||||
|
||||
#ifdef JANET_NET
|
||||
|
@ -34,9 +35,11 @@
|
|||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <mswsock.h>
|
||||
#ifdef JANET_MSVC
|
||||
#pragma comment (lib, "Ws2_32.lib")
|
||||
#pragma comment (lib, "Mswsock.lib")
|
||||
#pragma comment (lib, "Advapi32.lib")
|
||||
#endif
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
|
@ -76,12 +79,20 @@ const JanetAbstractType janet_address_type = {
|
|||
|
||||
/* maximum number of bytes in a socket address host (post name resolution) */
|
||||
#ifdef JANET_WINDOWS
|
||||
#ifdef JANET_NO_IPV6
|
||||
#define SA_ADDRSTRLEN (INET_ADDRSTRLEN + 1)
|
||||
#else
|
||||
#define SA_ADDRSTRLEN (INET6_ADDRSTRLEN + 1)
|
||||
#endif
|
||||
typedef unsigned short in_port_t;
|
||||
#else
|
||||
#define JANET_SA_MAX(a, b) (((a) > (b))? (a) : (b))
|
||||
#ifdef JANET_NO_IPV6
|
||||
#define SA_ADDRSTRLEN JANET_SA_MAX(INET_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
|
||||
#else
|
||||
#define SA_ADDRSTRLEN JANET_SA_MAX(INET6_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static JanetStream *make_stream(JSock handle, uint32_t flags);
|
||||
|
||||
|
@ -109,12 +120,57 @@ static void janet_net_socknoblock(JSock s) {
|
|||
#endif
|
||||
}
|
||||
|
||||
/* State machine for async connect */
|
||||
|
||||
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
JanetStream *stream = fiber->ev_stream;
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
#ifndef JANET_WINDOWS
|
||||
/* Wait until we have an actual event before checking.
|
||||
* Windows doesn't support async connect with this, just try immediately.*/
|
||||
case JANET_ASYNC_EVENT_INIT:
|
||||
#endif
|
||||
case JANET_ASYNC_EVENT_DEINIT:
|
||||
return;
|
||||
case JANET_ASYNC_EVENT_CLOSE:
|
||||
janet_cancel(fiber, janet_cstringv("stream closed"));
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
#ifdef JANET_WINDOWS
|
||||
int res = 0;
|
||||
int size = sizeof(res);
|
||||
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size);
|
||||
#else
|
||||
int res = 0;
|
||||
socklen_t size = sizeof res;
|
||||
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
|
||||
#endif
|
||||
if (r == 0) {
|
||||
if (res == 0) {
|
||||
janet_schedule(fiber, janet_wrap_abstract(stream));
|
||||
} else {
|
||||
janet_cancel(fiber, janet_cstringv(janet_strerror(res)));
|
||||
stream->flags |= JANET_STREAM_TOCLOSE;
|
||||
}
|
||||
} else {
|
||||
janet_cancel(fiber, janet_ev_lasterr());
|
||||
stream->flags |= JANET_STREAM_TOCLOSE;
|
||||
}
|
||||
janet_async_end(fiber);
|
||||
}
|
||||
|
||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
|
||||
}
|
||||
|
||||
/* State machine for accepting connections. */
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
|
||||
typedef struct {
|
||||
JanetListenerState head;
|
||||
WSAOVERLAPPED overlapped;
|
||||
JanetFunction *function;
|
||||
JanetStream *lstream;
|
||||
|
@ -122,72 +178,74 @@ typedef struct {
|
|||
char buf[1024];
|
||||
} NetStateAccept;
|
||||
|
||||
static int net_sched_accept_impl(NetStateAccept *state, Janet *err);
|
||||
static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err);
|
||||
|
||||
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) {
|
||||
NetStateAccept *state = (NetStateAccept *)s;
|
||||
void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_MARK: {
|
||||
if (state->lstream) janet_mark(janet_wrap_abstract(state->lstream));
|
||||
if (state->astream) janet_mark(janet_wrap_abstract(state->astream));
|
||||
if (state->function) janet_mark(janet_wrap_abstract(state->function));
|
||||
if (state->function) janet_mark(janet_wrap_function(state->function));
|
||||
break;
|
||||
}
|
||||
case JANET_ASYNC_EVENT_CLOSE:
|
||||
janet_schedule(s->fiber, janet_wrap_nil());
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||
int seconds;
|
||||
int bytes = sizeof(seconds);
|
||||
if (NO_ERROR != getsockopt((SOCKET) state->astream->handle, SOL_SOCKET, SO_CONNECT_TIME,
|
||||
(char *)&seconds, &bytes)) {
|
||||
janet_cancel(s->fiber, janet_cstringv("failed to accept connection"));
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
if (state->astream->flags & JANET_STREAM_CLOSED) {
|
||||
janet_cancel(fiber, janet_cstringv("failed to accept connection"));
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
SOCKET lsock = (SOCKET) state->lstream->handle;
|
||||
if (NO_ERROR != setsockopt((SOCKET) state->astream->handle, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
|
||||
(char *) & (state->lstream->handle), sizeof(SOCKET))) {
|
||||
janet_cancel(s->fiber, janet_cstringv("failed to accept connection"));
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
(char *) &lsock, sizeof(lsock))) {
|
||||
janet_cancel(fiber, janet_cstringv("failed to accept connection"));
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
|
||||
Janet streamv = janet_wrap_abstract(state->astream);
|
||||
if (state->function) {
|
||||
/* Schedule worker */
|
||||
JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv);
|
||||
fiber->supervisor_channel = s->fiber->supervisor_channel;
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
|
||||
sub_fiber->supervisor_channel = fiber->supervisor_channel;
|
||||
janet_schedule(sub_fiber, janet_wrap_nil());
|
||||
/* Now listen again for next connection */
|
||||
Janet err;
|
||||
if (net_sched_accept_impl(state, &err)) {
|
||||
janet_cancel(s->fiber, err);
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
if (net_sched_accept_impl(state, fiber, &err)) {
|
||||
janet_cancel(fiber, err);
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
janet_schedule(s->fiber, streamv);
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_schedule(fiber, streamv);
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return JANET_ASYNC_STATUS_NOT_DONE;
|
||||
}
|
||||
|
||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
|
||||
Janet err;
|
||||
SOCKET lsock = (SOCKET) stream->handle;
|
||||
JanetListenerState *s = janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL);
|
||||
NetStateAccept *state = (NetStateAccept *)s;
|
||||
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
||||
memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
|
||||
memset(&state->buf, 0, 1024);
|
||||
state->function = fun;
|
||||
state->lstream = stream;
|
||||
s->tag = &state->overlapped;
|
||||
if (net_sched_accept_impl(state, &err)) janet_panicv(err);
|
||||
janet_await();
|
||||
if (net_sched_accept_impl(state, janet_root_fiber(), &err)) {
|
||||
janet_free(state);
|
||||
janet_panicv(err);
|
||||
}
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
|
||||
}
|
||||
|
||||
static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
|
||||
static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err) {
|
||||
SOCKET lsock = (SOCKET) state->lstream->handle;
|
||||
SOCKET asock = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||||
if (asock == INVALID_SOCKET) {
|
||||
|
@ -199,7 +257,11 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
|
|||
int socksize = sizeof(SOCKADDR_STORAGE) + 16;
|
||||
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) {
|
||||
int code = WSAGetLastError();
|
||||
if (code == WSA_IO_PENDING) return 0; /* indicates io is happening async */
|
||||
if (code == WSA_IO_PENDING) {
|
||||
/* indicates io is happening async */
|
||||
janet_async_in_flight(fiber);
|
||||
return 0;
|
||||
}
|
||||
*err = janet_ev_lasterr();
|
||||
return 1;
|
||||
}
|
||||
|
@ -209,12 +271,12 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
|
|||
#else
|
||||
|
||||
typedef struct {
|
||||
JanetListenerState head;
|
||||
JanetFunction *function;
|
||||
} NetStateAccept;
|
||||
|
||||
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) {
|
||||
NetStateAccept *state = (NetStateAccept *)s;
|
||||
void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
JanetStream *stream = fiber->ev_stream;
|
||||
NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
|
@ -223,36 +285,44 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
|
|||
break;
|
||||
}
|
||||
case JANET_ASYNC_EVENT_CLOSE:
|
||||
janet_schedule(s->fiber, janet_wrap_nil());
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
case JANET_ASYNC_EVENT_INIT:
|
||||
case JANET_ASYNC_EVENT_READ: {
|
||||
JSock connfd = accept(s->stream->handle, NULL, NULL);
|
||||
#if defined(JANET_LINUX)
|
||||
JSock connfd = accept4(stream->handle, NULL, NULL, SOCK_CLOEXEC);
|
||||
#else
|
||||
/* On BSDs, CLOEXEC should be inherited from server socket */
|
||||
JSock connfd = accept(stream->handle, NULL, NULL);
|
||||
#endif
|
||||
if (JSOCKVALID(connfd)) {
|
||||
janet_net_socknoblock(connfd);
|
||||
JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||
Janet streamv = janet_wrap_abstract(stream);
|
||||
if (state->function) {
|
||||
JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv);
|
||||
fiber->supervisor_channel = s->fiber->supervisor_channel;
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
|
||||
sub_fiber->supervisor_channel = fiber->supervisor_channel;
|
||||
janet_schedule(sub_fiber, janet_wrap_nil());
|
||||
} else {
|
||||
janet_schedule(s->fiber, streamv);
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_schedule(fiber, streamv);
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return JANET_ASYNC_STATUS_NOT_DONE;
|
||||
}
|
||||
|
||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
|
||||
NetStateAccept *state = (NetStateAccept *) janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL);
|
||||
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
||||
memset(state, 0, sizeof(NetStateAccept));
|
||||
state->function = fun;
|
||||
janet_await();
|
||||
if (fun) janet_stream_level_triggered(stream);
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/* Adress info */
|
||||
|
@ -330,6 +400,7 @@ JANET_CORE_FN(cfun_net_sockaddr,
|
|||
"given in the port argument. On Linux, abstract "
|
||||
"unix domain sockets are specified with a leading '@' character in port. If `multi` is truthy, will "
|
||||
"return all address that match in an array instead of just the first.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_NET_CONNECT); /* connect OR listen */
|
||||
janet_arity(argc, 2, 4);
|
||||
int socktype = janet_get_sockettype(argv, argc, 2);
|
||||
int is_unix = 0;
|
||||
|
@ -375,6 +446,7 @@ JANET_CORE_FN(cfun_net_connect,
|
|||
"to specify a connection type, either :stream or :datagram. The default is :stream. "
|
||||
"Bindhost is an optional string to select from what address to make the outgoing "
|
||||
"connection, with the default being the same as using the OS's preferred address. ") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_NET_CONNECT);
|
||||
janet_arity(argc, 2, 5);
|
||||
|
||||
/* Check arguments */
|
||||
|
@ -382,7 +454,7 @@ JANET_CORE_FN(cfun_net_connect,
|
|||
int is_unix = 0;
|
||||
char *bindhost = (char *) janet_optcstring(argv, argc, 3, NULL);
|
||||
char *bindport = NULL;
|
||||
if (janet_checkint(argv[4])) {
|
||||
if (argc >= 5 && janet_checkint(argv[4])) {
|
||||
bindport = (char *)janet_to_string(argv[4]);
|
||||
} else {
|
||||
bindport = (char *)janet_optcstring(argv, argc, 4, NULL);
|
||||
|
@ -431,7 +503,7 @@ JANET_CORE_FN(cfun_net_connect,
|
|||
struct addrinfo *rp = NULL;
|
||||
for (rp = ai; rp != NULL; rp = rp->ai_next) {
|
||||
#ifdef JANET_WINDOWS
|
||||
sock = WSASocketW(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||||
sock = WSASocketW(rp->ai_family, rp->ai_socktype, rp->ai_protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||||
#else
|
||||
sock = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
|
||||
#endif
|
||||
|
@ -453,7 +525,7 @@ JANET_CORE_FN(cfun_net_connect,
|
|||
if (binding) {
|
||||
struct addrinfo *rp = NULL;
|
||||
int did_bind = 0;
|
||||
for (rp = ai; rp != NULL; rp = rp->ai_next) {
|
||||
for (rp = binding; rp != NULL; rp = rp->ai_next) {
|
||||
if (bind(sock, rp->ai_addr, (int) rp->ai_addrlen) == 0) {
|
||||
did_bind = 1;
|
||||
break;
|
||||
|
@ -470,14 +542,20 @@ JANET_CORE_FN(cfun_net_connect,
|
|||
}
|
||||
}
|
||||
|
||||
/* Wrap socket in abstract type JanetStream */
|
||||
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||
|
||||
/* Set up the socket for non-blocking IO before connecting */
|
||||
janet_net_socknoblock(sock);
|
||||
|
||||
/* Connect to socket */
|
||||
#ifdef JANET_WINDOWS
|
||||
int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
|
||||
Janet lasterr = janet_ev_lasterr();
|
||||
int err = WSAGetLastError();
|
||||
freeaddrinfo(ai);
|
||||
#else
|
||||
int status = connect(sock, addr, addrlen);
|
||||
Janet lasterr = janet_ev_lasterr();
|
||||
int err = errno;
|
||||
if (is_unix) {
|
||||
janet_free(ai);
|
||||
} else {
|
||||
|
@ -485,17 +563,19 @@ JANET_CORE_FN(cfun_net_connect,
|
|||
}
|
||||
#endif
|
||||
|
||||
if (status == -1) {
|
||||
JSOCKCLOSE(sock);
|
||||
janet_panicf("could not connect socket: %V", lasterr);
|
||||
if (status) {
|
||||
#ifdef JANET_WINDOWS
|
||||
if (err != WSAEWOULDBLOCK) {
|
||||
#else
|
||||
if (err != EINPROGRESS) {
|
||||
#endif
|
||||
JSOCKCLOSE(sock);
|
||||
Janet lasterr = janet_ev_lasterr();
|
||||
janet_panicf("could not connect socket: %V", lasterr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set up the socket for non-blocking IO after connect - TODO - non-blocking connect? */
|
||||
janet_net_socknoblock(sock);
|
||||
|
||||
/* Wrap socket in abstract type JanetStream */
|
||||
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||
return janet_wrap_abstract(stream);
|
||||
net_sched_connect(stream);
|
||||
}
|
||||
|
||||
static const char *serverify_socket(JSock sfd) {
|
||||
|
@ -568,6 +648,7 @@ JANET_CORE_FN(cfun_net_listen,
|
|||
"The type parameter specifies the type of network connection, either "
|
||||
"a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is "
|
||||
":stream. The host and port arguments are the same as in net/address.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_NET_LISTEN);
|
||||
janet_arity(argc, 2, 3);
|
||||
|
||||
/* Get host, port, and handler*/
|
||||
|
@ -649,7 +730,7 @@ struct sockaddr_in6 *sin6; // IPv6 address + port
|
|||
struct sockaddr_un *sun; // Unix Domain Socket Address
|
||||
*/
|
||||
|
||||
/* Turn a socket address into a host, port pair (port is optional).
|
||||
/* Turn a socket address into a host, port pair.
|
||||
* For unix domain sockets, returned tuple will have only a single element, the path string. */
|
||||
static Janet janet_so_getname(const void *sa_any) {
|
||||
const struct sockaddr *sa = sa_any;
|
||||
|
@ -663,16 +744,18 @@ static Janet janet_so_getname(const void *sa_any) {
|
|||
janet_panic("unable to decode ipv4 host address");
|
||||
}
|
||||
Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai->sin_port))};
|
||||
return janet_wrap_tuple(janet_tuple_n(pair, sai->sin_port ? 2 : 1));
|
||||
return janet_wrap_tuple(janet_tuple_n(pair, 2));
|
||||
}
|
||||
#ifndef JANET_NO_IPV6
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *sai6 = sa_any;
|
||||
if (!inet_ntop(AF_INET6, &(sai6->sin6_addr), buffer, sizeof(buffer))) {
|
||||
janet_panic("unable to decode ipv4 host address");
|
||||
}
|
||||
Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai6->sin6_port))};
|
||||
return janet_wrap_tuple(janet_tuple_n(pair, sai6->sin6_port ? 2 : 1));
|
||||
return janet_wrap_tuple(janet_tuple_n(pair, 2));
|
||||
}
|
||||
#endif
|
||||
#ifndef JANET_WINDOWS
|
||||
case AF_UNIX: {
|
||||
const struct sockaddr_un *sun = sa_any;
|
||||
|
@ -695,13 +778,14 @@ JANET_CORE_FN(cfun_net_getsockname,
|
|||
"Gets the local address and port in a tuple in that order.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetStream *js = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
if (js->flags & JANET_STREAM_CLOSED) janet_panic("stream closed");
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen = sizeof(ss);
|
||||
memset(&ss, 0, slen);
|
||||
if (getsockname((JSock)js->handle, (struct sockaddr *) &ss, &slen)) {
|
||||
janet_panicf("Failed to get localname on %v: %V", argv[0], janet_ev_lasterr());
|
||||
}
|
||||
janet_assert(slen <= sizeof(ss), "socket address truncated");
|
||||
janet_assert(slen <= (socklen_t) sizeof(ss), "socket address truncated");
|
||||
return janet_so_getname(&ss);
|
||||
}
|
||||
|
||||
|
@ -710,19 +794,20 @@ JANET_CORE_FN(cfun_net_getpeername,
|
|||
"Gets the remote peer's address and port in a tuple in that order.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetStream *js = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
if (js->flags & JANET_STREAM_CLOSED) janet_panic("stream closed");
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen = sizeof(ss);
|
||||
memset(&ss, 0, slen);
|
||||
if (getpeername((JSock)js->handle, (struct sockaddr *)&ss, &slen)) {
|
||||
janet_panicf("Failed to get peername on %v: %V", argv[0], janet_ev_lasterr());
|
||||
}
|
||||
janet_assert(slen <= sizeof(ss), "socket address truncated");
|
||||
janet_assert(slen <= (socklen_t) sizeof(ss), "socket address truncated");
|
||||
return janet_so_getname(&ss);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_net_address_unpack,
|
||||
"(net/address-unpack address)",
|
||||
"Given an address returned by net/adress, return a host, port pair. Unix domain sockets "
|
||||
"Given an address returned by net/address, return a host, port pair. Unix domain sockets "
|
||||
"will have only the path in the returned tuple.") {
|
||||
janet_fixarity(argc, 1);
|
||||
struct sockaddr *sa = janet_getabstract(argv, 0, &janet_address_type);
|
||||
|
@ -737,6 +822,7 @@ JANET_CORE_FN(cfun_stream_accept_loop,
|
|||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
janet_stream_flags(stream, JANET_STREAM_ACCEPTABLE | JANET_STREAM_SOCKET);
|
||||
JanetFunction *fun = janet_getfunction(argv, 1);
|
||||
if (fun->def->min_arity < 1) janet_panic("handler function must take at least 1 argument");
|
||||
janet_sched_accept(stream, fun);
|
||||
}
|
||||
|
||||
|
@ -773,7 +859,6 @@ JANET_CORE_FN(cfun_stream_read,
|
|||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_recv(stream, buffer, n, MSG_NOSIGNAL);
|
||||
}
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_chunk,
|
||||
|
@ -788,11 +873,10 @@ JANET_CORE_FN(cfun_stream_chunk,
|
|||
double to = janet_optnumber(argv, argc, 3, INFINITY);
|
||||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_recvchunk(stream, buffer, n, MSG_NOSIGNAL);
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_recv_from,
|
||||
"(net/recv-from stream nbytes buf &opt timoeut)",
|
||||
"(net/recv-from stream nbytes buf &opt timeout)",
|
||||
"Receives data from a server stream and puts it into a buffer. Returns the socket-address the "
|
||||
"packet came from. Takes an optional timeout in seconds, after which will return nil.") {
|
||||
janet_arity(argc, 3, 4);
|
||||
|
@ -803,7 +887,6 @@ JANET_CORE_FN(cfun_stream_recv_from,
|
|||
double to = janet_optnumber(argv, argc, 3, INFINITY);
|
||||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_recvfrom(stream, buffer, n, MSG_NOSIGNAL);
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_write,
|
||||
|
@ -823,7 +906,6 @@ JANET_CORE_FN(cfun_stream_write,
|
|||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_send_string(stream, bytes.bytes, MSG_NOSIGNAL);
|
||||
}
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_send_to,
|
||||
|
@ -844,7 +926,6 @@ JANET_CORE_FN(cfun_stream_send_to,
|
|||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_sendto_string(stream, bytes.bytes, dest, MSG_NOSIGNAL);
|
||||
}
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_flush,
|
||||
|
@ -862,6 +943,104 @@ JANET_CORE_FN(cfun_stream_flush,
|
|||
return argv[0];
|
||||
}
|
||||
|
||||
struct sockopt_type {
|
||||
const char *name;
|
||||
int level;
|
||||
int optname;
|
||||
enum JanetType type;
|
||||
};
|
||||
|
||||
/* List of supported socket options; The type JANET_POINTER is used
|
||||
* for options that require special handling depending on the type. */
|
||||
static const struct sockopt_type sockopt_type_list[] = {
|
||||
{ "so-broadcast", SOL_SOCKET, SO_BROADCAST, JANET_BOOLEAN },
|
||||
{ "so-reuseaddr", SOL_SOCKET, SO_REUSEADDR, JANET_BOOLEAN },
|
||||
{ "so-keepalive", SOL_SOCKET, SO_KEEPALIVE, JANET_BOOLEAN },
|
||||
{ "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER },
|
||||
{ "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER },
|
||||
{ "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER },
|
||||
#ifndef JANET_NO_IPV6
|
||||
{ "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
|
||||
{ "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
|
||||
#endif
|
||||
{ NULL, 0, 0, JANET_POINTER }
|
||||
};
|
||||
|
||||
JANET_CORE_FN(cfun_net_setsockopt,
|
||||
"(net/setsockopt stream option value)",
|
||||
"set socket options.\n"
|
||||
"\n"
|
||||
"supported options and associated value types:\n"
|
||||
"- :so-broadcast boolean\n"
|
||||
"- :so-reuseaddr boolean\n"
|
||||
"- :so-keepalive boolean\n"
|
||||
"- :ip-multicast-ttl number\n"
|
||||
"- :ip-add-membership string\n"
|
||||
"- :ip-drop-membership string\n"
|
||||
"- :ipv6-join-group string\n"
|
||||
"- :ipv6-leave-group string\n") {
|
||||
janet_arity(argc, 3, 3);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
janet_stream_flags(stream, JANET_STREAM_SOCKET);
|
||||
JanetKeyword optstr = janet_getkeyword(argv, 1);
|
||||
|
||||
const struct sockopt_type *st = sockopt_type_list;
|
||||
while (st->name) {
|
||||
if (janet_cstrcmp(optstr, st->name) == 0) {
|
||||
break;
|
||||
}
|
||||
st++;
|
||||
}
|
||||
|
||||
if (st->name == NULL) {
|
||||
janet_panicf("unknown socket option %q", argv[1]);
|
||||
}
|
||||
|
||||
union {
|
||||
int v_int;
|
||||
struct ip_mreq v_mreq;
|
||||
#ifndef JANET_NO_IPV6
|
||||
struct ipv6_mreq v_mreq6;
|
||||
#endif
|
||||
} val;
|
||||
|
||||
void *optval = (void *)&val;
|
||||
socklen_t optlen = 0;
|
||||
|
||||
if (st->type == JANET_BOOLEAN) {
|
||||
val.v_int = janet_getboolean(argv, 2);
|
||||
optlen = sizeof(val.v_int);
|
||||
} else if (st->type == JANET_NUMBER) {
|
||||
val.v_int = janet_getinteger(argv, 2);
|
||||
optlen = sizeof(val.v_int);
|
||||
} else if (st->optname == IP_ADD_MEMBERSHIP || st->optname == IP_DROP_MEMBERSHIP) {
|
||||
const char *addr = janet_getcstring(argv, 2);
|
||||
memset(&val.v_mreq, 0, sizeof val.v_mreq);
|
||||
val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr);
|
||||
optlen = sizeof(val.v_mreq);
|
||||
#ifndef JANET_NO_IPV6
|
||||
} else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) {
|
||||
const char *addr = janet_getcstring(argv, 2);
|
||||
memset(&val.v_mreq6, 0, sizeof val.v_mreq6);
|
||||
val.v_mreq6.ipv6mr_interface = 0;
|
||||
inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr);
|
||||
optlen = sizeof(val.v_mreq6);
|
||||
#endif
|
||||
} else {
|
||||
janet_panicf("invalid socket option type");
|
||||
}
|
||||
|
||||
janet_assert(optlen != 0, "invalid socket option value");
|
||||
|
||||
int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen);
|
||||
if (r == -1) {
|
||||
janet_panicf("setsockopt(%q): %s", argv[1], janet_strerror(errno));
|
||||
}
|
||||
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static const JanetMethod net_stream_methods[] = {
|
||||
{"chunk", cfun_stream_chunk},
|
||||
{"close", janet_cfun_stream_close},
|
||||
|
@ -876,6 +1055,7 @@ static const JanetMethod net_stream_methods[] = {
|
|||
{"evchunk", janet_cfun_stream_chunk},
|
||||
{"evwrite", janet_cfun_stream_write},
|
||||
{"shutdown", cfun_net_shutdown},
|
||||
{"setsockopt", cfun_net_setsockopt},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
@ -883,7 +1063,6 @@ static JanetStream *make_stream(JSock handle, uint32_t flags) {
|
|||
return janet_stream((JanetHandle) handle, flags | JANET_STREAM_SOCKET, net_stream_methods);
|
||||
}
|
||||
|
||||
|
||||
void janet_lib_net(JanetTable *env) {
|
||||
JanetRegExt net_cfuns[] = {
|
||||
JANET_CORE_REG("net/address", cfun_net_sockaddr),
|
||||
|
@ -901,6 +1080,7 @@ void janet_lib_net(JanetTable *env) {
|
|||
JANET_CORE_REG("net/peername", cfun_net_getpeername),
|
||||
JANET_CORE_REG("net/localname", cfun_net_getsockname),
|
||||
JANET_CORE_REG("net/address-unpack", cfun_net_address_unpack),
|
||||
JANET_CORE_REG("net/setsockopt", cfun_net_setsockopt),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, net_cfuns);
|
||||
|
|
921
src/core/os.c
921
src/core/os.c
File diff suppressed because it is too large
Load Diff
108
src/core/parse.c
108
src/core/parse.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -51,15 +51,15 @@ static const uint32_t symchars[8] = {
|
|||
};
|
||||
|
||||
/* Check if a character is a valid symbol character
|
||||
* symbol chars are A-Z, a-z, 0-9, or one of !$&*+-./:<=>@\^_~| */
|
||||
static int is_symbol_char(uint8_t c) {
|
||||
* symbol chars are A-Z, a-z, 0-9, or one of !$&*+-./:<=>@\^_| */
|
||||
int janet_is_symbol_char(uint8_t c) {
|
||||
return symchars[c >> 5] & ((uint32_t)1 << (c & 0x1F));
|
||||
}
|
||||
|
||||
/* Validate some utf8. Useful for identifiers. Only validates
|
||||
* the encoding, does not check for valid code points (they
|
||||
* are less well defined than the encoding). */
|
||||
static int valid_utf8(const uint8_t *str, int32_t len) {
|
||||
int janet_valid_utf8(const uint8_t *str, int32_t len) {
|
||||
int32_t i = 0;
|
||||
int32_t j;
|
||||
while (i < len) {
|
||||
|
@ -206,6 +206,37 @@ static void popstate(JanetParser *p, Janet val) {
|
|||
}
|
||||
}
|
||||
|
||||
static void delim_error(JanetParser *parser, size_t stack_index, char c, const char *msg) {
|
||||
JanetParseState *s = parser->states + stack_index;
|
||||
JanetBuffer *buffer = janet_buffer(40);
|
||||
if (msg) {
|
||||
janet_buffer_push_cstring(buffer, msg);
|
||||
}
|
||||
if (c) {
|
||||
janet_buffer_push_u8(buffer, c);
|
||||
}
|
||||
if (stack_index > 0) {
|
||||
janet_buffer_push_cstring(buffer, ", ");
|
||||
if (s->flags & PFLAG_PARENS) {
|
||||
janet_buffer_push_u8(buffer, '(');
|
||||
} else if (s->flags & PFLAG_SQRBRACKETS) {
|
||||
janet_buffer_push_u8(buffer, '[');
|
||||
} else if (s->flags & PFLAG_CURLYBRACKETS) {
|
||||
janet_buffer_push_u8(buffer, '{');
|
||||
} else if (s->flags & PFLAG_STRING) {
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
} else if (s->flags & PFLAG_LONGSTRING) {
|
||||
int32_t i;
|
||||
for (i = 0; i < s->argn; i++) {
|
||||
janet_buffer_push_u8(buffer, '`');
|
||||
}
|
||||
}
|
||||
janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
|
||||
}
|
||||
parser->error = (const char *) janet_string(buffer->data, buffer->count);
|
||||
parser->flag |= JANET_PARSER_GENERATED_ERROR;
|
||||
}
|
||||
|
||||
static int checkescape(uint8_t c) {
|
||||
switch (c) {
|
||||
default:
|
||||
|
@ -228,6 +259,14 @@ static int checkescape(uint8_t c) {
|
|||
return '\f';
|
||||
case 'v':
|
||||
return '\v';
|
||||
case 'a':
|
||||
return '\a';
|
||||
case 'b':
|
||||
return '\b';
|
||||
case '\'':
|
||||
return '\'';
|
||||
case '?':
|
||||
return '?';
|
||||
case 'e':
|
||||
return 27;
|
||||
case '"':
|
||||
|
@ -411,7 +450,7 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||
Janet ret;
|
||||
double numval;
|
||||
int32_t blen;
|
||||
if (is_symbol_char(c)) {
|
||||
if (janet_is_symbol_char(c)) {
|
||||
push_buf(p, (uint8_t) c);
|
||||
if (c > 127) state->argn = 1; /* Use to indicate non ascii */
|
||||
return 1;
|
||||
|
@ -422,7 +461,7 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||
int start_num = start_dig || p->buf[0] == '-' || p->buf[0] == '+' || p->buf[0] == '.';
|
||||
if (p->buf[0] == ':') {
|
||||
/* Don't do full utf-8 check unless we have seen non ascii characters. */
|
||||
int valid = (!state->argn) || valid_utf8(p->buf + 1, blen - 1);
|
||||
int valid = (!state->argn) || janet_valid_utf8(p->buf + 1, blen - 1);
|
||||
if (!valid) {
|
||||
p->error = "invalid utf-8 in keyword";
|
||||
return 0;
|
||||
|
@ -442,7 +481,7 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||
return 0;
|
||||
} else {
|
||||
/* Don't do full utf-8 check unless we have seen non ascii characters. */
|
||||
int valid = (!state->argn) || valid_utf8(p->buf, blen);
|
||||
int valid = (!state->argn) || janet_valid_utf8(p->buf, blen);
|
||||
if (!valid) {
|
||||
p->error = "invalid utf-8 in symbol";
|
||||
return 0;
|
||||
|
@ -582,7 +621,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||
switch (c) {
|
||||
default:
|
||||
if (is_whitespace(c)) return 1;
|
||||
if (!is_symbol_char(c)) {
|
||||
if (!janet_is_symbol_char(c)) {
|
||||
p->error = "unexpected character";
|
||||
return 1;
|
||||
}
|
||||
|
@ -612,7 +651,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||
case '}': {
|
||||
Janet ds;
|
||||
if (p->statecount == 1) {
|
||||
p->error = "unexpected delimiter";
|
||||
delim_error(p, 0, c, "unexpected closing delimiter ");
|
||||
return 1;
|
||||
}
|
||||
if ((c == ')' && (state->flags & PFLAG_PARENS)) ||
|
||||
|
@ -633,7 +672,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
|
|||
ds = close_struct(p, state);
|
||||
}
|
||||
} else {
|
||||
p->error = "mismatched delimiter";
|
||||
delim_error(p, p->statecount - 1, c, "mismatched delimiter ");
|
||||
return 1;
|
||||
}
|
||||
popstate(p, ds);
|
||||
|
@ -684,26 +723,7 @@ void janet_parser_eof(JanetParser *parser) {
|
|||
size_t oldline = parser->line;
|
||||
janet_parser_consume(parser, '\n');
|
||||
if (parser->statecount > 1) {
|
||||
JanetParseState *s = parser->states + (parser->statecount - 1);
|
||||
JanetBuffer *buffer = janet_buffer(40);
|
||||
janet_buffer_push_cstring(buffer, "unexpected end of source, ");
|
||||
if (s->flags & PFLAG_PARENS) {
|
||||
janet_buffer_push_u8(buffer, '(');
|
||||
} else if (s->flags & PFLAG_SQRBRACKETS) {
|
||||
janet_buffer_push_u8(buffer, '[');
|
||||
} else if (s->flags & PFLAG_CURLYBRACKETS) {
|
||||
janet_buffer_push_u8(buffer, '{');
|
||||
} else if (s->flags & PFLAG_STRING) {
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
} else if (s->flags & PFLAG_LONGSTRING) {
|
||||
int32_t i;
|
||||
for (i = 0; i < s->argn; i++) {
|
||||
janet_buffer_push_u8(buffer, '`');
|
||||
}
|
||||
}
|
||||
janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
|
||||
parser->error = (const char *) janet_string(buffer->data, buffer->count);
|
||||
parser->flag |= JANET_PARSER_GENERATED_ERROR;
|
||||
delim_error(parser, parser->statecount - 1, 0, "unexpected end of source");
|
||||
}
|
||||
parser->line = oldline;
|
||||
parser->column = oldcolumn;
|
||||
|
@ -746,6 +766,7 @@ Janet janet_parser_produce(JanetParser *parser) {
|
|||
}
|
||||
parser->pending--;
|
||||
parser->argcount--;
|
||||
parser->states[0].argn--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -759,6 +780,7 @@ Janet janet_parser_produce_wrapped(JanetParser *parser) {
|
|||
}
|
||||
parser->pending--;
|
||||
parser->argcount--;
|
||||
parser->states[0].argn--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -881,7 +903,7 @@ const JanetAbstractType janet_parser_type = {
|
|||
JANET_CORE_FN(cfun_parse_parser,
|
||||
"(parser/new)",
|
||||
"Creates and returns a new parser object. Parsers are state machines "
|
||||
"that can receive bytes, and generate a stream of values.") {
|
||||
"that can receive bytes and generate a stream of values.") {
|
||||
(void) argv;
|
||||
janet_fixarity(argc, 0);
|
||||
JanetParser *p = janet_abstract(&janet_parser_type, sizeof(JanetParser));
|
||||
|
@ -892,7 +914,7 @@ JANET_CORE_FN(cfun_parse_parser,
|
|||
JANET_CORE_FN(cfun_parse_consume,
|
||||
"(parser/consume parser bytes &opt index)",
|
||||
"Input bytes into the parser and parse them. Will not throw errors "
|
||||
"if there is a parse error. Starts at the byte index given by index. Returns "
|
||||
"if there is a parse error. Starts at the byte index given by `index`. Returns "
|
||||
"the number of bytes read.") {
|
||||
janet_arity(argc, 2, 3);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
|
||||
|
@ -920,7 +942,7 @@ JANET_CORE_FN(cfun_parse_consume,
|
|||
|
||||
JANET_CORE_FN(cfun_parse_eof,
|
||||
"(parser/eof parser)",
|
||||
"Indicate that the end of file was reached to the parser. This puts the parser in the :dead state.") {
|
||||
"Indicate to the parser that the end of file was reached. This puts the parser in the :dead state.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
|
||||
janet_parser_eof(p);
|
||||
|
@ -980,7 +1002,7 @@ JANET_CORE_FN(cfun_parse_has_more,
|
|||
|
||||
JANET_CORE_FN(cfun_parse_byte,
|
||||
"(parser/byte parser b)",
|
||||
"Input a single byte into the parser byte stream. Returns the parser.") {
|
||||
"Input a single byte `b` into the parser byte stream. Returns the parser.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
|
||||
int32_t i = janet_getinteger(argv, 1);
|
||||
|
@ -1020,7 +1042,7 @@ JANET_CORE_FN(cfun_parse_error,
|
|||
"If the parser is in the error state, returns the message associated with "
|
||||
"that error. Otherwise, returns nil. Also flushes the parser state and parser "
|
||||
"queue, so be sure to handle everything in the queue before calling "
|
||||
"parser/error.") {
|
||||
"`parser/error`.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
|
||||
const char *err = janet_parser_error(p);
|
||||
|
@ -1093,8 +1115,9 @@ static Janet janet_wrap_parse_state(JanetParseState *s, Janet *args,
|
|||
|
||||
if (s->flags & PFLAG_CONTAINER) {
|
||||
JanetArray *container_args = janet_array(s->argn);
|
||||
container_args->count = s->argn;
|
||||
safe_memcpy(container_args->data, args, sizeof(args[0])*s->argn);
|
||||
for (int32_t i = 0; i < s->argn; i++) {
|
||||
janet_array_push(container_args, args[i]);
|
||||
}
|
||||
janet_table_put(state, janet_ckeywordv("args"),
|
||||
janet_wrap_array(container_args));
|
||||
}
|
||||
|
@ -1179,7 +1202,8 @@ static Janet parser_state_delimiters(const JanetParser *_p) {
|
|||
}
|
||||
}
|
||||
}
|
||||
str = janet_string(p->buf + oldcount, (int32_t)(p->bufcount - oldcount));
|
||||
/* avoid ptr arithmetic on NULL */
|
||||
str = janet_string(oldcount ? p->buf + oldcount : p->buf, (int32_t)(p->bufcount - oldcount));
|
||||
p->bufcount = oldcount;
|
||||
return janet_wrap_string(str);
|
||||
}
|
||||
|
@ -1189,11 +1213,15 @@ static Janet parser_state_frames(const JanetParser *p) {
|
|||
JanetArray *states = janet_array(count);
|
||||
states->count = count;
|
||||
uint8_t *buf = p->buf;
|
||||
Janet *args = p->args;
|
||||
/* Iterate arg stack backwards */
|
||||
Janet *args = p->argcount ? p->args + p->argcount : p->args; /* avoid ptr arithmetic on NULL */
|
||||
for (int32_t i = count - 1; i >= 0; --i) {
|
||||
JanetParseState *s = p->states + i;
|
||||
/* avoid ptr arithmetic on args if NULL */
|
||||
if ((s->flags & PFLAG_CONTAINER) && s->argn) {
|
||||
args -= s->argn;
|
||||
}
|
||||
states->data[i] = janet_wrap_parse_state(s, args, buf, (uint32_t) p->bufcount);
|
||||
args -= s->argn;
|
||||
}
|
||||
return janet_wrap_array(states);
|
||||
}
|
||||
|
|
190
src/core/peg.c
190
src/core/peg.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -39,6 +39,10 @@
|
|||
typedef struct {
|
||||
const uint8_t *text_start;
|
||||
const uint8_t *text_end;
|
||||
/* text_end can be restricted by some rules, but
|
||||
outer_text_end will always contain the real end of
|
||||
input, which we need to generate a line mapping */
|
||||
const uint8_t *outer_text_end;
|
||||
const uint32_t *bytecode;
|
||||
const Janet *constants;
|
||||
JanetArray *captures;
|
||||
|
@ -114,12 +118,12 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) {
|
|||
/* Generate if not made yet */
|
||||
if (s->linemaplen < 0) {
|
||||
int32_t newline_count = 0;
|
||||
for (const uint8_t *c = s->text_start; c < s->text_end; c++) {
|
||||
for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
|
||||
if (*c == '\n') newline_count++;
|
||||
}
|
||||
int32_t *mem = janet_smalloc(sizeof(int32_t) * newline_count);
|
||||
size_t index = 0;
|
||||
for (const uint8_t *c = s->text_start; c < s->text_end; c++) {
|
||||
for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
|
||||
if (*c == '\n') mem[index++] = (int32_t)(c - s->text_start);
|
||||
}
|
||||
s->linemaplen = newline_count;
|
||||
|
@ -179,7 +183,7 @@ static const uint8_t *peg_rule(
|
|||
const uint32_t *rule,
|
||||
const uint8_t *text) {
|
||||
tail:
|
||||
switch (*rule & 0x1F) {
|
||||
switch (*rule) {
|
||||
default:
|
||||
janet_panic("unexpected opcode");
|
||||
return NULL;
|
||||
|
@ -211,9 +215,10 @@ tail:
|
|||
}
|
||||
|
||||
case RULE_SET: {
|
||||
if (text >= s->text_end) return NULL;
|
||||
uint32_t word = rule[1 + (text[0] >> 5)];
|
||||
uint32_t mask = (uint32_t)1 << (text[0] & 0x1F);
|
||||
return (text < s->text_end && (word & mask))
|
||||
return (word & mask)
|
||||
? text + 1
|
||||
: NULL;
|
||||
}
|
||||
|
@ -260,30 +265,52 @@ tail:
|
|||
goto tail;
|
||||
}
|
||||
|
||||
case RULE_IF:
|
||||
case RULE_IFNOT: {
|
||||
case RULE_IF: {
|
||||
const uint32_t *rule_a = s->bytecode + rule[1];
|
||||
const uint32_t *rule_b = s->bytecode + rule[2];
|
||||
down1(s);
|
||||
const uint8_t *result = peg_rule(s, rule_a, text);
|
||||
up1(s);
|
||||
if (rule[0] == RULE_IF ? !result : !!result) return NULL;
|
||||
if (!result) return NULL;
|
||||
rule = rule_b;
|
||||
goto tail;
|
||||
}
|
||||
case RULE_IFNOT: {
|
||||
const uint32_t *rule_a = s->bytecode + rule[1];
|
||||
const uint32_t *rule_b = s->bytecode + rule[2];
|
||||
down1(s);
|
||||
CapState cs = cap_save(s);
|
||||
const uint8_t *result = peg_rule(s, rule_a, text);
|
||||
if (!!result) {
|
||||
up1(s);
|
||||
return NULL;
|
||||
} else {
|
||||
cap_load(s, cs);
|
||||
up1(s);
|
||||
rule = rule_b;
|
||||
goto tail;
|
||||
}
|
||||
}
|
||||
|
||||
case RULE_NOT: {
|
||||
const uint32_t *rule_a = s->bytecode + rule[1];
|
||||
down1(s);
|
||||
CapState cs = cap_save(s);
|
||||
const uint8_t *result = peg_rule(s, rule_a, text);
|
||||
up1(s);
|
||||
return (result) ? NULL : text;
|
||||
if (result) {
|
||||
up1(s);
|
||||
return NULL;
|
||||
} else {
|
||||
cap_load(s, cs);
|
||||
up1(s);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
case RULE_THRU:
|
||||
case RULE_TO: {
|
||||
const uint32_t *rule_a = s->bytecode + rule[1];
|
||||
const uint8_t *next_text;
|
||||
const uint8_t *next_text = NULL;
|
||||
CapState cs = cap_save(s);
|
||||
down1(s);
|
||||
while (text <= s->text_end) {
|
||||
|
@ -293,6 +320,7 @@ tail:
|
|||
if (rule[0] == RULE_TO) cap_load(s, cs2);
|
||||
break;
|
||||
}
|
||||
cap_load(s, cs2);
|
||||
text++;
|
||||
}
|
||||
up1(s);
|
||||
|
@ -458,6 +486,68 @@ tail:
|
|||
return result;
|
||||
}
|
||||
|
||||
case RULE_SUB: {
|
||||
const uint8_t *text_start = text;
|
||||
const uint32_t *rule_window = s->bytecode + rule[1];
|
||||
const uint32_t *rule_subpattern = s->bytecode + rule[2];
|
||||
down1(s);
|
||||
const uint8_t *window_end = peg_rule(s, rule_window, text);
|
||||
up1(s);
|
||||
if (!window_end) {
|
||||
return NULL;
|
||||
}
|
||||
const uint8_t *saved_end = s->text_end;
|
||||
s->text_end = window_end;
|
||||
down1(s);
|
||||
const uint8_t *next_text = peg_rule(s, rule_subpattern, text_start);
|
||||
up1(s);
|
||||
s->text_end = saved_end;
|
||||
|
||||
if (!next_text) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window_end;
|
||||
}
|
||||
|
||||
case RULE_SPLIT: {
|
||||
const uint8_t *saved_end = s->text_end;
|
||||
const uint32_t *rule_separator = s->bytecode + rule[1];
|
||||
const uint32_t *rule_subpattern = s->bytecode + rule[2];
|
||||
|
||||
const uint8_t *separator_end = NULL;
|
||||
do {
|
||||
const uint8_t *text_start = text;
|
||||
CapState cs = cap_save(s);
|
||||
down1(s);
|
||||
while (text <= s->text_end) {
|
||||
separator_end = peg_rule(s, rule_separator, text);
|
||||
cap_load(s, cs);
|
||||
if (separator_end) {
|
||||
break;
|
||||
}
|
||||
text++;
|
||||
}
|
||||
up1(s);
|
||||
|
||||
if (separator_end) {
|
||||
s->text_end = text;
|
||||
text = separator_end;
|
||||
}
|
||||
|
||||
down1(s);
|
||||
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, text_start);
|
||||
up1(s);
|
||||
s->text_end = saved_end;
|
||||
|
||||
if (!subpattern_end) {
|
||||
return NULL;
|
||||
}
|
||||
} while (separator_end);
|
||||
|
||||
return s->text_end;
|
||||
}
|
||||
|
||||
case RULE_REPLACE:
|
||||
case RULE_MATCHTIME: {
|
||||
uint32_t tag = rule[3];
|
||||
|
@ -1010,7 +1100,7 @@ static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) {
|
|||
emit_3(r, RULE_CAPTURE_NUM, rule, base, tag);
|
||||
return;
|
||||
error:
|
||||
peg_panicf(b, "expected integer between 2 and 36, got %v", argv[2]);
|
||||
peg_panicf(b, "expected integer between 2 and 36, got %v", argv[1]);
|
||||
}
|
||||
|
||||
static void spec_reference(Builder *b, int32_t argc, const Janet *argv) {
|
||||
|
@ -1076,13 +1166,29 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
|
|||
Janet fun = argv[1];
|
||||
if (!janet_checktype(fun, JANET_FUNCTION) &&
|
||||
!janet_checktype(fun, JANET_CFUNCTION)) {
|
||||
peg_panicf(b, "expected function|cfunction, got %v", fun);
|
||||
peg_panicf(b, "expected function or cfunction, got %v", fun);
|
||||
}
|
||||
uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
|
||||
uint32_t cindex = emit_constant(b, fun);
|
||||
emit_3(r, RULE_MATCHTIME, subrule, cindex, tag);
|
||||
}
|
||||
|
||||
static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_fixarity(b, argc, 2);
|
||||
Reserve r = reserve(b, 3);
|
||||
uint32_t subrule1 = peg_compile1(b, argv[0]);
|
||||
uint32_t subrule2 = peg_compile1(b, argv[1]);
|
||||
emit_2(r, RULE_SUB, subrule1, subrule2);
|
||||
}
|
||||
|
||||
static void spec_split(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_fixarity(b, argc, 2);
|
||||
Reserve r = reserve(b, 3);
|
||||
uint32_t subrule1 = peg_compile1(b, argv[0]);
|
||||
uint32_t subrule2 = peg_compile1(b, argv[1]);
|
||||
emit_2(r, RULE_SPLIT, subrule1, subrule2);
|
||||
}
|
||||
|
||||
#ifdef JANET_INT_TYPES
|
||||
#define JANET_MAX_READINT_WIDTH 8
|
||||
#else
|
||||
|
@ -1166,6 +1272,8 @@ static const SpecialPair peg_specials[] = {
|
|||
{"sequence", spec_sequence},
|
||||
{"set", spec_set},
|
||||
{"some", spec_some},
|
||||
{"split", spec_split},
|
||||
{"sub", spec_sub},
|
||||
{"thru", spec_thru},
|
||||
{"to", spec_to},
|
||||
{"uint", spec_uint_le},
|
||||
|
@ -1237,6 +1345,13 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
|
|||
default:
|
||||
peg_panic(b, "unexpected peg source");
|
||||
return 0;
|
||||
|
||||
case JANET_BOOLEAN: {
|
||||
int n = janet_unwrap_boolean(peg);
|
||||
Reserve r = reserve(b, 2);
|
||||
emit_1(r, n ? RULE_NCHAR : RULE_NOTNCHAR, 0);
|
||||
break;
|
||||
}
|
||||
case JANET_NUMBER: {
|
||||
int32_t n = peg_getinteger(b, peg);
|
||||
Reserve r = reserve(b, 2);
|
||||
|
@ -1253,6 +1368,18 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
|
|||
emit_bytes(b, RULE_LITERAL, len, str);
|
||||
break;
|
||||
}
|
||||
case JANET_TABLE: {
|
||||
/* Build grammar table */
|
||||
JanetTable *new_grammar = janet_table_clone(janet_unwrap_table(peg));
|
||||
new_grammar->proto = grammar;
|
||||
b->grammar = grammar = new_grammar;
|
||||
/* Run the main rule */
|
||||
Janet main_rule = janet_table_rawget(grammar, janet_ckeywordv("main"));
|
||||
if (janet_checktype(main_rule, JANET_NIL))
|
||||
peg_panic(b, "grammar requires :main rule");
|
||||
rule = peg_compile1(b, main_rule);
|
||||
break;
|
||||
}
|
||||
case JANET_STRUCT: {
|
||||
/* Build grammar table */
|
||||
const JanetKV *st = janet_unwrap_struct(peg);
|
||||
|
@ -1388,7 +1515,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
|||
uint32_t instr = bytecode[i];
|
||||
uint32_t *rule = bytecode + i;
|
||||
op_flags[i] |= 0x02;
|
||||
switch (instr & 0x1F) {
|
||||
switch (instr) {
|
||||
case RULE_LITERAL:
|
||||
i += 2 + ((rule[1] + 3) >> 2);
|
||||
break;
|
||||
|
@ -1481,6 +1608,15 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
|||
op_flags[rule[1]] |= 0x01;
|
||||
i += 4;
|
||||
break;
|
||||
case RULE_SUB:
|
||||
case RULE_SPLIT:
|
||||
/* [rule, rule] */
|
||||
if (rule[1] >= blen) goto bad;
|
||||
if (rule[2] >= blen) goto bad;
|
||||
op_flags[rule[1]] |= 0x01;
|
||||
op_flags[rule[2]] |= 0x01;
|
||||
i += 3;
|
||||
break;
|
||||
case RULE_ERROR:
|
||||
case RULE_DROP:
|
||||
case RULE_NOT:
|
||||
|
@ -1601,7 +1737,7 @@ typedef struct {
|
|||
JanetPeg *peg;
|
||||
PegState s;
|
||||
JanetByteView bytes;
|
||||
JanetByteView repl;
|
||||
Janet subst;
|
||||
int32_t start;
|
||||
} PegCall;
|
||||
|
||||
|
@ -1609,7 +1745,7 @@ typedef struct {
|
|||
static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
|
||||
PegCall ret;
|
||||
int32_t min = get_replace ? 3 : 2;
|
||||
janet_arity(argc, get_replace, -1);
|
||||
janet_arity(argc, min, -1);
|
||||
if (janet_checktype(argv[0], JANET_ABSTRACT) &&
|
||||
janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) {
|
||||
ret.peg = janet_unwrap_abstract(argv[0]);
|
||||
|
@ -1617,7 +1753,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
|
|||
ret.peg = compile_peg(argv[0]);
|
||||
}
|
||||
if (get_replace) {
|
||||
ret.repl = janet_getbytes(argv, 1);
|
||||
ret.subst = argv[1];
|
||||
ret.bytes = janet_getbytes(argv, 2);
|
||||
} else {
|
||||
ret.bytes = janet_getbytes(argv, 1);
|
||||
|
@ -1634,6 +1770,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
|
|||
ret.s.mode = PEG_MODE_NORMAL;
|
||||
ret.s.text_start = ret.bytes.bytes;
|
||||
ret.s.text_end = ret.bytes.bytes + ret.bytes.len;
|
||||
ret.s.outer_text_end = ret.s.text_end;
|
||||
ret.s.depth = JANET_RECURSION_GUARD;
|
||||
ret.s.captures = janet_array(0);
|
||||
ret.s.tagged_captures = janet_array(0);
|
||||
|
@ -1648,7 +1785,9 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
|
|||
}
|
||||
|
||||
static void peg_call_reset(PegCall *c) {
|
||||
c->s.depth = JANET_RECURSION_GUARD;
|
||||
c->s.captures->count = 0;
|
||||
c->s.tagged_captures->count = 0;
|
||||
c->s.scratch->count = 0;
|
||||
c->s.tags->count = 0;
|
||||
}
|
||||
|
@ -1700,7 +1839,8 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
|
|||
trail = i;
|
||||
}
|
||||
int32_t nexti = (int32_t)(result - c.bytes.bytes);
|
||||
janet_buffer_push_bytes(ret, c.repl.bytes, c.repl.len);
|
||||
JanetByteView subst = janet_text_substitution(&c.subst, c.bytes.bytes + i, nexti - i, c.s.captures);
|
||||
janet_buffer_push_bytes(ret, subst.bytes, subst.len);
|
||||
trail = nexti;
|
||||
if (nexti == i) nexti++;
|
||||
i = nexti;
|
||||
|
@ -1716,14 +1856,20 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
|
|||
}
|
||||
|
||||
JANET_CORE_FN(cfun_peg_replace_all,
|
||||
"(peg/replace-all peg repl text &opt start & args)",
|
||||
"Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.") {
|
||||
"(peg/replace-all peg subst text &opt start & args)",
|
||||
"Replace all matches of `peg` in `text` with `subst`, returning a new buffer. "
|
||||
"The peg does not need to make captures to do replacement. "
|
||||
"If `subst` is a function, it will be called with the "
|
||||
"matching text followed by any captures.") {
|
||||
return cfun_peg_replace_generic(argc, argv, 0);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_peg_replace,
|
||||
"(peg/replace peg repl text &opt start & args)",
|
||||
"Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. "
|
||||
"(peg/replace peg subst text &opt start & args)",
|
||||
"Replace first match of `peg` in `text` with `subst`, returning a new buffer. "
|
||||
"The peg does not need to make captures to do replacement. "
|
||||
"If `subst` is a function, it will be called with the "
|
||||
"matching text followed by any captures. "
|
||||
"If no matches are found, returns the input string in a new buffer.") {
|
||||
return cfun_peg_replace_generic(argc, argv, 1);
|
||||
}
|
||||
|
|
157
src/core/pp.c
157
src/core/pp.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <float.h>
|
||||
|
||||
/* Implements a pretty printer for Janet. The pretty printer
|
||||
* is simple and not that flexible, but fast. */
|
||||
|
@ -37,11 +39,15 @@
|
|||
/* Temporary buffer size */
|
||||
#define BUFSIZE 64
|
||||
|
||||
/* Preprocessor hacks */
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
|
||||
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
|
||||
const char *fmt = (x == floor(x) &&
|
||||
x <= JANET_INTMAX_DOUBLE &&
|
||||
x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g";
|
||||
x >= JANET_INTMIN_DOUBLE) ? "%.0f" : ("%." STR(DBL_DIG) "g");
|
||||
int count;
|
||||
if (x == 0.0) {
|
||||
/* Prevent printing of '-0' */
|
||||
|
@ -108,7 +114,7 @@ static void string_description_b(JanetBuffer *buffer, const char *title, void *p
|
|||
pbuf.p = pointer;
|
||||
*c++ = '<';
|
||||
/* Maximum of 32 bytes for abstract type name */
|
||||
for (i = 0; title[i] && i < 32; ++i)
|
||||
for (i = 0; i < 32 && title[i]; ++i)
|
||||
*c++ = ((uint8_t *)title) [i];
|
||||
*c++ = ' ';
|
||||
*c++ = '0';
|
||||
|
@ -151,6 +157,12 @@ static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, in
|
|||
case '\v':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\v", 2);
|
||||
break;
|
||||
case '\a':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\a", 2);
|
||||
break;
|
||||
case '\b':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\b", 2);
|
||||
break;
|
||||
case 27:
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\e", 2);
|
||||
break;
|
||||
|
@ -243,6 +255,10 @@ void janet_to_string_b(JanetBuffer *buffer, Janet x) {
|
|||
case JANET_FUNCTION: {
|
||||
JanetFunction *fun = janet_unwrap_function(x);
|
||||
JanetFuncDef *def = fun->def;
|
||||
if (def == NULL) {
|
||||
janet_buffer_push_cstring(buffer, "<incomplete function>");
|
||||
break;
|
||||
}
|
||||
if (def->name) {
|
||||
const uint8_t *n = def->name;
|
||||
janet_buffer_push_cstring(buffer, "<function ");
|
||||
|
@ -261,21 +277,13 @@ void janet_to_string_b(JanetBuffer *buffer, Janet x) {
|
|||
|
||||
/* See parse.c for full table */
|
||||
|
||||
static const uint32_t pp_symchars[8] = {
|
||||
0x00000000, 0xf7ffec72, 0xc7ffffff, 0x07fffffe,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000
|
||||
};
|
||||
|
||||
static int pp_is_symbol_char(uint8_t c) {
|
||||
return pp_symchars[c >> 5] & ((uint32_t)1 << (c & 0x1F));
|
||||
}
|
||||
|
||||
/* Check if a symbol or keyword contains no symbol characters */
|
||||
static int contains_bad_chars(const uint8_t *sym, int issym) {
|
||||
int32_t len = janet_string_length(sym);
|
||||
if (len && issym && sym[0] >= '0' && sym[0] <= '9') return 1;
|
||||
if (!janet_valid_utf8(sym, len)) return 1;
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
if (!pp_is_symbol_char(sym[i])) return 1;
|
||||
if (!janet_is_symbol_char(sym[i])) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -371,8 +379,10 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
|
|||
break;
|
||||
case JANET_NUMBER:
|
||||
janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2);
|
||||
int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", janet_unwrap_number(x));
|
||||
S->buffer->count += count;
|
||||
double num = janet_unwrap_number(x);
|
||||
if (isnan(num)) return 1;
|
||||
if (isinf(num)) return 1;
|
||||
janet_buffer_dtostr(S->buffer, num);
|
||||
break;
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
|
@ -570,12 +580,12 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
|||
case JANET_STRUCT:
|
||||
case JANET_TABLE: {
|
||||
int istable = janet_checktype(x, JANET_TABLE);
|
||||
janet_buffer_push_cstring(S->buffer, istable ? "@" : "{");
|
||||
|
||||
/* For object-like tables, print class name */
|
||||
if (istable) {
|
||||
JanetTable *t = janet_unwrap_table(x);
|
||||
JanetTable *proto = t->proto;
|
||||
janet_buffer_push_cstring(S->buffer, "@");
|
||||
if (NULL != proto) {
|
||||
Janet name = janet_table_get(proto, janet_ckeywordv("_name"));
|
||||
const uint8_t *n;
|
||||
|
@ -590,8 +600,25 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
|||
}
|
||||
}
|
||||
}
|
||||
janet_buffer_push_cstring(S->buffer, "{");
|
||||
} else {
|
||||
JanetStruct st = janet_unwrap_struct(x);
|
||||
JanetStruct proto = janet_struct_proto(st);
|
||||
if (NULL != proto) {
|
||||
Janet name = janet_struct_get(proto, janet_ckeywordv("_name"));
|
||||
const uint8_t *n;
|
||||
int32_t len;
|
||||
if (janet_bytes_view(name, &n, &len)) {
|
||||
if (S->flags & JANET_PRETTY_COLOR) {
|
||||
janet_buffer_push_cstring(S->buffer, janet_class_color);
|
||||
}
|
||||
janet_buffer_push_bytes(S->buffer, n, len);
|
||||
if (S->flags & JANET_PRETTY_COLOR) {
|
||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
janet_buffer_push_cstring(S->buffer, "{");
|
||||
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
|
@ -627,7 +654,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
|||
}
|
||||
}
|
||||
|
||||
janet_sorted_keys(kvs, cap, S->keysort_buffer + ks_start);
|
||||
janet_sorted_keys(kvs, cap, S->keysort_buffer == NULL ? NULL : S->keysort_buffer + ks_start);
|
||||
S->keysort_start += len;
|
||||
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
|
||||
len = JANET_PRETTY_DICT_LIMIT;
|
||||
|
@ -726,7 +753,7 @@ static void pushtypes(JanetBuffer *buffer, int types) {
|
|||
if (first) {
|
||||
first = 0;
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, '|');
|
||||
janet_buffer_push_cstring(buffer, (types == 1) ? " or " : ", ");
|
||||
}
|
||||
janet_buffer_push_cstring(buffer, janet_type_names[i]);
|
||||
}
|
||||
|
@ -741,20 +768,48 @@ static void pushtypes(JanetBuffer *buffer, int types) {
|
|||
|
||||
#define MAX_ITEM 256
|
||||
#define FMT_FLAGS "-+ #0"
|
||||
#define FMT_REPLACE_INTTYPES "diouxX"
|
||||
#define MAX_FORMAT 32
|
||||
|
||||
struct FmtMapping {
|
||||
char c;
|
||||
const char *mapping;
|
||||
};
|
||||
|
||||
/* Janet uses fixed width integer types for most things, so map
|
||||
* format specifiers to these fixed sizes */
|
||||
static const struct FmtMapping format_mappings[] = {
|
||||
{'D', PRId64},
|
||||
{'I', PRIi64},
|
||||
{'d', PRId64},
|
||||
{'i', PRIi64},
|
||||
{'o', PRIo64},
|
||||
{'u', PRIu64},
|
||||
{'x', PRIx64},
|
||||
{'X', PRIX64},
|
||||
};
|
||||
|
||||
static const char *get_fmt_mapping(char c) {
|
||||
for (size_t i = 0; i < (sizeof(format_mappings) / sizeof(struct FmtMapping)); i++) {
|
||||
if (format_mappings[i].c == c)
|
||||
return format_mappings[i].mapping;
|
||||
}
|
||||
janet_assert(0, "bad format mapping");
|
||||
}
|
||||
|
||||
static const char *scanformat(
|
||||
const char *strfrmt,
|
||||
char *form,
|
||||
char width[3],
|
||||
char precision[3]) {
|
||||
const char *p = strfrmt;
|
||||
|
||||
/* Parse strfrmt */
|
||||
memset(width, '\0', 3);
|
||||
memset(precision, '\0', 3);
|
||||
while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL)
|
||||
p++; /* skip flags */
|
||||
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS) / sizeof(char))
|
||||
janet_panic("invalid format (repeated flags)");
|
||||
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS)) janet_panic("invalid format (repeated flags)");
|
||||
if (isdigit((int)(*p)))
|
||||
width[0] = *p++; /* skip width */
|
||||
if (isdigit((int)(*p)))
|
||||
|
@ -768,10 +823,23 @@ static const char *scanformat(
|
|||
}
|
||||
if (isdigit((int)(*p)))
|
||||
janet_panic("invalid format (width or precision too long)");
|
||||
|
||||
/* Write to form - replace characters with fixed size stuff */
|
||||
*(form++) = '%';
|
||||
memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char));
|
||||
form += (p - strfrmt) + 1;
|
||||
const char *p2 = strfrmt;
|
||||
while (p2 <= p) {
|
||||
char *loc = strchr(FMT_REPLACE_INTTYPES, *p2);
|
||||
if (loc != NULL && *loc != '\0') {
|
||||
const char *mapping = get_fmt_mapping(*p2++);
|
||||
size_t len = strlen(mapping);
|
||||
memcpy(form, mapping, len);
|
||||
form += len;
|
||||
} else {
|
||||
*(form++) = *(p2++);
|
||||
}
|
||||
}
|
||||
*form = '\0';
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -791,16 +859,27 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
|
|||
c = scanformat(c, form, width, precision);
|
||||
switch (*c++) {
|
||||
case 'c': {
|
||||
int n = va_arg(args, long);
|
||||
int n = va_arg(args, int);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'o':
|
||||
case 'i': {
|
||||
int64_t n = (int64_t) va_arg(args, int32_t);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
case 'I': {
|
||||
int64_t n = va_arg(args, int64_t);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
case 'X': {
|
||||
int32_t n = va_arg(args, long);
|
||||
case 'X':
|
||||
case 'o':
|
||||
case 'u': {
|
||||
uint64_t n = va_arg(args, uint64_t);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
|
@ -844,7 +923,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
|
|||
janet_buffer_push_cstring(b, typestr(va_arg(args, Janet)));
|
||||
break;
|
||||
case 'T': {
|
||||
int types = va_arg(args, long);
|
||||
int types = va_arg(args, int);
|
||||
pushtypes(b, types);
|
||||
break;
|
||||
}
|
||||
|
@ -953,12 +1032,19 @@ void janet_buffer_format(
|
|||
janet_getinteger(argv, arg));
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
case 'I':
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'o':
|
||||
case 'i': {
|
||||
int64_t n = janet_getinteger64(argv, arg);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
case 'X': {
|
||||
int32_t n = janet_getinteger(argv, arg);
|
||||
case 'X':
|
||||
case 'o':
|
||||
case 'u': {
|
||||
uint64_t n = janet_getuinteger64(argv, arg);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
|
@ -974,8 +1060,9 @@ void janet_buffer_format(
|
|||
break;
|
||||
}
|
||||
case 's': {
|
||||
const uint8_t *s = janet_getstring(argv, arg);
|
||||
int32_t l = janet_string_length(s);
|
||||
JanetByteView bytes = janet_getbytes(argv, arg);
|
||||
const uint8_t *s = bytes.bytes;
|
||||
int32_t l = bytes.len;
|
||||
if (form[2] == '\0')
|
||||
janet_buffer_push_bytes(b, s, l);
|
||||
else {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -27,6 +27,8 @@
|
|||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* The JanetRegisterAllocator is really just a bitset. */
|
||||
|
||||
void janetc_regalloc_init(JanetcRegisterAllocator *ra) {
|
||||
ra->chunks = NULL;
|
||||
ra->count = 0;
|
||||
|
@ -139,6 +141,14 @@ void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg) {
|
|||
ra->chunks[chunk] &= ~ithbit(bit);
|
||||
}
|
||||
|
||||
/* Check if a register is set. */
|
||||
int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg) {
|
||||
int32_t chunk = reg >> 5;
|
||||
int32_t bit = reg & 0x1F;
|
||||
while (chunk >= ra->count) pushchunk(ra);
|
||||
return !!(ra->chunks[chunk] & ithbit(bit));
|
||||
}
|
||||
|
||||
/* Get a register that will fit in 8 bits (< 256). Do not call this
|
||||
* twice with the same value of nth without calling janetc_regalloc_free
|
||||
* on the returned register before. */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -56,5 +56,6 @@ int32_t janetc_regalloc_temp(JanetcRegisterAllocator *ra, JanetcRegisterTemp nth
|
|||
void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRegisterTemp nth);
|
||||
void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src);
|
||||
void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg);
|
||||
int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -23,6 +23,7 @@
|
|||
#ifndef JANET_AMALG
|
||||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Run a string */
|
||||
|
@ -31,6 +32,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
|||
int errflags = 0, done = 0;
|
||||
int32_t index = 0;
|
||||
Janet ret = janet_wrap_nil();
|
||||
JanetFiber *fiber = NULL;
|
||||
const uint8_t *where = sourcePath ? janet_cstring(sourcePath) : NULL;
|
||||
|
||||
if (where) janet_gcroot(janet_wrap_string(where));
|
||||
|
@ -46,22 +48,30 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
|||
JanetCompileResult cres = janet_compile(form, env, where);
|
||||
if (cres.status == JANET_COMPILE_OK) {
|
||||
JanetFunction *f = janet_thunk(cres.funcdef);
|
||||
JanetFiber *fiber = janet_fiber(f, 64, 0, NULL);
|
||||
fiber = janet_fiber(f, 64, 0, NULL);
|
||||
fiber->env = env;
|
||||
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
|
||||
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
|
||||
janet_stacktrace(fiber, ret);
|
||||
janet_stacktrace_ext(fiber, ret, "");
|
||||
errflags |= 0x01;
|
||||
done = 1;
|
||||
}
|
||||
} else {
|
||||
ret = janet_wrap_string(cres.error);
|
||||
int32_t line = (int32_t) parser.line;
|
||||
int32_t col = (int32_t) parser.column;
|
||||
if ((cres.error_mapping.line > 0) &&
|
||||
(cres.error_mapping.column > 0)) {
|
||||
line = cres.error_mapping.line;
|
||||
col = cres.error_mapping.column;
|
||||
}
|
||||
if (cres.macrofiber) {
|
||||
janet_eprintf("compile error in %s: ", sourcePath);
|
||||
janet_stacktrace(cres.macrofiber, ret);
|
||||
janet_eprintf("%s:%d:%d: compile error", sourcePath,
|
||||
line, col);
|
||||
janet_stacktrace_ext(cres.macrofiber, ret, "");
|
||||
} else {
|
||||
janet_eprintf("compile error in %s: %s\n", sourcePath,
|
||||
(const char *)cres.error);
|
||||
janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
|
||||
line, col, (const char *)cres.error);
|
||||
}
|
||||
errflags |= 0x02;
|
||||
done = 1;
|
||||
|
@ -79,8 +89,8 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
|||
const char *e = janet_parser_error(&parser);
|
||||
errflags |= 0x04;
|
||||
ret = janet_cstringv(e);
|
||||
int32_t line = parser.line;
|
||||
int32_t col = parser.column;
|
||||
int32_t line = (int32_t) parser.line;
|
||||
int32_t col = (int32_t) parser.column;
|
||||
janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
|
||||
done = 1;
|
||||
break;
|
||||
|
@ -100,6 +110,19 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
|||
/* Clean up and return errors */
|
||||
janet_parser_deinit(&parser);
|
||||
if (where) janet_gcunroot(janet_wrap_string(where));
|
||||
#ifdef JANET_EV
|
||||
/* Enter the event loop if we are not already in it */
|
||||
if (janet_vm.stackn == 0) {
|
||||
if (fiber) {
|
||||
janet_gcroot(janet_wrap_fiber(fiber));
|
||||
}
|
||||
janet_loop();
|
||||
if (fiber) {
|
||||
janet_gcunroot(janet_wrap_fiber(fiber));
|
||||
ret = fiber->last_value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (out) *out = ret;
|
||||
return errflags;
|
||||
}
|
||||
|
@ -121,7 +144,7 @@ int janet_loop_fiber(JanetFiber *fiber) {
|
|||
Janet out;
|
||||
status = janet_continue(fiber, janet_wrap_nil(), &out);
|
||||
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
|
||||
janet_stacktrace(fiber, out);
|
||||
janet_stacktrace_ext(fiber, out, "");
|
||||
}
|
||||
#endif
|
||||
return status;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -31,7 +31,7 @@
|
|||
|
||||
static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (argn != 1) {
|
||||
janetc_cerror(opts.compiler, "expected 1 argument");
|
||||
janetc_cerror(opts.compiler, "expected 1 argument to quote");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
return janetc_cslot(argv[0]);
|
||||
|
@ -39,8 +39,12 @@ static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||
|
||||
static JanetSlot janetc_splice(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
JanetSlot ret;
|
||||
if (!(opts.flags & JANET_FOPTS_ACCEPT_SPLICE)) {
|
||||
janetc_cerror(opts.compiler, "splice can only be used in function parameters and data constructors, it has no effect here");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
if (argn != 1) {
|
||||
janetc_cerror(opts.compiler, "expected 1 argument");
|
||||
janetc_cerror(opts.compiler, "expected 1 argument to splice");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
ret = janetc_value(opts, argv[0]);
|
||||
|
@ -62,6 +66,8 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
|
|||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
JanetSlot *slots = NULL;
|
||||
JanetFopts subopts = opts;
|
||||
subopts.flags &= ~JANET_FOPTS_HINT;
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
return janetc_cslot(x);
|
||||
|
@ -73,7 +79,9 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
|
|||
const uint8_t *head = janet_unwrap_symbol(tup[0]);
|
||||
if (!janet_cstrcmp(head, "unquote")) {
|
||||
if (level == 0) {
|
||||
return janetc_value(janetc_fopts_default(opts.compiler), tup[1]);
|
||||
JanetFopts subopts = janetc_fopts_default(opts.compiler);
|
||||
subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
|
||||
return janetc_value(subopts, tup[1]);
|
||||
} else {
|
||||
level--;
|
||||
}
|
||||
|
@ -82,7 +90,7 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
|
|||
}
|
||||
}
|
||||
for (i = 0; i < len; i++)
|
||||
janet_v_push(slots, quasiquote(opts, tup[i], depth - 1, level));
|
||||
janet_v_push(slots, quasiquote(subopts, tup[i], depth - 1, level));
|
||||
return qq_slots(opts, slots, (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR)
|
||||
? JOP_MAKE_BRACKET_TUPLE
|
||||
: JOP_MAKE_TUPLE);
|
||||
|
@ -91,7 +99,7 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
|
|||
int32_t i;
|
||||
JanetArray *array = janet_unwrap_array(x);
|
||||
for (i = 0; i < array->count; i++)
|
||||
janet_v_push(slots, quasiquote(opts, array->data[i], depth - 1, level));
|
||||
janet_v_push(slots, quasiquote(subopts, array->data[i], depth - 1, level));
|
||||
return qq_slots(opts, slots, JOP_MAKE_ARRAY);
|
||||
}
|
||||
case JANET_TABLE:
|
||||
|
@ -100,8 +108,8 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
|
|||
int32_t len, cap = 0;
|
||||
janet_dictionary_view(x, &kvs, &len, &cap);
|
||||
while ((kv = janet_dictionary_next(kvs, cap, kv))) {
|
||||
JanetSlot key = quasiquote(opts, kv->key, depth - 1, level);
|
||||
JanetSlot value = quasiquote(opts, kv->value, depth - 1, level);
|
||||
JanetSlot key = quasiquote(subopts, kv->key, depth - 1, level);
|
||||
JanetSlot value = quasiquote(subopts, kv->value, depth - 1, level);
|
||||
key.flags &= ~JANET_SLOT_SPLICED;
|
||||
value.flags &= ~JANET_SLOT_SPLICED;
|
||||
janet_v_push(slots, key);
|
||||
|
@ -115,7 +123,7 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
|
|||
|
||||
static JanetSlot janetc_quasiquote(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (argn != 1) {
|
||||
janetc_cerror(opts.compiler, "expected 1 argument");
|
||||
janetc_cerror(opts.compiler, "expected 1 argument to quasiquote");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
return quasiquote(opts, argv[0], JANET_RECURSION_GUARD, 0);
|
||||
|
@ -141,7 +149,7 @@ static int destructure(JanetCompiler *c,
|
|||
JanetTable *attr) {
|
||||
switch (janet_type(left)) {
|
||||
default:
|
||||
janetc_cerror(c, "unexpected type in destructuring");
|
||||
janetc_error(c, janet_formatc("unexpected type in destructuring, got %v", left));
|
||||
return 1;
|
||||
case JANET_SYMBOL:
|
||||
/* Leaf, assign right to left */
|
||||
|
@ -154,6 +162,67 @@ static int destructure(JanetCompiler *c,
|
|||
for (int32_t i = 0; i < len; i++) {
|
||||
JanetSlot nextright = janetc_farslot(c);
|
||||
Janet subval = values[i];
|
||||
|
||||
if (janet_checktype(subval, JANET_SYMBOL) && !janet_cstrcmp(janet_unwrap_symbol(subval), "&")) {
|
||||
if (i + 1 >= len) {
|
||||
janetc_cerror(c, "expected symbol following '& in destructuring pattern");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (i + 2 < len) {
|
||||
int32_t num_extra = len - i - 1;
|
||||
Janet *extra = janet_tuple_begin(num_extra);
|
||||
janet_tuple_flag(extra) |= JANET_TUPLE_FLAG_BRACKETCTOR;
|
||||
|
||||
for (int32_t j = 0; j < num_extra; ++j) {
|
||||
extra[j] = values[j + i + 1];
|
||||
}
|
||||
|
||||
janetc_error(c, janet_formatc("expected a single symbol follow '& in destructuring pattern, found %q", janet_wrap_tuple(janet_tuple_end(extra))));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!janet_checktype(values[i + 1], JANET_SYMBOL)) {
|
||||
janetc_error(c, janet_formatc("expected symbol following '& in destructuring pattern, found %q", values[i + 1]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
JanetSlot argi = janetc_farslot(c);
|
||||
JanetSlot arg = janetc_farslot(c);
|
||||
JanetSlot len = janetc_farslot(c);
|
||||
|
||||
janetc_emit_si(c, JOP_LOAD_INTEGER, argi, i, 0);
|
||||
janetc_emit_ss(c, JOP_LENGTH, len, right, 0);
|
||||
|
||||
/* loop condition - reuse arg slot for the condition result */
|
||||
int32_t label_loop_start = janetc_emit_sss(c, JOP_LESS_THAN, arg, argi, len, 0);
|
||||
int32_t label_loop_cond_jump = janetc_emit_si(c, JOP_JUMP_IF_NOT, arg, 0, 0);
|
||||
|
||||
/* loop body */
|
||||
janetc_emit_sss(c, JOP_GET, arg, right, argi, 0);
|
||||
janetc_emit_s(c, JOP_PUSH, arg, 0);
|
||||
janetc_emit_ssi(c, JOP_ADD_IMMEDIATE, argi, argi, 1, 0);
|
||||
|
||||
/* loop - jump back to the start of the loop */
|
||||
int32_t label_loop_loop = janet_v_count(c->buffer);
|
||||
janetc_emit(c, JOP_JUMP);
|
||||
int32_t label_loop_exit = janet_v_count(c->buffer);
|
||||
|
||||
/* avoid shifting negative numbers */
|
||||
c->buffer[label_loop_cond_jump] |= (uint32_t)(label_loop_exit - label_loop_cond_jump) << 16;
|
||||
c->buffer[label_loop_loop] |= (uint32_t)(label_loop_start - label_loop_loop) << 8;
|
||||
|
||||
janetc_freeslot(c, argi);
|
||||
janetc_freeslot(c, arg);
|
||||
janetc_freeslot(c, len);
|
||||
|
||||
janetc_emit_s(c, JOP_MAKE_TUPLE, nextright, 1);
|
||||
|
||||
leaf(c, janet_unwrap_symbol(values[i + 1]), nextright, attr);
|
||||
janetc_freeslot(c, nextright);
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < 0x100) {
|
||||
janetc_emit_ssu(c, JOP_GET_INDEX, nextright, right, (uint8_t) i, 1);
|
||||
} else {
|
||||
|
@ -194,7 +263,7 @@ static const Janet *janetc_make_sourcemap(JanetCompiler *c) {
|
|||
|
||||
static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (argn != 2) {
|
||||
janetc_cerror(opts.compiler, "expected 2 arguments");
|
||||
janetc_cerror(opts.compiler, "expected 2 arguments to set");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
JanetFopts subopts = janetc_fopts_default(opts.compiler);
|
||||
|
@ -236,14 +305,24 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||
}
|
||||
|
||||
/* Add attributes to a global def or var table */
|
||||
static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv) {
|
||||
static JanetTable *handleattr(JanetCompiler *c, const char *kind, int32_t argn, const Janet *argv) {
|
||||
int32_t i;
|
||||
JanetTable *tab = janet_table(2);
|
||||
const char *binding_name = janet_type(argv[0]) == JANET_SYMBOL
|
||||
? ((const char *)janet_unwrap_symbol(argv[0]))
|
||||
: "<multiple bindings>";
|
||||
if (argn < 2) {
|
||||
janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind));
|
||||
return NULL;
|
||||
}
|
||||
for (i = 1; i < argn - 1; i++) {
|
||||
Janet attr = argv[i];
|
||||
switch (janet_type(attr)) {
|
||||
case JANET_TUPLE:
|
||||
janetc_cerror(c, "unexpected form - did you intend to use defn?");
|
||||
break;
|
||||
default:
|
||||
janetc_cerror(c, "could not add metadata to binding");
|
||||
janetc_error(c, janet_formatc("cannot add metadata %v to binding %s", attr, binding_name));
|
||||
break;
|
||||
case JANET_KEYWORD:
|
||||
janet_table_put(tab, attr, janet_wrap_true());
|
||||
|
@ -259,18 +338,52 @@ static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv)
|
|||
return tab;
|
||||
}
|
||||
|
||||
static JanetSlot dohead(JanetCompiler *c, JanetFopts opts, Janet *head, int32_t argn, const Janet *argv) {
|
||||
typedef struct SlotHeadPair {
|
||||
Janet lhs;
|
||||
JanetSlot rhs;
|
||||
} SlotHeadPair;
|
||||
|
||||
SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopts opts, Janet lhs, Janet rhs) {
|
||||
|
||||
/* Detect if we can do an optimization to avoid some allocations */
|
||||
int can_destructure_lhs = janet_checktype(lhs, JANET_TUPLE)
|
||||
|| janet_checktype(lhs, JANET_ARRAY);
|
||||
int rhs_is_indexed = janet_checktype(rhs, JANET_ARRAY)
|
||||
|| (janet_checktype(rhs, JANET_TUPLE) && (janet_tuple_flag(janet_unwrap_tuple(rhs)) & JANET_TUPLE_FLAG_BRACKETCTOR));
|
||||
uint32_t has_drop = opts.flags & JANET_FOPTS_DROP;
|
||||
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
JanetSlot ret;
|
||||
if (argn < 2) {
|
||||
janetc_cerror(c, "expected at least 2 arguments");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
*head = argv[0];
|
||||
subopts.flags = opts.flags & ~(JANET_FOPTS_TAIL | JANET_FOPTS_DROP);
|
||||
|
||||
if (has_drop && can_destructure_lhs && rhs_is_indexed) {
|
||||
/* Code is of the form (def [a b] [1 2]), avoid the allocation of two tuples */
|
||||
JanetView view_lhs = {0};
|
||||
JanetView view_rhs = {0};
|
||||
janet_indexed_view(lhs, &view_lhs.items, &view_lhs.len);
|
||||
janet_indexed_view(rhs, &view_rhs.items, &view_rhs.len);
|
||||
int found_amp = 0;
|
||||
for (int32_t i = 0; i < view_lhs.len; i++) {
|
||||
if (janet_symeq(view_lhs.items[i], "&")) {
|
||||
found_amp = 1;
|
||||
/* Good error will be generated later. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_amp) {
|
||||
for (int32_t i = 0; i < view_lhs.len; i++) {
|
||||
Janet sub_rhs = view_rhs.len <= i ? janet_wrap_nil() : view_rhs.items[i];
|
||||
into = dohead_destructure(c, into, subopts, view_lhs.items[i], sub_rhs);
|
||||
}
|
||||
return into;
|
||||
}
|
||||
}
|
||||
|
||||
/* No optimization, do the simple way */
|
||||
subopts.hint = opts.hint;
|
||||
ret = janetc_value(subopts, argv[argn - 1]);
|
||||
return ret;
|
||||
JanetSlot ret = janetc_value(subopts, rhs);
|
||||
SlotHeadPair shp = {lhs, ret};
|
||||
janet_v_push(into, shp);
|
||||
return into;
|
||||
}
|
||||
|
||||
/* Def or var a symbol in a local scope */
|
||||
|
@ -278,7 +391,17 @@ static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, Janet
|
|||
int isUnnamedRegister = !(ret.flags & JANET_SLOT_NAMED) &&
|
||||
ret.index > 0 &&
|
||||
ret.envindex >= 0;
|
||||
if (!isUnnamedRegister) {
|
||||
/* optimization for `(def x my-def)` - don't emit a movn/movf instruction, we can just alias my-def */
|
||||
/* TODO - implement optimization for `(def x my-var)` correctly as well w/ de-aliasing */
|
||||
int canAlias = !(flags & JANET_SLOT_MUTABLE) &&
|
||||
!(ret.flags & JANET_SLOT_MUTABLE) &&
|
||||
(ret.flags & JANET_SLOT_NAMED) &&
|
||||
(ret.index >= 0) &&
|
||||
(ret.envindex == -1);
|
||||
if (canAlias) {
|
||||
ret.flags &= ~JANET_SLOT_MUTABLE;
|
||||
isUnnamedRegister = 1; /* don't free slot after use - is an alias for another slot */
|
||||
} else if (!isUnnamedRegister) {
|
||||
/* Slot is not able to be named */
|
||||
JanetSlot localslot = janetc_farslot(c);
|
||||
janetc_copy(c, localslot, ret);
|
||||
|
@ -298,8 +421,20 @@ static int varleaf(
|
|||
/* Global var, generate var */
|
||||
JanetSlot refslot;
|
||||
JanetTable *entry = janet_table_clone(reftab);
|
||||
JanetArray *ref = janet_array(1);
|
||||
janet_array_push(ref, janet_wrap_nil());
|
||||
|
||||
Janet redef_kw = janet_ckeywordv("redef");
|
||||
int is_redef = janet_truthy(janet_table_get(c->env, redef_kw));
|
||||
|
||||
JanetArray *ref;
|
||||
JanetBinding old_binding;
|
||||
if (is_redef && (old_binding = janet_resolve_ext(c->env, sym),
|
||||
old_binding.type == JANET_BINDING_VAR)) {
|
||||
ref = janet_unwrap_array(old_binding.value);
|
||||
} else {
|
||||
ref = janet_array(1);
|
||||
janet_array_push(ref, janet_wrap_nil());
|
||||
}
|
||||
|
||||
janet_table_put(entry, janet_ckeywordv("ref"), janet_wrap_array(ref));
|
||||
janet_table_put(entry, janet_ckeywordv("source-map"),
|
||||
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||
|
@ -314,11 +449,23 @@ static int varleaf(
|
|||
|
||||
static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
Janet head;
|
||||
JanetSlot ret = dohead(c, opts, &head, argn, argv);
|
||||
if (c->result.status == JANET_COMPILE_ERROR)
|
||||
JanetTable *attr_table = handleattr(c, "var", argn, argv);
|
||||
if (c->result.status == JANET_COMPILE_ERROR) {
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
destructure(c, argv[0], ret, varleaf, handleattr(c, argn, argv));
|
||||
}
|
||||
SlotHeadPair *into = NULL;
|
||||
into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]);
|
||||
if (c->result.status == JANET_COMPILE_ERROR) {
|
||||
janet_v_free(into);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
JanetSlot ret;
|
||||
janet_assert(janet_v_count(into) > 0, "bad destructure");
|
||||
for (int32_t i = 0; i < janet_v_count(into); i++) {
|
||||
destructure(c, into[i].lhs, into[i].rhs, varleaf, attr_table);
|
||||
ret = into[i].rhs;
|
||||
}
|
||||
janet_v_free(into);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -331,29 +478,78 @@ static int defleaf(
|
|||
JanetTable *entry = janet_table_clone(tab);
|
||||
janet_table_put(entry, janet_ckeywordv("source-map"),
|
||||
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||
JanetSlot valsym = janetc_cslot(janet_ckeywordv("value"));
|
||||
JanetSlot tabslot = janetc_cslot(janet_wrap_table(entry));
|
||||
|
||||
Janet redef_kw = janet_ckeywordv("redef");
|
||||
int is_redef = janet_truthy(janet_table_get(c->env, redef_kw));
|
||||
if (is_redef) janet_table_put(entry, redef_kw, janet_wrap_true());
|
||||
|
||||
if (is_redef) {
|
||||
JanetBinding binding = janet_resolve_ext(c->env, sym);
|
||||
JanetArray *ref;
|
||||
if (binding.type == JANET_BINDING_DYNAMIC_DEF || binding.type == JANET_BINDING_DYNAMIC_MACRO) {
|
||||
ref = janet_unwrap_array(binding.value);
|
||||
} else {
|
||||
ref = janet_array(1);
|
||||
janet_array_push(ref, janet_wrap_nil());
|
||||
}
|
||||
janet_table_put(entry, janet_ckeywordv("ref"), janet_wrap_array(ref));
|
||||
JanetSlot refslot = janetc_cslot(janet_wrap_array(ref));
|
||||
janetc_emit_ssu(c, JOP_PUT_INDEX, refslot, s, 0, 0);
|
||||
} else {
|
||||
JanetSlot valsym = janetc_cslot(janet_ckeywordv("value"));
|
||||
JanetSlot tabslot = janetc_cslot(janet_wrap_table(entry));
|
||||
janetc_emit_sss(c, JOP_PUT, tabslot, valsym, s, 0);
|
||||
}
|
||||
|
||||
/* Add env entry to env */
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(entry));
|
||||
|
||||
/* Put value in table when evaulated */
|
||||
janetc_emit_sss(c, JOP_PUT, tabslot, valsym, s, 0);
|
||||
}
|
||||
return namelocal(c, sym, 0, s);
|
||||
}
|
||||
|
||||
static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
Janet head;
|
||||
opts.flags &= ~JANET_FOPTS_HINT;
|
||||
JanetSlot ret = dohead(c, opts, &head, argn, argv);
|
||||
if (c->result.status == JANET_COMPILE_ERROR)
|
||||
JanetTable *attr_table = handleattr(c, "def", argn, argv);
|
||||
if (c->result.status == JANET_COMPILE_ERROR) {
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
destructure(c, argv[0], ret, defleaf, handleattr(c, argn, argv));
|
||||
}
|
||||
opts.flags &= ~JANET_FOPTS_HINT;
|
||||
SlotHeadPair *into = NULL;
|
||||
into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]);
|
||||
if (c->result.status == JANET_COMPILE_ERROR) {
|
||||
janet_v_free(into);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
JanetSlot ret;
|
||||
janet_assert(janet_v_count(into) > 0, "bad destructure");
|
||||
for (int32_t i = 0; i < janet_v_count(into); i++) {
|
||||
destructure(c, into[i].lhs, into[i].rhs, defleaf, attr_table);
|
||||
ret = into[i].rhs;
|
||||
}
|
||||
janet_v_free(into);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if a form matches the pattern (= nil _) or (not= nil _) */
|
||||
static int janetc_check_nil_form(Janet x, Janet *capture, uint32_t fun_tag) {
|
||||
if (!janet_checktype(x, JANET_TUPLE)) return 0;
|
||||
JanetTuple tup = janet_unwrap_tuple(x);
|
||||
if (3 != janet_tuple_length(tup)) return 0;
|
||||
Janet op1 = tup[0];
|
||||
if (!janet_checktype(op1, JANET_FUNCTION)) return 0;
|
||||
JanetFunction *fun = janet_unwrap_function(op1);
|
||||
uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
|
||||
if (tag != fun_tag) return 0;
|
||||
if (janet_checktype(tup[1], JANET_NIL)) {
|
||||
*capture = tup[2];
|
||||
return 1;
|
||||
} else if (janet_checktype(tup[2], JANET_NIL)) {
|
||||
*capture = tup[1];
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* :condition
|
||||
* ...
|
||||
|
@ -374,6 +570,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
JanetScope condscope, tempscope;
|
||||
const int tail = opts.flags & JANET_FOPTS_TAIL;
|
||||
const int drop = opts.flags & JANET_FOPTS_DROP;
|
||||
uint8_t ifnjmp = JOP_JUMP_IF_NOT;
|
||||
|
||||
if (argn < 2 || argn > 3) {
|
||||
janetc_cerror(c, "expected 2 or 3 arguments to if");
|
||||
|
@ -387,6 +584,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
/* Get options */
|
||||
condopts = janetc_fopts_default(c);
|
||||
bodyopts = opts;
|
||||
bodyopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
|
||||
|
||||
/* Set target for compilation */
|
||||
target = (drop || tail)
|
||||
|
@ -395,12 +593,24 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
|
||||
/* Compile condition */
|
||||
janetc_scope(&condscope, c, 0, "if");
|
||||
cond = janetc_value(condopts, argv[0]);
|
||||
|
||||
Janet condform = argv[0];
|
||||
if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
|
||||
ifnjmp = JOP_JUMP_IF_NOT_NIL;
|
||||
} else if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
|
||||
ifnjmp = JOP_JUMP_IF_NIL;
|
||||
}
|
||||
|
||||
cond = janetc_value(condopts, condform);
|
||||
|
||||
/* Check constant condition. */
|
||||
/* TODO: Use type info for more short circuits */
|
||||
if (cond.flags & JANET_SLOT_CONSTANT) {
|
||||
if (!janet_truthy(cond.constant)) {
|
||||
int swap_condition = 0;
|
||||
if (ifnjmp == JOP_JUMP_IF_NOT && !janet_truthy(cond.constant)) swap_condition = 1;
|
||||
if (ifnjmp == JOP_JUMP_IF_NIL && janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
|
||||
if (ifnjmp == JOP_JUMP_IF_NOT_NIL && !janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
|
||||
if (swap_condition) {
|
||||
/* Swap the true and false bodies */
|
||||
Janet temp = falsebody;
|
||||
falsebody = truebody;
|
||||
|
@ -418,7 +628,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
}
|
||||
|
||||
/* Compile jump to right */
|
||||
labeljr = janetc_emit_si(c, JOP_JUMP_IF_NOT, cond, 0, 0);
|
||||
labeljr = janetc_emit_si(c, ifnjmp, cond, 0, 0);
|
||||
|
||||
/* Condition left body */
|
||||
janetc_scope(&tempscope, c, 0, "if-true");
|
||||
|
@ -428,7 +638,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
|
||||
/* Compile jump to done */
|
||||
labeljd = janet_v_count(c->buffer);
|
||||
if (!tail) janetc_emit(c, JOP_JUMP);
|
||||
if (!tail && !(drop && janet_checktype(falsebody, JANET_NIL))) janetc_emit(c, JOP_JUMP);
|
||||
|
||||
/* Compile right body */
|
||||
labelr = janet_v_count(c->buffer);
|
||||
|
@ -463,6 +673,7 @@ static JanetSlot janetc_do(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
subopts.flags = JANET_FOPTS_DROP;
|
||||
} else {
|
||||
subopts = opts;
|
||||
subopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
|
||||
}
|
||||
ret = janetc_value(subopts, argv[i]);
|
||||
if (i != argn - 1) {
|
||||
|
@ -473,7 +684,6 @@ static JanetSlot janetc_do(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Compile an upscope form. Upscope forms execute their body sequentially and
|
||||
* evaluate to the last expression in the body, but without lexical scope. */
|
||||
static JanetSlot janetc_upscope(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
|
@ -486,6 +696,7 @@ static JanetSlot janetc_upscope(JanetFopts opts, int32_t argn, const Janet *argv
|
|||
subopts.flags = JANET_FOPTS_DROP;
|
||||
} else {
|
||||
subopts = opts;
|
||||
subopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
|
||||
}
|
||||
ret = janetc_value(subopts, argv[i]);
|
||||
if (i != argn - 1) {
|
||||
|
@ -538,9 +749,8 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||
if (!(scope->flags & JANET_SCOPE_WHILE) && argn) {
|
||||
/* Closure body with return argument */
|
||||
subopts.flags |= JANET_FOPTS_TAIL;
|
||||
JanetSlot ret = janetc_value(subopts, argv[0]);
|
||||
ret.flags |= JANET_SLOT_RETURNED;
|
||||
return ret;
|
||||
janetc_value(subopts, argv[0]);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
} else {
|
||||
/* while loop IIFE or no argument */
|
||||
if (argn) {
|
||||
|
@ -548,9 +758,7 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||
janetc_value(subopts, argv[0]);
|
||||
}
|
||||
janetc_emit(c, JOP_RETURN_NIL);
|
||||
JanetSlot s = janetc_cslot(janet_wrap_nil());
|
||||
s.flags |= JANET_SLOT_RETURNED;
|
||||
return s;
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
} else {
|
||||
if (argn) {
|
||||
|
@ -563,20 +771,6 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||
}
|
||||
}
|
||||
|
||||
/* Check if a form matches the pattern (not= nil _) */
|
||||
static int janetc_check_notnil_form(Janet x, Janet *capture) {
|
||||
if (!janet_checktype(x, JANET_TUPLE)) return 0;
|
||||
JanetTuple tup = janet_unwrap_tuple(x);
|
||||
if (!janet_checktype(tup[0], JANET_FUNCTION)) return 0;
|
||||
if (3 != janet_tuple_length(tup)) return 0;
|
||||
JanetFunction *fun = janet_unwrap_function(tup[0]);
|
||||
uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
|
||||
if (tag != JANET_FUN_NEQ) return 0;
|
||||
if (!janet_checktype(tup[1], JANET_NIL)) return 0;
|
||||
*capture = tup[2];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* :whiletop
|
||||
* ...
|
||||
|
@ -593,12 +787,13 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||
JanetScope tempscope;
|
||||
int32_t labelwt, labeld, labeljt, labelc, i;
|
||||
int infinite = 0;
|
||||
int is_nil_form = 0;
|
||||
int is_notnil_form = 0;
|
||||
uint8_t ifjmp = JOP_JUMP_IF;
|
||||
uint8_t ifnjmp = JOP_JUMP_IF_NOT;
|
||||
|
||||
if (argn < 2) {
|
||||
janetc_cerror(c, "expected at least 2 arguments");
|
||||
if (argn < 1) {
|
||||
janetc_cerror(c, "expected at least 1 argument to while");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
|
||||
|
@ -606,11 +801,16 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||
|
||||
janetc_scope(&tempscope, c, JANET_SCOPE_WHILE, "while");
|
||||
|
||||
/* Check for `(not= nil _)` in condition, and if so, use the
|
||||
/* Check for `(= nil _)` or `(not= nil _)` in condition, and if so, use the
|
||||
* jmpnl or jmpnn instructions. This let's us implement `(each ...)`
|
||||
* more efficiently. */
|
||||
Janet condform = argv[0];
|
||||
if (janetc_check_notnil_form(condform, &condform)) {
|
||||
if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
|
||||
is_nil_form = 1;
|
||||
ifjmp = JOP_JUMP_IF_NIL;
|
||||
ifnjmp = JOP_JUMP_IF_NOT_NIL;
|
||||
}
|
||||
if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
|
||||
is_notnil_form = 1;
|
||||
ifjmp = JOP_JUMP_IF_NOT_NIL;
|
||||
ifnjmp = JOP_JUMP_IF_NIL;
|
||||
|
@ -622,7 +822,9 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||
/* Check for constant condition */
|
||||
if (cond.flags & JANET_SLOT_CONSTANT) {
|
||||
/* Loop never executes */
|
||||
int never_executes = is_notnil_form
|
||||
int never_executes = is_nil_form
|
||||
? !janet_checktype(cond.constant, JANET_NIL)
|
||||
: is_notnil_form
|
||||
? janet_checktype(cond.constant, JANET_NIL)
|
||||
: !janet_truthy(cond.constant);
|
||||
if (never_executes) {
|
||||
|
@ -713,7 +915,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
JanetSlot ret;
|
||||
Janet head;
|
||||
JanetScope fnscope;
|
||||
int32_t paramcount, argi, parami, arity, min_arity, max_arity, defindex, i;
|
||||
int32_t paramcount, argi, parami, arity, min_arity = 0, max_arity, defindex, i;
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
const Janet *params;
|
||||
const char *errmsg = NULL;
|
||||
|
@ -723,8 +925,10 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
int structarg = 0;
|
||||
int allow_extra = 0;
|
||||
int selfref = 0;
|
||||
int hasname = 0;
|
||||
int seenamp = 0;
|
||||
int seenopt = 0;
|
||||
int namedargs = 0;
|
||||
|
||||
/* Begin function */
|
||||
c->scope->flags |= JANET_SCOPE_CLOSURE;
|
||||
|
@ -740,6 +944,10 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
head = argv[0];
|
||||
if (janet_checktype(head, JANET_SYMBOL)) {
|
||||
selfref = 1;
|
||||
hasname = 1;
|
||||
parami = 1;
|
||||
} else if (janet_checktype(head, JANET_KEYWORD)) {
|
||||
hasname = 1;
|
||||
parami = 1;
|
||||
}
|
||||
if (parami >= argn || !janet_checktype(argv[parami], JANET_TUPLE)) {
|
||||
|
@ -749,6 +957,9 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
|
||||
/* Keep track of destructured parameters */
|
||||
JanetSlot *destructed_params = NULL;
|
||||
JanetSlot *named_params = NULL;
|
||||
JanetTable *named_table = NULL;
|
||||
JanetSlot named_slot;
|
||||
|
||||
/* Compile function parameters */
|
||||
params = janet_unwrap_tuple(argv[parami]);
|
||||
|
@ -756,49 +967,75 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
arity = paramcount;
|
||||
for (i = 0; i < paramcount; i++) {
|
||||
Janet param = params[i];
|
||||
if (janet_checktype(param, JANET_SYMBOL)) {
|
||||
if (namedargs) {
|
||||
arity--;
|
||||
if (!janet_checktype(param, JANET_SYMBOL)) {
|
||||
errmsg = "only named arguments can follow &named";
|
||||
goto error;
|
||||
}
|
||||
Janet key = janet_wrap_keyword(janet_unwrap_symbol(param));
|
||||
janet_table_put(named_table, key, param);
|
||||
janet_v_push(named_params, janetc_farslot(c));
|
||||
} else if (janet_checktype(param, JANET_SYMBOL)) {
|
||||
/* Check for varargs and unfixed arity */
|
||||
if (!janet_cstrcmp(janet_unwrap_symbol(param), "&")) {
|
||||
if (seenamp) {
|
||||
errmsg = "& in unexpected location";
|
||||
goto error;
|
||||
} else if (i == paramcount - 1) {
|
||||
allow_extra = 1;
|
||||
const uint8_t *sym = janet_unwrap_symbol(param);
|
||||
if (sym[0] == '&') {
|
||||
if (!janet_cstrcmp(sym, "&")) {
|
||||
if (seenamp) {
|
||||
errmsg = "& in unexpected location";
|
||||
goto error;
|
||||
} else if (i == paramcount - 1) {
|
||||
allow_extra = 1;
|
||||
arity--;
|
||||
} else if (i == paramcount - 2) {
|
||||
vararg = 1;
|
||||
arity -= 2;
|
||||
} else {
|
||||
errmsg = "& in unexpected location";
|
||||
goto error;
|
||||
}
|
||||
seenamp = 1;
|
||||
} else if (!janet_cstrcmp(sym, "&opt")) {
|
||||
if (seenopt) {
|
||||
errmsg = "only one &opt allowed";
|
||||
goto error;
|
||||
} else if (i == paramcount - 1) {
|
||||
errmsg = "&opt cannot be last item in parameter list";
|
||||
goto error;
|
||||
}
|
||||
min_arity = i;
|
||||
arity--;
|
||||
} else if (i == paramcount - 2) {
|
||||
vararg = 1;
|
||||
arity -= 2;
|
||||
} else {
|
||||
errmsg = "& in unexpected location";
|
||||
goto error;
|
||||
}
|
||||
seenamp = 1;
|
||||
} else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&opt")) {
|
||||
if (seenopt) {
|
||||
errmsg = "only one &opt allowed";
|
||||
goto error;
|
||||
} else if (i == paramcount - 1) {
|
||||
errmsg = "&opt cannot be last item in parameter list";
|
||||
goto error;
|
||||
}
|
||||
min_arity = i;
|
||||
arity--;
|
||||
seenopt = 1;
|
||||
} else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&keys")) {
|
||||
if (seenamp) {
|
||||
errmsg = "&keys in unexpected location";
|
||||
goto error;
|
||||
} else if (i == paramcount - 2) {
|
||||
seenopt = 1;
|
||||
} else if (!janet_cstrcmp(sym, "&keys")) {
|
||||
if (seenamp) {
|
||||
errmsg = "&keys in unexpected location";
|
||||
goto error;
|
||||
} else if (i == paramcount - 2) {
|
||||
vararg = 1;
|
||||
structarg = 1;
|
||||
arity -= 2;
|
||||
} else {
|
||||
errmsg = "&keys in unexpected location";
|
||||
goto error;
|
||||
}
|
||||
seenamp = 1;
|
||||
} else if (!janet_cstrcmp(sym, "&named")) {
|
||||
if (seenamp) {
|
||||
errmsg = "&named in unexpected location";
|
||||
goto error;
|
||||
}
|
||||
vararg = 1;
|
||||
structarg = 1;
|
||||
arity -= 2;
|
||||
arity--;
|
||||
seenamp = 1;
|
||||
namedargs = 1;
|
||||
named_table = janet_table(10);
|
||||
named_slot = janetc_farslot(c);
|
||||
} else {
|
||||
errmsg = "&keys in unexpected location";
|
||||
goto error;
|
||||
janetc_nameslot(c, sym, janetc_farslot(c));
|
||||
}
|
||||
seenamp = 1;
|
||||
} else {
|
||||
janetc_nameslot(c, janet_unwrap_symbol(param), janetc_farslot(c));
|
||||
janetc_nameslot(c, sym, janetc_farslot(c));
|
||||
}
|
||||
} else {
|
||||
janet_v_push(destructed_params, janetc_farslot(c));
|
||||
|
@ -810,6 +1047,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
for (i = 0; i < paramcount; i++) {
|
||||
Janet param = params[i];
|
||||
if (!janet_checktype(param, JANET_SYMBOL)) {
|
||||
janet_assert(janet_v_count(destructed_params) > j, "out of bounds");
|
||||
JanetSlot reg = destructed_params[j++];
|
||||
destructure(c, param, reg, defleaf, NULL);
|
||||
janetc_freeslot(c, reg);
|
||||
|
@ -817,15 +1055,37 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
}
|
||||
janet_v_free(destructed_params);
|
||||
|
||||
/* Compile named arguments */
|
||||
if (namedargs) {
|
||||
Janet param = janet_wrap_table(named_table);
|
||||
destructure(c, param, named_slot, defleaf, NULL);
|
||||
janetc_freeslot(c, named_slot);
|
||||
janet_v_free(named_params);
|
||||
}
|
||||
|
||||
max_arity = (vararg || allow_extra) ? INT32_MAX : arity;
|
||||
if (!seenopt) min_arity = arity;
|
||||
|
||||
/* Check for self ref */
|
||||
/* Check for self ref (also avoid if arguments shadow own name) */
|
||||
if (selfref) {
|
||||
JanetSlot slot = janetc_farslot(c);
|
||||
slot.flags = JANET_SLOT_NAMED | JANET_FUNCTION;
|
||||
janetc_emit_s(c, JOP_LOAD_SELF, slot, 1);
|
||||
janetc_nameslot(c, janet_unwrap_symbol(head), slot);
|
||||
/* Check if the parameters shadow the function name. If so, don't
|
||||
* emit JOP_LOAD_SELF and add a binding since that most users
|
||||
* seem to expect that function parameters take precedence over the
|
||||
* function name */
|
||||
const uint8_t *sym = janet_unwrap_symbol(head);
|
||||
int32_t len = janet_v_count(c->scope->syms);
|
||||
int found = 0;
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
if (c->scope->syms[i].sym == sym) {
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
JanetSlot slot = janetc_farslot(c);
|
||||
slot.flags = JANET_SLOT_NAMED | JANET_FUNCTION;
|
||||
janetc_emit_s(c, JOP_LOAD_SELF, slot, 1);
|
||||
janetc_nameslot(c, sym, slot);
|
||||
}
|
||||
}
|
||||
|
||||
/* Compile function body */
|
||||
|
@ -848,7 +1108,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
|||
if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
|
||||
if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
|
||||
if (selfref) def->name = janet_unwrap_symbol(head);
|
||||
if (hasname) def->name = janet_unwrap_symbol(head); /* Also correctly unwraps keyword */
|
||||
janet_def_addflags(def);
|
||||
defindex = janetc_addfuncdef(c, def);
|
||||
|
||||
|
@ -892,4 +1152,3 @@ const JanetSpecial *janetc_special(const uint8_t *name) {
|
|||
sizeof(JanetSpecial),
|
||||
name);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -24,6 +24,11 @@
|
|||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
JANET_THREAD_LOCAL JanetVM janet_vm;
|
||||
|
@ -57,5 +62,10 @@ void janet_vm_load(JanetVM *from) {
|
|||
* use NULL to interrupt the current VM when convenient */
|
||||
void janet_interpreter_interrupt(JanetVM *vm) {
|
||||
vm = vm ? vm : &janet_vm;
|
||||
vm->auto_suspend = 1;
|
||||
janet_atomic_inc(&vm->auto_suspend);
|
||||
}
|
||||
|
||||
void janet_interpreter_interrupt_handled(JanetVM *vm) {
|
||||
vm = vm ? vm : &janet_vm;
|
||||
janet_atomic_dec(&vm->auto_suspend);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -23,8 +23,15 @@
|
|||
#ifndef JANET_STATE_H_defined
|
||||
#define JANET_STATE_H_defined
|
||||
|
||||
#include <janet.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef JANET_EV
|
||||
#ifndef JANET_WINDOWS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef int64_t JanetTimestamp;
|
||||
|
||||
typedef struct JanetScratch {
|
||||
|
@ -54,7 +61,7 @@ typedef struct {
|
|||
int is_error;
|
||||
} JanetTimeout;
|
||||
|
||||
/* Registry table for C functions - containts metadata that can
|
||||
/* Registry table for C functions - contains metadata that can
|
||||
* be looked up by cfunction pointer. All strings here are pointing to
|
||||
* static memory not managed by Janet. */
|
||||
typedef struct {
|
||||
|
@ -82,10 +89,10 @@ struct JanetVM {
|
|||
|
||||
/* If this flag is true, suspend on function calls and backwards jumps.
|
||||
* When this occurs, this flag will be reset to 0. */
|
||||
int auto_suspend;
|
||||
volatile JanetAtomicInt auto_suspend;
|
||||
|
||||
/* The current running fiber on the current thread.
|
||||
* Set and unset by janet_run. */
|
||||
* Set and unset by functions in vm.c */
|
||||
JanetFiber *fiber;
|
||||
JanetFiber *root_fiber;
|
||||
|
||||
|
@ -101,7 +108,7 @@ struct JanetVM {
|
|||
size_t registry_count;
|
||||
int registry_dirty;
|
||||
|
||||
/* Registry for abstract abstract types that can be marshalled.
|
||||
/* Registry for abstract types that can be marshalled.
|
||||
* We need this to look up the constructors when unmarshalling. */
|
||||
JanetTable *abstract_registry;
|
||||
|
||||
|
@ -114,10 +121,12 @@ struct JanetVM {
|
|||
|
||||
/* Garbage collection */
|
||||
void *blocks;
|
||||
void *weak_blocks;
|
||||
size_t gc_interval;
|
||||
size_t next_collection;
|
||||
size_t block_count;
|
||||
int gc_suspend;
|
||||
int gc_mark_phase;
|
||||
|
||||
/* GC roots */
|
||||
Janet *roots;
|
||||
|
@ -129,6 +138,9 @@ struct JanetVM {
|
|||
size_t scratch_cap;
|
||||
size_t scratch_len;
|
||||
|
||||
/* Sandbox flags */
|
||||
uint32_t sandbox_flags;
|
||||
|
||||
/* Random number generator */
|
||||
JanetRNG rng;
|
||||
|
||||
|
@ -137,6 +149,11 @@ struct JanetVM {
|
|||
JanetTraversalNode *traversal_top;
|
||||
JanetTraversalNode *traversal_base;
|
||||
|
||||
/* Thread safe strerror error buffer - for janet_strerror */
|
||||
#ifndef JANET_WINDOWS
|
||||
char strerror_buf[256];
|
||||
#endif
|
||||
|
||||
/* Event loop and scheduler globals */
|
||||
#ifdef JANET_EV
|
||||
size_t tq_count;
|
||||
|
@ -144,24 +161,29 @@ struct JanetVM {
|
|||
JanetQueue spawn;
|
||||
JanetTimeout *tq;
|
||||
JanetRNG ev_rng;
|
||||
JanetListenerState **listeners;
|
||||
size_t listener_count;
|
||||
size_t listener_cap;
|
||||
size_t extra_listeners;
|
||||
volatile JanetAtomicInt listener_count; /* used in signal handler, must be volatile */
|
||||
JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */
|
||||
JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */
|
||||
JanetTable signal_handlers;
|
||||
#ifdef JANET_WINDOWS
|
||||
void **iocp;
|
||||
#elif defined(JANET_EV_EPOLL)
|
||||
pthread_attr_t new_thread_attr;
|
||||
JanetHandle selfpipe[2];
|
||||
int epoll;
|
||||
int timerfd;
|
||||
int timer_enabled;
|
||||
#elif defined(JANET_EV_KQUEUE)
|
||||
pthread_attr_t new_thread_attr;
|
||||
JanetHandle selfpipe[2];
|
||||
int kq;
|
||||
int timer;
|
||||
int timer_enabled;
|
||||
#else
|
||||
JanetStream **streams;
|
||||
size_t stream_count;
|
||||
size_t stream_capacity;
|
||||
pthread_attr_t new_thread_attr;
|
||||
JanetHandle selfpipe[2];
|
||||
struct pollfd *fds;
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -173,10 +173,11 @@ static int32_t kmp_next(struct kmp_state *state) {
|
|||
JANET_CORE_FN(cfun_string_slice,
|
||||
"(string/slice bytes &opt start end)",
|
||||
"Returns a substring from a byte sequence. The substring is from "
|
||||
"index start inclusive to index end exclusive. All indexing "
|
||||
"is from 0. 'start' and 'end' can also be negative to indicate indexing "
|
||||
"from the end of the string. Note that index -1 is synonymous with "
|
||||
"index (length bytes) to allow a full negative slice range. ") {
|
||||
"index `start` inclusive to index `end`, exclusive. All indexing "
|
||||
"is from 0. `start` and `end` can also be negative to indicate indexing "
|
||||
"from the end of the string. Note that if `start` is negative it is "
|
||||
"exclusive, and if `end` is negative it is inclusive, to allow a full "
|
||||
"negative slice range.") {
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
return janet_stringv(view.bytes + range.start, range.end - range.start);
|
||||
|
@ -184,7 +185,7 @@ JANET_CORE_FN(cfun_string_slice,
|
|||
|
||||
JANET_CORE_FN(cfun_symbol_slice,
|
||||
"(symbol/slice bytes &opt start end)",
|
||||
"Same a string/slice, but returns a symbol.") {
|
||||
"Same as string/slice, but returns a symbol.") {
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
return janet_symbolv(view.bytes + range.start, range.end - range.start);
|
||||
|
@ -192,7 +193,7 @@ JANET_CORE_FN(cfun_symbol_slice,
|
|||
|
||||
JANET_CORE_FN(cfun_keyword_slice,
|
||||
"(keyword/slice bytes &opt start end)",
|
||||
"Same a string/slice, but returns a keyword.") {
|
||||
"Same as string/slice, but returns a keyword.") {
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
return janet_keywordv(view.bytes + range.start, range.end - range.start);
|
||||
|
@ -200,7 +201,7 @@ JANET_CORE_FN(cfun_keyword_slice,
|
|||
|
||||
JANET_CORE_FN(cfun_string_repeat,
|
||||
"(string/repeat bytes n)",
|
||||
"Returns a string that is n copies of bytes concatenated.") {
|
||||
"Returns a string that is `n` copies of `bytes` concatenated.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
int32_t rep = janet_getinteger(argv, 1);
|
||||
|
@ -282,7 +283,7 @@ JANET_CORE_FN(cfun_string_asciiupper,
|
|||
|
||||
JANET_CORE_FN(cfun_string_reverse,
|
||||
"(string/reverse str)",
|
||||
"Returns a string that is the reversed version of str.") {
|
||||
"Returns a string that is the reversed version of `str`.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
uint8_t *buf = janet_string_begin(view.len);
|
||||
|
@ -308,8 +309,8 @@ static void findsetup(int32_t argc, Janet *argv, struct kmp_state *s, int32_t ex
|
|||
|
||||
JANET_CORE_FN(cfun_string_find,
|
||||
"(string/find patt str &opt start-index)",
|
||||
"Searches for the first instance of pattern patt in string "
|
||||
"str. Returns the index of the first character in patt if found, "
|
||||
"Searches for the first instance of pattern `patt` in string "
|
||||
"`str`. Returns the index of the first character in `patt` if found, "
|
||||
"otherwise returns nil.") {
|
||||
int32_t result;
|
||||
struct kmp_state state;
|
||||
|
@ -323,7 +324,7 @@ JANET_CORE_FN(cfun_string_find,
|
|||
|
||||
JANET_CORE_FN(cfun_string_hasprefix,
|
||||
"(string/has-prefix? pfx str)",
|
||||
"Tests whether str starts with pfx.") {
|
||||
"Tests whether `str` starts with `pfx`.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetByteView prefix = janet_getbytes(argv, 0);
|
||||
JanetByteView str = janet_getbytes(argv, 1);
|
||||
|
@ -334,7 +335,7 @@ JANET_CORE_FN(cfun_string_hasprefix,
|
|||
|
||||
JANET_CORE_FN(cfun_string_hassuffix,
|
||||
"(string/has-suffix? sfx str)",
|
||||
"Tests whether str ends with sfx.") {
|
||||
"Tests whether `str` ends with `sfx`.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetByteView suffix = janet_getbytes(argv, 0);
|
||||
JanetByteView str = janet_getbytes(argv, 1);
|
||||
|
@ -347,9 +348,9 @@ JANET_CORE_FN(cfun_string_hassuffix,
|
|||
|
||||
JANET_CORE_FN(cfun_string_findall,
|
||||
"(string/find-all patt str &opt start-index)",
|
||||
"Searches for all instances of pattern patt in string "
|
||||
"str. Returns an array of all indices of found patterns. Overlapping "
|
||||
"instances of the pattern are counted individually, meaning a byte in str "
|
||||
"Searches for all instances of pattern `patt` in string "
|
||||
"`str`. Returns an array of all indices of found patterns. Overlapping "
|
||||
"instances of the pattern are counted individually, meaning a byte in `str` "
|
||||
"may contribute to multiple found patterns.") {
|
||||
int32_t result;
|
||||
struct kmp_state state;
|
||||
|
@ -364,14 +365,13 @@ JANET_CORE_FN(cfun_string_findall,
|
|||
|
||||
struct replace_state {
|
||||
struct kmp_state kmp;
|
||||
const uint8_t *subst;
|
||||
int32_t substlen;
|
||||
Janet subst;
|
||||
};
|
||||
|
||||
static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
|
||||
janet_arity(argc, 3, 4);
|
||||
JanetByteView pat = janet_getbytes(argv, 0);
|
||||
JanetByteView subst = janet_getbytes(argv, 1);
|
||||
Janet subst = argv[1];
|
||||
JanetByteView text = janet_getbytes(argv, 2);
|
||||
int32_t start = 0;
|
||||
if (argc == 4) {
|
||||
|
@ -380,14 +380,15 @@ static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
|
|||
}
|
||||
kmp_init(&s->kmp, text.bytes, text.len, pat.bytes, pat.len);
|
||||
s->kmp.i = start;
|
||||
s->subst = subst.bytes;
|
||||
s->substlen = subst.len;
|
||||
s->subst = subst;
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_string_replace,
|
||||
"(string/replace patt subst str)",
|
||||
"Replace the first occurrence of patt with subst in the string str. "
|
||||
"Will return the new string if patt is found, otherwise returns str.") {
|
||||
"Replace the first occurrence of `patt` with `subst` in the string `str`. "
|
||||
"If `subst` is a function, it will be called with `patt` only if a match is found, "
|
||||
"and should return the actual replacement text to use. "
|
||||
"Will return the new string if `patt` is found, otherwise returns `str`.") {
|
||||
int32_t result;
|
||||
struct replace_state s;
|
||||
uint8_t *buf;
|
||||
|
@ -397,10 +398,11 @@ JANET_CORE_FN(cfun_string_replace,
|
|||
kmp_deinit(&s.kmp);
|
||||
return janet_stringv(s.kmp.text, s.kmp.textlen);
|
||||
}
|
||||
buf = janet_string_begin(s.kmp.textlen - s.kmp.patlen + s.substlen);
|
||||
JanetByteView subst = janet_text_substitution(&s.subst, s.kmp.text + result, s.kmp.patlen, NULL);
|
||||
buf = janet_string_begin(s.kmp.textlen - s.kmp.patlen + subst.len);
|
||||
safe_memcpy(buf, s.kmp.text, result);
|
||||
safe_memcpy(buf + result, s.subst, s.substlen);
|
||||
safe_memcpy(buf + result + s.substlen,
|
||||
safe_memcpy(buf + result, subst.bytes, subst.len);
|
||||
safe_memcpy(buf + result + subst.len,
|
||||
s.kmp.text + result + s.kmp.patlen,
|
||||
s.kmp.textlen - result - s.kmp.patlen);
|
||||
kmp_deinit(&s.kmp);
|
||||
|
@ -409,9 +411,11 @@ JANET_CORE_FN(cfun_string_replace,
|
|||
|
||||
JANET_CORE_FN(cfun_string_replaceall,
|
||||
"(string/replace-all patt subst str)",
|
||||
"Replace all instances of patt with subst in the string str. Overlapping "
|
||||
"Replace all instances of `patt` with `subst` in the string `str`. Overlapping "
|
||||
"matches will not be counted, only the first match in such a span will be replaced. "
|
||||
"Will return the new string if patt is found, otherwise returns str.") {
|
||||
"If `subst` is a function, it will be called with `patt` once for each match, "
|
||||
"and should return the actual replacement text to use. "
|
||||
"Will return the new string if `patt` is found, otherwise returns `str`.") {
|
||||
int32_t result;
|
||||
struct replace_state s;
|
||||
JanetBuffer b;
|
||||
|
@ -419,8 +423,9 @@ JANET_CORE_FN(cfun_string_replaceall,
|
|||
replacesetup(argc, argv, &s);
|
||||
janet_buffer_init(&b, s.kmp.textlen);
|
||||
while ((result = kmp_next(&s.kmp)) >= 0) {
|
||||
JanetByteView subst = janet_text_substitution(&s.subst, s.kmp.text + result, s.kmp.patlen, NULL);
|
||||
janet_buffer_push_bytes(&b, s.kmp.text + lastindex, result - lastindex);
|
||||
janet_buffer_push_bytes(&b, s.subst, s.substlen);
|
||||
janet_buffer_push_bytes(&b, subst.bytes, subst.len);
|
||||
lastindex = result + s.kmp.patlen;
|
||||
kmp_seti(&s.kmp, lastindex);
|
||||
}
|
||||
|
@ -433,11 +438,11 @@ JANET_CORE_FN(cfun_string_replaceall,
|
|||
|
||||
JANET_CORE_FN(cfun_string_split,
|
||||
"(string/split delim str &opt start limit)",
|
||||
"Splits a string str with delimiter delim and returns an array of "
|
||||
"substrings. The substrings will not contain the delimiter delim. If delim "
|
||||
"Splits a string `str` with delimiter `delim` and returns an array of "
|
||||
"substrings. The substrings will not contain the delimiter `delim`. If `delim` "
|
||||
"is not found, the returned array will have one element. Will start searching "
|
||||
"for delim at the index start (if provided), and return up to a maximum "
|
||||
"of limit results (if provided).") {
|
||||
"for `delim` at the index `start` (if provided), and return up to a maximum "
|
||||
"of `limit` results (if provided).") {
|
||||
int32_t result;
|
||||
JanetArray *array;
|
||||
struct kmp_state state;
|
||||
|
@ -461,9 +466,9 @@ JANET_CORE_FN(cfun_string_split,
|
|||
|
||||
JANET_CORE_FN(cfun_string_checkset,
|
||||
"(string/check-set set str)",
|
||||
"Checks that the string str only contains bytes that appear in the string set. "
|
||||
"Returns true if all bytes in str appear in set, false if some bytes in str do "
|
||||
"not appear in set.") {
|
||||
"Checks that the string `str` only contains bytes that appear in the string `set`. "
|
||||
"Returns true if all bytes in `str` appear in `set`, false if some bytes in `str` do "
|
||||
"not appear in `set`.") {
|
||||
uint32_t bitset[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
janet_fixarity(argc, 2);
|
||||
JanetByteView set = janet_getbytes(argv, 0);
|
||||
|
@ -488,7 +493,7 @@ JANET_CORE_FN(cfun_string_checkset,
|
|||
JANET_CORE_FN(cfun_string_join,
|
||||
"(string/join parts &opt sep)",
|
||||
"Joins an array of strings into one string, optionally separated by "
|
||||
"a separator string sep.") {
|
||||
"a separator string `sep`.") {
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetView parts = janet_getindexed(argv, 0);
|
||||
JanetByteView joiner;
|
||||
|
@ -530,8 +535,31 @@ JANET_CORE_FN(cfun_string_join,
|
|||
|
||||
JANET_CORE_FN(cfun_string_format,
|
||||
"(string/format format & values)",
|
||||
"Similar to snprintf, but specialized for operating with Janet values. Returns "
|
||||
"a new string.") {
|
||||
"Similar to C's `snprintf`, but specialized for operating with Janet values. Returns "
|
||||
"a new string.\n\n"
|
||||
"The following conversion specifiers are supported, where the upper case specifiers generate "
|
||||
"upper case output:\n"
|
||||
"- `c`: ASCII character.\n"
|
||||
"- `d`, `i`: integer, formatted as a decimal number.\n"
|
||||
"- `x`, `X`: integer, formatted as a hexadecimal number.\n"
|
||||
"- `o`: integer, formatted as an octal number.\n"
|
||||
"- `f`, `F`: floating point number, formatted as a decimal number.\n"
|
||||
"- `e`, `E`: floating point number, formatted in scientific notation.\n"
|
||||
"- `g`, `G`: floating point number, formatted in its shortest form.\n"
|
||||
"- `a`, `A`: floating point number, formatted as a hexadecimal number.\n"
|
||||
"- `s`: formatted as a string, precision indicates padding and maximum length.\n"
|
||||
"- `t`: emit the type of the given value.\n"
|
||||
"- `v`: format with (describe x)\n"
|
||||
"- `V`: format with (string x)\n"
|
||||
"- `j`: format to jdn (Janet data notation).\n"
|
||||
"\n"
|
||||
"The following conversion specifiers are used for \"pretty-printing\", where the upper-case "
|
||||
"variants generate colored output. These specifiers can take a precision "
|
||||
"argument to specify the maximum nesting depth to print.\n"
|
||||
"- `p`, `P`: pretty format, truncating if necessary\n"
|
||||
"- `m`, `M`: pretty format without truncating.\n"
|
||||
"- `q`, `Q`: pretty format on one line, truncating if necessary.\n"
|
||||
"- `n`, `N`: pretty format on one line without truncation.\n") {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetBuffer *buffer = janet_buffer(0);
|
||||
const char *strfrmt = (const char *) janet_getstring(argv, 0);
|
||||
|
@ -574,7 +602,7 @@ static void trim_help_args(int32_t argc, Janet *argv, JanetByteView *str, JanetB
|
|||
JANET_CORE_FN(cfun_string_trim,
|
||||
"(string/trim str &opt set)",
|
||||
"Trim leading and trailing whitespace from a byte sequence. If the argument "
|
||||
"set is provided, consider only characters in set to be whitespace.") {
|
||||
"`set` is provided, consider only characters in `set` to be whitespace.") {
|
||||
JanetByteView str, set;
|
||||
trim_help_args(argc, argv, &str, &set);
|
||||
int32_t left_edge = trim_help_leftedge(str, set);
|
||||
|
@ -587,7 +615,7 @@ JANET_CORE_FN(cfun_string_trim,
|
|||
JANET_CORE_FN(cfun_string_triml,
|
||||
"(string/triml str &opt set)",
|
||||
"Trim leading whitespace from a byte sequence. If the argument "
|
||||
"set is provided, consider only characters in set to be whitespace.") {
|
||||
"`set` is provided, consider only characters in `set` to be whitespace.") {
|
||||
JanetByteView str, set;
|
||||
trim_help_args(argc, argv, &str, &set);
|
||||
int32_t left_edge = trim_help_leftedge(str, set);
|
||||
|
@ -597,7 +625,7 @@ JANET_CORE_FN(cfun_string_triml,
|
|||
JANET_CORE_FN(cfun_string_trimr,
|
||||
"(string/trimr str &opt set)",
|
||||
"Trim trailing whitespace from a byte sequence. If the argument "
|
||||
"set is provided, consider only characters in set to be whitespace.") {
|
||||
"`set` is provided, consider only characters in `set` to be whitespace.") {
|
||||
JanetByteView str, set;
|
||||
trim_help_args(argc, argv, &str, &set);
|
||||
int32_t right_edge = trim_help_rightedge(str, set);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -490,3 +490,18 @@ int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) {
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
void janet_buffer_dtostr(JanetBuffer *buffer, double x) {
|
||||
#define BUFSIZE 32
|
||||
janet_buffer_extra(buffer, BUFSIZE);
|
||||
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%.17g", x);
|
||||
#undef BUFSIZE
|
||||
/* fix locale issues with commas */
|
||||
for (int i = 0; i < count; i++) {
|
||||
char c = buffer->data[buffer->count + i];
|
||||
if (c == ',') {
|
||||
buffer->data[buffer->count + i] = '.';
|
||||
}
|
||||
}
|
||||
buffer->count += count;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -39,13 +39,14 @@ JanetKV *janet_struct_begin(int32_t count) {
|
|||
head->length = count;
|
||||
head->capacity = capacity;
|
||||
head->hash = 0;
|
||||
head->proto = NULL;
|
||||
|
||||
JanetKV *st = (JanetKV *)(head->data);
|
||||
janet_memempty(st, capacity);
|
||||
return st;
|
||||
}
|
||||
|
||||
/* Find an item in a struct. Should be similar to janet_dict_find, but
|
||||
/* Find an item in a struct without looking for prototypes. Should be similar to janet_dict_find, but
|
||||
* specialized to structs (slightly more compact). */
|
||||
const JanetKV *janet_struct_find(const JanetKV *st, Janet key) {
|
||||
int32_t cap = janet_struct_capacity(st);
|
||||
|
@ -68,7 +69,7 @@ const JanetKV *janet_struct_find(const JanetKV *st, Janet key) {
|
|||
* preforms an in-place insertion sort. This ensures the internal structure of the
|
||||
* hash map is independent of insertion order.
|
||||
*/
|
||||
void janet_struct_put(JanetKV *st, Janet key, Janet value) {
|
||||
void janet_struct_put_ext(JanetKV *st, Janet key, Janet value, int replace) {
|
||||
int32_t cap = janet_struct_capacity(st);
|
||||
int32_t hash = janet_hash(key);
|
||||
int32_t index = janet_maphash(cap, hash);
|
||||
|
@ -123,13 +124,19 @@ void janet_struct_put(JanetKV *st, Janet key, Janet value) {
|
|||
dist = otherdist;
|
||||
hash = otherhash;
|
||||
} else if (status == 0) {
|
||||
/* A key was added to the struct more than once - replace old value */
|
||||
kv->value = value;
|
||||
if (replace) {
|
||||
/* A key was added to the struct more than once - replace old value */
|
||||
kv->value = value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void janet_struct_put(JanetKV *st, Janet key, Janet value) {
|
||||
janet_struct_put_ext(st, key, value, 1);
|
||||
}
|
||||
|
||||
/* Finish building a struct */
|
||||
const JanetKV *janet_struct_end(JanetKV *st) {
|
||||
if (janet_struct_hash(st) != janet_struct_length(st)) {
|
||||
|
@ -143,16 +150,43 @@ const JanetKV *janet_struct_end(JanetKV *st) {
|
|||
janet_struct_put(newst, kv->key, kv->value);
|
||||
}
|
||||
}
|
||||
janet_struct_proto(newst) = janet_struct_proto(st);
|
||||
st = newst;
|
||||
}
|
||||
janet_struct_hash(st) = janet_kv_calchash(st, janet_struct_capacity(st));
|
||||
if (janet_struct_proto(st)) {
|
||||
janet_struct_hash(st) += 2654435761u * janet_struct_hash(janet_struct_proto(st));
|
||||
}
|
||||
return (const JanetKV *)st;
|
||||
}
|
||||
|
||||
/* Get an item from a struct without looking into prototypes. */
|
||||
Janet janet_struct_rawget(const JanetKV *st, Janet key) {
|
||||
const JanetKV *kv = janet_struct_find(st, key);
|
||||
return kv ? kv->value : janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Get an item from a struct */
|
||||
Janet janet_struct_get(const JanetKV *st, Janet key) {
|
||||
const JanetKV *kv = janet_struct_find(st, key);
|
||||
return kv ? kv->value : janet_wrap_nil();
|
||||
for (int i = JANET_MAX_PROTO_DEPTH; st && i; --i, st = janet_struct_proto(st)) {
|
||||
const JanetKV *kv = janet_struct_find(st, key);
|
||||
if (NULL != kv && !janet_checktype(kv->key, JANET_NIL)) {
|
||||
return kv->value;
|
||||
}
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Get an item from a struct, and record which prototype the item came from. */
|
||||
Janet janet_struct_get_ex(const JanetKV *st, Janet key, JanetStruct *which) {
|
||||
for (int i = JANET_MAX_PROTO_DEPTH; st && i; --i, st = janet_struct_proto(st)) {
|
||||
const JanetKV *kv = janet_struct_find(st, key);
|
||||
if (NULL != kv && !janet_checktype(kv->key, JANET_NIL)) {
|
||||
*which = st;
|
||||
return kv->value;
|
||||
}
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Convert struct to table */
|
||||
|
@ -167,3 +201,107 @@ JanetTable *janet_struct_to_table(const JanetKV *st) {
|
|||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
/* C Functions */
|
||||
|
||||
JANET_CORE_FN(cfun_struct_with_proto,
|
||||
"(struct/with-proto proto & kvs)",
|
||||
"Create a structure, as with the usual struct constructor but set the "
|
||||
"struct prototype as well.") {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetStruct proto = janet_optstruct(argv, argc, 0, NULL);
|
||||
if (!(argc & 1))
|
||||
janet_panic("expected odd number of arguments");
|
||||
JanetKV *st = janet_struct_begin(argc / 2);
|
||||
for (int32_t i = 1; i < argc; i += 2) {
|
||||
janet_struct_put(st, argv[i], argv[i + 1]);
|
||||
}
|
||||
janet_struct_proto(st) = proto;
|
||||
return janet_wrap_struct(janet_struct_end(st));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_struct_getproto,
|
||||
"(struct/getproto st)",
|
||||
"Return the prototype of a struct, or nil if it doesn't have one.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetStruct st = janet_getstruct(argv, 0);
|
||||
return janet_struct_proto(st)
|
||||
? janet_wrap_struct(janet_struct_proto(st))
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_struct_flatten,
|
||||
"(struct/proto-flatten st)",
|
||||
"Convert a struct with prototypes to a struct with no prototypes by merging "
|
||||
"all key value pairs from recursive prototypes into one new struct.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetStruct st = janet_getstruct(argv, 0);
|
||||
|
||||
/* get an upper bounds on the number of items in the final struct */
|
||||
int64_t pair_count = 0;
|
||||
JanetStruct cursor = st;
|
||||
while (cursor) {
|
||||
pair_count += janet_struct_length(cursor);
|
||||
cursor = janet_struct_proto(cursor);
|
||||
}
|
||||
|
||||
if (pair_count > INT32_MAX) {
|
||||
janet_panic("struct too large");
|
||||
}
|
||||
|
||||
JanetKV *accum = janet_struct_begin((int32_t) pair_count);
|
||||
cursor = st;
|
||||
while (cursor) {
|
||||
for (int32_t i = 0; i < janet_struct_capacity(cursor); i++) {
|
||||
const JanetKV *kv = cursor + i;
|
||||
if (!janet_checktype(kv->key, JANET_NIL)) {
|
||||
janet_struct_put_ext(accum, kv->key, kv->value, 0);
|
||||
}
|
||||
}
|
||||
cursor = janet_struct_proto(cursor);
|
||||
}
|
||||
return janet_wrap_struct(janet_struct_end(accum));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_struct_to_table,
|
||||
"(struct/to-table st &opt recursive)",
|
||||
"Convert a struct to a table. If recursive is true, also convert the "
|
||||
"table's prototypes into the new struct's prototypes as well.") {
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetStruct st = janet_getstruct(argv, 0);
|
||||
int recursive = argc > 1 && janet_truthy(argv[1]);
|
||||
JanetTable *tab = NULL;
|
||||
JanetStruct cursor = st;
|
||||
JanetTable *tab_cursor = tab;
|
||||
do {
|
||||
if (tab) {
|
||||
tab_cursor->proto = janet_table(janet_struct_length(cursor));
|
||||
tab_cursor = tab_cursor->proto;
|
||||
} else {
|
||||
tab = janet_table(janet_struct_length(cursor));
|
||||
tab_cursor = tab;
|
||||
}
|
||||
/* TODO - implement as memcpy since struct memory should be compatible
|
||||
* with table memory */
|
||||
for (int32_t i = 0; i < janet_struct_capacity(cursor); i++) {
|
||||
const JanetKV *kv = cursor + i;
|
||||
if (!janet_checktype(kv->key, JANET_NIL)) {
|
||||
janet_table_put(tab_cursor, kv->key, kv->value);
|
||||
}
|
||||
}
|
||||
cursor = janet_struct_proto(cursor);
|
||||
} while (recursive && cursor);
|
||||
return janet_wrap_table(tab);
|
||||
}
|
||||
|
||||
/* Load the struct module */
|
||||
void janet_lib_struct(JanetTable *env) {
|
||||
JanetRegExt struct_cfuns[] = {
|
||||
JANET_CORE_REG("struct/with-proto", cfun_struct_with_proto),
|
||||
JANET_CORE_REG("struct/getproto", cfun_struct_getproto),
|
||||
JANET_CORE_REG("struct/proto-flatten", cfun_struct_flatten),
|
||||
JANET_CORE_REG("struct/to-table", cfun_struct_to_table),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, struct_cfuns);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -108,6 +108,7 @@ static const uint8_t **janet_symcache_findmem(
|
|||
}
|
||||
notfound:
|
||||
*success = 0;
|
||||
janet_assert(firstEmpty != NULL, "symcache failed to get memory");
|
||||
return firstEmpty;
|
||||
}
|
||||
|
||||
|
@ -233,6 +234,7 @@ const uint8_t *janet_symbol_gen(void) {
|
|||
head->hash = hash;
|
||||
sym = (uint8_t *)(head->data);
|
||||
memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter));
|
||||
sym[head->length] = 0;
|
||||
janet_symcache_put((const uint8_t *)sym, bucket);
|
||||
return (const uint8_t *)sym;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
178
src/core/table.c
178
src/core/table.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -87,11 +87,27 @@ void janet_table_deinit(JanetTable *table) {
|
|||
}
|
||||
|
||||
/* Create a new table */
|
||||
|
||||
JanetTable *janet_table(int32_t capacity) {
|
||||
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
|
||||
return janet_table_init_impl(table, capacity, 0);
|
||||
}
|
||||
|
||||
JanetTable *janet_table_weakk(int32_t capacity) {
|
||||
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKK, sizeof(JanetTable));
|
||||
return janet_table_init_impl(table, capacity, 0);
|
||||
}
|
||||
|
||||
JanetTable *janet_table_weakv(int32_t capacity) {
|
||||
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKV, sizeof(JanetTable));
|
||||
return janet_table_init_impl(table, capacity, 0);
|
||||
}
|
||||
|
||||
JanetTable *janet_table_weakkv(int32_t capacity) {
|
||||
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKKV, sizeof(JanetTable));
|
||||
return janet_table_init_impl(table, capacity, 0);
|
||||
}
|
||||
|
||||
/* Find the bucket that contains the given key. Will also return
|
||||
* bucket where key should go if not in the table. */
|
||||
JanetKV *janet_table_find(JanetTable *t, Janet key) {
|
||||
|
@ -111,12 +127,11 @@ static void janet_table_rehash(JanetTable *t, int32_t size) {
|
|||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
int32_t i, oldcapacity;
|
||||
oldcapacity = t->capacity;
|
||||
int32_t oldcapacity = t->capacity;
|
||||
t->data = newdata;
|
||||
t->capacity = size;
|
||||
t->deleted = 0;
|
||||
for (i = 0; i < oldcapacity; i++) {
|
||||
for (int32_t i = 0; i < oldcapacity; i++) {
|
||||
JanetKV *kv = olddata + i;
|
||||
if (!janet_checktype(kv->key, JANET_NIL)) {
|
||||
JanetKV *newkv = janet_table_find(t, kv->key);
|
||||
|
@ -132,37 +147,21 @@ static void janet_table_rehash(JanetTable *t, int32_t size) {
|
|||
|
||||
/* Get a value out of the table */
|
||||
Janet janet_table_get(JanetTable *t, Janet key) {
|
||||
JanetKV *bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
|
||||
return bucket->value;
|
||||
/* Check prototypes */
|
||||
{
|
||||
int i;
|
||||
for (i = JANET_MAX_PROTO_DEPTH, t = t->proto; t && i; t = t->proto, --i) {
|
||||
bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
|
||||
return bucket->value;
|
||||
}
|
||||
for (int i = JANET_MAX_PROTO_DEPTH; t && i; t = t->proto, --i) {
|
||||
JanetKV *bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
|
||||
return bucket->value;
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Get a value out of the table, and record which prototype it was from. */
|
||||
Janet janet_table_get_ex(JanetTable *t, Janet key, JanetTable **which) {
|
||||
JanetKV *bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
|
||||
*which = t;
|
||||
return bucket->value;
|
||||
}
|
||||
/* Check prototypes */
|
||||
{
|
||||
int i;
|
||||
for (i = JANET_MAX_PROTO_DEPTH, t = t->proto; t && i; t = t->proto, --i) {
|
||||
bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
|
||||
*which = t;
|
||||
return bucket->value;
|
||||
}
|
||||
for (int i = JANET_MAX_PROTO_DEPTH; t && i; t = t->proto, --i) {
|
||||
JanetKV *bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
|
||||
*which = t;
|
||||
return bucket->value;
|
||||
}
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
|
@ -217,6 +216,23 @@ void janet_table_put(JanetTable *t, Janet key, Janet value) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Used internally so don't check arguments
|
||||
* Put into a table, but if the key already exists do nothing. */
|
||||
static void janet_table_put_no_overwrite(JanetTable *t, Janet key, Janet value) {
|
||||
JanetKV *bucket = janet_table_find(t, key);
|
||||
if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
|
||||
return;
|
||||
if (NULL == bucket || 2 * (t->count + t->deleted + 1) > t->capacity) {
|
||||
janet_table_rehash(t, janet_tablen(2 * t->count + 2));
|
||||
}
|
||||
bucket = janet_table_find(t, key);
|
||||
if (janet_checktype(bucket->value, JANET_BOOLEAN))
|
||||
--t->deleted;
|
||||
bucket->key = key;
|
||||
bucket->value = value;
|
||||
++t->count;
|
||||
}
|
||||
|
||||
/* Clear a table */
|
||||
void janet_table_clear(JanetTable *t) {
|
||||
int32_t capacity = t->capacity;
|
||||
|
@ -226,19 +242,6 @@ void janet_table_clear(JanetTable *t) {
|
|||
t->deleted = 0;
|
||||
}
|
||||
|
||||
/* Convert table to struct */
|
||||
const JanetKV *janet_table_to_struct(JanetTable *t) {
|
||||
JanetKV *st = janet_struct_begin(t->count);
|
||||
JanetKV *kv = t->data;
|
||||
JanetKV *end = t->data + t->capacity;
|
||||
while (kv < end) {
|
||||
if (!janet_checktype(kv->key, JANET_NIL))
|
||||
janet_struct_put(st, kv->key, kv->value);
|
||||
kv++;
|
||||
}
|
||||
return janet_struct_end(st);
|
||||
}
|
||||
|
||||
/* Clone a table. */
|
||||
JanetTable *janet_table_clone(JanetTable *table) {
|
||||
JanetTable *newTable = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
|
||||
|
@ -275,22 +278,85 @@ void janet_table_merge_struct(JanetTable *table, const JanetKV *other) {
|
|||
janet_table_mergekv(table, other, janet_struct_capacity(other));
|
||||
}
|
||||
|
||||
/* Convert table to struct */
|
||||
const JanetKV *janet_table_to_struct(JanetTable *t) {
|
||||
JanetKV *st = janet_struct_begin(t->count);
|
||||
JanetKV *kv = t->data;
|
||||
JanetKV *end = t->data + t->capacity;
|
||||
while (kv < end) {
|
||||
if (!janet_checktype(kv->key, JANET_NIL))
|
||||
janet_struct_put(st, kv->key, kv->value);
|
||||
kv++;
|
||||
}
|
||||
return janet_struct_end(st);
|
||||
}
|
||||
|
||||
JanetTable *janet_table_proto_flatten(JanetTable *t) {
|
||||
JanetTable *newTable = janet_table(0);
|
||||
while (t) {
|
||||
JanetKV *kv = t->data;
|
||||
JanetKV *end = t->data + t->capacity;
|
||||
while (kv < end) {
|
||||
if (!janet_checktype(kv->key, JANET_NIL))
|
||||
janet_table_put_no_overwrite(newTable, kv->key, kv->value);
|
||||
kv++;
|
||||
}
|
||||
t = t->proto;
|
||||
}
|
||||
return newTable;
|
||||
}
|
||||
|
||||
/* C Functions */
|
||||
|
||||
JANET_CORE_FN(cfun_table_new,
|
||||
"(table/new capacity)",
|
||||
"Creates a new empty table with pre-allocated memory "
|
||||
"for capacity entries. This means that if one knows the number of "
|
||||
"entries going to go in a table on creation, extra memory allocation "
|
||||
"can be avoided. Returns the new table.") {
|
||||
"for `capacity` entries. This means that if one knows the number of "
|
||||
"entries going into a table on creation, extra memory allocation "
|
||||
"can be avoided. "
|
||||
"Returns the new table.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
int32_t cap = janet_getnat(argv, 0);
|
||||
return janet_wrap_table(janet_table(cap));
|
||||
}
|
||||
/*
|
||||
uint32_t flags = janet_getflags(argv, 1, "kv");
|
||||
if (flags == 0) return janet_wrap_table(janet_table(cap));
|
||||
if (flags == 1) return janet_wrap_table(janet_table_weakk(cap));
|
||||
if (flags == 2) return janet_wrap_table(janet_table_weakv(cap));
|
||||
return janet_wrap_table(janet_table_weakkv(cap));
|
||||
*/
|
||||
|
||||
JANET_CORE_FN(cfun_table_weak,
|
||||
"(table/weak capacity)",
|
||||
"Creates a new empty table with weak references to keys and values. Similar to `table/new`. "
|
||||
"Returns the new table.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getnat(argv, 0);
|
||||
return janet_wrap_table(janet_table_weakkv(cap));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_table_weak_keys,
|
||||
"(table/weak-keys capacity)",
|
||||
"Creates a new empty table with weak references to keys and normal references to values. Similar to `table/new`. "
|
||||
"Returns the new table.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getnat(argv, 0);
|
||||
return janet_wrap_table(janet_table_weakk(cap));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_table_weak_values,
|
||||
"(table/weak-values capacity)",
|
||||
"Creates a new empty table with normal references to keys and weak references to values. Similar to `table/new`. "
|
||||
"Returns the new table.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getnat(argv, 0);
|
||||
return janet_wrap_table(janet_table_weakv(cap));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_table_getproto,
|
||||
"(table/getproto tab)",
|
||||
"Get the prototype table of a table. Returns nil if a table "
|
||||
"Get the prototype table of a table. Returns nil if the table "
|
||||
"has no prototype, otherwise returns the prototype.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *t = janet_gettable(argv, 0);
|
||||
|
@ -301,7 +367,7 @@ JANET_CORE_FN(cfun_table_getproto,
|
|||
|
||||
JANET_CORE_FN(cfun_table_setproto,
|
||||
"(table/setproto tab proto)",
|
||||
"Set the prototype of a table. Returns the original table tab.") {
|
||||
"Set the prototype of a table. Returns the original table `tab`.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
JanetTable *proto = NULL;
|
||||
|
@ -323,8 +389,8 @@ JANET_CORE_FN(cfun_table_tostruct,
|
|||
|
||||
JANET_CORE_FN(cfun_table_rawget,
|
||||
"(table/rawget tab key)",
|
||||
"Gets a value from a table without looking at the prototype table. "
|
||||
"If a table tab does not contain t directly, the function will return "
|
||||
"Gets a value from a table `tab` without looking at the prototype table. "
|
||||
"If `tab` does not contain the key directly, the function will return "
|
||||
"nil without checking the prototype. Returns the value in the table.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
|
@ -349,16 +415,28 @@ JANET_CORE_FN(cfun_table_clear,
|
|||
return janet_wrap_table(table);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_table_proto_flatten,
|
||||
"(table/proto-flatten tab)",
|
||||
"Create a new table that is the result of merging all prototypes into a new table.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
return janet_wrap_table(janet_table_proto_flatten(table));
|
||||
}
|
||||
|
||||
/* Load the table module */
|
||||
void janet_lib_table(JanetTable *env) {
|
||||
JanetRegExt table_cfuns[] = {
|
||||
JANET_CORE_REG("table/new", cfun_table_new),
|
||||
JANET_CORE_REG("table/weak", cfun_table_weak),
|
||||
JANET_CORE_REG("table/weak-keys", cfun_table_weak_keys),
|
||||
JANET_CORE_REG("table/weak-values", cfun_table_weak_values),
|
||||
JANET_CORE_REG("table/to-struct", cfun_table_tostruct),
|
||||
JANET_CORE_REG("table/getproto", cfun_table_getproto),
|
||||
JANET_CORE_REG("table/setproto", cfun_table_setproto),
|
||||
JANET_CORE_REG("table/rawget", cfun_table_rawget),
|
||||
JANET_CORE_REG("table/clone", cfun_table_clone),
|
||||
JANET_CORE_REG("table/clear", cfun_table_clear),
|
||||
JANET_CORE_REG("table/proto-flatten", cfun_table_proto_flatten),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, table_cfuns);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -65,13 +65,13 @@ JANET_CORE_FN(cfun_tuple_brackets,
|
|||
|
||||
JANET_CORE_FN(cfun_tuple_slice,
|
||||
"(tuple/slice arrtup [,start=0 [,end=(length arrtup)]])",
|
||||
"Take a sub sequence of an array or tuple from index start "
|
||||
"inclusive to index end exclusive. If start or end are not provided, "
|
||||
"they default to 0 and the length of arrtup respectively. "
|
||||
"'start' and 'end' can also be negative to indicate indexing "
|
||||
"from the end of the input. Note that index -1 is synonymous with "
|
||||
"index '(length arrtup)' to allow a full negative slice range. "
|
||||
"Returns the new tuple.") {
|
||||
"Take a sub-sequence of an array or tuple from index `start` "
|
||||
"inclusive to index `end` exclusive. If `start` or `end` are not provided, "
|
||||
"they default to 0 and the length of `arrtup`, respectively. "
|
||||
"`start` and `end` can also be negative to indicate indexing "
|
||||
"from the end of the input. Note that if `start` is negative it is "
|
||||
"exclusive, and if `end` is negative it is inclusive, to allow a full "
|
||||
"negative slice range. Returns the new tuple.") {
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
return janet_wrap_tuple(janet_tuple_n(view.items + range.start, range.end - range.start));
|
||||
|
@ -96,7 +96,7 @@ JANET_CORE_FN(cfun_tuple_type,
|
|||
JANET_CORE_FN(cfun_tuple_sourcemap,
|
||||
"(tuple/sourcemap tup)",
|
||||
"Returns the sourcemap metadata attached to a tuple, "
|
||||
" which is another tuple (line, column).") {
|
||||
"which is another tuple (line, column).") {
|
||||
janet_fixarity(argc, 1);
|
||||
const Janet *tup = janet_gettuple(argv, 0);
|
||||
Janet contents[2];
|
||||
|
|
360
src/core/util.c
360
src/core/util.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -36,6 +36,19 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#ifdef JANET_DYNAMIC_MODULES
|
||||
#include <psapi.h>
|
||||
#ifdef JANET_MSVC
|
||||
#pragma comment (lib, "Psapi.lib")
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JANET_APPLE
|
||||
#include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* Base 64 lookup table for digits */
|
||||
|
@ -79,8 +92,8 @@ const char *const janet_signal_names[14] = {
|
|||
"user5",
|
||||
"user6",
|
||||
"user7",
|
||||
"user8",
|
||||
"user9"
|
||||
"interrupt",
|
||||
"await"
|
||||
};
|
||||
|
||||
const char *const janet_status_names[16] = {
|
||||
|
@ -96,8 +109,8 @@ const char *const janet_status_names[16] = {
|
|||
"user5",
|
||||
"user6",
|
||||
"user7",
|
||||
"user8",
|
||||
"user9",
|
||||
"interrupted",
|
||||
"suspended",
|
||||
"new",
|
||||
"alive"
|
||||
};
|
||||
|
@ -105,6 +118,7 @@ const char *const janet_status_names[16] = {
|
|||
#ifndef JANET_PRF
|
||||
|
||||
int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
|
||||
if (NULL == str) return 5381;
|
||||
const uint8_t *end = str + len;
|
||||
uint32_t hash = 5381;
|
||||
while (str < end)
|
||||
|
@ -224,13 +238,17 @@ int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
|
|||
|
||||
#endif
|
||||
|
||||
uint32_t janet_hash_mix(uint32_t input, uint32_t more) {
|
||||
uint32_t mix1 = (more + 0x9e3779b9 + (input << 6) + (input >> 2));
|
||||
return input ^ (0x9e3779b9 + (mix1 << 6) + (mix1 >> 2));
|
||||
}
|
||||
|
||||
/* Computes hash of an array of values */
|
||||
int32_t janet_array_calchash(const Janet *array, int32_t len) {
|
||||
const Janet *end = array + len;
|
||||
uint32_t hash = 0;
|
||||
uint32_t hash = 33;
|
||||
while (array < end) {
|
||||
uint32_t elem = janet_hash(*array++);
|
||||
hash ^= elem + 0x9e3779b9 + (hash << 6) + (hash >> 2);
|
||||
hash = janet_hash_mix(hash, janet_hash(*array++));
|
||||
}
|
||||
return (int32_t) hash;
|
||||
}
|
||||
|
@ -238,10 +256,10 @@ int32_t janet_array_calchash(const Janet *array, int32_t len) {
|
|||
/* Computes hash of an array of values */
|
||||
int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len) {
|
||||
const JanetKV *end = kvs + len;
|
||||
uint32_t hash = 0;
|
||||
uint32_t hash = 33;
|
||||
while (kvs < end) {
|
||||
hash ^= janet_hash(kvs->key) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
|
||||
hash ^= janet_hash(kvs->value) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
|
||||
hash = janet_hash_mix(hash, janet_hash(kvs->key));
|
||||
hash = janet_hash_mix(hash, janet_hash(kvs->value));
|
||||
kvs++;
|
||||
}
|
||||
return (int32_t) hash;
|
||||
|
@ -250,6 +268,7 @@ int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len) {
|
|||
/* Calculate next power of 2. May overflow. If n is 0,
|
||||
* will return 0. */
|
||||
int32_t janet_tablen(int32_t n) {
|
||||
if (n < 0) return 0;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
|
@ -480,7 +499,7 @@ typedef struct {
|
|||
static void namebuf_init(NameBuf *namebuf, const char *prefix) {
|
||||
size_t plen = strlen(prefix);
|
||||
namebuf->plen = plen;
|
||||
namebuf->buf = janet_malloc(namebuf->plen + 256);
|
||||
namebuf->buf = janet_smalloc(namebuf->plen + 256);
|
||||
if (NULL == namebuf->buf) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -489,12 +508,12 @@ static void namebuf_init(NameBuf *namebuf, const char *prefix) {
|
|||
}
|
||||
|
||||
static void namebuf_deinit(NameBuf *namebuf) {
|
||||
janet_free(namebuf->buf);
|
||||
janet_sfree(namebuf->buf);
|
||||
}
|
||||
|
||||
static char *namebuf_name(NameBuf *namebuf, const char *suffix) {
|
||||
size_t slen = strlen(suffix);
|
||||
namebuf->buf = janet_realloc(namebuf->buf, namebuf->plen + 2 + slen);
|
||||
namebuf->buf = janet_srealloc(namebuf->buf, namebuf->plen + 2 + slen);
|
||||
if (NULL == namebuf->buf) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -593,10 +612,8 @@ void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetReg
|
|||
}
|
||||
#endif
|
||||
|
||||
JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
|
||||
Janet ref;
|
||||
JanetBinding janet_binding_from_entry(Janet entry) {
|
||||
JanetTable *entry_table;
|
||||
Janet entry = janet_table_get(env, janet_wrap_symbol(sym));
|
||||
JanetBinding binding = {
|
||||
JANET_BINDING_NONE,
|
||||
janet_wrap_nil(),
|
||||
|
@ -623,29 +640,94 @@ JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
|
|||
binding.deprecation = JANET_BINDING_DEP_NORMAL;
|
||||
}
|
||||
|
||||
if (!janet_checktype(
|
||||
janet_table_get(entry_table, janet_ckeywordv("macro")),
|
||||
JANET_NIL)) {
|
||||
binding.value = janet_table_get(entry_table, janet_ckeywordv("value"));
|
||||
binding.type = JANET_BINDING_MACRO;
|
||||
int macro = janet_truthy(janet_table_get(entry_table, janet_ckeywordv("macro")));
|
||||
Janet value = janet_table_get(entry_table, janet_ckeywordv("value"));
|
||||
Janet ref = janet_table_get(entry_table, janet_ckeywordv("ref"));
|
||||
int ref_is_valid = janet_checktype(ref, JANET_ARRAY);
|
||||
int redef = ref_is_valid && janet_truthy(janet_table_get(entry_table, janet_ckeywordv("redef")));
|
||||
|
||||
if (macro) {
|
||||
binding.value = redef ? ref : value;
|
||||
binding.type = redef ? JANET_BINDING_DYNAMIC_MACRO : JANET_BINDING_MACRO;
|
||||
return binding;
|
||||
}
|
||||
|
||||
ref = janet_table_get(entry_table, janet_ckeywordv("ref"));
|
||||
if (janet_checktype(ref, JANET_ARRAY)) {
|
||||
if (ref_is_valid) {
|
||||
binding.value = ref;
|
||||
binding.type = JANET_BINDING_VAR;
|
||||
return binding;
|
||||
binding.type = redef ? JANET_BINDING_DYNAMIC_DEF : JANET_BINDING_VAR;
|
||||
} else {
|
||||
binding.value = value;
|
||||
binding.type = JANET_BINDING_DEF;
|
||||
}
|
||||
|
||||
binding.value = janet_table_get(entry_table, janet_ckeywordv("value"));
|
||||
binding.type = JANET_BINDING_DEF;
|
||||
return binding;
|
||||
}
|
||||
|
||||
/* If the value at the given address can be coerced to a byte view,
|
||||
return that byte view. If it can't, replace the value at the address
|
||||
with the result of janet_to_string, and return a byte view over that
|
||||
string. */
|
||||
static JanetByteView memoize_byte_view(Janet *value) {
|
||||
JanetByteView result;
|
||||
if (!janet_bytes_view(*value, &result.bytes, &result.len)) {
|
||||
JanetString str = janet_to_string(*value);
|
||||
*value = janet_wrap_string(str);
|
||||
result.bytes = str;
|
||||
result.len = janet_string_length(str);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static JanetByteView to_byte_view(Janet value) {
|
||||
JanetByteView result;
|
||||
if (!janet_bytes_view(value, &result.bytes, &result.len)) {
|
||||
JanetString str = janet_to_string(value);
|
||||
result.bytes = str;
|
||||
result.len = janet_string_length(str);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
JanetByteView janet_text_substitution(
|
||||
Janet *subst,
|
||||
const uint8_t *bytes,
|
||||
uint32_t len,
|
||||
JanetArray *extra_argv) {
|
||||
int32_t extra_argc = extra_argv == NULL ? 0 : extra_argv->count;
|
||||
JanetType type = janet_type(*subst);
|
||||
switch (type) {
|
||||
case JANET_FUNCTION:
|
||||
case JANET_CFUNCTION: {
|
||||
int32_t argc = 1 + extra_argc;
|
||||
Janet *argv = janet_tuple_begin(argc);
|
||||
argv[0] = janet_stringv(bytes, len);
|
||||
for (int32_t i = 0; i < extra_argc; i++) {
|
||||
argv[i + 1] = extra_argv->data[i];
|
||||
}
|
||||
janet_tuple_end(argv);
|
||||
if (type == JANET_FUNCTION) {
|
||||
return to_byte_view(janet_call(janet_unwrap_function(*subst), argc, argv));
|
||||
} else {
|
||||
return to_byte_view(janet_unwrap_cfunction(*subst)(argc, argv));
|
||||
}
|
||||
}
|
||||
default:
|
||||
return memoize_byte_view(subst);
|
||||
}
|
||||
}
|
||||
|
||||
JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
|
||||
Janet entry = janet_table_get(env, janet_wrap_symbol(sym));
|
||||
return janet_binding_from_entry(entry);
|
||||
}
|
||||
|
||||
JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out) {
|
||||
JanetBinding binding = janet_resolve_ext(env, sym);
|
||||
*out = binding.value;
|
||||
if (binding.type == JANET_BINDING_DYNAMIC_DEF || binding.type == JANET_BINDING_DYNAMIC_MACRO) {
|
||||
*out = janet_array_peek(janet_unwrap_array(binding.value));
|
||||
} else {
|
||||
*out = binding.value;
|
||||
}
|
||||
return binding.type;
|
||||
}
|
||||
|
||||
|
@ -675,15 +757,25 @@ int janet_indexed_view(Janet seq, const Janet **data, int32_t *len) {
|
|||
/* Read both strings and buffer as unsigned character array + int32_t len.
|
||||
* Returns 1 if the view can be constructed and 0 if the type is invalid. */
|
||||
int janet_bytes_view(Janet str, const uint8_t **data, int32_t *len) {
|
||||
if (janet_checktype(str, JANET_STRING) || janet_checktype(str, JANET_SYMBOL) ||
|
||||
janet_checktype(str, JANET_KEYWORD)) {
|
||||
JanetType t = janet_type(str);
|
||||
if (t == JANET_STRING || t == JANET_SYMBOL || t == JANET_KEYWORD) {
|
||||
*data = janet_unwrap_string(str);
|
||||
*len = janet_string_length(janet_unwrap_string(str));
|
||||
return 1;
|
||||
} else if (janet_checktype(str, JANET_BUFFER)) {
|
||||
} else if (t == JANET_BUFFER) {
|
||||
*data = janet_unwrap_buffer(str)->data;
|
||||
*len = janet_unwrap_buffer(str)->count;
|
||||
return 1;
|
||||
} else if (t == JANET_ABSTRACT) {
|
||||
void *abst = janet_unwrap_abstract(str);
|
||||
const JanetAbstractType *atype = janet_abstract_type(abst);
|
||||
if (NULL == atype->bytes) {
|
||||
return 0;
|
||||
}
|
||||
JanetByteView view = atype->bytes(abst, janet_abstract_size(abst));
|
||||
*data = view.bytes;
|
||||
*len = view.len;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -713,6 +805,13 @@ int janet_checkint(Janet x) {
|
|||
return janet_checkintrange(dval);
|
||||
}
|
||||
|
||||
int janet_checkuint(Janet x) {
|
||||
if (!janet_checktype(x, JANET_NUMBER))
|
||||
return 0;
|
||||
double dval = janet_unwrap_number(x);
|
||||
return janet_checkuintrange(dval);
|
||||
}
|
||||
|
||||
int janet_checkint64(Janet x) {
|
||||
if (!janet_checktype(x, JANET_NUMBER))
|
||||
return 0;
|
||||
|
@ -720,6 +819,13 @@ int janet_checkint64(Janet x) {
|
|||
return janet_checkint64range(dval);
|
||||
}
|
||||
|
||||
int janet_checkuint64(Janet x) {
|
||||
if (!janet_checktype(x, JANET_NUMBER))
|
||||
return 0;
|
||||
double dval = janet_unwrap_number(x);
|
||||
return janet_checkuint64range(dval);
|
||||
}
|
||||
|
||||
int janet_checksize(Janet x) {
|
||||
if (!janet_checktype(x, JANET_NUMBER))
|
||||
return 0;
|
||||
|
@ -775,41 +881,92 @@ int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffe
|
|||
|
||||
/* Clock shims for various platforms */
|
||||
#ifdef JANET_GETTIME
|
||||
/* For macos */
|
||||
#ifdef __MACH__
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
#ifdef JANET_WINDOWS
|
||||
int janet_gettime(struct timespec *spec) {
|
||||
FILETIME ftime;
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
|
||||
/* Windows epoch is January 1, 1601 apparently */
|
||||
wintime -= 116444736000000000LL;
|
||||
spec->tv_sec = wintime / 10000000LL;
|
||||
/* Resolution is 100 nanoseconds. */
|
||||
spec->tv_nsec = wintime % 10000000LL * 100;
|
||||
#include <profileapi.h>
|
||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
|
||||
if (source == JANET_TIME_REALTIME) {
|
||||
FILETIME ftime;
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
|
||||
/* Windows epoch is January 1, 1601 apparently */
|
||||
wintime -= 116444736000000000LL;
|
||||
spec->tv_sec = wintime / 10000000LL;
|
||||
/* Resolution is 100 nanoseconds. */
|
||||
spec->tv_nsec = wintime % 10000000LL * 100;
|
||||
} else if (source == JANET_TIME_MONOTONIC) {
|
||||
LARGE_INTEGER count;
|
||||
LARGE_INTEGER perf_freq;
|
||||
QueryPerformanceCounter(&count);
|
||||
QueryPerformanceFrequency(&perf_freq);
|
||||
spec->tv_sec = count.QuadPart / perf_freq.QuadPart;
|
||||
spec->tv_nsec = (long)((count.QuadPart % perf_freq.QuadPart) * 1000000000 / perf_freq.QuadPart);
|
||||
} else if (source == JANET_TIME_CPUTIME) {
|
||||
FILETIME creationTime, exitTime, kernelTime, userTime;
|
||||
GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime);
|
||||
int64_t tmp = ((int64_t)userTime.dwHighDateTime << 32) + userTime.dwLowDateTime;
|
||||
spec->tv_sec = tmp / 10000000LL;
|
||||
spec->tv_nsec = tmp % 10000000LL * 100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__MACH__)
|
||||
int janet_gettime(struct timespec *spec) {
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
spec->tv_sec = mts.tv_sec;
|
||||
spec->tv_nsec = mts.tv_nsec;
|
||||
/* clock_gettime() wasn't available on Mac until 10.12. */
|
||||
#elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12)
|
||||
#include <mach/clock.h>
|
||||
#include <mach/mach.h>
|
||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
|
||||
if (source == JANET_TIME_REALTIME) {
|
||||
clock_serv_t cclock;
|
||||
mach_timespec_t mts;
|
||||
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
||||
clock_get_time(cclock, &mts);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
spec->tv_sec = mts.tv_sec;
|
||||
spec->tv_nsec = mts.tv_nsec;
|
||||
} else if (source == JANET_TIME_MONOTONIC) {
|
||||
clock_serv_t cclock;
|
||||
int nsecs;
|
||||
mach_msg_type_number_t count;
|
||||
host_get_clock_service(mach_host_self(), clock, &cclock);
|
||||
clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count);
|
||||
mach_port_deallocate(mach_task_self(), cclock);
|
||||
clock_getres(CLOCK_MONOTONIC, spec);
|
||||
}
|
||||
if (source == JANET_TIME_CPUTIME) {
|
||||
clock_t tmp = clock();
|
||||
spec->tv_sec = tmp;
|
||||
spec->tv_nsec = (tmp - spec->tv_sec) * 1.0e9;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int janet_gettime(struct timespec *spec) {
|
||||
return clock_gettime(CLOCK_REALTIME, spec);
|
||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
|
||||
clockid_t cid = CLOCK_REALTIME;
|
||||
if (source == JANET_TIME_REALTIME) {
|
||||
cid = CLOCK_REALTIME;
|
||||
} else if (source == JANET_TIME_MONOTONIC) {
|
||||
cid = CLOCK_MONOTONIC;
|
||||
} else if (source == JANET_TIME_CPUTIME) {
|
||||
cid = CLOCK_PROCESS_CPUTIME_ID;
|
||||
}
|
||||
return clock_gettime(cid, spec);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Better strerror (thread-safe if available) */
|
||||
const char *janet_strerror(int e) {
|
||||
#ifdef JANET_WINDOWS
|
||||
/* Microsoft strerror seems sane here and is thread safe by default */
|
||||
return strerror(e);
|
||||
#elif defined(_GNU_SOURCE)
|
||||
/* See https://linux.die.net/man/3/strerror_r */
|
||||
return strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf));
|
||||
#else
|
||||
strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf));
|
||||
return janet_vm.strerror_buf;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Setting C99 standard makes this not available, but it should
|
||||
* work/link properly if we detect a BSD */
|
||||
#if defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
|
||||
|
@ -817,18 +974,22 @@ void arc4random_buf(void *buf, size_t nbytes);
|
|||
#endif
|
||||
|
||||
int janet_cryptorand(uint8_t *out, size_t n) {
|
||||
#ifndef JANET_NO_CRYPTORAND
|
||||
#ifdef JANET_WINDOWS
|
||||
for (size_t i = 0; i < n; i += sizeof(unsigned int)) {
|
||||
unsigned int v;
|
||||
if (rand_s(&v))
|
||||
return -1;
|
||||
for (int32_t j = 0; (j < sizeof(unsigned int)) && (i + j < n); j++) {
|
||||
for (int32_t j = 0; (j < (int32_t) sizeof(unsigned int)) && (i + j < n); j++) {
|
||||
out[i + j] = v & 0xff;
|
||||
v = v >> 8;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#elif defined(JANET_LINUX) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) )
|
||||
#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
|
||||
arc4random_buf(out, n);
|
||||
return 0;
|
||||
#else
|
||||
/* We should be able to call getrandom on linux, but it doesn't seem
|
||||
to be uniformly supported on linux distros.
|
||||
On Mac, arc4random_buf wasn't available on until 10.7.
|
||||
|
@ -850,16 +1011,89 @@ int janet_cryptorand(uint8_t *out, size_t n) {
|
|||
}
|
||||
RETRY_EINTR(rc, close(randfd));
|
||||
return 0;
|
||||
#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
|
||||
arc4random_buf(out, n);
|
||||
return 0;
|
||||
#endif
|
||||
#else
|
||||
(void) n;
|
||||
(void) out;
|
||||
(void) n;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Dynamic library loading */
|
||||
|
||||
char *get_processed_name(const char *name) {
|
||||
if (name[0] == '.') return (char *) name;
|
||||
const char *c;
|
||||
for (c = name; *c; c++) {
|
||||
if (*c == '/') return (char *) name;
|
||||
}
|
||||
size_t l = (size_t)(c - name);
|
||||
char *ret = janet_malloc(l + 3);
|
||||
if (NULL == ret) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
ret[0] = '.';
|
||||
ret[1] = '/';
|
||||
memcpy(ret + 2, name, l + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(JANET_NO_DYNAMIC_MODULES)
|
||||
|
||||
const char *error_clib(void) {
|
||||
return "dynamic modules not supported";
|
||||
}
|
||||
|
||||
#else
|
||||
#if defined(JANET_WINDOWS)
|
||||
|
||||
static char error_clib_buf[256];
|
||||
char *error_clib(void) {
|
||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
error_clib_buf, sizeof(error_clib_buf), NULL);
|
||||
error_clib_buf[strlen(error_clib_buf) - 1] = '\0';
|
||||
return error_clib_buf;
|
||||
}
|
||||
|
||||
Clib load_clib(const char *name) {
|
||||
if (name == NULL) {
|
||||
return GetModuleHandle(NULL);
|
||||
} else {
|
||||
return LoadLibrary(name);
|
||||
}
|
||||
}
|
||||
|
||||
void free_clib(HINSTANCE clib) {
|
||||
if (clib != GetModuleHandle(NULL)) {
|
||||
FreeLibrary(clib);
|
||||
}
|
||||
}
|
||||
|
||||
void *symbol_clib(HINSTANCE clib, const char *sym) {
|
||||
if (clib != GetModuleHandle(NULL)) {
|
||||
return GetProcAddress(clib, sym);
|
||||
} else {
|
||||
/* Look up symbols from all loaded modules */
|
||||
HMODULE hMods[1024];
|
||||
DWORD needed = 0;
|
||||
if (EnumProcessModules(GetCurrentProcess(), hMods, sizeof(hMods), &needed)) {
|
||||
needed /= sizeof(HMODULE);
|
||||
for (DWORD i = 0; i < needed; i++) {
|
||||
void *address = GetProcAddress(hMods[i], sym);
|
||||
if (NULL != address) {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
janet_panicf("ffi: %s", error_clib());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Alloc function macro fills */
|
||||
void *(janet_malloc)(size_t size) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -31,6 +31,14 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef JANET_EV
|
||||
#ifndef JANET_WINDOWS
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(JANET_REDUCED_OS) || !defined(JANET_SINGLE_THREADED)
|
||||
#include <time.h>
|
||||
|
@ -41,11 +49,11 @@
|
|||
#ifndef JANET_EXIT
|
||||
#include <stdio.h>
|
||||
#define JANET_EXIT(m) do { \
|
||||
fprintf(stderr, "C runtime error at line %d in file %s: %s\n",\
|
||||
fprintf(stderr, "janet internal error at line %d in file %s: %s\n",\
|
||||
__LINE__,\
|
||||
__FILE__,\
|
||||
(m));\
|
||||
exit(1);\
|
||||
abort();\
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
@ -56,7 +64,10 @@
|
|||
} while (0)
|
||||
|
||||
/* Utils */
|
||||
uint32_t janet_hash_mix(uint32_t input, uint32_t more);
|
||||
#define janet_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1))
|
||||
int janet_valid_utf8(const uint8_t *str, int32_t len);
|
||||
int janet_is_symbol_char(uint8_t c);
|
||||
extern const char janet_base64[65];
|
||||
int32_t janet_array_calchash(const Janet *array, int32_t len);
|
||||
int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len);
|
||||
|
@ -69,6 +80,8 @@ void janet_memempty(JanetKV *mem, int32_t count);
|
|||
void *janet_memalloc_empty(int32_t count);
|
||||
JanetTable *janet_get_core_table(const char *name);
|
||||
void janet_def_addflags(JanetFuncDef *def);
|
||||
void janet_buffer_dtostr(JanetBuffer *buffer, double x);
|
||||
const char *janet_strerror(int e);
|
||||
const void *janet_strbinsearch(
|
||||
const void *tab,
|
||||
size_t tabcount,
|
||||
|
@ -81,6 +94,12 @@ void janet_buffer_format(
|
|||
int32_t argc,
|
||||
Janet *argv);
|
||||
Janet janet_next_impl(Janet ds, Janet key, int is_interpreter);
|
||||
JanetBinding janet_binding_from_entry(Janet entry);
|
||||
JanetByteView janet_text_substitution(
|
||||
Janet *subst,
|
||||
const uint8_t *bytes,
|
||||
uint32_t len,
|
||||
JanetArray *extra_args);
|
||||
|
||||
/* Registry functions */
|
||||
void janet_registry_put(
|
||||
|
@ -109,7 +128,12 @@ void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetReg
|
|||
|
||||
/* Clock gettime */
|
||||
#ifdef JANET_GETTIME
|
||||
int janet_gettime(struct timespec *spec);
|
||||
enum JanetTimeSource {
|
||||
JANET_TIME_REALTIME,
|
||||
JANET_TIME_MONOTONIC,
|
||||
JANET_TIME_CPUTIME
|
||||
};
|
||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source);
|
||||
#endif
|
||||
|
||||
/* strdup */
|
||||
|
@ -117,6 +141,31 @@ int janet_gettime(struct timespec *spec);
|
|||
#define strdup(x) _strdup(x)
|
||||
#endif
|
||||
|
||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
|
||||
* with native code. */
|
||||
#if defined(JANET_NO_DYNAMIC_MODULES)
|
||||
typedef int Clib;
|
||||
#define load_clib(name) ((void) name, 0)
|
||||
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
|
||||
const char *error_clib(void);
|
||||
#define free_clib(c) ((void) (c), 0)
|
||||
#elif defined(JANET_WINDOWS)
|
||||
#include <windows.h>
|
||||
typedef HINSTANCE Clib;
|
||||
void *symbol_clib(Clib clib, const char *sym);
|
||||
void free_clib(Clib clib);
|
||||
Clib load_clib(const char *name);
|
||||
char *error_clib(void);
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
typedef void *Clib;
|
||||
#define load_clib(name) dlopen((name), RTLD_NOW)
|
||||
#define free_clib(lib) dlclose((lib))
|
||||
#define symbol_clib(lib, sym) dlsym((lib), (sym))
|
||||
#define error_clib dlerror
|
||||
#endif
|
||||
char *get_processed_name(const char *name);
|
||||
|
||||
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
|
||||
|
||||
/* Initialize builtin libraries */
|
||||
|
@ -126,6 +175,7 @@ void janet_lib_array(JanetTable *env);
|
|||
void janet_lib_tuple(JanetTable *env);
|
||||
void janet_lib_buffer(JanetTable *env);
|
||||
void janet_lib_table(JanetTable *env);
|
||||
void janet_lib_struct(JanetTable *env);
|
||||
void janet_lib_fiber(JanetTable *env);
|
||||
void janet_lib_os(JanetTable *env);
|
||||
void janet_lib_string(JanetTable *env);
|
||||
|
@ -154,5 +204,8 @@ void janet_lib_ev(JanetTable *env);
|
|||
void janet_ev_mark(void);
|
||||
int janet_make_pipe(JanetHandle handles[2], int mode);
|
||||
#endif
|
||||
#ifdef JANET_FFI
|
||||
void janet_lib_ffi(JanetTable *env);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -101,6 +101,17 @@ static int traversal_next(Janet *x, Janet *y) {
|
|||
janet_vm.traversal = t;
|
||||
return 0;
|
||||
}
|
||||
/* Traverse prototype */
|
||||
JanetStruct sproto = sself->proto;
|
||||
JanetStruct oproto = sother->proto;
|
||||
if (sproto && !oproto) return 3;
|
||||
if (!sproto && oproto) return 1;
|
||||
if (oproto && sproto) {
|
||||
*x = janet_wrap_struct(sproto);
|
||||
*y = janet_wrap_struct(oproto);
|
||||
janet_vm.traversal = t - 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
t--;
|
||||
}
|
||||
|
@ -261,6 +272,7 @@ int janet_equals(Janet x, Janet y) {
|
|||
const Janet *t1 = janet_unwrap_tuple(x);
|
||||
const Janet *t2 = janet_unwrap_tuple(y);
|
||||
if (t1 == t2) break;
|
||||
if (JANET_TUPLE_FLAG_BRACKETCTOR & (janet_tuple_flag(t1) ^ janet_tuple_flag(t2))) return 0;
|
||||
if (janet_tuple_hash(t1) != janet_tuple_hash(t2)) return 0;
|
||||
if (janet_tuple_length(t1) != janet_tuple_length(t2)) return 0;
|
||||
push_traversal_node(janet_tuple_head(t1), janet_tuple_head(t2), 0);
|
||||
|
@ -273,6 +285,8 @@ int janet_equals(Janet x, Janet y) {
|
|||
if (s1 == s2) break;
|
||||
if (janet_struct_hash(s1) != janet_struct_hash(s2)) return 0;
|
||||
if (janet_struct_length(s1) != janet_struct_length(s2)) return 0;
|
||||
if (janet_struct_proto(s1) && !janet_struct_proto(s2)) return 0;
|
||||
if (!janet_struct_proto(s1) && janet_struct_proto(s2)) return 0;
|
||||
push_traversal_node(janet_struct_head(s1), janet_struct_head(s2), 0);
|
||||
break;
|
||||
}
|
||||
|
@ -282,6 +296,15 @@ int janet_equals(Janet x, Janet y) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static uint64_t murmur64(uint64_t h) {
|
||||
h ^= h >> 33;
|
||||
h *= 0xff51afd7ed558ccdUL;
|
||||
h ^= h >> 33;
|
||||
h *= 0xc4ceb9fe1a85ec53UL;
|
||||
h ^= h >> 33;
|
||||
return h;
|
||||
}
|
||||
|
||||
/* Computes a hash value for a function */
|
||||
int32_t janet_hash(Janet x) {
|
||||
int32_t hash = 0;
|
||||
|
@ -299,6 +322,7 @@ int32_t janet_hash(Janet x) {
|
|||
break;
|
||||
case JANET_TUPLE:
|
||||
hash = janet_tuple_hash(janet_unwrap_tuple(x));
|
||||
hash += (janet_tuple_flag(janet_unwrap_tuple(x)) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : 0;
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
hash = janet_struct_hash(janet_unwrap_struct(x));
|
||||
|
@ -309,9 +333,11 @@ int32_t janet_hash(Janet x) {
|
|||
uint64_t u;
|
||||
} as;
|
||||
as.d = janet_unwrap_number(x);
|
||||
as.d += 0.0; /* normalize negative 0 */
|
||||
uint32_t lo = (uint32_t)(as.u & 0xFFFFFFFF);
|
||||
uint32_t hi = (uint32_t)(as.u >> 32);
|
||||
hash = (int32_t)(hi ^ (lo >> 3));
|
||||
uint32_t hilo = (hi ^ lo) * 2654435769u;
|
||||
hash = (int32_t)((hilo << 16) | (hilo >> 16));
|
||||
break;
|
||||
}
|
||||
case JANET_ABSTRACT: {
|
||||
|
@ -325,15 +351,14 @@ int32_t janet_hash(Janet x) {
|
|||
/* fallthrough */
|
||||
default:
|
||||
if (sizeof(double) == sizeof(void *)) {
|
||||
/* Assuming 8 byte pointer */
|
||||
uint64_t i = janet_u64(x);
|
||||
uint32_t lo = (uint32_t)(i & 0xFFFFFFFF);
|
||||
uint32_t hi = (uint32_t)(i >> 32);
|
||||
hash = (int32_t)(hi ^ (lo >> 3));
|
||||
/* Assuming 8 byte pointer (8 byte aligned) */
|
||||
uint64_t i = murmur64(janet_u64(x));
|
||||
hash = (int32_t)(i >> 32);
|
||||
} else {
|
||||
/* Assuming 4 byte pointer (or smaller) */
|
||||
hash = (int32_t)((char *)janet_unwrap_pointer(x) - (char *)0);
|
||||
hash >>= 2;
|
||||
uintptr_t diff = (uintptr_t) janet_unwrap_pointer(x);
|
||||
uint32_t hilo = (uint32_t) diff * 2654435769u;
|
||||
hash = (int32_t)((hilo << 16) | (hilo >> 16));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -389,6 +414,9 @@ int janet_compare(Janet x, Janet y) {
|
|||
case JANET_TUPLE: {
|
||||
const Janet *lhs = janet_unwrap_tuple(x);
|
||||
const Janet *rhs = janet_unwrap_tuple(y);
|
||||
if (JANET_TUPLE_FLAG_BRACKETCTOR & (janet_tuple_flag(lhs) ^ janet_tuple_flag(rhs))) {
|
||||
return (janet_tuple_flag(lhs) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : -1;
|
||||
}
|
||||
push_traversal_node(janet_tuple_head(lhs), janet_tuple_head(rhs), 1);
|
||||
break;
|
||||
}
|
||||
|
@ -411,20 +439,21 @@ int janet_compare(Janet x, Janet y) {
|
|||
return status - 2;
|
||||
}
|
||||
|
||||
static int32_t getter_checkint(Janet key, int32_t max) {
|
||||
static int32_t getter_checkint(JanetType type, Janet key, int32_t max) {
|
||||
if (!janet_checkint(key)) goto bad;
|
||||
int32_t ret = janet_unwrap_integer(key);
|
||||
if (ret < 0) goto bad;
|
||||
if (ret >= max) goto bad;
|
||||
return ret;
|
||||
bad:
|
||||
janet_panicf("expected integer key in range [0, %d), got %v", max, key);
|
||||
janet_panicf("expected integer key for %s in range [0, %d), got %v", janet_type_names[type], max, key);
|
||||
}
|
||||
|
||||
/* Gets a value and returns. Can panic. */
|
||||
Janet janet_in(Janet ds, Janet key) {
|
||||
Janet value;
|
||||
switch (janet_type(ds)) {
|
||||
JanetType type = janet_type(ds);
|
||||
switch (type) {
|
||||
default:
|
||||
janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, ds);
|
||||
break;
|
||||
|
@ -436,19 +465,19 @@ Janet janet_in(Janet ds, Janet key) {
|
|||
break;
|
||||
case JANET_ARRAY: {
|
||||
JanetArray *array = janet_unwrap_array(ds);
|
||||
int32_t index = getter_checkint(key, array->count);
|
||||
int32_t index = getter_checkint(type, key, array->count);
|
||||
value = array->data[index];
|
||||
break;
|
||||
}
|
||||
case JANET_TUPLE: {
|
||||
const Janet *tuple = janet_unwrap_tuple(ds);
|
||||
int32_t len = janet_tuple_length(tuple);
|
||||
value = tuple[getter_checkint(key, len)];
|
||||
value = tuple[getter_checkint(type, key, len)];
|
||||
break;
|
||||
}
|
||||
case JANET_BUFFER: {
|
||||
JanetBuffer *buffer = janet_unwrap_buffer(ds);
|
||||
int32_t index = getter_checkint(key, buffer->count);
|
||||
int32_t index = getter_checkint(type, key, buffer->count);
|
||||
value = janet_wrap_integer(buffer->data[index]);
|
||||
break;
|
||||
}
|
||||
|
@ -456,7 +485,7 @@ Janet janet_in(Janet ds, Janet key) {
|
|||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD: {
|
||||
const uint8_t *str = janet_unwrap_string(ds);
|
||||
int32_t index = getter_checkint(key, janet_string_length(str));
|
||||
int32_t index = getter_checkint(type, key, janet_string_length(str));
|
||||
value = janet_wrap_integer(str[index]);
|
||||
break;
|
||||
}
|
||||
|
@ -628,6 +657,15 @@ int32_t janet_length(Janet x) {
|
|||
case JANET_TABLE:
|
||||
return janet_unwrap_table(x)->count;
|
||||
case JANET_ABSTRACT: {
|
||||
void *abst = janet_unwrap_abstract(x);
|
||||
const JanetAbstractType *type = janet_abstract_type(abst);
|
||||
if (type->length != NULL) {
|
||||
size_t len = type->length(abst, janet_abstract_size(abst));
|
||||
if (len > INT32_MAX) {
|
||||
janet_panicf("invalid integer length %u", len);
|
||||
}
|
||||
return (int32_t)(len);
|
||||
}
|
||||
Janet argv[1] = { x };
|
||||
Janet len = janet_mcall("length", 1, argv);
|
||||
if (!janet_checkint(len))
|
||||
|
@ -656,6 +694,21 @@ Janet janet_lengthv(Janet x) {
|
|||
case JANET_TABLE:
|
||||
return janet_wrap_integer(janet_unwrap_table(x)->count);
|
||||
case JANET_ABSTRACT: {
|
||||
void *abst = janet_unwrap_abstract(x);
|
||||
const JanetAbstractType *type = janet_abstract_type(abst);
|
||||
if (type->length != NULL) {
|
||||
size_t len = type->length(abst, janet_abstract_size(abst));
|
||||
/* If len is always less then double, we can never overflow */
|
||||
#ifdef JANET_32
|
||||
return janet_wrap_number(len);
|
||||
#else
|
||||
if (len < (size_t) JANET_INTMAX_INT64) {
|
||||
return janet_wrap_number((double) len);
|
||||
} else {
|
||||
janet_panicf("integer length %u too large", len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
Janet argv[1] = { x };
|
||||
return janet_mcall("length", 1, argv);
|
||||
}
|
||||
|
@ -705,13 +758,14 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
|
|||
}
|
||||
|
||||
void janet_put(Janet ds, Janet key, Janet value) {
|
||||
switch (janet_type(ds)) {
|
||||
JanetType type = janet_type(ds);
|
||||
switch (type) {
|
||||
default:
|
||||
janet_panicf("expected %T, got %v",
|
||||
JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds);
|
||||
case JANET_ARRAY: {
|
||||
JanetArray *array = janet_unwrap_array(ds);
|
||||
int32_t index = getter_checkint(key, INT32_MAX - 1);
|
||||
int32_t index = getter_checkint(type, key, INT32_MAX - 1);
|
||||
if (index >= array->count) {
|
||||
janet_array_setcount(array, index + 1);
|
||||
}
|
||||
|
@ -720,7 +774,7 @@ void janet_put(Janet ds, Janet key, Janet value) {
|
|||
}
|
||||
case JANET_BUFFER: {
|
||||
JanetBuffer *buffer = janet_unwrap_buffer(ds);
|
||||
int32_t index = getter_checkint(key, INT32_MAX - 1);
|
||||
int32_t index = getter_checkint(type, key, INT32_MAX - 1);
|
||||
if (!janet_checkint(value))
|
||||
janet_panicf("can only put integers in buffers, got %v", value);
|
||||
if (index >= buffer->count) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -40,7 +40,7 @@ void *janet_v_grow(void *v, int32_t increment, int32_t itemsize) {
|
|||
|
||||
/* Convert a buffer to normal allocated memory (forget capacity) */
|
||||
void *janet_v_flattenmem(void *v, int32_t itemsize) {
|
||||
int32_t *p;
|
||||
char *p;
|
||||
if (NULL == v) return NULL;
|
||||
size_t size = (size_t) itemsize * janet_v__cnt(v);
|
||||
p = janet_malloc(size);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
174
src/core/vm.c
174
src/core/vm.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -116,7 +116,6 @@
|
|||
#else
|
||||
#define vm_maybe_auto_suspend(COND) do { \
|
||||
if ((COND) && janet_vm.auto_suspend) { \
|
||||
janet_vm.auto_suspend = 0; \
|
||||
fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \
|
||||
vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \
|
||||
} \
|
||||
|
@ -138,7 +137,7 @@
|
|||
vm_pcnext();\
|
||||
}\
|
||||
}
|
||||
#define _vm_bitop_immediate(op, type1)\
|
||||
#define _vm_bitop_immediate(op, type1, rangecheck, msg)\
|
||||
{\
|
||||
Janet op1 = stack[B];\
|
||||
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
||||
|
@ -147,13 +146,15 @@
|
|||
stack[A] = janet_mcall(#op, 2, _argv);\
|
||||
vm_checkgc_pcnext();\
|
||||
} else {\
|
||||
type1 x1 = (type1) janet_unwrap_integer(op1);\
|
||||
stack[A] = janet_wrap_integer(x1 op CS);\
|
||||
double y1 = janet_unwrap_number(op1);\
|
||||
if (!rangecheck(y1)) { vm_commit(); janet_panicf("value %v out of range for " msg, op1); }\
|
||||
type1 x1 = (type1) y1;\
|
||||
stack[A] = janet_wrap_number((type1) (x1 op CS));\
|
||||
vm_pcnext();\
|
||||
}\
|
||||
}
|
||||
#define vm_bitop_immediate(op) _vm_bitop_immediate(op, int32_t);
|
||||
#define vm_bitopu_immediate(op) _vm_bitop_immediate(op, uint32_t);
|
||||
#define vm_bitop_immediate(op) _vm_bitop_immediate(op, int32_t, janet_checkintrange, "32-bit signed integers");
|
||||
#define vm_bitopu_immediate(op) _vm_bitop_immediate(op, uint32_t, janet_checkuintrange, "32-bit unsigned integers");
|
||||
#define _vm_binop(op, wrap)\
|
||||
{\
|
||||
Janet op1 = stack[B];\
|
||||
|
@ -170,14 +171,18 @@
|
|||
}\
|
||||
}
|
||||
#define vm_binop(op) _vm_binop(op, janet_wrap_number)
|
||||
#define _vm_bitop(op, type1)\
|
||||
#define _vm_bitop(op, type1, rangecheck, msg)\
|
||||
{\
|
||||
Janet op1 = stack[B];\
|
||||
Janet op2 = stack[C];\
|
||||
if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) {\
|
||||
type1 x1 = (type1) janet_unwrap_integer(op1);\
|
||||
int32_t x2 = janet_unwrap_integer(op2);\
|
||||
stack[A] = janet_wrap_integer(x1 op x2);\
|
||||
double y1 = janet_unwrap_number(op1);\
|
||||
double y2 = janet_unwrap_number(op2);\
|
||||
if (!rangecheck(y1)) { vm_commit(); janet_panicf("value %v out of range for " msg, op1); }\
|
||||
if (!janet_checkintrange(y2)) { vm_commit(); janet_panicf("rhs must be valid 32-bit signed integer, got %f", op2); }\
|
||||
type1 x1 = (type1) y1;\
|
||||
int32_t x2 = (int32_t) y2;\
|
||||
stack[A] = janet_wrap_number((type1) (x1 op x2));\
|
||||
vm_pcnext();\
|
||||
} else {\
|
||||
vm_commit();\
|
||||
|
@ -185,8 +190,8 @@
|
|||
vm_checkgc_pcnext();\
|
||||
}\
|
||||
}
|
||||
#define vm_bitop(op) _vm_bitop(op, int32_t)
|
||||
#define vm_bitopu(op) _vm_bitop(op, uint32_t)
|
||||
#define vm_bitop(op) _vm_bitop(op, int32_t, janet_checkintrange, "32-bit signed integers")
|
||||
#define vm_bitopu(op) _vm_bitop(op, uint32_t, janet_checkuintrange, "32-bit unsigned integers")
|
||||
#define vm_compop(op) \
|
||||
{\
|
||||
Janet op1 = stack[B];\
|
||||
|
@ -220,14 +225,14 @@
|
|||
/* Trace a function call */
|
||||
static void vm_do_trace(JanetFunction *func, int32_t argc, const Janet *argv) {
|
||||
if (func->def->name) {
|
||||
janet_printf("trace (%S", func->def->name);
|
||||
janet_eprintf("trace (%S", func->def->name);
|
||||
} else {
|
||||
janet_printf("trace (%p", janet_wrap_function(func));
|
||||
janet_eprintf("trace (%p", janet_wrap_function(func));
|
||||
}
|
||||
for (int32_t i = 0; i < argc; i++) {
|
||||
janet_printf(" %p", argv[i]);
|
||||
janet_eprintf(" %p", argv[i]);
|
||||
}
|
||||
janet_printf(")\n");
|
||||
janet_eprintf(")\n");
|
||||
}
|
||||
|
||||
/* Invoke a method once we have looked it up */
|
||||
|
@ -295,6 +300,16 @@ static Janet janet_method_lookup(Janet x, const char *name) {
|
|||
return method_to_fun(janet_ckeywordv(name), x);
|
||||
}
|
||||
|
||||
static Janet janet_unary_call(const char *method, Janet arg) {
|
||||
Janet m = janet_method_lookup(arg, method);
|
||||
if (janet_checktype(m, JANET_NIL)) {
|
||||
janet_panicf("could not find method :%s for %v", method, arg);
|
||||
} else {
|
||||
Janet argv[1] = { arg };
|
||||
return janet_method_invoke(m, 1, argv);
|
||||
}
|
||||
}
|
||||
|
||||
/* Call a method first on the righthand side, and then on the left hand side with a prefix */
|
||||
static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lhs, Janet rhs) {
|
||||
Janet lm = janet_method_lookup(lhs, lmethod);
|
||||
|
@ -303,7 +318,7 @@ static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lh
|
|||
Janet lr = janet_method_lookup(rhs, rmethod);
|
||||
Janet argv[2] = { rhs, lhs };
|
||||
if (janet_checktype(lr, JANET_NIL)) {
|
||||
janet_panicf("could not find method :%s for %v, or :%s for %v",
|
||||
janet_panicf("could not find method :%s for %v or :%s for %v",
|
||||
lmethod, lhs,
|
||||
rmethod, rhs);
|
||||
}
|
||||
|
@ -315,7 +330,7 @@ static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lh
|
|||
}
|
||||
|
||||
/* Forward declaration */
|
||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out);
|
||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out, int is_cancel);
|
||||
static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *out);
|
||||
|
||||
/* Interpreter main loop */
|
||||
|
@ -331,11 +346,13 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
&&label_JOP_RETURN_NIL,
|
||||
&&label_JOP_ADD_IMMEDIATE,
|
||||
&&label_JOP_ADD,
|
||||
&&label_JOP_SUBTRACT_IMMEDIATE,
|
||||
&&label_JOP_SUBTRACT,
|
||||
&&label_JOP_MULTIPLY_IMMEDIATE,
|
||||
&&label_JOP_MULTIPLY,
|
||||
&&label_JOP_DIVIDE_IMMEDIATE,
|
||||
&&label_JOP_DIVIDE,
|
||||
&&label_JOP_DIVIDE_FLOOR,
|
||||
&&label_JOP_MODULO,
|
||||
&&label_JOP_REMAINDER,
|
||||
&&label_JOP_BAND,
|
||||
|
@ -576,8 +593,6 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op,
|
||||
&&label_unknown_op
|
||||
};
|
||||
#endif
|
||||
|
@ -667,6 +682,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
VM_OP(JOP_ADD)
|
||||
vm_binop(+);
|
||||
|
||||
VM_OP(JOP_SUBTRACT_IMMEDIATE)
|
||||
vm_binop_immediate(-);
|
||||
|
||||
VM_OP(JOP_SUBTRACT)
|
||||
vm_binop(-);
|
||||
|
||||
|
@ -682,14 +700,33 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
VM_OP(JOP_DIVIDE)
|
||||
vm_binop( /);
|
||||
|
||||
VM_OP(JOP_DIVIDE_FLOOR) {
|
||||
Janet op1 = stack[B];
|
||||
Janet op2 = stack[C];
|
||||
if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) {
|
||||
double x1 = janet_unwrap_number(op1);
|
||||
double x2 = janet_unwrap_number(op2);
|
||||
stack[A] = janet_wrap_number(floor(x1 / x2));
|
||||
vm_pcnext();
|
||||
} else {
|
||||
vm_commit();
|
||||
stack[A] = janet_binop_call("div", "rdiv", op1, op2);
|
||||
vm_checkgc_pcnext();
|
||||
}
|
||||
}
|
||||
|
||||
VM_OP(JOP_MODULO) {
|
||||
Janet op1 = stack[B];
|
||||
Janet op2 = stack[C];
|
||||
if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) {
|
||||
double x1 = janet_unwrap_number(op1);
|
||||
double x2 = janet_unwrap_number(op2);
|
||||
double intres = x2 * floor(x1 / x2);
|
||||
stack[A] = janet_wrap_number(x1 - intres);
|
||||
if (x2 == 0) {
|
||||
stack[A] = janet_wrap_number(x1);
|
||||
} else {
|
||||
double intres = x2 * floor(x1 / x2);
|
||||
stack[A] = janet_wrap_number(x1 - intres);
|
||||
}
|
||||
vm_pcnext();
|
||||
} else {
|
||||
vm_commit();
|
||||
|
@ -724,9 +761,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
|
||||
VM_OP(JOP_BNOT) {
|
||||
Janet op = stack[E];
|
||||
vm_assert_type(op, JANET_NUMBER);
|
||||
stack[A] = janet_wrap_integer(~janet_unwrap_integer(op));
|
||||
vm_pcnext();
|
||||
if (janet_checktype(op, JANET_NUMBER)) {
|
||||
stack[A] = janet_wrap_integer(~janet_unwrap_integer(op));
|
||||
vm_pcnext();
|
||||
} else {
|
||||
vm_commit();
|
||||
stack[A] = janet_unary_call("~", op);
|
||||
vm_checkgc_pcnext();
|
||||
}
|
||||
}
|
||||
|
||||
VM_OP(JOP_SHIFT_RIGHT_UNSIGNED)
|
||||
|
@ -757,13 +799,13 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
|
||||
VM_OP(JOP_JUMP)
|
||||
pc += DS;
|
||||
vm_maybe_auto_suspend(DS < 0);
|
||||
vm_maybe_auto_suspend(DS <= 0);
|
||||
vm_next();
|
||||
|
||||
VM_OP(JOP_JUMP_IF)
|
||||
if (janet_truthy(stack[A])) {
|
||||
pc += ES;
|
||||
vm_maybe_auto_suspend(ES < 0);
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
} else {
|
||||
pc++;
|
||||
}
|
||||
|
@ -774,14 +816,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
pc++;
|
||||
} else {
|
||||
pc += ES;
|
||||
vm_maybe_auto_suspend(ES < 0);
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
}
|
||||
vm_next();
|
||||
|
||||
VM_OP(JOP_JUMP_IF_NIL)
|
||||
if (janet_checktype(stack[A], JANET_NIL)) {
|
||||
pc += ES;
|
||||
vm_maybe_auto_suspend(ES < 0);
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
} else {
|
||||
pc++;
|
||||
}
|
||||
|
@ -792,7 +834,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
pc++;
|
||||
} else {
|
||||
pc += ES;
|
||||
vm_maybe_auto_suspend(ES < 0);
|
||||
vm_maybe_auto_suspend(ES <= 0);
|
||||
}
|
||||
vm_next();
|
||||
|
||||
|
@ -819,7 +861,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_EQUALS_IMMEDIATE)
|
||||
stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) == (double) CS);
|
||||
stack[A] = janet_wrap_boolean(janet_checktype(stack[B], JANET_NUMBER) && (janet_unwrap_number(stack[B]) == (double) CS));
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_NOT_EQUALS)
|
||||
|
@ -827,7 +869,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_NOT_EQUALS_IMMEDIATE)
|
||||
stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) != (double) CS);
|
||||
stack[A] = janet_wrap_boolean(!janet_checktype(stack[B], JANET_NUMBER) || (janet_unwrap_number(stack[B]) != (double) CS));
|
||||
vm_pcnext();
|
||||
|
||||
VM_OP(JOP_COMPARE)
|
||||
|
@ -918,7 +960,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
int32_t i;
|
||||
for (i = 0; i < elen; ++i) {
|
||||
int32_t inherit = fd->environments[i];
|
||||
if (inherit == -1) {
|
||||
if (inherit == -1 || inherit >= func->def->environments_length) {
|
||||
JanetStackFrame *frame = janet_stack_frame(stack);
|
||||
if (!frame->env) {
|
||||
/* Lazy capture of current stack frame */
|
||||
|
@ -980,7 +1022,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
if (func->gc.flags & JANET_FUNCFLAG_TRACE) {
|
||||
vm_do_trace(func, fiber->stacktop - fiber->stackstart, fiber->data + fiber->stackstart);
|
||||
}
|
||||
janet_stack_frame(stack)->pc = pc;
|
||||
vm_commit();
|
||||
if (janet_fiber_funcframe(fiber, func)) {
|
||||
int32_t n = fiber->stacktop - fiber->stackstart;
|
||||
janet_panicf("%v called with %d argument%s, expected %d",
|
||||
|
@ -1056,7 +1098,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
vm_maybe_auto_suspend(1);
|
||||
vm_assert_type(stack[B], JANET_FIBER);
|
||||
JanetFiber *child = janet_unwrap_fiber(stack[B]);
|
||||
if (janet_check_can_resume(child, &retreg)) {
|
||||
if (janet_check_can_resume(child, &retreg, 0)) {
|
||||
vm_commit();
|
||||
janet_panicv(retreg);
|
||||
}
|
||||
|
@ -1096,7 +1138,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||
Janet retreg;
|
||||
vm_assert_type(stack[B], JANET_FIBER);
|
||||
JanetFiber *child = janet_unwrap_fiber(stack[B]);
|
||||
if (janet_check_can_resume(child, &retreg)) {
|
||||
if (janet_check_can_resume(child, &retreg, 1)) {
|
||||
vm_commit();
|
||||
janet_panicv(retreg);
|
||||
}
|
||||
|
@ -1285,6 +1327,12 @@ JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) {
|
|||
return signal;
|
||||
}
|
||||
|
||||
static Janet void_cfunction(int32_t argc, Janet *argv) {
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("placeholder");
|
||||
}
|
||||
|
||||
Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
|
||||
/* Check entry conditions */
|
||||
if (!janet_vm.fiber)
|
||||
|
@ -1292,9 +1340,17 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
|
|||
if (janet_vm.stackn >= JANET_RECURSION_GUARD)
|
||||
janet_panic("C stack recursed too deeply");
|
||||
|
||||
/* Dirty stack */
|
||||
int32_t dirty_stack = janet_vm.fiber->stacktop - janet_vm.fiber->stackstart;
|
||||
if (dirty_stack) {
|
||||
janet_fiber_cframe(janet_vm.fiber, void_cfunction);
|
||||
}
|
||||
|
||||
/* Tracing */
|
||||
if (fun->gc.flags & JANET_FUNCFLAG_TRACE) {
|
||||
janet_vm.stackn++;
|
||||
vm_do_trace(fun, argc, argv);
|
||||
janet_vm.stackn--;
|
||||
}
|
||||
|
||||
/* Push frame */
|
||||
|
@ -1322,6 +1378,10 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
|
|||
/* Teardown */
|
||||
janet_vm.stackn = oldn;
|
||||
janet_gcunlock(handle);
|
||||
if (dirty_stack) {
|
||||
janet_fiber_popframe(janet_vm.fiber);
|
||||
janet_vm.fiber->stacktop += dirty_stack;
|
||||
}
|
||||
|
||||
if (signal != JANET_SIGNAL_OK) {
|
||||
janet_panicv(*janet_vm.return_reg);
|
||||
|
@ -1330,7 +1390,7 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
|
|||
return *janet_vm.return_reg;
|
||||
}
|
||||
|
||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out) {
|
||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out, int is_cancel) {
|
||||
/* Check conditions */
|
||||
JanetFiberStatus old_status = janet_fiber_status(fiber);
|
||||
if (janet_vm.stackn >= JANET_RECURSION_GUARD) {
|
||||
|
@ -1338,6 +1398,20 @@ static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out) {
|
|||
*out = janet_cstringv("C stack recursed too deeply");
|
||||
return JANET_SIGNAL_ERROR;
|
||||
}
|
||||
/* If a "task" fiber is trying to be used as a normal fiber, detect that. See bug #920.
|
||||
* Fibers must be marked as root fibers manually, or by the ev scheduler. */
|
||||
if (janet_vm.fiber != NULL && (fiber->gc.flags & JANET_FIBER_FLAG_ROOT)) {
|
||||
#ifdef JANET_EV
|
||||
*out = janet_cstringv(is_cancel
|
||||
? "cannot cancel root fiber, use ev/cancel"
|
||||
: "cannot resume root fiber, use ev/go");
|
||||
#else
|
||||
*out = janet_cstringv(is_cancel
|
||||
? "cannot cancel root fiber"
|
||||
: "cannot resume root fiber");
|
||||
#endif
|
||||
return JANET_SIGNAL_ERROR;
|
||||
}
|
||||
if (old_status == JANET_STATUS_ALIVE ||
|
||||
old_status == JANET_STATUS_DEAD ||
|
||||
(old_status >= JANET_STATUS_USER0 && old_status <= JANET_STATUS_USER4) ||
|
||||
|
@ -1391,6 +1465,7 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
|
|||
if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
|
||||
*out = in;
|
||||
janet_fiber_set_status(fiber, sig);
|
||||
fiber->last_value = child->last_value;
|
||||
return sig;
|
||||
}
|
||||
/* Check if we need any special handling for certain opcodes */
|
||||
|
@ -1452,14 +1527,14 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
|
|||
/* Enter the main vm loop */
|
||||
JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
|
||||
/* Check conditions */
|
||||
JanetSignal tmp_signal = janet_check_can_resume(fiber, out);
|
||||
JanetSignal tmp_signal = janet_check_can_resume(fiber, out, 0);
|
||||
if (tmp_signal) return tmp_signal;
|
||||
return janet_continue_no_check(fiber, in, out);
|
||||
}
|
||||
|
||||
/* Enter the main vm loop but immediately raise a signal */
|
||||
JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig) {
|
||||
JanetSignal tmp_signal = janet_check_can_resume(fiber, out);
|
||||
JanetSignal tmp_signal = janet_check_can_resume(fiber, out, sig != JANET_SIGNAL_OK);
|
||||
if (tmp_signal) return tmp_signal;
|
||||
if (sig != JANET_SIGNAL_OK) {
|
||||
JanetFiber *child = fiber;
|
||||
|
@ -1484,7 +1559,7 @@ JanetSignal janet_pcall(
|
|||
fiber = janet_fiber(fun, 64, argc, argv);
|
||||
}
|
||||
if (f) *f = fiber;
|
||||
if (!fiber) {
|
||||
if (NULL == fiber) {
|
||||
*out = janet_cstringv("arity mismatch");
|
||||
return JANET_SIGNAL_ERROR;
|
||||
}
|
||||
|
@ -1510,9 +1585,11 @@ int janet_init(void) {
|
|||
|
||||
/* Garbage collection */
|
||||
janet_vm.blocks = NULL;
|
||||
janet_vm.weak_blocks = NULL;
|
||||
janet_vm.next_collection = 0;
|
||||
janet_vm.gc_interval = 0x400000;
|
||||
janet_vm.block_count = 0;
|
||||
janet_vm.gc_mark_phase = 0;
|
||||
|
||||
janet_symcache_init();
|
||||
|
||||
|
@ -1527,6 +1604,9 @@ int janet_init(void) {
|
|||
janet_vm.scratch_len = 0;
|
||||
janet_vm.scratch_cap = 0;
|
||||
|
||||
/* Sandbox flags */
|
||||
janet_vm.sandbox_flags = 0;
|
||||
|
||||
/* Initialize registry */
|
||||
janet_vm.registry = NULL;
|
||||
janet_vm.registry_cap = 0;
|
||||
|
@ -1568,6 +1648,18 @@ int janet_init(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Disable some features at runtime with no way to re-enable them */
|
||||
void janet_sandbox(uint32_t flags) {
|
||||
janet_sandbox_assert(JANET_SANDBOX_SANDBOX);
|
||||
janet_vm.sandbox_flags |= flags;
|
||||
}
|
||||
|
||||
void janet_sandbox_assert(uint32_t forbidden_flags) {
|
||||
if (forbidden_flags & janet_vm.sandbox_flags) {
|
||||
janet_panic("operation forbidden by sandbox");
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear all memory associated with the VM */
|
||||
void janet_deinit(void) {
|
||||
janet_clear_memory();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -43,10 +43,10 @@ int (janet_truthy)(Janet x) {
|
|||
return janet_truthy(x);
|
||||
}
|
||||
|
||||
const JanetKV *(janet_unwrap_struct)(Janet x) {
|
||||
JanetStruct(janet_unwrap_struct)(Janet x) {
|
||||
return janet_unwrap_struct(x);
|
||||
}
|
||||
const Janet *(janet_unwrap_tuple)(Janet x) {
|
||||
JanetTuple(janet_unwrap_tuple)(Janet x) {
|
||||
return janet_unwrap_tuple(x);
|
||||
}
|
||||
JanetFiber *(janet_unwrap_fiber)(Janet x) {
|
||||
|
@ -61,16 +61,16 @@ JanetTable *(janet_unwrap_table)(Janet x) {
|
|||
JanetBuffer *(janet_unwrap_buffer)(Janet x) {
|
||||
return janet_unwrap_buffer(x);
|
||||
}
|
||||
const uint8_t *(janet_unwrap_string)(Janet x) {
|
||||
JanetString(janet_unwrap_string)(Janet x) {
|
||||
return janet_unwrap_string(x);
|
||||
}
|
||||
const uint8_t *(janet_unwrap_symbol)(Janet x) {
|
||||
JanetSymbol(janet_unwrap_symbol)(Janet x) {
|
||||
return janet_unwrap_symbol(x);
|
||||
}
|
||||
const uint8_t *(janet_unwrap_keyword)(Janet x) {
|
||||
JanetKeyword(janet_unwrap_keyword)(Janet x) {
|
||||
return janet_unwrap_keyword(x);
|
||||
}
|
||||
void *(janet_unwrap_abstract)(Janet x) {
|
||||
JanetAbstract(janet_unwrap_abstract)(Janet x) {
|
||||
return janet_unwrap_abstract(x);
|
||||
}
|
||||
void *(janet_unwrap_pointer)(Janet x) {
|
||||
|
@ -102,22 +102,22 @@ Janet(janet_wrap_false)(void) {
|
|||
Janet(janet_wrap_boolean)(int x) {
|
||||
return janet_wrap_boolean(x);
|
||||
}
|
||||
Janet(janet_wrap_string)(const uint8_t *x) {
|
||||
Janet(janet_wrap_string)(JanetString x) {
|
||||
return janet_wrap_string(x);
|
||||
}
|
||||
Janet(janet_wrap_symbol)(const uint8_t *x) {
|
||||
Janet(janet_wrap_symbol)(JanetSymbol x) {
|
||||
return janet_wrap_symbol(x);
|
||||
}
|
||||
Janet(janet_wrap_keyword)(const uint8_t *x) {
|
||||
Janet(janet_wrap_keyword)(JanetKeyword x) {
|
||||
return janet_wrap_keyword(x);
|
||||
}
|
||||
Janet(janet_wrap_array)(JanetArray *x) {
|
||||
return janet_wrap_array(x);
|
||||
}
|
||||
Janet(janet_wrap_tuple)(const Janet *x) {
|
||||
Janet(janet_wrap_tuple)(JanetTuple x) {
|
||||
return janet_wrap_tuple(x);
|
||||
}
|
||||
Janet(janet_wrap_struct)(const JanetKV *x) {
|
||||
Janet(janet_wrap_struct)(JanetStruct x) {
|
||||
return janet_wrap_struct(x);
|
||||
}
|
||||
Janet(janet_wrap_fiber)(JanetFiber *x) {
|
||||
|
@ -135,7 +135,7 @@ Janet(janet_wrap_cfunction)(JanetCFunction x) {
|
|||
Janet(janet_wrap_table)(JanetTable *x) {
|
||||
return janet_wrap_table(x);
|
||||
}
|
||||
Janet(janet_wrap_abstract)(void *x) {
|
||||
Janet(janet_wrap_abstract)(JanetAbstract x) {
|
||||
return janet_wrap_abstract(x);
|
||||
}
|
||||
Janet(janet_wrap_pointer)(void *x) {
|
||||
|
@ -317,4 +317,3 @@ JANET_WRAP_DEFINE(pointer, void *, JANET_POINTER, pointer)
|
|||
#undef JANET_WRAP_DEFINE
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -57,8 +57,8 @@ extern "C" {
|
|||
#define JANET_BSD 1
|
||||
#endif
|
||||
|
||||
/* Check for Mac */
|
||||
#ifdef __APPLE__
|
||||
/* Check for macOS or OS X */
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
#define JANET_APPLE 1
|
||||
#endif
|
||||
|
||||
|
@ -67,6 +67,11 @@ extern "C" {
|
|||
#define JANET_LINUX 1
|
||||
#endif
|
||||
|
||||
/* Check for Cygwin */
|
||||
#if defined(__CYGWIN__)
|
||||
#define JANET_CYGWIN 1
|
||||
#endif
|
||||
|
||||
/* Check Unix */
|
||||
#if defined(_AIX) \
|
||||
|| defined(__APPLE__) /* Darwin */ \
|
||||
|
@ -87,6 +92,16 @@ extern "C" {
|
|||
#define JANET_WINDOWS 1
|
||||
#endif
|
||||
|
||||
/* Check if compiling with MSVC - else assume a GCC-like compiler by default */
|
||||
#ifdef _MSC_VER
|
||||
#define JANET_MSVC
|
||||
#endif
|
||||
|
||||
/* Check Mingw 32-bit and 64-bit */
|
||||
#ifdef __MINGW32__
|
||||
#define JANET_MINGW
|
||||
#endif
|
||||
|
||||
/* Check 64-bit vs 32-bit */
|
||||
#if ((defined(__x86_64__) || defined(_M_X64)) \
|
||||
&& (defined(JANET_POSIX) || defined(JANET_WINDOWS))) \
|
||||
|
@ -96,7 +111,9 @@ extern "C" {
|
|||
|| (defined(__sparc__) && defined(__arch64__) || defined (__sparcv9)) /* BE */ \
|
||||
|| defined(__s390x__) /* S390 64-bit (BE) */ \
|
||||
|| (defined(__ppc64__) || defined(__PPC64__)) \
|
||||
|| defined(__aarch64__) /* ARM 64-bit */
|
||||
|| defined(__aarch64__) /* ARM 64-bit */ \
|
||||
|| (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ \
|
||||
|| defined(__loongarch64) /* LoongArch64 64-bit */
|
||||
#define JANET_64 1
|
||||
#else
|
||||
#define JANET_32 1
|
||||
|
@ -163,6 +180,21 @@ extern "C" {
|
|||
#define JANET_DYNAMIC_MODULES
|
||||
#endif
|
||||
|
||||
/* Enable or disable the FFI library. Currently, FFI only enabled on
|
||||
* x86-64 operating systems. */
|
||||
#ifndef JANET_NO_FFI
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
#define JANET_FFI
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* If FFI is enabled and FFI-JIT is not disabled... */
|
||||
#ifdef JANET_FFI
|
||||
#ifndef JANET_NO_FFI_JIT
|
||||
#define JANET_FFI_JIT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Enable or disable the assembler. Enabled by default. */
|
||||
#ifndef JANET_NO_ASSEMBLER
|
||||
#define JANET_ASSEMBLER
|
||||
|
@ -203,10 +235,28 @@ extern "C" {
|
|||
#define JANET_EV_KQUEUE
|
||||
#endif
|
||||
|
||||
/* Use poll as last resort */
|
||||
#if !defined(JANET_WINDOWS) && !defined(JANET_EV_EPOLL) && !defined(JANET_EV_KQUEUE)
|
||||
#define JANET_EV_POLL
|
||||
#endif
|
||||
|
||||
/* How to export symbols */
|
||||
#ifndef JANET_EXPORT
|
||||
#ifdef JANET_WINDOWS
|
||||
#define JANET_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define JANET_EXPORT __attribute__((visibility ("default")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* How declare API functions */
|
||||
#ifndef JANET_API
|
||||
#ifdef JANET_WINDOWS
|
||||
#ifdef JANET_DLL_IMPORT
|
||||
#define JANET_API __declspec(dllimport)
|
||||
#else
|
||||
#define JANET_API __declspec(dllexport)
|
||||
#endif
|
||||
#else
|
||||
#define JANET_API __attribute__((visibility ("default")))
|
||||
#endif
|
||||
|
@ -228,7 +278,7 @@ extern "C" {
|
|||
/* Maximum depth to follow table prototypes before giving up and returning nil. */
|
||||
#define JANET_MAX_PROTO_DEPTH 200
|
||||
|
||||
/* Maximum depth to follow table prototypes before giving up and returning nil. */
|
||||
/* Prevent macros to expand too deeply and error out. */
|
||||
#define JANET_MAX_MACRO_EXPAND 200
|
||||
|
||||
/* Define default max stack size for stacks before raising a stack overflow error.
|
||||
|
@ -249,10 +299,11 @@ extern "C" {
|
|||
#ifndef JANET_NO_NANBOX
|
||||
#ifdef JANET_32
|
||||
#define JANET_NANBOX_32
|
||||
#elif defined(__x86_64__) || defined(_WIN64)
|
||||
#elif defined(__x86_64__) || defined(_WIN64) || defined(__riscv)
|
||||
/* We will only enable nanboxing by default on 64 bit systems
|
||||
* on x86. This is mainly because the approach is tied to the
|
||||
* implicit 47 bit address space. */
|
||||
* for x64 and risc-v. This is mainly because the approach is tied to the
|
||||
* implicit 47 bit address space. Many arches allow/require this, but not all,
|
||||
* and it requires cooperation from the OS. ARM should also work in many configurations. */
|
||||
#define JANET_NANBOX_64
|
||||
#endif
|
||||
#endif
|
||||
|
@ -299,10 +350,10 @@ typedef struct {
|
|||
JANET_CURRENT_CONFIG_BITS })
|
||||
#endif
|
||||
|
||||
/* What to do when out of memory */
|
||||
#ifndef JANET_OUT_OF_MEMORY
|
||||
#include <stdio.h>
|
||||
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
|
||||
/* Some extra includes if EV is enabled */
|
||||
#ifdef JANET_EV
|
||||
typedef struct JanetOSMutex JanetOSMutex;
|
||||
typedef struct JanetOSRWLock JanetOSRWLock;
|
||||
#endif
|
||||
|
||||
/***** END SECTION CONFIG *****/
|
||||
|
@ -322,23 +373,9 @@ typedef struct {
|
|||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Some extra includes if EV is enabled */
|
||||
#ifdef JANET_EV
|
||||
#ifdef JANET_WINDOWS
|
||||
typedef struct JanetDudCriticalSection {
|
||||
/* Avoid including windows.h here - instead, create a structure of the same size */
|
||||
/* Needs to be same size as crtical section see WinNT.h for CRITCIAL_SECTION definition */
|
||||
void *debug_info;
|
||||
long lock_count;
|
||||
long recursion_count;
|
||||
void *owning_thread;
|
||||
void *lock_semaphore;
|
||||
unsigned long spin_count;
|
||||
} JanetOSMutex;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
typedef pthread_mutex_t JanetOSMutex;
|
||||
#endif
|
||||
/* What to do when out of memory */
|
||||
#ifndef JANET_OUT_OF_MEMORY
|
||||
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "%s:%d - janet out of memory\n", __FILE__, __LINE__); exit(1); } while (0)
|
||||
#endif
|
||||
|
||||
#ifdef JANET_BSD
|
||||
|
@ -375,12 +412,11 @@ typedef enum {
|
|||
JANET_SIGNAL_USER6,
|
||||
JANET_SIGNAL_USER7,
|
||||
JANET_SIGNAL_USER8,
|
||||
JANET_SIGNAL_USER9
|
||||
JANET_SIGNAL_USER9,
|
||||
JANET_SIGNAL_INTERRUPT = JANET_SIGNAL_USER8,
|
||||
JANET_SIGNAL_EVENT = JANET_SIGNAL_USER9,
|
||||
} JanetSignal;
|
||||
|
||||
#define JANET_SIGNAL_EVENT JANET_SIGNAL_USER9
|
||||
#define JANET_SIGNAL_INTERRUPT JANET_SIGNAL_USER8
|
||||
|
||||
/* Fiber statuses - mostly corresponds to signals. */
|
||||
typedef enum {
|
||||
JANET_STATUS_DEAD,
|
||||
|
@ -430,6 +466,7 @@ typedef struct JanetReg JanetReg;
|
|||
typedef struct JanetRegExt JanetRegExt;
|
||||
typedef struct JanetMethod JanetMethod;
|
||||
typedef struct JanetSourceMapping JanetSourceMapping;
|
||||
typedef struct JanetSymbolMap JanetSymbolMap;
|
||||
typedef struct JanetView JanetView;
|
||||
typedef struct JanetByteView JanetByteView;
|
||||
typedef struct JanetDictView JanetDictView;
|
||||
|
@ -543,69 +580,81 @@ typedef void *JanetAbstract;
|
|||
|
||||
#define JANET_STREAM_CLOSED 0x1
|
||||
#define JANET_STREAM_SOCKET 0x2
|
||||
#define JANET_STREAM_IOCP 0x4
|
||||
#define JANET_STREAM_UNREGISTERED 0x4
|
||||
#define JANET_STREAM_READABLE 0x200
|
||||
#define JANET_STREAM_WRITABLE 0x400
|
||||
#define JANET_STREAM_ACCEPTABLE 0x800
|
||||
#define JANET_STREAM_UDPSERVER 0x1000
|
||||
#define JANET_STREAM_TOCLOSE 0x10000
|
||||
|
||||
typedef enum {
|
||||
JANET_ASYNC_EVENT_INIT,
|
||||
JANET_ASYNC_EVENT_MARK,
|
||||
JANET_ASYNC_EVENT_DEINIT,
|
||||
JANET_ASYNC_EVENT_CLOSE,
|
||||
JANET_ASYNC_EVENT_ERR,
|
||||
JANET_ASYNC_EVENT_HUP,
|
||||
JANET_ASYNC_EVENT_READ,
|
||||
JANET_ASYNC_EVENT_WRITE,
|
||||
JANET_ASYNC_EVENT_CANCEL,
|
||||
JANET_ASYNC_EVENT_COMPLETE, /* Used on windows for IOCP */
|
||||
JANET_ASYNC_EVENT_USER
|
||||
JANET_ASYNC_EVENT_INIT = 0,
|
||||
JANET_ASYNC_EVENT_MARK = 1,
|
||||
JANET_ASYNC_EVENT_DEINIT = 2,
|
||||
JANET_ASYNC_EVENT_CLOSE = 3,
|
||||
JANET_ASYNC_EVENT_ERR = 4,
|
||||
JANET_ASYNC_EVENT_HUP = 5,
|
||||
JANET_ASYNC_EVENT_READ = 6,
|
||||
JANET_ASYNC_EVENT_WRITE = 7,
|
||||
JANET_ASYNC_EVENT_COMPLETE = 8, /* Used on windows for IOCP */
|
||||
JANET_ASYNC_EVENT_FAILED = 9 /* Used on windows for IOCP */
|
||||
} JanetAsyncEvent;
|
||||
|
||||
#define JANET_ASYNC_LISTEN_READ (1 << JANET_ASYNC_EVENT_READ)
|
||||
#define JANET_ASYNC_LISTEN_WRITE (1 << JANET_ASYNC_EVENT_WRITE)
|
||||
|
||||
typedef enum {
|
||||
JANET_ASYNC_STATUS_NOT_DONE,
|
||||
JANET_ASYNC_STATUS_DONE
|
||||
} JanetAsyncStatus;
|
||||
JANET_ASYNC_LISTEN_READ = 1,
|
||||
JANET_ASYNC_LISTEN_WRITE,
|
||||
JANET_ASYNC_LISTEN_BOTH
|
||||
} JanetAsyncMode;
|
||||
|
||||
/* Typedefs */
|
||||
typedef struct JanetListenerState JanetListenerState;
|
||||
typedef struct JanetStream JanetStream;
|
||||
typedef JanetAsyncStatus(*JanetListener)(JanetListenerState *state, JanetAsyncEvent event);
|
||||
|
||||
/* Wrapper around file descriptors and HANDLEs that can be polled. */
|
||||
struct JanetStream {
|
||||
JanetHandle handle;
|
||||
uint32_t flags;
|
||||
/* Linked list of all in-flight IO routines for this stream */
|
||||
JanetListenerState *state;
|
||||
uint32_t index;
|
||||
JanetFiber *read_fiber;
|
||||
JanetFiber *write_fiber;
|
||||
const void *methods; /* Methods for this stream */
|
||||
/* internal - used to disallow multiple concurrent reads / writes on the same stream.
|
||||
* this constraint may be lifted later but allowing such would require more internal book keeping
|
||||
* for some implementations. You can read and write at the same time on the same stream, though. */
|
||||
int _mask;
|
||||
};
|
||||
|
||||
/* Interface for state machine based event loop */
|
||||
struct JanetListenerState {
|
||||
JanetListener machine;
|
||||
JanetFiber *fiber;
|
||||
JanetStream *stream;
|
||||
void *event; /* Used to pass data from asynchronous IO event. Contents depend on both
|
||||
implementation of the event loop and the particular event. */
|
||||
typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event);
|
||||
|
||||
/* Start listening for events from a stream on the current root fiber. After
|
||||
* calling this, users should call janet_await() before returning from the
|
||||
* current C Function. This also will call janet_await.
|
||||
* mode is which events to listen for, and callback is the function pointer to
|
||||
* call when ever an event is sent from the event loop. state is an optional (can be NULL)
|
||||
* pointer to data allocated with janet_malloc. This pointer will be passed to callback as
|
||||
* fiber->ev_state. It will also be freed for you by the runtime when the event loop determines
|
||||
* it can no longer be referenced. On windows, the contents of state MUST contained an OVERLAPPED struct. */
|
||||
JANET_API JANET_NO_RETURN void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
|
||||
|
||||
/* Do not send any more events to the given callback. Call this after scheduling fiber to be resume
|
||||
* or canceled. */
|
||||
JANET_API void janet_async_end(JanetFiber *fiber);
|
||||
|
||||
/* Needed for windows to mark a fiber as waiting for an IOCP completion event. Noop on other platforms. */
|
||||
JANET_API void janet_async_in_flight(JanetFiber *fiber);
|
||||
|
||||
/* On some platforms, it is important to be able to control if a stream is edge-trigger or level triggered.
|
||||
* For example, a server that is accepting connections might want to be level triggered or edge-triggered
|
||||
* depending on expected service. */
|
||||
JANET_API void janet_stream_edge_triggered(JanetStream *stream);
|
||||
JANET_API void janet_stream_level_triggered(JanetStream *stream);
|
||||
|
||||
#endif
|
||||
|
||||
/* Janet uses atomic integers in several places for synchronization between threads and
|
||||
* signals. Define them here */
|
||||
#ifdef JANET_WINDOWS
|
||||
void *tag; /* Used to associate listeners with an overlapped structure */
|
||||
int bytes; /* Used to track how many bytes were transfered. */
|
||||
#endif
|
||||
/* internal */
|
||||
size_t _index;
|
||||
int _mask;
|
||||
JanetListenerState *_next;
|
||||
};
|
||||
typedef long JanetAtomicInt;
|
||||
#else
|
||||
typedef int32_t JanetAtomicInt;
|
||||
#endif
|
||||
JANET_API JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x);
|
||||
JANET_API JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x);
|
||||
JANET_API JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x);
|
||||
|
||||
/* We provide three possible implementations of Janets. The preferred
|
||||
* nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the
|
||||
|
@ -633,10 +682,10 @@ struct JanetListenerState {
|
|||
* external bindings, we should prefer using the Head structs directly, and
|
||||
* use the host language to add sugar around the manipulation of the Janet types. */
|
||||
|
||||
JANET_API JanetStructHead *janet_struct_head(const JanetKV *st);
|
||||
JANET_API JanetStructHead *janet_struct_head(JanetStruct st);
|
||||
JANET_API JanetAbstractHead *janet_abstract_head(const void *abstract);
|
||||
JANET_API JanetStringHead *janet_string_head(const uint8_t *s);
|
||||
JANET_API JanetTupleHead *janet_tuple_head(const Janet *tuple);
|
||||
JANET_API JanetStringHead *janet_string_head(JanetString s);
|
||||
JANET_API JanetTupleHead *janet_tuple_head(JanetTuple tuple);
|
||||
|
||||
/* Some language bindings won't have access to the macro versions. */
|
||||
|
||||
|
@ -645,16 +694,16 @@ JANET_API int janet_checktype(Janet x, JanetType type);
|
|||
JANET_API int janet_checktypes(Janet x, int typeflags);
|
||||
JANET_API int janet_truthy(Janet x);
|
||||
|
||||
JANET_API const JanetKV *janet_unwrap_struct(Janet x);
|
||||
JANET_API const Janet *janet_unwrap_tuple(Janet x);
|
||||
JANET_API JanetStruct janet_unwrap_struct(Janet x);
|
||||
JANET_API JanetTuple janet_unwrap_tuple(Janet x);
|
||||
JANET_API JanetFiber *janet_unwrap_fiber(Janet x);
|
||||
JANET_API JanetArray *janet_unwrap_array(Janet x);
|
||||
JANET_API JanetTable *janet_unwrap_table(Janet x);
|
||||
JANET_API JanetBuffer *janet_unwrap_buffer(Janet x);
|
||||
JANET_API const uint8_t *janet_unwrap_string(Janet x);
|
||||
JANET_API const uint8_t *janet_unwrap_symbol(Janet x);
|
||||
JANET_API const uint8_t *janet_unwrap_keyword(Janet x);
|
||||
JANET_API void *janet_unwrap_abstract(Janet x);
|
||||
JANET_API JanetString janet_unwrap_string(Janet x);
|
||||
JANET_API JanetSymbol janet_unwrap_symbol(Janet x);
|
||||
JANET_API JanetKeyword janet_unwrap_keyword(Janet x);
|
||||
JANET_API JanetAbstract janet_unwrap_abstract(Janet x);
|
||||
JANET_API void *janet_unwrap_pointer(Janet x);
|
||||
JANET_API JanetFunction *janet_unwrap_function(Janet x);
|
||||
JANET_API JanetCFunction janet_unwrap_cfunction(Janet x);
|
||||
|
@ -667,18 +716,18 @@ JANET_API Janet janet_wrap_number(double x);
|
|||
JANET_API Janet janet_wrap_true(void);
|
||||
JANET_API Janet janet_wrap_false(void);
|
||||
JANET_API Janet janet_wrap_boolean(int x);
|
||||
JANET_API Janet janet_wrap_string(const uint8_t *x);
|
||||
JANET_API Janet janet_wrap_symbol(const uint8_t *x);
|
||||
JANET_API Janet janet_wrap_keyword(const uint8_t *x);
|
||||
JANET_API Janet janet_wrap_string(JanetString x);
|
||||
JANET_API Janet janet_wrap_symbol(JanetSymbol x);
|
||||
JANET_API Janet janet_wrap_keyword(JanetKeyword x);
|
||||
JANET_API Janet janet_wrap_array(JanetArray *x);
|
||||
JANET_API Janet janet_wrap_tuple(const Janet *x);
|
||||
JANET_API Janet janet_wrap_struct(const JanetKV *x);
|
||||
JANET_API Janet janet_wrap_tuple(JanetTuple x);
|
||||
JANET_API Janet janet_wrap_struct(JanetStruct x);
|
||||
JANET_API Janet janet_wrap_fiber(JanetFiber *x);
|
||||
JANET_API Janet janet_wrap_buffer(JanetBuffer *x);
|
||||
JANET_API Janet janet_wrap_function(JanetFunction *x);
|
||||
JANET_API Janet janet_wrap_cfunction(JanetCFunction x);
|
||||
JANET_API Janet janet_wrap_table(JanetTable *x);
|
||||
JANET_API Janet janet_wrap_abstract(void *x);
|
||||
JANET_API Janet janet_wrap_abstract(JanetAbstract x);
|
||||
JANET_API Janet janet_wrap_pointer(void *x);
|
||||
JANET_API Janet janet_wrap_integer(int32_t x);
|
||||
|
||||
|
@ -710,6 +759,7 @@ JANET_API Janet janet_wrap_integer(int32_t x);
|
|||
? janet_nanbox_isnumber(x) \
|
||||
: janet_nanbox_checkauxtype((x), (t)))
|
||||
|
||||
/* Use JANET_API so that modules will use a local version of these functions if possible */
|
||||
JANET_API void *janet_nanbox_to_pointer(Janet x);
|
||||
JANET_API Janet janet_nanbox_from_pointer(void *p, uint64_t tagmask);
|
||||
JANET_API Janet janet_nanbox_from_cpointer(const void *p, uint64_t tagmask);
|
||||
|
@ -756,14 +806,14 @@ JANET_API Janet janet_nanbox_from_bits(uint64_t bits);
|
|||
#define janet_wrap_pointer(s) janet_nanbox_wrap_((s), JANET_POINTER)
|
||||
|
||||
/* Unwrap the pointer types */
|
||||
#define janet_unwrap_struct(x) ((const JanetKV *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_tuple(x) ((const Janet *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_struct(x) ((JanetStruct)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_tuple(x) ((JanetTuple)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_fiber(x) ((JanetFiber *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_array(x) ((JanetArray *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_table(x) ((JanetTable *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_buffer(x) ((JanetBuffer *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_string(x) ((const uint8_t *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_symbol(x) ((const uint8_t *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_string(x) ((JanetString)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_symbol(x) ((JanetSymbol)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_keyword(x) ((const uint8_t *)janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_abstract(x) (janet_nanbox_to_pointer(x))
|
||||
#define janet_unwrap_pointer(x) (janet_nanbox_to_pointer(x))
|
||||
|
@ -805,15 +855,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
|
|||
#define janet_wrap_cfunction(s) janet_nanbox32_from_tagp(JANET_CFUNCTION, (void *)(s))
|
||||
#define janet_wrap_pointer(s) janet_nanbox32_from_tagp(JANET_POINTER, (void *)(s))
|
||||
|
||||
#define janet_unwrap_struct(x) ((const JanetKV *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_tuple(x) ((const Janet *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_struct(x) ((JanetStruct)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_tuple(x) ((JanetTuple)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_fiber(x) ((JanetFiber *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_array(x) ((JanetArray *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_table(x) ((JanetTable *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_buffer(x) ((JanetBuffer *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_string(x) ((const uint8_t *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_symbol(x) ((const uint8_t *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_keyword(x) ((const uint8_t *)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_string(x) ((JanetString)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_symbol(x) ((JanetSymbol)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_keyword(x) ((JanetKeyword)(x).tagged.payload.pointer)
|
||||
#define janet_unwrap_abstract(x) ((x).tagged.payload.pointer)
|
||||
#define janet_unwrap_pointer(x) ((x).tagged.payload.pointer)
|
||||
#define janet_unwrap_function(x) ((JanetFunction *)(x).tagged.payload.pointer)
|
||||
|
@ -828,15 +878,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
|
|||
#define janet_truthy(x) \
|
||||
((x).type != JANET_NIL && ((x).type != JANET_BOOLEAN || ((x).as.u64 & 0x1)))
|
||||
|
||||
#define janet_unwrap_struct(x) ((const JanetKV *)(x).as.pointer)
|
||||
#define janet_unwrap_tuple(x) ((const Janet *)(x).as.pointer)
|
||||
#define janet_unwrap_struct(x) ((JanetStruct)(x).as.pointer)
|
||||
#define janet_unwrap_tuple(x) ((JanetTuple)(x).as.pointer)
|
||||
#define janet_unwrap_fiber(x) ((JanetFiber *)(x).as.pointer)
|
||||
#define janet_unwrap_array(x) ((JanetArray *)(x).as.pointer)
|
||||
#define janet_unwrap_table(x) ((JanetTable *)(x).as.pointer)
|
||||
#define janet_unwrap_buffer(x) ((JanetBuffer *)(x).as.pointer)
|
||||
#define janet_unwrap_string(x) ((const uint8_t *)(x).as.pointer)
|
||||
#define janet_unwrap_symbol(x) ((const uint8_t *)(x).as.pointer)
|
||||
#define janet_unwrap_keyword(x) ((const uint8_t *)(x).as.pointer)
|
||||
#define janet_unwrap_string(x) ((JanetString)(x).as.pointer)
|
||||
#define janet_unwrap_symbol(x) ((JanetSymbol)(x).as.pointer)
|
||||
#define janet_unwrap_keyword(x) ((JanetKeyword)(x).as.pointer)
|
||||
#define janet_unwrap_abstract(x) ((x).as.pointer)
|
||||
#define janet_unwrap_pointer(x) ((x).as.pointer)
|
||||
#define janet_unwrap_function(x) ((JanetFunction *)(x).as.pointer)
|
||||
|
@ -848,11 +898,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
|
|||
#endif
|
||||
|
||||
JANET_API int janet_checkint(Janet x);
|
||||
JANET_API int janet_checkuint(Janet x);
|
||||
JANET_API int janet_checkint64(Janet x);
|
||||
JANET_API int janet_checkuint64(Janet x);
|
||||
JANET_API int janet_checksize(Janet x);
|
||||
JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at);
|
||||
#define janet_checkintrange(x) ((x) >= INT32_MIN && (x) <= INT32_MAX && (x) == (int32_t)(x))
|
||||
#define janet_checkuintrange(x) ((x) >= 0 && (x) <= UINT32_MAX && (x) == (uint32_t)(x))
|
||||
#define janet_checkint64range(x) ((x) >= JANET_INTMIN_DOUBLE && (x) <= JANET_INTMAX_DOUBLE && (x) == (int64_t)(x))
|
||||
#define janet_checkuint64range(x) ((x) >= 0 && (x) <= JANET_INTMAX_DOUBLE && (x) == (uint64_t)(x))
|
||||
#define janet_unwrap_integer(x) ((int32_t) janet_unwrap_number(x))
|
||||
#define janet_wrap_integer(x) janet_wrap_number((int32_t)(x))
|
||||
|
||||
|
@ -865,8 +919,8 @@ struct JanetGCObject {
|
|||
int32_t flags;
|
||||
union {
|
||||
JanetGCObject *next;
|
||||
int32_t refcount; /* For threaded abstract types */
|
||||
};
|
||||
volatile JanetAtomicInt refcount; /* For threaded abstract types */
|
||||
} data;
|
||||
};
|
||||
|
||||
/* A lightweight green thread in janet. Does not correspond to
|
||||
|
@ -888,8 +942,10 @@ struct JanetFiber {
|
|||
* that is, fibers that are scheduled on the event loop and behave much like threads
|
||||
* in a multi-tasking system. It would be possible to move these fields to a new
|
||||
* type, say "JanetTask", that as separate from fibers to save a bit of space. */
|
||||
JanetListenerState *waiting;
|
||||
uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */
|
||||
JanetEVCallback ev_callback; /* Call this before starting scheduled fibers */
|
||||
JanetStream *ev_stream; /* which stream we are waiting on */
|
||||
void *ev_state; /* Extra data for ev callback state. On windows, first element must be OVERLAPPED. */
|
||||
void *supervisor_channel; /* Channel to push self to when complete */
|
||||
#endif
|
||||
};
|
||||
|
@ -961,6 +1017,7 @@ struct JanetStructHead {
|
|||
int32_t length;
|
||||
int32_t hash;
|
||||
int32_t capacity;
|
||||
const JanetKV *proto;
|
||||
const JanetKV data[];
|
||||
};
|
||||
|
||||
|
@ -983,6 +1040,7 @@ struct JanetAbstractHead {
|
|||
/* Some function definition flags */
|
||||
#define JANET_FUNCDEF_FLAG_VARARG 0x10000
|
||||
#define JANET_FUNCDEF_FLAG_NEEDSENV 0x20000
|
||||
#define JANET_FUNCDEF_FLAG_HASSYMBOLMAP 0x40000
|
||||
#define JANET_FUNCDEF_FLAG_HASNAME 0x80000
|
||||
#define JANET_FUNCDEF_FLAG_HASSOURCE 0x100000
|
||||
#define JANET_FUNCDEF_FLAG_HASDEFS 0x200000
|
||||
|
@ -998,6 +1056,14 @@ struct JanetSourceMapping {
|
|||
int32_t column;
|
||||
};
|
||||
|
||||
/* Symbol to slot mapping & lifetime structure. */
|
||||
struct JanetSymbolMap {
|
||||
uint32_t birth_pc;
|
||||
uint32_t death_pc;
|
||||
uint32_t slot_index;
|
||||
const uint8_t *symbol;
|
||||
};
|
||||
|
||||
/* A function definition. Contains information needed to instantiate closures. */
|
||||
struct JanetFuncDef {
|
||||
JanetGCObject gc;
|
||||
|
@ -1011,6 +1077,7 @@ struct JanetFuncDef {
|
|||
JanetSourceMapping *sourcemap;
|
||||
JanetString source;
|
||||
JanetString name;
|
||||
JanetSymbolMap *symbolmap;
|
||||
|
||||
int32_t flags;
|
||||
int32_t slotcount; /* The amount of stack space required for the function */
|
||||
|
@ -1021,6 +1088,7 @@ struct JanetFuncDef {
|
|||
int32_t bytecode_length;
|
||||
int32_t environments_length;
|
||||
int32_t defs_length;
|
||||
int32_t symbolmap_length;
|
||||
};
|
||||
|
||||
/* A function environment */
|
||||
|
@ -1096,6 +1164,8 @@ struct JanetAbstractType {
|
|||
int32_t (*hash)(void *p, size_t len);
|
||||
Janet(*next)(void *p, Janet key);
|
||||
Janet(*call)(void *p, int32_t argc, Janet *argv);
|
||||
size_t (*length)(void *p, size_t len);
|
||||
JanetByteView(*bytes)(void *p, size_t len);
|
||||
};
|
||||
|
||||
/* Some macros to let us add extra types to JanetAbstract types without
|
||||
|
@ -1113,7 +1183,9 @@ struct JanetAbstractType {
|
|||
#define JANET_ATEND_COMPARE NULL,JANET_ATEND_HASH
|
||||
#define JANET_ATEND_HASH NULL,JANET_ATEND_NEXT
|
||||
#define JANET_ATEND_NEXT NULL,JANET_ATEND_CALL
|
||||
#define JANET_ATEND_CALL
|
||||
#define JANET_ATEND_CALL NULL,JANET_ATEND_LENGTH
|
||||
#define JANET_ATEND_LENGTH NULL,JANET_ATEND_BYTES
|
||||
#define JANET_ATEND_BYTES
|
||||
|
||||
struct JanetReg {
|
||||
const char *name;
|
||||
|
@ -1179,17 +1251,6 @@ typedef struct {
|
|||
Janet payload;
|
||||
} JanetTryState;
|
||||
|
||||
/* Thread types */
|
||||
#ifdef JANET_THREADS
|
||||
typedef struct JanetThread JanetThread;
|
||||
typedef struct JanetMailbox JanetMailbox;
|
||||
struct JanetThread {
|
||||
JanetMailbox *mailbox;
|
||||
JanetTable *encode;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
/***** END SECTION TYPES *****/
|
||||
|
||||
/***** START SECTION OPCODES *****/
|
||||
|
@ -1233,11 +1294,13 @@ enum JanetOpCode {
|
|||
JOP_RETURN_NIL,
|
||||
JOP_ADD_IMMEDIATE,
|
||||
JOP_ADD,
|
||||
JOP_SUBTRACT_IMMEDIATE,
|
||||
JOP_SUBTRACT,
|
||||
JOP_MULTIPLY_IMMEDIATE,
|
||||
JOP_MULTIPLY,
|
||||
JOP_DIVIDE_IMMEDIATE,
|
||||
JOP_DIVIDE,
|
||||
JOP_DIVIDE_FLOOR,
|
||||
JOP_MODULO,
|
||||
JOP_REMAINDER,
|
||||
JOP_BAND,
|
||||
|
@ -1357,9 +1420,7 @@ JANET_API void janet_stream_flags(JanetStream *stream, uint32_t flags);
|
|||
JANET_API void janet_schedule(JanetFiber *fiber, Janet value);
|
||||
JANET_API void janet_cancel(JanetFiber *fiber, Janet value);
|
||||
JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig);
|
||||
|
||||
/* Start a state machine listening for events from a stream */
|
||||
JANET_API JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user);
|
||||
JANET_API void janet_schedule_soon(JanetFiber *fiber, Janet value, JanetSignal sig);
|
||||
|
||||
/* Shorthand for yielding to event loop in C */
|
||||
JANET_NO_RETURN JANET_API void janet_await(void);
|
||||
|
@ -1378,11 +1439,19 @@ JANET_API void *janet_abstract_threaded(const JanetAbstractType *atype, size_t s
|
|||
JANET_API int32_t janet_abstract_incref(void *abst);
|
||||
JANET_API int32_t janet_abstract_decref(void *abst);
|
||||
|
||||
/* Expose some OS sync primitives to make portable abstract types easier to implement */
|
||||
/* Expose some OS sync primitives */
|
||||
JANET_API size_t janet_os_mutex_size(void);
|
||||
JANET_API size_t janet_os_rwlock_size(void);
|
||||
JANET_API void janet_os_mutex_init(JanetOSMutex *mutex);
|
||||
JANET_API void janet_os_mutex_deinit(JanetOSMutex *mutex);
|
||||
JANET_API void janet_os_mutex_lock(JanetOSMutex *mutex);
|
||||
JANET_API void janet_os_mutex_unlock(JanetOSMutex *mutex);
|
||||
JANET_API void janet_os_rwlock_init(JanetOSRWLock *rwlock);
|
||||
JANET_API void janet_os_rwlock_deinit(JanetOSRWLock *rwlock);
|
||||
JANET_API void janet_os_rwlock_rlock(JanetOSRWLock *rwlock);
|
||||
JANET_API void janet_os_rwlock_wlock(JanetOSRWLock *rwlock);
|
||||
JANET_API void janet_os_rwlock_runlock(JanetOSRWLock *rwlock);
|
||||
JANET_API void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock);
|
||||
|
||||
/* Get last error from an IO operation */
|
||||
JANET_API Janet janet_ev_lasterr(void);
|
||||
|
@ -1439,22 +1508,22 @@ JANET_API void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGeneric
|
|||
JANET_API void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value);
|
||||
|
||||
/* Read async from a stream */
|
||||
JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
|
||||
JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
|
||||
#ifdef JANET_NET
|
||||
JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
|
||||
JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
|
||||
JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
|
||||
#endif
|
||||
|
||||
/* Write async to a stream */
|
||||
JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf);
|
||||
JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str);
|
||||
#ifdef JANET_NET
|
||||
JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags);
|
||||
JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags);
|
||||
JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags);
|
||||
JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags);
|
||||
JANET_NO_RETURN JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1543,6 +1612,7 @@ JANET_API double janet_rng_double(JanetRNG *rng);
|
|||
|
||||
/* Array functions */
|
||||
JANET_API JanetArray *janet_array(int32_t capacity);
|
||||
JANET_API JanetArray *janet_array_weak(int32_t capacity);
|
||||
JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n);
|
||||
JANET_API void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth);
|
||||
JANET_API void janet_array_setcount(JanetArray *array, int32_t count);
|
||||
|
@ -1551,8 +1621,10 @@ JANET_API Janet janet_array_pop(JanetArray *array);
|
|||
JANET_API Janet janet_array_peek(JanetArray *array);
|
||||
|
||||
/* Buffer functions */
|
||||
#define JANET_BUFFER_FLAG_NO_REALLOC 0x10000
|
||||
JANET_API JanetBuffer *janet_buffer(int32_t capacity);
|
||||
JANET_API JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity);
|
||||
JANET_API JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t count);
|
||||
JANET_API void janet_buffer_deinit(JanetBuffer *buffer);
|
||||
JANET_API void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth);
|
||||
JANET_API void janet_buffer_setcount(JanetBuffer *buffer, int32_t count);
|
||||
|
@ -1570,7 +1642,7 @@ JANET_API void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x);
|
|||
#define JANET_TUPLE_FLAG_BRACKETCTOR 0x10000
|
||||
|
||||
#define janet_tuple_head(t) ((JanetTupleHead *)((char *)t - offsetof(JanetTupleHead, data)))
|
||||
#define janet_tuple_from_head(gcobject) ((const Janet *)((char *)gcobject + offsetof(JanetTupleHead, data)))
|
||||
#define janet_tuple_from_head(gcobject) ((JanetTuple)((char *)gcobject + offsetof(JanetTupleHead, data)))
|
||||
#define janet_tuple_length(t) (janet_tuple_head(t)->length)
|
||||
#define janet_tuple_hash(t) (janet_tuple_head(t)->hash)
|
||||
#define janet_tuple_sm_line(t) (janet_tuple_head(t)->sm_line)
|
||||
|
@ -1616,14 +1688,17 @@ JANET_API JanetSymbol janet_symbol_gen(void);
|
|||
|
||||
/* Structs */
|
||||
#define janet_struct_head(t) ((JanetStructHead *)((char *)t - offsetof(JanetStructHead, data)))
|
||||
#define janet_struct_from_head(t) ((const JanetKV *)((char *)gcobject + offsetof(JanetStructHead, data)))
|
||||
#define janet_struct_from_head(t) ((JanetStruct)((char *)gcobject + offsetof(JanetStructHead, data)))
|
||||
#define janet_struct_length(t) (janet_struct_head(t)->length)
|
||||
#define janet_struct_capacity(t) (janet_struct_head(t)->capacity)
|
||||
#define janet_struct_hash(t) (janet_struct_head(t)->hash)
|
||||
#define janet_struct_proto(t) (janet_struct_head(t)->proto)
|
||||
JANET_API JanetKV *janet_struct_begin(int32_t count);
|
||||
JANET_API void janet_struct_put(JanetKV *st, Janet key, Janet value);
|
||||
JANET_API JanetStruct janet_struct_end(JanetKV *st);
|
||||
JANET_API Janet janet_struct_get(JanetStruct st, Janet key);
|
||||
JANET_API Janet janet_struct_rawget(JanetStruct st, Janet key);
|
||||
JANET_API Janet janet_struct_get_ex(JanetStruct st, Janet key, JanetStruct *which);
|
||||
JANET_API JanetTable *janet_struct_to_table(JanetStruct st);
|
||||
JANET_API const JanetKV *janet_struct_find(JanetStruct st, Janet key);
|
||||
|
||||
|
@ -1648,6 +1723,7 @@ JANET_API void janet_table_clear(JanetTable *table);
|
|||
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv);
|
||||
JANET_API JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv);
|
||||
JANET_API JanetFiberStatus janet_fiber_status(JanetFiber *fiber);
|
||||
JANET_API int janet_fiber_can_resume(JanetFiber *fiber);
|
||||
JANET_API JanetFiber *janet_current_fiber(void);
|
||||
JANET_API JanetFiber *janet_root_fiber(void);
|
||||
|
||||
|
@ -1674,6 +1750,7 @@ JANET_API JanetModule janet_native(const char *name, JanetString *error);
|
|||
|
||||
/* Marshaling */
|
||||
#define JANET_MARSHAL_UNSAFE 0x20000
|
||||
#define JANET_MARSHAL_NO_CYCLES 0x40000
|
||||
|
||||
JANET_API void janet_marshal(
|
||||
JanetBuffer *buf,
|
||||
|
@ -1752,6 +1829,7 @@ JANET_API void janet_vm_free(JanetVM *vm);
|
|||
JANET_API void janet_vm_save(JanetVM *into);
|
||||
JANET_API void janet_vm_load(JanetVM *from);
|
||||
JANET_API void janet_interpreter_interrupt(JanetVM *vm);
|
||||
JANET_API void janet_interpreter_interrupt_handled(JanetVM *vm);
|
||||
JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out);
|
||||
JANET_API JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig);
|
||||
JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);
|
||||
|
@ -1759,6 +1837,29 @@ JANET_API JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out);
|
|||
JANET_API Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv);
|
||||
JANET_API Janet janet_mcall(const char *name, int32_t argc, Janet *argv);
|
||||
JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err);
|
||||
JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix);
|
||||
|
||||
/* Sandboxing API */
|
||||
#define JANET_SANDBOX_SANDBOX 1
|
||||
#define JANET_SANDBOX_SUBPROCESS 2
|
||||
#define JANET_SANDBOX_NET_CONNECT 4
|
||||
#define JANET_SANDBOX_NET_LISTEN 8
|
||||
#define JANET_SANDBOX_FFI_DEFINE 16
|
||||
#define JANET_SANDBOX_FS_WRITE 32
|
||||
#define JANET_SANDBOX_FS_READ 64
|
||||
#define JANET_SANDBOX_HRTIME 128
|
||||
#define JANET_SANDBOX_ENV 256
|
||||
#define JANET_SANDBOX_DYNAMIC_MODULES 512
|
||||
#define JANET_SANDBOX_FS_TEMP 1024
|
||||
#define JANET_SANDBOX_FFI_USE 2048
|
||||
#define JANET_SANDBOX_FFI_JIT 4096
|
||||
#define JANET_SANDBOX_SIGNAL 8192
|
||||
#define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT)
|
||||
#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP)
|
||||
#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN)
|
||||
#define JANET_SANDBOX_ALL (UINT32_MAX)
|
||||
JANET_API void janet_sandbox(uint32_t flags);
|
||||
JANET_API void janet_sandbox_assert(uint32_t forbidden_flags);
|
||||
|
||||
/* Scratch Memory API */
|
||||
typedef void (*JanetScratchFinalizer)(void *);
|
||||
|
@ -1774,7 +1875,9 @@ typedef enum {
|
|||
JANET_BINDING_NONE,
|
||||
JANET_BINDING_DEF,
|
||||
JANET_BINDING_VAR,
|
||||
JANET_BINDING_MACRO
|
||||
JANET_BINDING_MACRO,
|
||||
JANET_BINDING_DYNAMIC_DEF,
|
||||
JANET_BINDING_DYNAMIC_MACRO
|
||||
} JanetBindingType;
|
||||
|
||||
typedef struct {
|
||||
|
@ -1816,7 +1919,7 @@ JANET_API Janet janet_resolve_core(const char *name);
|
|||
/* sourcemaps only */
|
||||
#define JANET_REG_S(JNAME, CNAME) {JNAME, CNAME, NULL, __FILE__, CNAME##_sourceline_}
|
||||
#define JANET_FN_S(CNAME, USAGE, DOCSTRING) \
|
||||
static int32_t CNAME##_sourceline_ = __LINE__; \
|
||||
static const int32_t CNAME##_sourceline_ = __LINE__; \
|
||||
Janet CNAME (int32_t argc, Janet *argv)
|
||||
#define JANET_DEF_S(ENV, JNAME, VAL, DOC) \
|
||||
janet_def_sm(ENV, JNAME, VAL, NULL, __FILE__, __LINE__)
|
||||
|
@ -1832,13 +1935,12 @@ JANET_API Janet janet_resolve_core(const char *name);
|
|||
/* sourcemaps and docstrings */
|
||||
#define JANET_REG_SD(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, __FILE__, CNAME##_sourceline_}
|
||||
#define JANET_FN_SD(CNAME, USAGE, DOCSTRING) \
|
||||
static int32_t CNAME##_sourceline_ = __LINE__; \
|
||||
static const int32_t CNAME##_sourceline_ = __LINE__; \
|
||||
static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \
|
||||
Janet CNAME (int32_t argc, Janet *argv)
|
||||
#define JANET_DEF_SD(ENV, JNAME, VAL, DOC) \
|
||||
janet_def_sm(ENV, JNAME, VAL, DOC, __FILE__, __LINE__)
|
||||
|
||||
|
||||
/* Choose defaults for source mapping and docstring based on config defs */
|
||||
#if defined(JANET_NO_SOURCEMAPS) && defined(JANET_NO_DOCSTRINGS)
|
||||
#define JANET_REG JANET_REG_
|
||||
|
@ -1875,10 +1977,10 @@ JANET_API void janet_register(const char *name, JanetCFunction cfun);
|
|||
#endif
|
||||
#ifndef JANET_ENTRY_NAME
|
||||
#define JANET_MODULE_ENTRY \
|
||||
JANET_MODULE_PREFIX JANET_API JanetBuildConfig _janet_mod_config(void) { \
|
||||
JANET_MODULE_PREFIX JANET_EXPORT JanetBuildConfig _janet_mod_config(void) { \
|
||||
return janet_config_current(); \
|
||||
} \
|
||||
JANET_MODULE_PREFIX JANET_API void _janet_init
|
||||
JANET_MODULE_PREFIX JANET_EXPORT void _janet_init
|
||||
#else
|
||||
#define JANET_MODULE_ENTRY JANET_MODULE_PREFIX JANET_API void JANET_ENTRY_NAME
|
||||
#endif
|
||||
|
@ -1906,6 +2008,7 @@ JANET_API JanetTable *janet_gettable(const Janet *argv, int32_t n);
|
|||
JANET_API JanetStruct janet_getstruct(const Janet *argv, int32_t n);
|
||||
JANET_API JanetString janet_getstring(const Janet *argv, int32_t n);
|
||||
JANET_API const char *janet_getcstring(const Janet *argv, int32_t n);
|
||||
JANET_API const char *janet_getcbytes(const Janet *argv, int32_t n);
|
||||
JANET_API JanetSymbol janet_getsymbol(const Janet *argv, int32_t n);
|
||||
JANET_API JanetKeyword janet_getkeyword(const Janet *argv, int32_t n);
|
||||
JANET_API JanetBuffer *janet_getbuffer(const Janet *argv, int32_t n);
|
||||
|
@ -1918,6 +2021,7 @@ JANET_API void *janet_getpointer(const Janet *argv, int32_t n);
|
|||
JANET_API int32_t janet_getnat(const Janet *argv, int32_t n);
|
||||
JANET_API int32_t janet_getinteger(const Janet *argv, int32_t n);
|
||||
JANET_API int64_t janet_getinteger64(const Janet *argv, int32_t n);
|
||||
JANET_API uint64_t janet_getuinteger64(const Janet *argv, int32_t n);
|
||||
JANET_API size_t janet_getsize(const Janet *argv, int32_t n);
|
||||
JANET_API JanetView janet_getindexed(const Janet *argv, int32_t n);
|
||||
JANET_API JanetByteView janet_getbytes(const Janet *argv, int32_t n);
|
||||
|
@ -1925,6 +2029,8 @@ JANET_API JanetDictView janet_getdictionary(const Janet *argv, int32_t n);
|
|||
JANET_API void *janet_getabstract(const Janet *argv, int32_t n, const JanetAbstractType *at);
|
||||
JANET_API JanetRange janet_getslice(int32_t argc, const Janet *argv);
|
||||
JANET_API int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const char *which);
|
||||
JANET_API int32_t janet_getstartrange(const Janet *argv, int32_t argc, int32_t n, int32_t length);
|
||||
JANET_API int32_t janet_getendrange(const Janet *argv, int32_t argc, int32_t n, int32_t length);
|
||||
JANET_API int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which);
|
||||
JANET_API uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags);
|
||||
|
||||
|
@ -1934,6 +2040,7 @@ JANET_API JanetTuple janet_opttuple(const Janet *argv, int32_t argc, int32_t n,
|
|||
JANET_API JanetStruct janet_optstruct(const Janet *argv, int32_t argc, int32_t n, JanetStruct dflt);
|
||||
JANET_API JanetString janet_optstring(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
|
||||
JANET_API const char *janet_optcstring(const Janet *argv, int32_t argc, int32_t n, const char *dflt);
|
||||
JANET_API const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt);
|
||||
JANET_API JanetSymbol janet_optsymbol(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
|
||||
JANET_API JanetKeyword janet_optkeyword(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
|
||||
JANET_API JanetFiber *janet_optfiber(const Janet *argv, int32_t argc, int32_t n, JanetFiber *dflt);
|
||||
|
@ -1965,7 +2072,6 @@ extern JANET_API const JanetAbstractType janet_file_type;
|
|||
#define JANET_FILE_CLOSED 32
|
||||
#define JANET_FILE_BINARY 64
|
||||
#define JANET_FILE_SERIALIZABLE 128
|
||||
#define JANET_FILE_PIPED 256
|
||||
#define JANET_FILE_NONIL 512
|
||||
|
||||
JANET_API Janet janet_makefile(FILE *f, int32_t flags);
|
||||
|
@ -1983,6 +2089,7 @@ JANET_API int janet_cryptorand(uint8_t *out, size_t n);
|
|||
JANET_API void janet_marshal_size(JanetMarshalContext *ctx, size_t value);
|
||||
JANET_API void janet_marshal_int(JanetMarshalContext *ctx, int32_t value);
|
||||
JANET_API void janet_marshal_int64(JanetMarshalContext *ctx, int64_t value);
|
||||
JANET_API void janet_marshal_ptr(JanetMarshalContext *ctx, const void *value);
|
||||
JANET_API void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value);
|
||||
JANET_API void janet_marshal_bytes(JanetMarshalContext *ctx, const uint8_t *bytes, size_t len);
|
||||
JANET_API void janet_marshal_janet(JanetMarshalContext *ctx, Janet x);
|
||||
|
@ -1992,10 +2099,12 @@ JANET_API void janet_unmarshal_ensure(JanetMarshalContext *ctx, size_t size);
|
|||
JANET_API size_t janet_unmarshal_size(JanetMarshalContext *ctx);
|
||||
JANET_API int32_t janet_unmarshal_int(JanetMarshalContext *ctx);
|
||||
JANET_API int64_t janet_unmarshal_int64(JanetMarshalContext *ctx);
|
||||
JANET_API void *janet_unmarshal_ptr(JanetMarshalContext *ctx);
|
||||
JANET_API uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx);
|
||||
JANET_API void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, size_t len);
|
||||
JANET_API Janet janet_unmarshal_janet(JanetMarshalContext *ctx);
|
||||
JANET_API JanetAbstract janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size);
|
||||
JANET_API JanetAbstract janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size);
|
||||
JANET_API void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p);
|
||||
|
||||
JANET_API void janet_register_abstract_type(const JanetAbstractType *at);
|
||||
|
@ -2038,7 +2147,9 @@ typedef enum {
|
|||
RULE_LINE, /* [tag] */
|
||||
RULE_COLUMN, /* [tag] */
|
||||
RULE_UNREF, /* [rule, tag] */
|
||||
RULE_CAPTURE_NUM /* [rule, tag] */
|
||||
RULE_CAPTURE_NUM, /* [rule, tag] */
|
||||
RULE_SUB, /* [rule, rule] */
|
||||
RULE_SPLIT /* [rule, rule] */
|
||||
} JanetPegOpcod;
|
||||
|
||||
typedef struct {
|
||||
|
@ -2072,16 +2183,6 @@ JANET_API int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out);
|
|||
|
||||
#endif
|
||||
|
||||
#ifdef JANET_THREADS
|
||||
|
||||
extern JANET_API const JanetAbstractType janet_thread_type;
|
||||
|
||||
JANET_API int janet_thread_receive(Janet *msg_out, double timeout);
|
||||
JANET_API int janet_thread_send(JanetThread *thread, Janet msg, double timeout);
|
||||
JANET_API JanetThread *janet_thread_current(void);
|
||||
|
||||
#endif
|
||||
|
||||
/* Custom allocator support */
|
||||
JANET_API void *(janet_malloc)(size_t);
|
||||
JANET_API void *(janet_realloc)(void *, size_t);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -25,6 +25,7 @@
|
|||
#endif
|
||||
|
||||
#include <janet.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
@ -32,6 +33,9 @@
|
|||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
||||
#endif
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void janet_line_init();
|
||||
|
@ -75,6 +79,9 @@ static void simpleline(JanetBuffer *buffer) {
|
|||
int c;
|
||||
for (;;) {
|
||||
c = fgetc(in);
|
||||
if (c < 0 && !feof(in) && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
if (feof(in) || c < 0) {
|
||||
break;
|
||||
}
|
||||
|
@ -83,8 +90,30 @@ static void simpleline(JanetBuffer *buffer) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Windows */
|
||||
#if defined(JANET_WINDOWS) || defined(JANET_SIMPLE_GETLINE)
|
||||
/* State */
|
||||
|
||||
#ifndef JANET_SIMPLE_GETLINE
|
||||
/* static state */
|
||||
#define JANET_LINE_MAX 1024
|
||||
#define JANET_MATCH_MAX 256
|
||||
#define JANET_HISTORY_MAX 100
|
||||
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
|
||||
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
|
||||
static JANET_THREAD_LOCAL int gbl_plen = 2;
|
||||
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
|
||||
static JANET_THREAD_LOCAL int gbl_len = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_pos = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_cols = 80;
|
||||
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
|
||||
static JANET_THREAD_LOCAL int gbl_history_count = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_historyi = 0;
|
||||
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
|
||||
static JANET_THREAD_LOCAL int gbl_match_count = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
|
||||
#endif
|
||||
|
||||
/* Fallback */
|
||||
#if defined(JANET_SIMPLE_GETLINE)
|
||||
|
||||
void janet_line_init() {
|
||||
;
|
||||
|
@ -101,6 +130,83 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
|
|||
simpleline(buffer);
|
||||
}
|
||||
|
||||
/* Rich implementation */
|
||||
#else
|
||||
|
||||
/* Windows */
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <io.h>
|
||||
|
||||
static void setup_console_output(void) {
|
||||
/* Enable color console on windows 10 console and utf8 output and other processing */
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD dwMode = 0;
|
||||
GetConsoleMode(hOut, &dwMode);
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
dwMode |= ENABLE_PROCESSED_OUTPUT;
|
||||
SetConsoleMode(hOut, dwMode);
|
||||
if (IsValidCodePage(65001)) {
|
||||
SetConsoleOutputCP(65001);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ansi terminal raw mode */
|
||||
static int rawmode(void) {
|
||||
if (gbl_israwmode) return 0;
|
||||
HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD dwMode = 0;
|
||||
GetConsoleMode(hOut, &dwMode);
|
||||
dwMode &= ~ENABLE_LINE_INPUT;
|
||||
dwMode &= ~ENABLE_INSERT_MODE;
|
||||
dwMode &= ~ENABLE_ECHO_INPUT;
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
dwMode &= ~ENABLE_PROCESSED_INPUT;
|
||||
if (!SetConsoleMode(hOut, dwMode)) return 1;
|
||||
gbl_israwmode = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disable raw mode */
|
||||
static void norawmode(void) {
|
||||
if (!gbl_israwmode) return;
|
||||
HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD dwMode = 0;
|
||||
GetConsoleMode(hOut, &dwMode);
|
||||
dwMode |= ENABLE_LINE_INPUT;
|
||||
dwMode |= ENABLE_INSERT_MODE;
|
||||
dwMode |= ENABLE_ECHO_INPUT;
|
||||
dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
dwMode |= ENABLE_PROCESSED_INPUT;
|
||||
SetConsoleMode(hOut, dwMode);
|
||||
gbl_israwmode = 0;
|
||||
}
|
||||
|
||||
static long write_console(const char *bytes, size_t n) {
|
||||
DWORD nwritten = 0;
|
||||
BOOL result = WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), bytes, (DWORD) n, &nwritten, NULL);
|
||||
if (!result) return -1; /* error */
|
||||
return (long)nwritten;
|
||||
}
|
||||
|
||||
static long read_console(char *into, size_t n) {
|
||||
DWORD numread;
|
||||
BOOL result = ReadConsole(GetStdHandle(STD_INPUT_HANDLE), into, (DWORD) n, &numread, NULL);
|
||||
if (!result) return -1; /* error */
|
||||
return (long)numread;
|
||||
}
|
||||
|
||||
static int check_simpleline(JanetBuffer *buffer) {
|
||||
if (!_isatty(_fileno(stdin)) || rawmode()) {
|
||||
simpleline(buffer);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Posix */
|
||||
#else
|
||||
|
||||
|
@ -112,7 +218,6 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
|
|||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -122,25 +227,7 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
|
|||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* static state */
|
||||
#define JANET_LINE_MAX 1024
|
||||
#define JANET_MATCH_MAX 256
|
||||
#define JANET_HISTORY_MAX 100
|
||||
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
|
||||
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
|
||||
static JANET_THREAD_LOCAL int gbl_plen = 2;
|
||||
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
|
||||
static JANET_THREAD_LOCAL int gbl_len = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_pos = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_cols = 80;
|
||||
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
|
||||
static JANET_THREAD_LOCAL int gbl_history_count = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_historyi = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_sigint_flag = 0;
|
||||
static JANET_THREAD_LOCAL struct termios gbl_termios_start;
|
||||
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
|
||||
static JANET_THREAD_LOCAL int gbl_match_count = 0;
|
||||
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
|
||||
|
||||
/* Unsupported terminal list from linenoise */
|
||||
static const char *badterms[] = {
|
||||
|
@ -150,15 +237,6 @@ static const char *badterms[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static char *sdup(const char *s) {
|
||||
size_t len = strlen(s) + 1;
|
||||
char *mem = janet_malloc(len);
|
||||
if (!mem) {
|
||||
return NULL;
|
||||
}
|
||||
return memcpy(mem, s, len);
|
||||
}
|
||||
|
||||
/* Ansi terminal raw mode */
|
||||
static int rawmode(void) {
|
||||
struct termios t;
|
||||
|
@ -184,13 +262,54 @@ static void norawmode(void) {
|
|||
gbl_israwmode = 0;
|
||||
}
|
||||
|
||||
static int checktermsupport() {
|
||||
const char *t = getenv("TERM");
|
||||
int i;
|
||||
if (!t) return 1;
|
||||
for (i = 0; badterms[i]; i++)
|
||||
if (!strcmp(t, badterms[i])) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long write_console(char *bytes, size_t n) {
|
||||
return write(STDOUT_FILENO, bytes, n);
|
||||
}
|
||||
|
||||
static long read_console(char *into, size_t n) {
|
||||
return read(STDIN_FILENO, into, n);
|
||||
}
|
||||
|
||||
static int check_simpleline(JanetBuffer *buffer) {
|
||||
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
|
||||
simpleline(buffer);
|
||||
return 1;
|
||||
}
|
||||
if (rawmode()) {
|
||||
simpleline(buffer);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static char *sdup(const char *s) {
|
||||
size_t len = strlen(s) + 1;
|
||||
char *mem = janet_malloc(len);
|
||||
if (!mem) {
|
||||
return NULL;
|
||||
}
|
||||
return memcpy(mem, s, len);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
static int curpos(void) {
|
||||
char buf[32];
|
||||
int cols, rows;
|
||||
unsigned int i = 0;
|
||||
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1;
|
||||
if (write_console("\x1b[6n", 4) != 4) return -1;
|
||||
while (i < sizeof(buf) - 1) {
|
||||
if (read(STDIN_FILENO, buf + i, 1) != 1) break;
|
||||
if (read_console(buf + i, 1) != 1) break;
|
||||
if (buf[i] == 'R') break;
|
||||
i++;
|
||||
}
|
||||
|
@ -199,20 +318,26 @@ static int curpos(void) {
|
|||
if (sscanf(buf + 2, "%d;%d", &rows, &cols) != 2) return -1;
|
||||
return cols;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int getcols(void) {
|
||||
#ifdef _WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
|
||||
return (int)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
|
||||
#else
|
||||
struct winsize ws;
|
||||
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
|
||||
int start, cols;
|
||||
start = curpos();
|
||||
if (start == -1) goto failed;
|
||||
if (write(STDOUT_FILENO, "\x1b[999C", 6) != 6) goto failed;
|
||||
if (write_console("\x1b[999C", 6) != 6) goto failed;
|
||||
cols = curpos();
|
||||
if (cols == -1) goto failed;
|
||||
if (cols > start) {
|
||||
char seq[32];
|
||||
snprintf(seq, 32, "\x1b[%dD", cols - start);
|
||||
if (write(STDOUT_FILENO, seq, strlen(seq)) == -1) {
|
||||
if (write_console(seq, strlen(seq)) == -1) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -222,10 +347,11 @@ static int getcols(void) {
|
|||
}
|
||||
failed:
|
||||
return 80;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void clear(void) {
|
||||
if (write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7) <= 0) {
|
||||
if (write_console("\x1b[H\x1b[2J", 7) <= 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +383,7 @@ static void refresh(void) {
|
|||
/* Move cursor to original position. */
|
||||
snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen));
|
||||
janet_buffer_push_cstring(&b, seq);
|
||||
if (write(STDOUT_FILENO, b.data, b.count) == -1) {
|
||||
if (write_console((char *) b.data, b.count) == -1) {
|
||||
exit(1);
|
||||
}
|
||||
janet_buffer_deinit(&b);
|
||||
|
@ -283,7 +409,7 @@ static int insert(char c, int draw) {
|
|||
if (gbl_plen + gbl_len < gbl_cols) {
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
if (write(STDOUT_FILENO, &c, 1) == -1) return -1;
|
||||
if (write_console(&c, 1) == -1) return -1;
|
||||
} else {
|
||||
refresh();
|
||||
}
|
||||
|
@ -310,7 +436,7 @@ static void historymove(int delta) {
|
|||
gbl_historyi = gbl_history_count - 1;
|
||||
}
|
||||
strncpy(gbl_buf, gbl_history[gbl_historyi], JANET_LINE_MAX - 1);
|
||||
gbl_pos = gbl_len = strlen(gbl_buf);
|
||||
gbl_pos = gbl_len = (int) strlen(gbl_buf);
|
||||
gbl_buf[gbl_len] = '\0';
|
||||
|
||||
refresh();
|
||||
|
@ -376,10 +502,10 @@ static void kright(void) {
|
|||
}
|
||||
|
||||
static void krightw(void) {
|
||||
while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) {
|
||||
while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) {
|
||||
gbl_pos++;
|
||||
}
|
||||
while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) {
|
||||
while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) {
|
||||
gbl_pos++;
|
||||
}
|
||||
refresh();
|
||||
|
@ -422,7 +548,6 @@ static void kdeletew(void) {
|
|||
refresh();
|
||||
}
|
||||
|
||||
|
||||
/* See tools/symchargen.c */
|
||||
static int is_symbol_char_gen(uint8_t c) {
|
||||
if (c & 0x80) return 1;
|
||||
|
@ -525,6 +650,7 @@ static void check_specials(JanetByteView src) {
|
|||
check_cmatch(src, "unquote");
|
||||
check_cmatch(src, "var");
|
||||
check_cmatch(src, "while");
|
||||
check_cmatch(src, "upscope");
|
||||
}
|
||||
|
||||
static void resolve_format(JanetTable *entry) {
|
||||
|
@ -738,12 +864,16 @@ static int line() {
|
|||
|
||||
addhistory();
|
||||
|
||||
if (write(STDOUT_FILENO, gbl_prompt, gbl_plen) == -1) return -1;
|
||||
if (write_console((char *) gbl_prompt, gbl_plen) == -1) return -1;
|
||||
for (;;) {
|
||||
char c;
|
||||
char seq[3];
|
||||
|
||||
if (read(STDIN_FILENO, &c, 1) <= 0) return -1;
|
||||
int rc;
|
||||
do {
|
||||
rc = read_console(&c, 1);
|
||||
} while (rc < 0 && errno == EINTR);
|
||||
if (rc <= 0) return -1;
|
||||
|
||||
switch (c) {
|
||||
default:
|
||||
|
@ -759,8 +889,13 @@ static int line() {
|
|||
break;
|
||||
case 3: /* ctrl-c */
|
||||
clearlines();
|
||||
gbl_sigint_flag = 1;
|
||||
return -1;
|
||||
norawmode();
|
||||
#ifdef _WIN32
|
||||
ExitProcess(1);
|
||||
#else
|
||||
kill(getpid(), SIGINT);
|
||||
#endif
|
||||
/* fallthrough */
|
||||
case 17: /* ctrl-q */
|
||||
gbl_cancel_current_repl_form = 1;
|
||||
clearlines();
|
||||
|
@ -820,23 +955,26 @@ static int line() {
|
|||
case 23: /* ctrl-w */
|
||||
kbackspacew();
|
||||
break;
|
||||
#ifndef _WIN32
|
||||
case 26: /* ctrl-z */
|
||||
clearlines();
|
||||
norawmode();
|
||||
kill(getpid(), SIGSTOP);
|
||||
rawmode();
|
||||
refresh();
|
||||
break;
|
||||
#endif
|
||||
case 27: /* escape sequence */
|
||||
/* Read the next two bytes representing the escape sequence.
|
||||
* Use two calls to handle slow terminals returning the two
|
||||
* chars at different times. */
|
||||
if (read(STDIN_FILENO, seq, 1) == -1) break;
|
||||
if (read_console(seq, 1) == -1) break;
|
||||
/* Esc[ = Control Sequence Introducer (CSI) */
|
||||
if (seq[0] == '[') {
|
||||
if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
|
||||
if (read_console(seq + 1, 1) == -1) break;
|
||||
if (seq[1] >= '0' && seq[1] <= '9') {
|
||||
/* Extended escape, read additional byte. */
|
||||
if (read(STDIN_FILENO, seq + 2, 1) == -1) break;
|
||||
if (read_console(seq + 2, 1) == -1) break;
|
||||
if (seq[2] == '~') {
|
||||
switch (seq[1]) {
|
||||
case '1': /* Home */
|
||||
|
@ -855,7 +993,7 @@ static int line() {
|
|||
}
|
||||
}
|
||||
} else if (seq[0] == 'O') {
|
||||
if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
|
||||
if (read_console(seq + 1, 1) == -1) break;
|
||||
switch (seq[1]) {
|
||||
default:
|
||||
break;
|
||||
|
@ -938,35 +1076,15 @@ void janet_line_deinit() {
|
|||
gbl_historyi = 0;
|
||||
}
|
||||
|
||||
static int checktermsupport() {
|
||||
const char *t = getenv("TERM");
|
||||
int i;
|
||||
if (!t) return 1;
|
||||
for (i = 0; badterms[i]; i++)
|
||||
if (!strcmp(t, badterms[i])) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void janet_line_get(const char *p, JanetBuffer *buffer) {
|
||||
gbl_prompt = p;
|
||||
buffer->count = 0;
|
||||
gbl_historyi = 0;
|
||||
if (check_simpleline(buffer)) return;
|
||||
FILE *out = janet_dynfile("err", stderr);
|
||||
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
|
||||
simpleline(buffer);
|
||||
return;
|
||||
}
|
||||
if (rawmode()) {
|
||||
simpleline(buffer);
|
||||
return;
|
||||
}
|
||||
if (line()) {
|
||||
norawmode();
|
||||
if (gbl_sigint_flag) {
|
||||
raise(SIGINT);
|
||||
} else {
|
||||
fputc('\n', out);
|
||||
}
|
||||
fputc('\n', out);
|
||||
return;
|
||||
}
|
||||
fflush(stdin);
|
||||
|
@ -979,6 +1097,13 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
|
|||
replacehistory();
|
||||
}
|
||||
|
||||
static void clear_at_exit(void) {
|
||||
if (!gbl_israwmode) {
|
||||
clearlines();
|
||||
norawmode();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -991,18 +1116,11 @@ int main(int argc, char **argv) {
|
|||
JanetTable *env;
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Enable color console on windows 10 console and utf8 output. */
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD dwMode = 0;
|
||||
GetConsoleMode(hOut, &dwMode);
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
SetConsoleMode(hOut, dwMode);
|
||||
SetConsoleOutputCP(65001);
|
||||
setup_console_output();
|
||||
#endif
|
||||
|
||||
#if !defined(JANET_WINDOWS) && !defined(JANET_SIMPLE_GETLINE)
|
||||
/* Try and not leave the terminal in a bad state */
|
||||
atexit(norawmode);
|
||||
#if !defined(JANET_SIMPLE_GETLINE)
|
||||
atexit(clear_at_exit);
|
||||
#endif
|
||||
|
||||
#if defined(JANET_PRF)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Calvin Rose
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
(var num-tests-passed 0)
|
||||
(var num-tests-run 0)
|
||||
(var suite-num 0)
|
||||
(var suite-name 0)
|
||||
(var start-time 0)
|
||||
|
||||
(def is-verbose (os/getenv "VERBOSE"))
|
||||
|
||||
(defn assert
|
||||
"Override's the default assert with some nice error handling."
|
||||
[x &opt e]
|
||||
|
@ -12,11 +14,12 @@
|
|||
(++ num-tests-run)
|
||||
(when x (++ num-tests-passed))
|
||||
(def str (string e))
|
||||
(def truncated
|
||||
(if (> (length e) 40) (string (string/slice e 0 35) "...") (describe e)))
|
||||
(def frame (last (debug/stack (fiber/current))))
|
||||
(def line-info (string/format "%s:%d"
|
||||
(frame :source) (frame :source-line)))
|
||||
(if x
|
||||
(eprintf "\e[32m✔\e[0m %s: %v" truncated x)
|
||||
(eprintf "\n\e[31m✘\e[0m %s: %v" truncated x))
|
||||
(when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x))
|
||||
(do (eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush)))
|
||||
x)
|
||||
|
||||
(defmacro assert-error
|
||||
|
@ -24,18 +27,30 @@
|
|||
(def errsym (keyword (gensym)))
|
||||
~(assert (= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
|
||||
|
||||
(defn check-compile-error
|
||||
[form]
|
||||
(def result (compile form))
|
||||
(assert (table? result) (string/format "expected compilation error for %j, but compiled without error" form)))
|
||||
|
||||
(defmacro assert-no-error
|
||||
[msg & forms]
|
||||
(def errsym (keyword (gensym)))
|
||||
~(assert (not= ,errsym (try (do ,;forms) ([_] ,errsym))) ,msg))
|
||||
(def e (gensym))
|
||||
(def f (gensym))
|
||||
(if is-verbose
|
||||
~(try (do ,;forms (,assert true ,msg)) ([,e ,f] (,assert false ,msg) (,debug/stacktrace ,f ,e "\e[31m✘\e[0m ")))
|
||||
~(try (do ,;forms (,assert true ,msg)) ([_] (,assert false ,msg)))))
|
||||
|
||||
(defn start-suite [x]
|
||||
(set suite-num x)
|
||||
(defn start-suite [&opt x]
|
||||
(default x (dyn :current-file))
|
||||
(set suite-name
|
||||
(cond
|
||||
(number? x) (string x)
|
||||
(string x)))
|
||||
(set start-time (os/clock))
|
||||
(eprint "\nRunning test suite " x " tests...\n "))
|
||||
(eprint "Starting suite " suite-name "..."))
|
||||
|
||||
(defn end-suite []
|
||||
(def delta (- (os/clock) start-time))
|
||||
(eprintf "\n\nTest suite %d finished in %.3f seconds" suite-num delta)
|
||||
(eprint num-tests-passed " of " num-tests-run " tests passed.\n")
|
||||
(eprinf "Finished suite %s in %.3f seconds - " suite-name delta)
|
||||
(eprint num-tests-passed " of " num-tests-run " tests passed.")
|
||||
(if (not= num-tests-passed num-tests-run) (os/exit 1)))
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
# Copyright (c) 2023 Calvin Rose
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# 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.
|
||||
|
||||
(import ./helper :prefix "" :exit true)
|
||||
(start-suite)
|
||||
|
||||
# Array tests
|
||||
# e05022f
|
||||
(defn array=
|
||||
"Check if two arrays are equal in an element by element comparison"
|
||||
[a b]
|
||||
(if (and (array? a) (array? b))
|
||||
(= (apply tuple a) (apply tuple b))))
|
||||
(assert (= (apply tuple @[1 2 3 4 5]) (tuple 1 2 3 4 5)) "array to tuple")
|
||||
(def arr (array))
|
||||
(array/push arr :hello)
|
||||
(array/push arr :world)
|
||||
(assert (array= arr @[:hello :world]) "array comparison")
|
||||
(assert (array= @[1 2 3 4 5] @[1 2 3 4 5]) "array comparison 2")
|
||||
(assert (array= @[:one :two :three :four :five]
|
||||
@[:one :two :three :four :five]) "array comparison 3")
|
||||
(assert (array= (array/slice @[1 2 3] 0 2) @[1 2]) "array/slice 1")
|
||||
(assert (array= (array/slice @[0 7 3 9 1 4] 2 -2) @[3 9 1]) "array/slice 2")
|
||||
|
||||
# Array remove
|
||||
# 687a3c9
|
||||
(assert (deep= (array/remove @[1 2 3 4 5] 2) @[1 2 4 5]) "array/remove 1")
|
||||
(assert (deep= (array/remove @[1 2 3 4 5] 2 2) @[1 2 5]) "array/remove 2")
|
||||
(assert (deep= (array/remove @[1 2 3 4 5] 2 200) @[1 2]) "array/remove 3")
|
||||
(assert (deep= (array/remove @[1 2 3 4 5] -2 200) @[1 2 3]) "array/remove 4")
|
||||
|
||||
|
||||
# array/peek
|
||||
(assert (nil? (array/peek @[])) "array/peek empty")
|
||||
|
||||
# array/fill
|
||||
(assert (deep= (array/fill @[1 1] 2) @[2 2]) "array/fill 1")
|
||||
|
||||
# array/concat
|
||||
(assert (deep= (array/concat @[1 2] @[3 4] 5 6) @[1 2 3 4 5 6]) "array/concat 1")
|
||||
(def a @[1 2])
|
||||
(assert (deep= (array/concat a a) @[1 2 1 2]) "array/concat self")
|
||||
|
||||
# array/insert
|
||||
(assert (deep= (array/insert @[:a :a :a :a] 2 :b :b) @[:a :a :b :b :a :a]) "array/insert 1")
|
||||
(assert (deep= (array/insert @[:a :b] -1 :c :d) @[:a :b :c :d]) "array/insert 2")
|
||||
|
||||
# array/remove
|
||||
(assert-error "removal index 3 out of range [0,2]" (array/remove @[1 2] 3))
|
||||
(assert-error "expected non-negative integer for argument n, got -1" (array/remove @[1 2] 1 -1))
|
||||
|
||||
# array/pop
|
||||
(assert (= (array/pop @[1]) 1) "array/pop 1")
|
||||
(assert (= (array/pop @[]) nil) "array/pop empty")
|
||||
|
||||
# Code coverage
|
||||
(def a @[1])
|
||||
(array/pop a)
|
||||
(array/trim a)
|
||||
(array/ensure @[1 1] 6 2)
|
||||
|
||||
|
||||
(end-suite)
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
# Copyright (c) 2023 Calvin Rose
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# 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.
|
||||
|
||||
(import ./helper :prefix "" :exit true)
|
||||
(start-suite)
|
||||
|
||||
# Assembly test
|
||||
# Fibonacci sequence, implemented with naive recursion.
|
||||
# a679f60
|
||||
(def fibasm (asm '{
|
||||
:arity 1
|
||||
:bytecode [
|
||||
(ltim 1 0 0x2) # $1 = $0 < 2
|
||||
(jmpif 1 :done) # if ($1) goto :done
|
||||
(lds 1) # $1 = self
|
||||
(addim 0 0 -0x1) # $0 = $0 - 1
|
||||
(push 0) # push($0), push argument for next function call
|
||||
(call 2 1) # $2 = call($1)
|
||||
(addim 0 0 -0x1) # $0 = $0 - 1
|
||||
(push 0) # push($0)
|
||||
(call 0 1) # $0 = call($1)
|
||||
(add 0 0 2) # $0 = $0 + $2 (integers)
|
||||
:done
|
||||
(ret 0) # return $0
|
||||
]
|
||||
}))
|
||||
|
||||
(assert (= 0 (fibasm 0)) "fibasm 1")
|
||||
(assert (= 1 (fibasm 1)) "fibasm 2")
|
||||
(assert (= 55 (fibasm 10)) "fibasm 3")
|
||||
(assert (= 6765 (fibasm 20)) "fibasm 4")
|
||||
|
||||
# dacbe29
|
||||
(def f (asm (disasm (fn [x] (fn [y] (+ x y))))))
|
||||
(assert (= ((f 10) 37) 47) "asm environment tables")
|
||||
|
||||
# issue #1424
|
||||
(assert-no-error "arity > used slots (issue #1424)"
|
||||
(asm
|
||||
(disasm
|
||||
(fn []
|
||||
(def foo (fn [one two] one))
|
||||
(foo 100 200)))))
|
||||
|
||||
(end-suite)
|
||||
|
|
@ -0,0 +1,982 @@
|
|||
# Copyright (c) 2023 Calvin Rose
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# 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.
|
||||
|
||||
(import ./helper :prefix "" :exit true)
|
||||
(start-suite)
|
||||
|
||||
# Let
|
||||
# 807f981
|
||||
(assert (= (let [a 1 b 2] (+ a b)) 3) "simple let")
|
||||
(assert (= (let [[a b] @[1 2]] (+ a b)) 3) "destructured let")
|
||||
(assert (= (let [[a [c d] b] @[1 (tuple 4 3) 2]] (+ a b c d)) 10)
|
||||
"double destructured let")
|
||||
|
||||
# Macros
|
||||
# b305a7c
|
||||
(defn dub [x] (+ x x))
|
||||
(assert (= 2 (dub 1)) "defn macro")
|
||||
(do
|
||||
(defn trip [x] (+ x x x))
|
||||
(assert (= 3 (trip 1)) "defn macro triple"))
|
||||
(do
|
||||
(var i 0)
|
||||
(when true
|
||||
(++ i)
|
||||
(++ i)
|
||||
(++ i)
|
||||
(++ i)
|
||||
(++ i)
|
||||
(++ i))
|
||||
(assert (= i 6) "when macro"))
|
||||
|
||||
# Add truthy? to core
|
||||
# ded08b6
|
||||
(assert (= true ;(map truthy? [0 "" true @{} {} [] '()])) "truthy values")
|
||||
(assert (= false ;(map truthy? [nil false])) "non-truthy values")
|
||||
|
||||
## Polymorphic comparison -- Issue #272
|
||||
# 81d301a42
|
||||
|
||||
# confirm polymorphic comparison delegation to primitive comparators:
|
||||
(assert (= 0 (cmp 3 3)) "compare-primitive integers (1)")
|
||||
(assert (= -1 (cmp 3 5)) "compare-primitive integers (2)")
|
||||
(assert (= 1 (cmp "foo" "bar")) "compare-primitive strings")
|
||||
(assert (= 0 (compare 1 1)) "compare integers (1)")
|
||||
(assert (= -1 (compare 1 2)) "compare integers (2)")
|
||||
(assert (= 1 (compare "foo" "bar")) "compare strings (1)")
|
||||
|
||||
(assert (compare< 1 2 3 4 5 6) "compare less than integers")
|
||||
(assert (not (compare> 1 2 3 4 5 6)) "compare not greater than integers")
|
||||
(assert (compare< 1.0 2.0 3.0 4.0 5.0 6.0) "compare less than reals")
|
||||
(assert (compare> 6 5 4 3 2 1) "compare greater than integers")
|
||||
(assert (compare> 6.0 5.0 4.0 3.0 2.0 1.0) "compare greater than reals")
|
||||
(assert (not (compare< 6.0 5.0 4.0 3.0 2.0 1.0)) "compare less than reals")
|
||||
(assert (compare<= 1 2 3 3 4 5 6) "compare less than or equal to integers")
|
||||
(assert (compare<= 1.0 2.0 3.0 3.0 4.0 5.0 6.0)
|
||||
"compare less than or equal to reals")
|
||||
(assert (compare>= 6 5 4 4 3 2 1)
|
||||
"compare greater than or equal to integers")
|
||||
(assert (compare>= 6.0 5.0 4.0 4.0 3.0 2.0 1.0)
|
||||
"compare greater than or equal to reals")
|
||||
(assert (compare< 1.0 nil false true
|
||||
(fiber/new (fn [] 1))
|
||||
"hi"
|
||||
(quote hello)
|
||||
:hello
|
||||
(array 1 2 3)
|
||||
(tuple 1 2 3)
|
||||
(table "a" "b" "c" "d")
|
||||
(struct 1 2 3 4)
|
||||
(buffer "hi")
|
||||
(fn [x] (+ x x))
|
||||
print) "compare type ordering")
|
||||
|
||||
# test polymorphic compare with 'objects' (table/setproto)
|
||||
(def mynum
|
||||
@{:type :mynum :v 0 :compare
|
||||
(fn [self other]
|
||||
(case (type other)
|
||||
:number (cmp (self :v) other)
|
||||
:table (when (= (get other :type) :mynum)
|
||||
(cmp (self :v) (other :v)))))})
|
||||
|
||||
(let [n3 (table/setproto @{:v 3} mynum)]
|
||||
(assert (= 0 (compare 3 n3)) "compare num to object (1)")
|
||||
(assert (= -1 (compare n3 4)) "compare object to num (2)")
|
||||
(assert (= 1 (compare (table/setproto @{:v 4} mynum) n3))
|
||||
"compare object to object")
|
||||
(assert (compare< 2 n3 4) "compare< poly")
|
||||
(assert (compare> 4 n3 2) "compare> poly")
|
||||
(assert (compare<= 2 3 n3 4) "compare<= poly")
|
||||
(assert (compare= 3 n3 (table/setproto @{:v 3} mynum)) "compare= poly")
|
||||
(assert (deep= (sorted @[4 5 n3 2] compare<) @[2 n3 4 5])
|
||||
"polymorphic sort"))
|
||||
|
||||
# Add any? predicate to core
|
||||
# 7478ad11
|
||||
(assert (= nil (any? [])) "any? 1")
|
||||
(assert (= nil (any? [false nil])) "any? 2")
|
||||
(assert (= false (any? [nil false])) "any? 3")
|
||||
(assert (= 1 (any? [1])) "any? 4")
|
||||
(assert (nan? (any? [nil math/nan nil])) "any? 5")
|
||||
(assert (= true
|
||||
(any? [nil nil false nil nil true nil nil nil nil false :a nil]))
|
||||
"any? 6")
|
||||
|
||||
(assert (= true (every? [])) "every? 1")
|
||||
(assert (= true (every? [1 true])) "every? 2")
|
||||
(assert (= 1 (every? [true 1])) "every? 3")
|
||||
(assert (= nil (every? [nil])) "every? 4")
|
||||
(assert (= 2 (every? [1 math/nan 2])) "every? 5")
|
||||
(assert (= false
|
||||
(every? [1 1 true 1 1 false 1 1 1 1 true :a nil]))
|
||||
"every? 6")
|
||||
|
||||
# Some higher order functions and macros
|
||||
# 5e2de33
|
||||
(def my-array @[1 2 3 4 5 6])
|
||||
(assert (= (if-let [x (get my-array 5)] x) 6) "if-let 1")
|
||||
(assert (= (if-let [y (get @{} :key)] 10 nil) nil) "if-let 2")
|
||||
(assert (= (if-let [a my-array k (next a)] :t :f) :t) "if-let 3")
|
||||
(assert (= (if-let [a my-array k (next a 5)] :t :f) :f) "if-let 4")
|
||||
(assert (= (if-let [[a b] my-array] a) 1) "if-let 5")
|
||||
(assert (= (if-let [{:a a :b b} {:a 1 :b 2}] b) 2) "if-let 6")
|
||||
(assert (= (if-let [[a b] nil] :t :f) :f) "if-let 7")
|
||||
|
||||
# #1191
|
||||
(var cnt 0)
|
||||
(defmacro upcnt [] (++ cnt))
|
||||
(assert (= (if-let [a true b true c true] nil (upcnt)) nil) "issue #1191")
|
||||
(assert (= cnt 1) "issue #1191")
|
||||
|
||||
(assert (= 14 (sum (map inc @[1 2 3 4]))) "sum map")
|
||||
(def myfun (juxt + - * /))
|
||||
(assert (= [2 -2 2 0.5] (myfun 2)) "juxt")
|
||||
|
||||
# Case statements
|
||||
# 5249228
|
||||
(assert
|
||||
(= :six (case (+ 1 2 3)
|
||||
1 :one
|
||||
2 :two
|
||||
3 :three
|
||||
4 :four
|
||||
5 :five
|
||||
6 :six
|
||||
7 :seven
|
||||
8 :eight
|
||||
9 :nine)) "case macro")
|
||||
|
||||
(assert (= 7 (case :a :b 5 :c 6 :u 10 7)) "case with default")
|
||||
|
||||
# Testing the seq, tabseq, catseq, and loop macros
|
||||
# 547529e
|
||||
(def xs (apply tuple (seq [x :range [0 10] :when (even? x)]
|
||||
(tuple (/ x 2) x))))
|
||||
(assert (= xs '((0 0) (1 2) (2 4) (3 6) (4 8))) "seq macro 1")
|
||||
|
||||
# 624be87c9
|
||||
(def xs (apply tuple (seq [x :down [8 -2] :when (even? x)]
|
||||
(tuple (/ x 2) x))))
|
||||
(assert (= xs '((4 8) (3 6) (2 4) (1 2) (0 0))) "seq macro 2")
|
||||
|
||||
# Looping idea
|
||||
# 45f8db0
|
||||
(def xs
|
||||
(seq [x :in [-1 0 1] y :in [-1 0 1] :when (not= x y 0)] (tuple x y)))
|
||||
(def txs (apply tuple xs))
|
||||
|
||||
(assert (= txs [[-1 -1] [-1 0] [-1 1] [0 -1] [0 1] [1 -1] [1 0] [1 1]])
|
||||
"nested seq")
|
||||
|
||||
# :unless modifier
|
||||
(assert (deep= (seq [i :range [0 10] :unless (odd? i)] i)
|
||||
@[0 2 4 6 8])
|
||||
":unless modifier")
|
||||
|
||||
# 515891b03
|
||||
(assert (deep= (tabseq [i :in (range 3)] i (* 3 i))
|
||||
@{0 0 1 3 2 6}))
|
||||
|
||||
(assert (deep= (tabseq [i :in (range 3)] i)
|
||||
@{}))
|
||||
|
||||
# ccd874fe4
|
||||
(def xs (catseq [x :range [0 3]] [x x]))
|
||||
(assert (deep= xs @[0 0 1 1 2 2]) "catseq")
|
||||
|
||||
# :range-to and :down-to
|
||||
# e0c9910d8
|
||||
(assert (deep= (seq [x :range-to [0 10]] x) (seq [x :range [0 11]] x))
|
||||
"loop :range-to")
|
||||
(assert (deep= (seq [x :down-to [10 0]] x) (seq [x :down [10 -1]] x))
|
||||
"loop :down-to")
|
||||
|
||||
# one-term :range forms
|
||||
(assert (deep= (seq [x :range [10]] x) (seq [x :range [0 10]] x))
|
||||
"one-term :range")
|
||||
(assert (deep= (seq [x :down [10]] x) (seq [x :down [10 0]] x))
|
||||
"one-term :down")
|
||||
|
||||
# 7880d7320
|
||||
(def res @{})
|
||||
(loop [[k v] :pairs @{1 2 3 4 5 6}]
|
||||
(put res k v))
|
||||
(assert (and
|
||||
(= (get res 1) 2)
|
||||
(= (get res 3) 4)
|
||||
(= (get res 5) 6)) "loop :pairs")
|
||||
|
||||
# Issue #428
|
||||
# 08a3687eb
|
||||
(var result nil)
|
||||
(defn f [] (yield {:a :ok}))
|
||||
(assert-no-error "issue 428 1"
|
||||
(loop [{:a x} :in (fiber/new f)] (set result x)))
|
||||
(assert (= result :ok) "issue 428 2")
|
||||
|
||||
# Generators
|
||||
# 184fe31e0
|
||||
(def gen (generate [x :range [0 100] :when (pos? (% x 4))] x))
|
||||
(var gencount 0)
|
||||
(loop [x :in gen]
|
||||
(++ gencount)
|
||||
(assert (pos? (% x 4)) "generate in loop"))
|
||||
(assert (= gencount 75) "generate loop count")
|
||||
|
||||
# more loop checks
|
||||
(assert (deep= (seq [i :range [0 10]] i) @[0 1 2 3 4 5 6 7 8 9]) "seq 1")
|
||||
(assert (deep= (seq [i :range [0 10 2]] i) @[0 2 4 6 8]) "seq 2")
|
||||
(assert (deep= (seq [i :range [10]] i) @[0 1 2 3 4 5 6 7 8 9]) "seq 3")
|
||||
(assert (deep= (seq [i :range-to [10]] i) @[0 1 2 3 4 5 6 7 8 9 10]) "seq 4")
|
||||
(def gen (generate [x :range-to [0 nil 2]] x))
|
||||
(assert (deep= (take 5 gen) @[0 2 4 6 8]) "generate nil limit")
|
||||
(def gen (generate [x :range [0 nil 2]] x))
|
||||
(assert (deep= (take 5 gen) @[0 2 4 6 8]) "generate nil limit 2")
|
||||
|
||||
# Even and odd
|
||||
# ff163a5ae
|
||||
(assert (odd? 9) "odd? 1")
|
||||
(assert (odd? -9) "odd? 2")
|
||||
(assert (not (odd? 10)) "odd? 3")
|
||||
(assert (not (odd? 0)) "odd? 4")
|
||||
(assert (not (odd? -10)) "odd? 5")
|
||||
(assert (not (odd? 1.1)) "odd? 6")
|
||||
(assert (not (odd? -0.1)) "odd? 7")
|
||||
(assert (not (odd? -1.1)) "odd? 8")
|
||||
(assert (not (odd? -1.6)) "odd? 9")
|
||||
|
||||
(assert (even? 10) "even? 1")
|
||||
(assert (even? -10) "even? 2")
|
||||
(assert (even? 0) "even? 3")
|
||||
(assert (not (even? 9)) "even? 4")
|
||||
(assert (not (even? -9)) "even? 5")
|
||||
(assert (not (even? 0.1)) "even? 6")
|
||||
(assert (not (even? -0.1)) "even? 7")
|
||||
(assert (not (even? -10.1)) "even? 8")
|
||||
(assert (not (even? -10.6)) "even? 9")
|
||||
|
||||
# Map arities
|
||||
# 25ded775a
|
||||
(assert (deep= (map inc [1 2 3]) @[2 3 4]))
|
||||
(assert (deep= (map + [1 2 3] [10 20 30]) @[11 22 33]))
|
||||
(assert (deep= (map + [1 2 3] [10 20 30] [100 200 300]) @[111 222 333]))
|
||||
(assert (deep= (map + [1 2 3] [10 20 30] [100 200 300] [1000 2000 3000])
|
||||
@[1111 2222 3333]))
|
||||
(assert (deep= (map +
|
||||
[1 2 3] [10 20 30] [100 200 300] [1000 2000 3000]
|
||||
[10000 20000 30000])
|
||||
@[11111 22222 33333]))
|
||||
# 77e62a2
|
||||
(assert (deep= (map +
|
||||
[1 2 3] [10 20 30] [100 200 300] [1000 2000 3000]
|
||||
[10000 20000 30000] [100000 200000 300000])
|
||||
@[111111 222222 333333]))
|
||||
|
||||
# Mapping uses the shortest sequence
|
||||
# a69799aa4
|
||||
(assert (deep= (map + [1 2 3 4] [10 20 30]) @[11 22 33]))
|
||||
(assert (deep= (map + [1 2 3 4] [10 20 30] [100 200]) @[111 222]))
|
||||
(assert (deep= (map + [1 2 3 4] [10 20 30] [100 200] [1000]) @[1111]))
|
||||
# 77e62a2
|
||||
(assert (deep= (map + [1 2 3 4] [10 20 30] [100 200] [1000] []) @[]))
|
||||
|
||||
# Variadic arguments to map-like functions
|
||||
# 77e62a2
|
||||
(assert (deep= (mapcat tuple [1 2 3 4] [5 6 7 8]) @[1 5 2 6 3 7 4 8]))
|
||||
(assert (deep= (keep |(if (> $1 0) (/ $0 $1)) [1 2 3 4 5] [1 2 1 0 1])
|
||||
@[1 1 3 5]))
|
||||
|
||||
(assert (= (count = [1 3 2 4 3 5 4 2 1] [1 2 3 4 5 4 3 2 1]) 4))
|
||||
|
||||
(assert (= (some not= (range 5) (range 5)) nil))
|
||||
(assert (= (some = [1 2 3 4 5] [5 4 3 2 1]) true))
|
||||
|
||||
(assert (= (all = (range 5) (range 5)) true))
|
||||
(assert (= (all not= [1 2 3 4 5] [5 4 3 2 1]) false))
|
||||
|
||||
# 4194374
|
||||
(assert (= false (deep-not= [1] [1])) "issue #1149")
|
||||
|
||||
# Merge sort
|
||||
# f5b29b8
|
||||
# Imperative (and verbose) merge sort merge
|
||||
(defn merge-sort
|
||||
[xs ys]
|
||||
(def ret @[])
|
||||
(def xlen (length xs))
|
||||
(def ylen (length ys))
|
||||
(var i 0)
|
||||
(var j 0)
|
||||
# Main merge
|
||||
(while (if (< i xlen) (< j ylen))
|
||||
(def xi (get xs i))
|
||||
(def yj (get ys j))
|
||||
(if (< xi yj)
|
||||
(do (array/push ret xi) (set i (+ i 1)))
|
||||
(do (array/push ret yj) (set j (+ j 1)))))
|
||||
# Push rest of xs
|
||||
(while (< i xlen)
|
||||
(def xi (get xs i))
|
||||
(array/push ret xi)
|
||||
(set i (+ i 1)))
|
||||
# Push rest of ys
|
||||
(while (< j ylen)
|
||||
(def yj (get ys j))
|
||||
(array/push ret yj)
|
||||
(set j (+ j 1)))
|
||||
ret)
|
||||
|
||||
(assert (apply <= (merge-sort @[1 3 5] @[2 4 6])) "merge sort merge 1")
|
||||
(assert (apply <= (merge-sort @[1 2 3] @[4 5 6])) "merge sort merge 2")
|
||||
(assert (apply <= (merge-sort @[1 3 5] @[2 4 6 6 6 9])) "merge sort merge 3")
|
||||
(assert (apply <= (merge-sort '(1 3 5) @[2 4 6 6 6 9])) "merge sort merge 4")
|
||||
|
||||
(assert (deep= @[1 2 3 4 5] (sort @[5 3 4 1 2])) "sort 1")
|
||||
(assert (deep= @[{:a 1} {:a 4} {:a 7}]
|
||||
(sort-by |($ :a) @[{:a 4} {:a 7} {:a 1}])) "sort 2")
|
||||
(assert (deep= @[1 2 3 4 5] (sorted [5 3 4 1 2])) "sort 3")
|
||||
(assert (deep= @[{:a 1} {:a 4} {:a 7}]
|
||||
(sorted-by |($ :a) [{:a 4} {:a 7} {:a 1}])) "sort 4")
|
||||
|
||||
# Sort function
|
||||
# 2ca9300bf
|
||||
(assert (deep=
|
||||
(range 99)
|
||||
(sort (mapcat (fn [[x y z]] [z y x]) (partition 3 (range 99)))))
|
||||
"sort 5")
|
||||
(assert (<= ;(sort (map (fn [x] (math/random)) (range 1000)))) "sort 6")
|
||||
|
||||
# #1283
|
||||
(assert (deep=
|
||||
(partition 2 (generate [ i :in [:a :b :c :d :e]] i))
|
||||
'@[(:a :b) (:c :d) (:e)]))
|
||||
(assert (= (mean (generate [i :in [2 3 5 7 11]] i))
|
||||
5.6))
|
||||
|
||||
# And and or
|
||||
# c16a9d846
|
||||
(assert (= (and true true) true) "and true true")
|
||||
(assert (= (and true false) false) "and true false")
|
||||
(assert (= (and false true) false) "and false true")
|
||||
(assert (= (and true true true) true) "and true true true")
|
||||
(assert (= (and 0 1 2) 2) "and 0 1 2")
|
||||
(assert (= (and 0 1 nil) nil) "and 0 1 nil")
|
||||
(assert (= (and 1) 1) "and 1")
|
||||
(assert (= (and) true) "and with no arguments")
|
||||
(assert (= (and 1 true) true) "and with trailing true")
|
||||
(assert (= (and 1 true 2) 2) "and with internal true")
|
||||
|
||||
(assert (= (or true true) true) "or true true")
|
||||
(assert (= (or true false) true) "or true false")
|
||||
(assert (= (or false true) true) "or false true")
|
||||
(assert (= (or false false) false) "or false true")
|
||||
(assert (= (or true true false) true) "or true true false")
|
||||
(assert (= (or 0 1 2) 0) "or 0 1 2")
|
||||
(assert (= (or nil 1 2) 1) "or nil 1 2")
|
||||
(assert (= (or 1) 1) "or 1")
|
||||
(assert (= (or) nil) "or with no arguments")
|
||||
|
||||
# And/or checks
|
||||
# 6123c41f1
|
||||
(assert (= false (and false false)) "and 1")
|
||||
(assert (= false (or false false)) "or 1")
|
||||
|
||||
# 11cd1279d
|
||||
(assert (deep= @{:a 1 :b 2 :c 3} (zipcoll '[:a :b :c] '[1 2 3])) "zipcoll")
|
||||
|
||||
# bc8be266f
|
||||
(def- a 100)
|
||||
(assert (= a 100) "def-")
|
||||
|
||||
# bc8be266f
|
||||
(assert (= :first
|
||||
(match @[1 3 5]
|
||||
@[x y z] :first
|
||||
:second)) "match 1")
|
||||
|
||||
(def val1 :avalue)
|
||||
(assert (= :second
|
||||
(match val1
|
||||
@[x y z] :first
|
||||
:avalue :second
|
||||
:third)) "match 2")
|
||||
|
||||
(assert (= 100
|
||||
(match @[50 40]
|
||||
@[x x] (* x 3)
|
||||
@[x y] (+ x y 10)
|
||||
0)) "match 3")
|
||||
|
||||
# Match checks
|
||||
# 47e8f669f
|
||||
(assert (= :hi (match nil nil :hi)) "match 1")
|
||||
(assert (= :hi (match {:a :hi} {:a a} a)) "match 2")
|
||||
(assert (= nil (match {:a :hi} {:a a :b b} a)) "match 3")
|
||||
(assert (= nil (match [1 2] [a b c] a)) "match 4")
|
||||
(assert (= 2 (match [1 2] [a b] b)) "match 5")
|
||||
# db631097b
|
||||
(assert (= [2 :a :b] (match [1 2 :a :b] [o & rest] rest)) "match 6")
|
||||
(assert (= [] (match @[:a] @[x & r] r :fallback)) "match 7")
|
||||
(assert (= :fallback (match @[1] @[x y & r] r :fallback)) "match 8")
|
||||
(assert (= [1 2 3 4] (match @[1 2 3 4] @[x y z & r] [x y z ;r] :fallback))
|
||||
"match 9")
|
||||
|
||||
# Test cases for #293
|
||||
# d3b9b8d45
|
||||
(assert (= :yes (match [1 2 3] [_ a _] :yes :no)) "match wildcard 1")
|
||||
(assert (= :no (match [1 2 3] [__ a __] :yes :no)) "match wildcard 2")
|
||||
(assert (= :yes (match [1 2 [1 2 3]] [_ a [_ _ _]] :yes :no))
|
||||
"match wildcard 3")
|
||||
(assert (= :yes (match [1 2 3] (_ (even? 2)) :yes :no)) "match wildcard 4")
|
||||
(assert (= :yes (match {:a 1} {:a _} :yes :no)) "match wildcard 5")
|
||||
(assert (= false (match {:a 1 :b 2 :c 3}
|
||||
{:a a :b _ :c _ :d _} :no
|
||||
{:a _ :b _ :c _} false
|
||||
:no)) "match wildcard 6")
|
||||
(assert (= nil (match {:a 1 :b 2 :c 3}
|
||||
{:a a :b _ :c _ :d _} :no
|
||||
{:a _ :b _ :c _} nil
|
||||
:no)) "match wildcard 7")
|
||||
# issue #529 - 602010600
|
||||
(assert (= "t" (match [true nil] [true _] "t")) "match wildcard 8")
|
||||
|
||||
# quoted match test
|
||||
# 425a0fcf0
|
||||
(assert (= :yes (match 'john 'john :yes _ :nope)) "quoted literal match 1")
|
||||
(assert (= :nope (match 'john ''john :yes _ :nope)) "quoted literal match 2")
|
||||
|
||||
# Some macros
|
||||
# 7880d7320
|
||||
(assert (= 2 (if-not 1 3 2)) "if-not 1")
|
||||
(assert (= 3 (if-not false 3)) "if-not 2")
|
||||
(assert (= 3 (if-not nil 3 2)) "if-not 3")
|
||||
(assert (= nil (if-not true 3)) "if-not 4")
|
||||
|
||||
(assert (= 4 (unless false (+ 1 2 3) 4)) "unless")
|
||||
|
||||
# take
|
||||
# 18da183ef
|
||||
(assert (deep= (take 0 []) []) "take 1")
|
||||
(assert (deep= (take 10 []) []) "take 2")
|
||||
(assert (deep= (take 0 [1 2 3 4 5]) []) "take 3")
|
||||
(assert (deep= (take 10 [1 2 3]) [1 2 3]) "take 4")
|
||||
(assert (deep= (take -1 [:a :b :c]) [:c]) "take 5")
|
||||
# 34019222c
|
||||
(assert (deep= (take 3 (generate [x :in [1 2 3 4 5]] x)) @[1 2 3])
|
||||
"take from fiber")
|
||||
# NB: repeatedly resuming a fiber created with `generate` includes a `nil`
|
||||
# as the final element. Thus a generate of 2 elements will create an array
|
||||
# of 3.
|
||||
(assert (= (length (take 4 (generate [x :in [1 2]] x))) 2)
|
||||
"take from short fiber")
|
||||
|
||||
# take-until
|
||||
# 18da183ef
|
||||
(assert (deep= (take-until pos? @[]) []) "take-until 1")
|
||||
(assert (deep= (take-until pos? @[1 2 3]) []) "take-until 2")
|
||||
(assert (deep= (take-until pos? @[-1 -2 -3]) [-1 -2 -3]) "take-until 3")
|
||||
(assert (deep= (take-until pos? @[-1 -2 3]) [-1 -2]) "take-until 4")
|
||||
(assert (deep= (take-until pos? @[-1 1 -2]) [-1]) "take-until 5")
|
||||
(assert (deep= (take-until |(= $ 115) "books") "book") "take-until 6")
|
||||
(assert (deep= (take-until |(= $ 115) (generate [x :in "books"] x))
|
||||
@[98 111 111 107]) "take-until from fiber")
|
||||
|
||||
# take-while
|
||||
# 18da183ef
|
||||
(assert (deep= (take-while neg? @[]) []) "take-while 1")
|
||||
(assert (deep= (take-while neg? @[1 2 3]) []) "take-while 2")
|
||||
(assert (deep= (take-while neg? @[-1 -2 -3]) [-1 -2 -3]) "take-while 3")
|
||||
(assert (deep= (take-while neg? @[-1 -2 3]) [-1 -2]) "take-while 4")
|
||||
(assert (deep= (take-while neg? @[-1 1 -2]) [-1]) "take-while 5")
|
||||
(assert (deep= (take-while neg? (generate [x :in @[-1 1 -2]] x))
|
||||
@[-1]) "take-while from fiber")
|
||||
|
||||
# drop
|
||||
# 18da183ef
|
||||
(assert (deep= (drop 0 []) []) "drop 1")
|
||||
(assert (deep= (drop 10 []) []) "drop 2")
|
||||
(assert (deep= (drop 0 [1 2 3 4 5]) [1 2 3 4 5]) "drop 3")
|
||||
(assert (deep= (drop 10 [1 2 3]) []) "drop 4")
|
||||
(assert (deep= (drop -1 [1 2 3]) [1 2]) "drop 5")
|
||||
(assert (deep= (drop -10 [1 2 3]) []) "drop 6")
|
||||
(assert (deep= (drop 1 "abc") "bc") "drop 7")
|
||||
(assert (deep= (drop 10 "abc") "") "drop 8")
|
||||
(assert (deep= (drop -1 "abc") "ab") "drop 9")
|
||||
(assert (deep= (drop -10 "abc") "") "drop 10")
|
||||
|
||||
# drop-until
|
||||
# 75dc08f
|
||||
(assert (deep= (drop-until pos? @[]) []) "drop-until 1")
|
||||
(assert (deep= (drop-until pos? @[1 2 3]) [1 2 3]) "drop-until 2")
|
||||
(assert (deep= (drop-until pos? @[-1 -2 -3]) []) "drop-until 3")
|
||||
(assert (deep= (drop-until pos? @[-1 -2 3]) [3]) "drop-until 4")
|
||||
(assert (deep= (drop-until pos? @[-1 1 -2]) [1 -2]) "drop-until 5")
|
||||
(assert (deep= (drop-until |(= $ 115) "books") "s") "drop-until 6")
|
||||
|
||||
# take-drop symmetry #1178
|
||||
(def items-list ['abcde :abcde "abcde" @"abcde" [1 2 3 4 5] @[1 2 3 4 5]])
|
||||
|
||||
(each items items-list
|
||||
(def len (length items))
|
||||
(for i 0 (+ len 1)
|
||||
(assert (deep= (take i items) (drop (- i len) items)) (string/format "take-drop symmetry %q %d" items i))
|
||||
(assert (deep= (take (- i) items) (drop (- len i) items)) (string/format "take-drop symmetry %q %d" items i))))
|
||||
|
||||
(defn squares []
|
||||
(coro
|
||||
(var [a b] [0 1])
|
||||
(forever (yield a) (+= a b) (+= b 2))))
|
||||
|
||||
(def sqr1 (squares))
|
||||
(assert (deep= (take 10 sqr1) @[0 1 4 9 16 25 36 49 64 81]))
|
||||
(assert (deep= (take 1 sqr1) @[100]) "take fiber next value")
|
||||
|
||||
(def sqr2 (drop 10 (squares)))
|
||||
(assert (deep= (take 1 sqr2) @[100]) "drop fiber next value")
|
||||
|
||||
(def dict @{:a 1 :b 2 :c 3 :d 4 :e 5})
|
||||
(def dict1 (take 2 dict))
|
||||
(def dict2 (drop 2 dict))
|
||||
|
||||
(assert (= (length dict1) 2) "take dictionary")
|
||||
(assert (= (length dict2) 3) "drop dictionary")
|
||||
(assert (deep= (merge dict1 dict2) dict) "take-drop symmetry for dictionary")
|
||||
|
||||
# Comment macro
|
||||
# issue #110 - 698e89aba
|
||||
(comment 1)
|
||||
(comment 1 2)
|
||||
(comment 1 2 3)
|
||||
(comment 1 2 3 4)
|
||||
|
||||
# comp should be variadic
|
||||
# 5c83ebd75, 02ce3031
|
||||
(assert (= 10 ((comp +) 1 2 3 4)) "variadic comp 1")
|
||||
(assert (= 11 ((comp inc +) 1 2 3 4)) "variadic comp 2")
|
||||
(assert (= 12 ((comp inc inc +) 1 2 3 4)) "variadic comp 3")
|
||||
(assert (= 13 ((comp inc inc inc +) 1 2 3 4)) "variadic comp 4")
|
||||
(assert (= 14 ((comp inc inc inc inc +) 1 2 3 4)) "variadic comp 5")
|
||||
(assert (= 15 ((comp inc inc inc inc inc +) 1 2 3 4)) "variadic comp 6")
|
||||
(assert (= 16 ((comp inc inc inc inc inc inc +) 1 2 3 4))
|
||||
"variadic comp 7")
|
||||
|
||||
# Function shorthand
|
||||
# 44e752d73
|
||||
(assert (= (|(+ 1 2 3)) 6) "function shorthand 1")
|
||||
(assert (= (|(+ 1 2 3 $) 4) 10) "function shorthand 2")
|
||||
(assert (= (|(+ 1 2 3 $0) 4) 10) "function shorthand 3")
|
||||
(assert (= (|(+ $0 $0 $0 $0) 4) 16) "function shorthand 4")
|
||||
(assert (= (|(+ $ $ $ $) 4) 16) "function shorthand 5")
|
||||
(assert (= (|4) 4) "function shorthand 6")
|
||||
(assert (= (((|||4))) 4) "function shorthand 7")
|
||||
(assert (= (|(+ $1 $1 $1 $1) 2 4) 16) "function shorthand 8")
|
||||
(assert (= (|(+ $0 $1 $3 $2 $6) 0 1 2 3 4 5 6) 12) "function shorthand 9")
|
||||
# 5f5147652
|
||||
(assert (= (|(+ $0 $99) ;(range 100)) 99) "function shorthand 10")
|
||||
|
||||
# 655d4b3aa
|
||||
(defn idx= [x y] (= (tuple/slice x) (tuple/slice y)))
|
||||
|
||||
# Simple take, drop, etc. tests.
|
||||
(assert (idx= (take 10 (range 100)) (range 10)) "take 10")
|
||||
(assert (idx= (drop 10 (range 100)) (range 10 100)) "drop 10")
|
||||
|
||||
# with-vars
|
||||
# 6ceaf9d28
|
||||
(var abc 123)
|
||||
(assert (= 356 (with-vars [abc 456] (- abc 100))) "with-vars 1")
|
||||
(assert-error "with-vars 2" (with-vars [abc 456] (error :oops)))
|
||||
(assert (= abc 123) "with-vars 3")
|
||||
|
||||
# Top level unquote
|
||||
# 2487162cc
|
||||
(defn constantly
|
||||
[]
|
||||
(comptime (math/random)))
|
||||
|
||||
(assert (= (constantly) (constantly)) "comptime 1")
|
||||
|
||||
# issue #232 - b872ee024
|
||||
(assert-error "arity issue in macro" (eval '(each [])))
|
||||
# c6b639b93
|
||||
(assert-error "comptime issue" (eval '(comptime (error "oops"))))
|
||||
|
||||
# 962cd7e5f
|
||||
(var counter 0)
|
||||
(when-with [x nil |$]
|
||||
(++ counter))
|
||||
(when-with [x 10 |$]
|
||||
(+= counter 10))
|
||||
|
||||
(assert (= 10 counter) "when-with 1")
|
||||
|
||||
(if-with [x nil |$] (++ counter) (+= counter 10))
|
||||
(if-with [x true |$] (+= counter 20) (+= counter 30))
|
||||
|
||||
(assert (= 40 counter) "if-with 1")
|
||||
|
||||
# a45509d28
|
||||
(def a @[])
|
||||
(eachk x [:a :b :c :d]
|
||||
(array/push a x))
|
||||
(assert (deep= (range 4) a) "eachk 1")
|
||||
|
||||
# issue 609 - 1fcaffe
|
||||
(with-dyns [:err @""]
|
||||
(tracev (def my-unique-var-name true))
|
||||
(assert my-unique-var-name "tracev upscopes"))
|
||||
|
||||
# Prompts and Labels
|
||||
# 59d288c
|
||||
(assert (= 10 (label a (for i 0 10 (if (= i 5) (return a 10))))) "label 1")
|
||||
|
||||
(defn recur
|
||||
[lab x y]
|
||||
(when (= x y) (return lab :done))
|
||||
(def res (label newlab (recur (or lab newlab) (+ x 1) y)))
|
||||
(if lab :oops res))
|
||||
(assert (= :done (recur nil 0 10)) "label 2")
|
||||
|
||||
(assert (= 10 (prompt :a (for i 0 10 (if (= i 5) (return :a 10)))))
|
||||
"prompt 1")
|
||||
|
||||
(defn- inner-loop
|
||||
[i]
|
||||
(if (= i 5)
|
||||
(return :a 10)))
|
||||
|
||||
(assert (= 10 (prompt :a (for i 0 10 (inner-loop i)))) "prompt 2")
|
||||
|
||||
(defn- inner-loop2
|
||||
[i]
|
||||
(try
|
||||
(if (= i 5)
|
||||
(error 10))
|
||||
([err] (return :a err))))
|
||||
|
||||
(assert (= 10 (prompt :a (for i 0 10 (inner-loop2 i)))) "prompt 3")
|
||||
|
||||
# chr
|
||||
# issue 304 - 77343e02e
|
||||
(assert (= (chr "a") 97) "chr 1")
|
||||
|
||||
# Reduce2
|
||||
# 3eb0927a2
|
||||
(assert (= (reduce + 0 (range 1 10)) (reduce2 + (range 10))) "reduce2 1")
|
||||
# 65379741f
|
||||
(assert (= (reduce * 1 (range 2 10)) (reduce2 * (range 1 10))) "reduce2 2")
|
||||
(assert (= nil (reduce2 * [])) "reduce2 3")
|
||||
|
||||
# Accumulate
|
||||
# 3eb0927a2
|
||||
(assert (deep= (accumulate + 0 (range 5)) @[0 1 3 6 10]) "accumulate 1")
|
||||
(assert (deep= (accumulate2 + (range 5)) @[0 1 3 6 10]) "accumulate2 1")
|
||||
# 65379741f
|
||||
(assert (deep= @[] (accumulate2 + [])) "accumulate2 2")
|
||||
(assert (deep= @[] (accumulate 0 + [])) "accumulate 2")
|
||||
|
||||
# in vs get regression
|
||||
# issue #340 - b63a0796f
|
||||
(assert (nil? (first @"")) "in vs get 1")
|
||||
(assert (nil? (last @"")) "in vs get 1")
|
||||
|
||||
# index-of
|
||||
# 259812314
|
||||
(assert (= nil (index-of 10 [])) "index-of 1")
|
||||
(assert (= nil (index-of 10 [1 2 3])) "index-of 2")
|
||||
(assert (= 1 (index-of 2 [1 2 3])) "index-of 3")
|
||||
(assert (= 0 (index-of :a [:a :b :c])) "index-of 4")
|
||||
(assert (= nil (index-of :a {})) "index-of 5")
|
||||
(assert (= :a (index-of :A {:a :A :b :B})) "index-of 6")
|
||||
(assert (= :a (index-of :A @{:a :A :b :B})) "index-of 7")
|
||||
(assert (= 0 (index-of (chr "a") "abc")) "index-of 8")
|
||||
(assert (= nil (index-of (chr "a") "")) "index-of 9")
|
||||
(assert (= nil (index-of 10 @[])) "index-of 10")
|
||||
(assert (= nil (index-of 10 @[1 2 3])) "index-of 11")
|
||||
|
||||
# e78a3d1
|
||||
# NOTE: These is a motivation for the has-value? and has-key? functions below
|
||||
|
||||
# returns false despite key present
|
||||
(assert (= false (index-of 8 {true 7 false 8}))
|
||||
"index-of corner key (false) 1")
|
||||
(assert (= false (index-of 8 @{false 8}))
|
||||
"index-of corner key (false) 2")
|
||||
# still returns null
|
||||
(assert (= nil (index-of 7 {false 8})) "index-of corner key (false) 3")
|
||||
|
||||
# has-value?
|
||||
(assert (= false (has-value? [] "foo")) "has-value? 1")
|
||||
(assert (= true (has-value? [4 7 1 3] 4)) "has-value? 2")
|
||||
(assert (= false (has-value? [4 7 1 3] 22)) "has-value? 3")
|
||||
(assert (= false (has-value? @[1 2 3] 4)) "has-value? 4")
|
||||
(assert (= true (has-value? @[:a :b :c] :a)) "has-value? 5")
|
||||
(assert (= false (has-value? {} :foo)) "has-value? 6")
|
||||
(assert (= true (has-value? {:a :A :b :B} :A)) "has-value? 7")
|
||||
(assert (= true (has-value? {:a :A :b :B} :A)) "has-value? 7")
|
||||
(assert (= true (has-value? @{:a :A :b :B} :A)) "has-value? 8")
|
||||
(assert (= true (has-value? "abc" (chr "a"))) "has-value? 9")
|
||||
(assert (= false (has-value? "abc" "1")) "has-value? 10")
|
||||
# weird true/false corner cases, should align with "index-of corner
|
||||
# key {k}" cases
|
||||
(assert (= true (has-value? {true 7 false 8} 8))
|
||||
"has-value? corner key (false) 1")
|
||||
(assert (= true (has-value? @{false 8} 8))
|
||||
"has-value? corner key (false) 2")
|
||||
(assert (= false (has-value? {false 8} 7))
|
||||
"has-value? corner key (false) 3")
|
||||
|
||||
# has-key?
|
||||
(do
|
||||
(var test-has-key-auto 0)
|
||||
(defn test-has-key [col key expected &keys {:name name}]
|
||||
``Test that has-key has the outcome `expected`, and that if
|
||||
the result is true, then ensure (in key) does not fail either``
|
||||
(assert (boolean? expected))
|
||||
(default name (string "has-key? " (++ test-has-key-auto)))
|
||||
(assert (= expected (has-key? col key)) name)
|
||||
(if
|
||||
# guarenteed by `has-key?` to never fail
|
||||
expected (in col key)
|
||||
# if `has-key?` is false, then `in` should fail (for indexed types)
|
||||
#
|
||||
# For dictionary types, it should return nil
|
||||
(let [[success retval] (protect (in col key))]
|
||||
(def should-succeed (dictionary? col))
|
||||
(assert
|
||||
(= success should-succeed)
|
||||
(string/format
|
||||
"%s: expected (in col key) to %s, but got %q"
|
||||
name (if expected "succeed" "fail") retval)))))
|
||||
|
||||
(test-has-key [] 0 false) # 1
|
||||
(test-has-key [4 7 1 3] 2 true) # 2
|
||||
(test-has-key [4 7 1 3] 22 false) # 3
|
||||
(test-has-key @[1 2 3] 4 false) # 4
|
||||
(test-has-key @[:a :b :c] 2 true) # 5
|
||||
(test-has-key {} :foo false) # 6
|
||||
(test-has-key {:a :A :b :B} :a true) # 7
|
||||
(test-has-key {:a :A :b :B} :A false) # 8
|
||||
(test-has-key @{:a :A :b :B} :a true) # 9
|
||||
(test-has-key "abc" 1 true) # 10
|
||||
(test-has-key "abc" 4 false) # 11
|
||||
# weird true/false corner cases
|
||||
#
|
||||
# Tries to mimic the corresponding corner cases in has-value? and
|
||||
# index-of, but with keys/values inverted
|
||||
#
|
||||
# in the first two cases (truthy? (get val col)) would have given false
|
||||
# negatives
|
||||
(test-has-key {7 true 8 false} 8 true :name
|
||||
"has-key? corner value (false) 1")
|
||||
(test-has-key @{8 false} 8 true :name
|
||||
"has-key? corner value (false) 2")
|
||||
(test-has-key @{8 false} 7 false :name
|
||||
"has-key? corner value (false) 3"))
|
||||
|
||||
# Regression
|
||||
# issue #463 - 7e7498350
|
||||
(assert (= {:x 10} (|(let [x $] ~{:x ,x}) 10)) "issue 463")
|
||||
|
||||
# macex testing
|
||||
# 7e7498350
|
||||
(assert (deep= (macex1 '~{1 2 3 4}) '~{1 2 3 4}) "macex1 qq struct")
|
||||
(assert (deep= (macex1 '~@{1 2 3 4}) '~@{1 2 3 4}) "macex1 qq table")
|
||||
(assert (deep= (macex1 '~(1 2 3 4)) '~[1 2 3 4]) "macex1 qq tuple")
|
||||
(assert (= :brackets (tuple/type (1 (macex1 '~[1 2 3 4]))))
|
||||
"macex1 qq bracket tuple")
|
||||
(assert (deep= (macex1 '~@[1 2 3 4 ,blah]) '~@[1 2 3 4 ,blah])
|
||||
"macex1 qq array")
|
||||
|
||||
# Sourcemaps in threading macros
|
||||
# b6175e429
|
||||
(defn check-threading [macro expansion]
|
||||
(def expanded (macex1 (tuple macro 0 '(x) '(y))))
|
||||
(assert (= expanded expansion) (string macro " expansion value"))
|
||||
(def smap-x (tuple/sourcemap (get expanded 1)))
|
||||
(def smap-y (tuple/sourcemap expanded))
|
||||
(def line first)
|
||||
(defn column [t] (t 1))
|
||||
(assert (not= smap-x [-1 -1]) (string macro " x sourcemap existence"))
|
||||
(assert (not= smap-y [-1 -1]) (string macro " y sourcemap existence"))
|
||||
(assert (or (< (line smap-x) (line smap-y))
|
||||
(and (= (line smap-x) (line smap-y))
|
||||
(< (column smap-x) (column smap-y))))
|
||||
(string macro " relation between x and y sourcemap")))
|
||||
|
||||
(check-threading '-> '(y (x 0)))
|
||||
(check-threading '->> '(y (x 0)))
|
||||
|
||||
# keep-syntax
|
||||
# b6175e429
|
||||
(let [brak '[1 2 3]
|
||||
par '(1 2 3)]
|
||||
|
||||
(tuple/setmap brak 2 1)
|
||||
|
||||
(assert (deep= (keep-syntax brak @[1 2 3]) @[1 2 3])
|
||||
"keep-syntax brackets ignore array")
|
||||
(assert (= (keep-syntax! brak @[1 2 3]) '[1 2 3])
|
||||
"keep-syntax! brackets replace array")
|
||||
|
||||
(assert (= (keep-syntax! par (map inc @[1 2 3])) '(2 3 4))
|
||||
"keep-syntax! parens coerce array")
|
||||
(assert (not= (keep-syntax! brak @[1 2 3]) '(1 2 3))
|
||||
"keep-syntax! brackets not parens")
|
||||
(assert (not= (keep-syntax! par @[1 2 3]) '[1 2 3])
|
||||
"keep-syntax! parens not brackets")
|
||||
(assert (= (tuple/sourcemap brak)
|
||||
(tuple/sourcemap (keep-syntax! brak @[1 2 3])))
|
||||
"keep-syntax! brackets source map")
|
||||
|
||||
(keep-syntax par brak)
|
||||
(assert (not= (tuple/sourcemap brak) (tuple/sourcemap par))
|
||||
"keep-syntax no mutate")
|
||||
(assert (= (keep-syntax 1 brak) brak) "keep-syntax brackets ignore type"))
|
||||
|
||||
# Curenv
|
||||
# 28439d822, f7c556e
|
||||
(assert (= (curenv) (curenv 0)) "curenv 1")
|
||||
(assert (= (table/getproto (curenv)) (curenv 1)) "curenv 2")
|
||||
(assert (= nil (curenv 1000000)) "curenv 3")
|
||||
(assert (= root-env (curenv 1)) "curenv 4")
|
||||
|
||||
# Import macro test
|
||||
# a31e079f9
|
||||
(assert-no-error "import macro 1" (macex '(import a :as b :fresh maybe)))
|
||||
(assert (deep= ~(,import* "a" :as "b" :fresh maybe)
|
||||
(macex '(import a :as b :fresh maybe))) "import macro 2")
|
||||
|
||||
# #477 walk preserving bracket type
|
||||
# 0a1d902f4
|
||||
(assert (= :brackets (tuple/type (postwalk identity '[])))
|
||||
"walk square brackets 1")
|
||||
(assert (= :brackets (tuple/type (walk identity '[])))
|
||||
"walk square brackets 2")
|
||||
|
||||
# Issue #751
|
||||
# 547fda6a4
|
||||
(def t {:side false})
|
||||
(assert (nil? (get-in t [:side :note])) "get-in with false value")
|
||||
(assert (= (get-in t [:side :note] "dflt") "dflt")
|
||||
"get-in with false value and default")
|
||||
|
||||
# Evaluate stream with `dofile`
|
||||
# 9cc4e4812
|
||||
(def [r w] (os/pipe))
|
||||
(:write w "(setdyn :x 10)")
|
||||
(:close w)
|
||||
(def stream-env (dofile r))
|
||||
(assert (= (stream-env :x) 10) "dofile stream 1")
|
||||
|
||||
# Test thaw and freeze
|
||||
# 9cc0645a1
|
||||
(def table-to-freeze @{:c 22 :b [1 2 3 4] :d @"test" :e "test2"})
|
||||
(def table-to-freeze-with-inline-proto
|
||||
@{:a @[1 2 3] :b @[1 2 3 4] :c 22 :d @"test" :e @"test2"})
|
||||
(def struct-to-thaw
|
||||
(struct/with-proto {:a [1 2 3]} :c 22 :b [1 2 3 4] :d "test" :e "test2"))
|
||||
(table/setproto table-to-freeze @{:a @[1 2 3]})
|
||||
|
||||
(assert (deep= {:a [1 2 3] :b [1 2 3 4] :c 22 :d "test" :e "test2"}
|
||||
(freeze table-to-freeze)))
|
||||
(assert (deep= table-to-freeze-with-inline-proto (thaw table-to-freeze)))
|
||||
(assert (deep= table-to-freeze-with-inline-proto (thaw struct-to-thaw)))
|
||||
|
||||
# Make sure Carriage Returns don't end up in doc strings
|
||||
# e528b86
|
||||
(assert (not (string/find "\r"
|
||||
(get ((fiber/getenv (fiber/current)) 'cond)
|
||||
:doc "")))
|
||||
"no \\r in doc strings")
|
||||
|
||||
# cff718f37
|
||||
(var counter 0)
|
||||
(def thunk (delay (++ counter)))
|
||||
(assert (= (thunk) 1) "delay 1")
|
||||
(assert (= counter 1) "delay 2")
|
||||
(assert (= (thunk) 1) "delay 3")
|
||||
(assert (= counter 1) "delay 4")
|
||||
|
||||
# maclintf
|
||||
(def env (table/clone (curenv)))
|
||||
((compile '(defmacro foo [] (maclintf :strict "oops")) env :anonymous))
|
||||
(def lints @[])
|
||||
(compile (tuple/setmap '(foo) 1 2) env :anonymous lints)
|
||||
(assert (deep= lints @[[:strict 1 2 "oops"]]) "maclintf 1")
|
||||
|
||||
(def env (table/clone (curenv)))
|
||||
((compile '(defmacro foo [& body] (maclintf :strict "foo-oops") ~(do ,;body)) env :anonymous))
|
||||
((compile '(defmacro bar [] (maclintf :strict "bar-oops")) env :anonymous))
|
||||
(def lints @[])
|
||||
# Compile (foo (bar)), but with explicit source map values
|
||||
(def bar-invoke (tuple/setmap '(bar) 3 4))
|
||||
(compile (tuple/setmap ~(foo ,bar-invoke) 1 2) env :anonymous lints)
|
||||
(assert (deep= lints @[[:strict 1 2 "foo-oops"]
|
||||
[:strict 3 4 "bar-oops"]])
|
||||
"maclintf 2")
|
||||
|
||||
# Bad bytecode wrt. using result from break expression
|
||||
(defn bytecode-roundtrip
|
||||
[f]
|
||||
(assert-no-error "bytecode round-trip" (unmarshal (marshal f make-image-dict))))
|
||||
|
||||
(defn case-1 [&] (def x (break 1)))
|
||||
(bytecode-roundtrip case-1)
|
||||
(defn foo [&])
|
||||
(defn case-2 [&]
|
||||
(foo (break (foo)))
|
||||
(foo))
|
||||
(bytecode-roundtrip case-2)
|
||||
(defn case-3 [&]
|
||||
(def x (break (do (foo)))))
|
||||
(bytecode-roundtrip case-3)
|
||||
(defn case-4 [&]
|
||||
(def x (break (break (foo)))))
|
||||
(bytecode-roundtrip case-4)
|
||||
(defn case-4 [&]
|
||||
(def x (break (break (break)))))
|
||||
(bytecode-roundtrip case-4)
|
||||
(defn case-5 []
|
||||
(def foo (fn [one two] one))
|
||||
(foo 100 200))
|
||||
(bytecode-roundtrip case-5)
|
||||
|
||||
# Debug bytecode of these functions
|
||||
# (pp (disasm case-1))
|
||||
# (pp (disasm case-2))
|
||||
# (pp (disasm case-3))
|
||||
|
||||
# Regression #1330
|
||||
(defn regress-1330 [&]
|
||||
(def a [1 2 3])
|
||||
(def b [;a])
|
||||
(identity a))
|
||||
(assert (= [1 2 3] (regress-1330)) "regression 1330")
|
||||
|
||||
# Issue 1341
|
||||
(assert (= () '() (macex '())) "macex ()")
|
||||
(assert (= '[] (macex '[])) "macex []")
|
||||
|
||||
(assert (= :a (with-env @{:b :a} (dyn :b))) "with-env dyn")
|
||||
(assert-error "unknown symbol +" (with-env @{} (eval '(+ 1 2))))
|
||||
|
||||
(end-suite)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue