mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 01:23:04 +00:00 
			
		
		
		
	Compare commits
	
		
			1713 Commits
		
	
	
		
			v1.10.0
			...
			marshal_co
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					117c741c29 | ||
| 
						 | 
					9bc5bec9f1 | ||
| 
						 | 
					a5f4e4d328 | ||
| 
						 | 
					db0abfde72 | ||
| 
						 | 
					edf263bcb5 | ||
| 
						 | 
					60fba585e3 | ||
| 
						 | 
					ebb6fe5be3 | ||
| 
						 | 
					d91c95bf92 | ||
| 
						 | 
					2007438424 | ||
| 
						 | 
					81423635ad | ||
| 
						 | 
					58d297364a | ||
| 
						 | 
					db902c90c4 | ||
| 
						 | 
					42ccd0f790 | ||
| 
						 | 
					20ec6f574e | ||
| 
						 | 
					b3db367ae7 | ||
| 
						 | 
					8a62c742e6 | ||
| 
						 | 
					b125cbeac9 | ||
| 
						 | 
					3f7a2c2197 | ||
| 
						 | 
					f6248369fe | ||
| 
						 | 
					c83f3ec097 | ||
| 
						 | 
					0cd00da354 | ||
| 
						 | 
					4b7b285aa9 | ||
| 
						 | 
					d63379e777 | ||
| 
						 | 
					b219b146fa | ||
| 
						 | 
					ff90b81ec3 | ||
| 
						 | 
					9120eaef79 | ||
| 
						 | 
					1ccd879916 | ||
| 
						 | 
					f977ace7f8 | ||
| 
						 | 
					c3f4dc0c15 | ||
| 
						 | 
					78eed9b11c | ||
| 
						 | 
					3a4d56afca | ||
| 
						 | 
					63bb93fc07 | ||
| 
						 | 
					5a39a04a79 | ||
| 
						 | 
					2fde34b519 | ||
| 
						 | 
					1ef5c038db | ||
| 
						 | 
					e2459cfb47 | ||
| 
						 | 
					cfffc0bcf1 | ||
| 
						 | 
					7272f43191 | ||
| 
						 | 
					2a7ea27bb7 | ||
| 
						 | 
					32c5b816ae | ||
| 
						 | 
					e54ea7a1d8 | ||
| 
						 | 
					1077efd03a | ||
| 
						 | 
					f9ab91511d | ||
| 
						 | 
					2c3ca2984e | ||
| 
						 | 
					94722e566c | ||
| 
						 | 
					163f7ee85d | ||
| 
						 | 
					52d3470cbe | ||
| 
						 | 
					0bd6e85c61 | ||
| 
						 | 
					e35c6b876f | ||
| 
						 | 
					9a2897e741 | ||
| 
						 | 
					70b2e8179d | ||
| 
						 | 
					5317edc65d | ||
| 
						 | 
					866d83579e | ||
| 
						 | 
					a238391b36 | ||
| 
						 | 
					5e152d30db | ||
| 
						 | 
					57c954783d | ||
| 
						 | 
					b5407ac708 | ||
| 
						 | 
					472ec730b5 | ||
| 
						 | 
					8c819b1f91 | ||
| 
						 | 
					528a516390 | ||
| 
						 | 
					6509e37c84 | ||
| 
						 | 
					649173f661 | ||
| 
						 | 
					1efb0adb35 | ||
| 
						 | 
					88a8e2c1df | ||
| 
						 | 
					bb4ff05d35 | ||
| 
						 | 
					dd3b601c87 | ||
| 
						 | 
					e22d101a62 | ||
| 
						 | 
					4b3c813f5a | ||
| 
						 | 
					67f375bea2 | ||
| 
						 | 
					88ba99b87e | ||
| 
						 | 
					53447e9d0b | ||
| 
						 | 
					c4c86f8671 | ||
| 
						 | 
					658941d26d | ||
| 
						 | 
					e4bf27b01c | ||
| 
						 | 
					7d48b75f81 | ||
| 
						 | 
					5f56bf836c | ||
| 
						 | 
					c0f5f97ddb | ||
| 
						 | 
					15177ac2e9 | ||
| 
						 | 
					8360bc93ac | ||
| 
						 | 
					e0ea844d50 | ||
| 
						 | 
					9675411f35 | ||
| 
						 | 
					e97299fc65 | ||
| 
						 | 
					26a113927e | ||
| 
						 | 
					d0aa7ef590 | ||
| 
						 | 
					5de889419f | ||
| 
						 | 
					0fcbda2da7 | ||
| 
						 | 
					14e33c295f | ||
| 
						 | 
					644ac8caf8 | ||
| 
						 | 
					77189b6e66 | ||
| 
						 | 
					4f8f7f66ee | ||
| 
						 | 
					b099bd97f2 | ||
| 
						 | 
					961c6ea15a | ||
| 
						 | 
					9c97d8f648 | ||
| 
						 | 
					ad7bf80611 | ||
| 
						 | 
					40080b23ae | ||
| 
						 | 
					7acb5c63e0 | ||
| 
						 | 
					fcca9bbab3 | ||
| 
						 | 
					dbb2187425 | ||
| 
						 | 
					82e51f9e81 | ||
| 
						 | 
					4782a76bca | ||
| 
						 | 
					d13788a4ed | ||
| 
						 | 
					e64a0175b1 | ||
| 
						 | 
					4aca94154f | ||
| 
						 | 
					ac5f118dac | ||
| 
						 | 
					a2812ec5eb | ||
| 
						 | 
					70f13f1b62 | ||
| 
						 | 
					77e62a25cb | ||
| 
						 | 
					09345ec786 | ||
| 
						 | 
					bad73baf98 | ||
| 
						 | 
					3602f5aa5d | ||
| 
						 | 
					672b705faf | ||
| 
						 | 
					64e3cdeb2b | ||
| 
						 | 
					909c906080 | ||
| 
						 | 
					71bde11e95 | ||
| 
						 | 
					fc20fbed92 | ||
| 
						 | 
					e6b7c85c37 | ||
| 
						 | 
					b3a92363f8 | ||
| 
						 | 
					e9f2d1aca7 | ||
| 
						 | 
					b4e3dbf331 | ||
| 
						 | 
					c3620786cf | ||
| 
						 | 
					41943746e4 | ||
| 
						 | 
					176e816b8c | ||
| 
						 | 
					50a19bd870 | ||
| 
						 | 
					57b751b994 | ||
| 
						 | 
					77732a8f44 | ||
| 
						 | 
					c47c2e538d | ||
| 
						 | 
					cc5545277d | ||
| 
						 | 
					63353b98cd | ||
| 
						 | 
					4dfc869b8a | ||
| 
						 | 
					b4b1c7d80b | ||
| 
						 | 
					e53c03028f | ||
| 
						 | 
					8680aef42f | ||
| 
						 | 
					c3fd71d643 | ||
| 
						 | 
					30c47d685d | ||
| 
						 | 
					80db682109 | ||
| 
						 | 
					e8e5f66f4c | ||
| 
						 | 
					aaf3d08bcd | ||
| 
						 | 
					61132d6c40 | ||
| 
						 | 
					9cc0645a1e | ||
| 
						 | 
					fc8c6a429e | ||
| 
						 | 
					2f966883d9 | ||
| 
						 | 
					320ba80ca1 | ||
| 
						 | 
					b621d4dd2e | ||
| 
						 | 
					56d927c72d | ||
| 
						 | 
					53afc2e50a | ||
| 
						 | 
					89debac8f6 | ||
| 
						 | 
					f2197fa2d8 | ||
| 
						 | 
					a6a097c111 | ||
| 
						 | 
					c3e28bc924 | ||
| 
						 | 
					8d78fb1f6b | ||
| 
						 | 
					148917d4ca | ||
| 
						 | 
					d8cf9bf942 | ||
| 
						 | 
					d6f5a060ed | ||
| 
						 | 
					692b6ef8ac | ||
| 
						 | 
					ac5f1fe1be | ||
| 
						 | 
					0f35acade1 | ||
| 
						 | 
					56d72ec4c5 | ||
| 
						 | 
					71d51c160d | ||
| 
						 | 
					0b58e505ee | ||
| 
						 | 
					2a6c615bec | ||
| 
						 | 
					ab8c5a0b5f | ||
| 
						 | 
					68c35feaea | ||
| 
						 | 
					88d0c2ca0f | ||
| 
						 | 
					398833ebe3 | ||
| 
						 | 
					358f5a03bf | ||
| 
						 | 
					fba1fdabe4 | ||
| 
						 | 
					d42afd21e5 | ||
| 
						 | 
					20ada86761 | ||
| 
						 | 
					3b353f1855 | ||
| 
						 | 
					1467ab4f93 | ||
| 
						 | 
					7e65c2bdad | ||
| 
						 | 
					84a4e3e98a | ||
| 
						 | 
					bcbeedb001 | ||
| 
						 | 
					e04b103b5d | ||
| 
						 | 
					ac75b94679 | ||
| 
						 | 
					d3bb06cfd6 | ||
| 
						 | 
					5cd729c4c1 | ||
| 
						 | 
					c9fd2bdf39 | ||
| 
						 | 
					e4be5992b3 | ||
| 
						 | 
					2ac4988f1b | ||
| 
						 | 
					19f14adb9e | ||
| 
						 | 
					86de039492 | ||
| 
						 | 
					2360164e4f | ||
| 
						 | 
					c93ddceadb | ||
| 
						 | 
					cd19dec44a | ||
| 
						 | 
					53ba9c800a | ||
| 
						 | 
					cabbaded68 | ||
| 
						 | 
					9bb589f827 | ||
| 
						 | 
					c3a06686c2 | ||
| 
						 | 
					7d57f87007 | ||
| 
						 | 
					4cc4a9d38b | ||
| 
						 | 
					02c7cd0194 | ||
| 
						 | 
					696efcb9e2 | ||
| 
						 | 
					6e9cde8ac1 | ||
| 
						 | 
					a9fae49671 | ||
| 
						 | 
					440af9fd64 | ||
| 
						 | 
					347721ae40 | ||
| 
						 | 
					daea91044c | ||
| 
						 | 
					4ed3f2c662 | ||
| 
						 | 
					3641c8f60a | ||
| 
						 | 
					e4b68cd940 | ||
| 
						 | 
					b8c936e2fe | ||
| 
						 | 
					83cd519702 | ||
| 
						 | 
					54b54f85f3 | ||
| 
						 | 
					ccd874fe4e | ||
| 
						 | 
					9dc7e8ed3a | ||
| 
						 | 
					485099fd6e | ||
| 
						 | 
					d359c6b43e | ||
| 
						 | 
					d9ed7a77f8 | ||
| 
						 | 
					4238a4ca6a | ||
| 
						 | 
					0902a5a981 | ||
| 
						 | 
					f3192303ab | ||
| 
						 | 
					bef5bd72c2 | ||
| 
						 | 
					b6175e4296 | ||
| 
						 | 
					3858b2e177 | ||
| 
						 | 
					9a76e77981 | ||
| 
						 | 
					8182d640cd | ||
| 
						 | 
					1c6fda1a5c | ||
| 
						 | 
					c51db1cf2f | ||
| 
						 | 
					4e7930fc4c | ||
| 
						 | 
					3563f8ccdb | ||
| 
						 | 
					575af763f6 | ||
| 
						 | 
					8b16b9b246 | ||
| 
						 | 
					01aab66667 | ||
| 
						 | 
					aa5c987a94 | ||
| 
						 | 
					75229332c8 | ||
| 
						 | 
					9d5b1ba838 | ||
| 
						 | 
					f27b225b34 | ||
| 
						 | 
					3c523d66e9 | ||
| 
						 | 
					1144c27c54 | ||
| 
						 | 
					b442b21d3f | ||
| 
						 | 
					746ff5307d | ||
| 
						 | 
					ef85b24d8f | ||
| 
						 | 
					c55d93512b | ||
| 
						 | 
					2e38f9ba61 | ||
| 
						 | 
					1cadff8e58 | ||
| 
						 | 
					d1eba60ba8 | ||
| 
						 | 
					057dccad8f | ||
| 
						 | 
					4285200b4b | ||
| 
						 | 
					73c2fbbc2a | ||
| 
						 | 
					37b7e170fa | ||
| 
						 | 
					b032d94877 | ||
| 
						 | 
					9476016741 | ||
| 
						 | 
					7a1c9c7798 | ||
| 
						 | 
					c7fb7b4451 | ||
| 
						 | 
					67c474fc7a | ||
| 
						 | 
					4e8154cf8a | ||
| 
						 | 
					9582d3c623 | ||
| 
						 | 
					0079500713 | ||
| 
						 | 
					55af6ce834 | ||
| 
						 | 
					3e82fdc125 | ||
| 
						 | 
					7344a6cfc0 | ||
| 
						 | 
					0aded71343 | ||
| 
						 | 
					7663b1e703 | ||
| 
						 | 
					282546c03f | ||
| 
						 | 
					f4bc89d1c0 | ||
| 
						 | 
					fa277c3797 | ||
| 
						 | 
					c0c8ab25e6 | ||
| 
						 | 
					b685bf3026 | ||
| 
						 | 
					ce31db09e4 | ||
| 
						 | 
					624a6cf619 | ||
| 
						 | 
					587aa87d28 | ||
| 
						 | 
					88813c4f87 | ||
| 
						 | 
					dacbe29771 | ||
| 
						 | 
					244833cfa1 | ||
| 
						 | 
					05e7f974e3 | ||
| 
						 | 
					0dbef65a73 | ||
| 
						 | 
					9106228787 | ||
| 
						 | 
					6ae3bdb25c | ||
| 
						 | 
					310bcec260 | ||
| 
						 | 
					8c4cc4e671 | ||
| 
						 | 
					c6eaaa83ed | ||
| 
						 | 
					8f598d6f96 | ||
| 
						 | 
					20bc323d17 | ||
| 
						 | 
					8b0bcf4db9 | ||
| 
						 | 
					8955e6f536 | ||
| 
						 | 
					f8ddea6452 | ||
| 
						 | 
					987e04086d | ||
| 
						 | 
					85f2acbf52 | ||
| 
						 | 
					1acf4c3ab7 | ||
| 
						 | 
					07a3158fba | ||
| 
						 | 
					2f8bed9d82 | ||
| 
						 | 
					a490937cd9 | ||
| 
						 | 
					8ee5942481 | ||
| 
						 | 
					93b469885a | ||
| 
						 | 
					d8d1de2dcb | ||
| 
						 | 
					ab224514f0 | ||
| 
						 | 
					75179de8da | ||
| 
						 | 
					c28df14e6b | ||
| 
						 | 
					b73855b193 | ||
| 
						 | 
					2093ab2baa | ||
| 
						 | 
					a0f40042cb | ||
| 
						 | 
					3254c2c477 | ||
| 
						 | 
					0a8eb9e3ba | ||
| 
						 | 
					70e0c6f9ef | ||
| 
						 | 
					a8a78d4525 | ||
| 
						 | 
					57e6ee963d | ||
| 
						 | 
					ce6bfb8420 | ||
| 
						 | 
					f0672bdc59 | ||
| 
						 | 
					23de953fbd | ||
| 
						 | 
					03c496bdd8 | ||
| 
						 | 
					d5ee6cf521 | ||
| 
						 | 
					fb7981e053 | ||
| 
						 | 
					846123ecab | ||
| 
						 | 
					373cb444fe | ||
| 
						 | 
					90f212df92 | ||
| 
						 | 
					12286e4246 | ||
| 
						 | 
					aa60c1f36a | ||
| 
						 | 
					c731f01067 | ||
| 
						 | 
					6c9c1cdb30 | ||
| 
						 | 
					9ba2b40e87 | ||
| 
						 | 
					7a3d055012 | ||
| 
						 | 
					0824f45e29 | ||
| 
						 | 
					4debe3446c | ||
| 
						 | 
					07fe9bcdf6 | ||
| 
						 | 
					6a557a73f5 | ||
| 
						 | 
					8d1cfe0c56 | ||
| 
						 | 
					a3a42eebea | ||
| 
						 | 
					76be8006a4 | ||
| 
						 | 
					bfcfd58259 | ||
| 
						 | 
					914a4360e7 | ||
| 
						 | 
					8c31874eeb | ||
| 
						 | 
					ef7afeb2ea | ||
| 
						 | 
					4067f883a2 | ||
| 
						 | 
					c8974fffbe | ||
| 
						 | 
					b75fb8dc9e | ||
| 
						 | 
					57356781a9 | ||
| 
						 | 
					e43eab5fd6 | ||
| 
						 | 
					894cd0e022 | ||
| 
						 | 
					db2c63fffc | ||
| 
						 | 
					60e0f32f1a | ||
| 
						 | 
					e731996a68 | ||
| 
						 | 
					2f69cd4209 | ||
| 
						 | 
					fd59de25c5 | ||
| 
						 | 
					af12c3d41a | ||
| 
						 | 
					54b52bbeb5 | ||
| 
						 | 
					1174c68d9a | ||
| 
						 | 
					448ea7167f | ||
| 
						 | 
					6b27008c99 | ||
| 
						 | 
					725c785882 | ||
| 
						 | 
					ab068cff67 | ||
| 
						 | 
					9dc03adfda | ||
| 
						 | 
					49f9e4eddf | ||
| 
						 | 
					43c47ac44c | ||
| 
						 | 
					1cebe64664 | ||
| 
						 | 
					f33c381043 | ||
| 
						 | 
					3479841c77 | ||
| 
						 | 
					6a899968a9 | ||
| 
						 | 
					bb8405a36e | ||
| 
						 | 
					c7bc711f63 | ||
| 
						 | 
					e326071c35 | ||
| 
						 | 
					ad6a669381 | ||
| 
						 | 
					e4c9dafc9a | ||
| 
						 | 
					dfc0aefd87 | ||
| 
						 | 
					356b39c6f5 | ||
| 
						 | 
					8da7bb6b68 | ||
| 
						 | 
					9341081a4d | ||
| 
						 | 
					324a086eb4 | ||
| 
						 | 
					ed595f52c2 | ||
| 
						 | 
					64ad0023bb | ||
| 
						 | 
					fe5f661d15 | ||
| 
						 | 
					ff26e3a8ba | ||
| 
						 | 
					14657a762c | ||
| 
						 | 
					4754fa3902 | ||
| 
						 | 
					f302f87337 | ||
| 
						 | 
					94dbcde292 | ||
| 
						 | 
					4336a174b1 | ||
| 
						 | 
					0adb13ed71 | ||
| 
						 | 
					e78a3d1c19 | ||
| 
						 | 
					c099ec05ee | ||
| 
						 | 
					a20612478e | ||
| 
						 | 
					f778e8bbd1 | ||
| 
						 | 
					7203c046f9 | ||
| 
						 | 
					754b61c593 | ||
| 
						 | 
					927e9e4e4d | ||
| 
						 | 
					699f9622d7 | ||
| 
						 | 
					765eb84c33 | ||
| 
						 | 
					03ba1f7021 | ||
| 
						 | 
					1f7f20788c | ||
| 
						 | 
					c59dd29190 | ||
| 
						 | 
					99f63a41a3 | ||
| 
						 | 
					a575f5df36 | ||
| 
						 | 
					12a1849090 | ||
| 
						 | 
					0817e627ee | ||
| 
						 | 
					14d90239a7 | ||
| 
						 | 
					f5d11dc656 | ||
| 
						 | 
					6dcf5bf077 | ||
| 
						 | 
					ac2082e9b3 | ||
| 
						 | 
					dbac495bee | ||
| 
						 | 
					fe5ccb163e | ||
| 
						 | 
					1aea5ee007 | ||
| 
						 | 
					13cd9f8067 | ||
| 
						 | 
					34496ecaf0 | ||
| 
						 | 
					c043b1d949 | ||
| 
						 | 
					9a6d2a7b32 | ||
| 
						 | 
					f8a9efa8e4 | ||
| 
						 | 
					5b2169e0d1 | ||
| 
						 | 
					2c927ea768 | ||
| 
						 | 
					f4bbcdcbc8 | ||
| 
						 | 
					79c375b1af | ||
| 
						 | 
					f443a3b3a1 | ||
| 
						 | 
					684d2d63f4 | ||
| 
						 | 
					1900d8f843 | ||
| 
						 | 
					3c2af95d21 | ||
| 
						 | 
					b35414ea0f | ||
| 
						 | 
					fb5b056f7b | ||
| 
						 | 
					7248c1dfdb | ||
| 
						 | 
					4c7ea9e893 | ||
| 
						 | 
					c7801ce277 | ||
| 
						 | 
					f741a8e3ff | ||
| 
						 | 
					6a92e8b609 | ||
| 
						 | 
					9da91a8217 | ||
| 
						 | 
					69853c8e5c | ||
| 
						 | 
					1f41b6c138 | ||
| 
						 | 
					e001efa9fd | ||
| 
						 | 
					435e64d4cf | ||
| 
						 | 
					f296c8f5fb | ||
| 
						 | 
					8d0e6ed32f | ||
| 
						 | 
					b6a36afffe | ||
| 
						 | 
					e422abc269 | ||
| 
						 | 
					221d71d07b | ||
| 
						 | 
					9f35f0837e | ||
| 
						 | 
					515891b035 | ||
| 
						 | 
					94a506876f | ||
| 
						 | 
					9bde57854a | ||
| 
						 | 
					f456369941 | ||
| 
						 | 
					8f0a1ffe5d | ||
| 
						 | 
					e4bafc621a | ||
| 
						 | 
					cfa39ab3b0 | ||
| 
						 | 
					47e91bfd89 | ||
| 
						 | 
					eecc388ebd | ||
| 
						 | 
					0a15a5ee56 | ||
| 
						 | 
					cfaae47cea | ||
| 
						 | 
					c1a0352592 | ||
| 
						 | 
					965f45aa3f | ||
| 
						 | 
					6ea27fe836 | ||
| 
						 | 
					0dccc22b38 | ||
| 
						 | 
					cbe833962b | ||
| 
						 | 
					b5720f6f10 | ||
| 
						 | 
					56b4e0b0ec | ||
| 
						 | 
					e316ccb1e0 | ||
| 
						 | 
					a6f93efd39 | ||
| 
						 | 
					20511cf608 | ||
| 
						 | 
					1a1dd39367 | ||
| 
						 | 
					589981bdcb | ||
| 
						 | 
					89546776b2 | ||
| 
						 | 
					f0d7b3cd12 | ||
| 
						 | 
					e37be627e0 | ||
| 
						 | 
					d803561582 | ||
| 
						 | 
					a1aab4008f | ||
| 
						 | 
					a1172529bf | ||
| 
						 | 
					1d905bf07f | ||
| 
						 | 
					eed678a14b | ||
| 
						 | 
					b1bdffbc34 | ||
| 
						 | 
					cff718f37d | ||
| 
						 | 
					40e9430278 | ||
| 
						 | 
					62fc55fc74 | ||
| 
						 | 
					80729353c8 | ||
| 
						 | 
					105ba5e124 | ||
| 
						 | 
					ad1b50d1f5 | ||
| 
						 | 
					1905437abe | ||
| 
						 | 
					87fc339c45 | ||
| 
						 | 
					3af7d61d3e | ||
| 
						 | 
					a45ef7a856 | ||
| 
						 | 
					299998055d | ||
| 
						 | 
					c9586d39ed | ||
| 
						 | 
					2e9f67f4e4 | ||
| 
						 | 
					e318170fea | ||
| 
						 | 
					73c4289792 | ||
| 
						 | 
					ea45d7ee47 | ||
| 
						 | 
					6d970725e7 | ||
| 
						 | 
					458c2c6d88 | ||
| 
						 | 
					0cc53a8964 | ||
| 
						 | 
					0bc96304a9 | ||
| 
						 | 
					c75b088ff8 | ||
| 
						 | 
					181f0341f5 | ||
| 
						 | 
					33bb08d53b | ||
| 
						 | 
					6d188f6e44 | ||
| 
						 | 
					c3648331f1 | ||
| 
						 | 
					a5b66029d3 | ||
| 
						 | 
					49bfe80191 | ||
| 
						 | 
					a5def77bfe | ||
| 
						 | 
					9ecb5b4791 | ||
| 
						 | 
					1cc48a370a | ||
| 
						 | 
					f1ec8d1e11 | ||
| 
						 | 
					55c34cd84f | ||
| 
						 | 
					aca52d1e36 | ||
| 
						 | 
					6f90df26a5 | ||
| 
						 | 
					9d9cb378ff | ||
| 
						 | 
					f92aac14aa | ||
| 
						 | 
					3f27d78ab5 | ||
| 
						 | 
					282d1ba22f | ||
| 
						 | 
					94c19575b1 | ||
| 
						 | 
					e3e485285b | ||
| 
						 | 
					986e36720e | ||
| 
						 | 
					74348ab6c2 | ||
| 
						 | 
					8d1ad99f42 | ||
| 
						 | 
					e69bbff195 | ||
| 
						 | 
					c9f33bbde0 | ||
| 
						 | 
					9c9f9d4fa6 | ||
| 
						 | 
					2f64a6b0cb | ||
| 
						 | 
					dfa78ad3c6 | ||
| 
						 | 
					677ae46f0c | ||
| 
						 | 
					6ada2a458f | ||
| 
						 | 
					8145f3b68d | ||
| 
						 | 
					48289acee6 | ||
| 
						 | 
					e5a989c6f9 | ||
| 
						 | 
					4c56704935 | ||
| 
						 | 
					9cda44f443 | ||
| 
						 | 
					431451bac2 | ||
| 
						 | 
					395ca7feea | ||
| 
						 | 
					e0b7533c39 | ||
| 
						 | 
					5b2a402930 | ||
| 
						 | 
					85129a1873 | ||
| 
						 | 
					487d333024 | ||
| 
						 | 
					fe7d35171f | ||
| 
						 | 
					b3aed13567 | ||
| 
						 | 
					a9d4d2bfa3 | ||
| 
						 | 
					1ff521683f | ||
| 
						 | 
					0395a03b6b | ||
| 
						 | 
					7fda7709ff | ||
| 
						 | 
					65a9200cff | ||
| 
						 | 
					473eec26c1 | ||
| 
						 | 
					9fa945ad93 | ||
| 
						 | 
					a895219d2f | ||
| 
						 | 
					427f7c362e | ||
| 
						 | 
					73f5c41fae | ||
| 
						 | 
					b4ec168401 | ||
| 
						 | 
					726d35c766 | ||
| 
						 | 
					6db796e10c | ||
| 
						 | 
					c38d9134cd | ||
| 
						 | 
					471204b163 | ||
| 
						 | 
					7f23bfa66d | ||
| 
						 | 
					9287b26042 | ||
| 
						 | 
					e22936fbf8 | ||
| 
						 | 
					04ace9fc16 | ||
| 
						 | 
					8466b333fb | ||
| 
						 | 
					96602612ba | ||
| 
						 | 
					690b98bff9 | ||
| 
						 | 
					8329131bfe | ||
| 
						 | 
					9986aab326 | ||
| 
						 | 
					0b105bc535 | ||
| 
						 | 
					51ac9c9506 | ||
| 
						 | 
					0310176696 | ||
| 
						 | 
					84a7a2bc3e | ||
| 
						 | 
					1e66a7e555 | ||
| 
						 | 
					2bffb9d682 | ||
| 
						 | 
					811125a760 | ||
| 
						 | 
					0dd91082a1 | ||
| 
						 | 
					c80587868e | ||
| 
						 | 
					8c52dc86c7 | ||
| 
						 | 
					be24592bc3 | ||
| 
						 | 
					0d1a5c621d | ||
| 
						 | 
					8a3eff3b65 | ||
| 
						 | 
					b1050b884d | ||
| 
						 | 
					181d883a1d | ||
| 
						 | 
					e01b65fd3d | ||
| 
						 | 
					bbd74b5ae2 | ||
| 
						 | 
					d5a5c49357 | ||
| 
						 | 
					a964b164a6 | ||
| 
						 | 
					1aac0489d7 | ||
| 
						 | 
					e474755887 | ||
| 
						 | 
					bf9a60f70d | ||
| 
						 | 
					a2ba0913d3 | ||
| 
						 | 
					f74df41fff | ||
| 
						 | 
					2a950e4ce9 | ||
| 
						 | 
					f05e5f908e | ||
| 
						 | 
					43139b43b1 | ||
| 
						 | 
					5811b47aad | ||
| 
						 | 
					54e3db4d8c | ||
| 
						 | 
					7491421c31 | ||
| 
						 | 
					9d0da74347 | ||
| 
						 | 
					e9870b293f | ||
| 
						 | 
					ab910d060b | ||
| 
						 | 
					b60ef68ac6 | ||
| 
						 | 
					c9986936ed | ||
| 
						 | 
					d77be46644 | ||
| 
						 | 
					3715d7a184 | ||
| 
						 | 
					1c96c7163a | ||
| 
						 | 
					9f733b25db | ||
| 
						 | 
					1419a33b64 | ||
| 
						 | 
					f270739f9f | ||
| 
						 | 
					e51a391286 | ||
| 
						 | 
					c815185574 | ||
| 
						 | 
					8045e29a52 | ||
| 
						 | 
					bbb3e16fd1 | ||
| 
						 | 
					3cd1657387 | ||
| 
						 | 
					d7ea122cf7 | ||
| 
						 | 
					6aea7c7f70 | ||
| 
						 | 
					56ba1d9cd3 | ||
| 
						 | 
					408b03ae0d | ||
| 
						 | 
					d94fd746af | ||
| 
						 | 
					dbd1316d1e | ||
| 
						 | 
					75845c0283 | ||
| 
						 | 
					88db9751d7 | ||
| 
						 | 
					6f645c4cb7 | ||
| 
						 | 
					4e31d85349 | ||
| 
						 | 
					de542a81c0 | ||
| 
						 | 
					461576e7a2 | ||
| 
						 | 
					21bd62b1ce | ||
| 
						 | 
					838cd1157c | ||
| 
						 | 
					2f068b91d8 | ||
| 
						 | 
					aba87bf1bd | ||
| 
						 | 
					e64da8ede4 | ||
| 
						 | 
					a9f38dfce4 | ||
| 
						 | 
					a097537a03 | ||
| 
						 | 
					66e0b53cf6 | ||
| 
						 | 
					06f2e81dd5 | ||
| 
						 | 
					40ae2e812f | ||
| 
						 | 
					06f613e40b | ||
| 
						 | 
					61c8c1e8d2 | ||
| 
						 | 
					ee924ee310 | ||
| 
						 | 
					fad0ce3ced | ||
| 
						 | 
					d396180939 | ||
| 
						 | 
					0d089abe67 | ||
| 
						 | 
					ed5c1dfc3c | ||
| 
						 | 
					6b949a7375 | ||
| 
						 | 
					3028e2908f | ||
| 
						 | 
					578803b01f | ||
| 
						 | 
					46738825c0 | ||
| 
						 | 
					56357699cb | ||
| 
						 | 
					fe8e718183 | ||
| 
						 | 
					1eb34989d4 | ||
| 
						 | 
					2f3b4c8bfb | ||
| 
						 | 
					6412768000 | ||
| 
						 | 
					82688b9a44 | ||
| 
						 | 
					651e12cfe4 | ||
| 
						 | 
					4118d581af | ||
| 
						 | 
					62608bec03 | ||
| 
						 | 
					71cffc973d | ||
| 
						 | 
					a8e49d084b | ||
| 
						 | 
					db631097b1 | ||
| 
						 | 
					0d31674166 | ||
| 
						 | 
					cb5af974a4 | ||
| 
						 | 
					f2f421a0a2 | ||
| 
						 | 
					413c46e2ee | ||
| 
						 | 
					3b412d51f0 | ||
| 
						 | 
					4931e2aee2 | ||
| 
						 | 
					ffadf673cf | ||
| 
						 | 
					5b5a7e5a24 | ||
| 
						 | 
					ab53208f47 | ||
| 
						 | 
					7c407705e8 | ||
| 
						 | 
					60378ff941 | ||
| 
						 | 
					30a0c77d19 | ||
| 
						 | 
					07ec89276b | ||
| 
						 | 
					a37dc1af9d | ||
| 
						 | 
					03458df140 | ||
| 
						 | 
					164eb9659e | ||
| 
						 | 
					99cfbaa63b | ||
| 
						 | 
					8d8a6534e3 | ||
| 
						 | 
					938c5013c9 | ||
| 
						 | 
					ea9d5ec793 | ||
| 
						 | 
					ec65f038a8 | ||
| 
						 | 
					199ec36d40 | ||
| 
						 | 
					1326ded048 | ||
| 
						 | 
					8347439644 | ||
| 
						 | 
					cddc2a8280 | ||
| 
						 | 
					97a8938407 | ||
| 
						 | 
					939d1dcae9 | ||
| 
						 | 
					9d5cc5c11f | ||
| 
						 | 
					d998f24d26 | ||
| 
						 | 
					d543f8857b | ||
| 
						 | 
					c48a942d22 | ||
| 
						 | 
					e1602618c3 | ||
| 
						 | 
					36be240623 | ||
| 
						 | 
					04e499c97f | ||
| 
						 | 
					f586a8a9dc | ||
| 
						 | 
					5112ed77d6 | ||
| 
						 | 
					bf29a54272 | ||
| 
						 | 
					6d9286a202 | ||
| 
						 | 
					92fdd07ca3 | ||
| 
						 | 
					1c937ad960 | ||
| 
						 | 
					f9891a5c04 | ||
| 
						 | 
					e8ad311d84 | ||
| 
						 | 
					545c09e202 | ||
| 
						 | 
					4dc281a05f | ||
| 
						 | 
					3a0af8caad | ||
| 
						 | 
					8ff2fecb26 | ||
| 
						 | 
					1855c6aed5 | ||
| 
						 | 
					d4c6643311 | ||
| 
						 | 
					e8c738002b | ||
| 
						 | 
					309c3aaeb8 | ||
| 
						 | 
					1f8bcadb3b | ||
| 
						 | 
					6f4af5fef8 | ||
| 
						 | 
					868cdb9f8b | ||
| 
						 | 
					2f76a429ef | ||
| 
						 | 
					a69799aa42 | ||
| 
						 | 
					139bef2142 | ||
| 
						 | 
					8ba142bcf4 | ||
| 
						 | 
					c49e4966f6 | ||
| 
						 | 
					516fa4e49d | ||
| 
						 | 
					6bf9f89429 | ||
| 
						 | 
					a0ddfcb109 | ||
| 
						 | 
					3df7921fdc | ||
| 
						 | 
					6172a9ca2d | ||
| 
						 | 
					4a40e57cf0 | ||
| 
						 | 
					cdedda4ca1 | ||
| 
						 | 
					e6babd84f7 | ||
| 
						 | 
					868ec1a7e3 | ||
| 
						 | 
					e08394c870 | ||
| 
						 | 
					a99500aebf | ||
| 
						 | 
					aa5095c23b | ||
| 
						 | 
					9e0f36e5a7 | ||
| 
						 | 
					d481d079ba | ||
| 
						 | 
					bc9ec7ac4a | ||
| 
						 | 
					6f7e81067c | ||
| 
						 | 
					af946f398e | ||
| 
						 | 
					c7ca26e9c7 | ||
| 
						 | 
					ef7129f45d | ||
| 
						 | 
					a20bdd334a | ||
| 
						 | 
					2ef49a92cc | ||
| 
						 | 
					75f56b68c6 | ||
| 
						 | 
					d34d319d89 | ||
| 
						 | 
					6660c1da38 | ||
| 
						 | 
					4e263b8c39 | ||
| 
						 | 
					3cb604df02 | ||
| 
						 | 
					af9dc7a69e | ||
| 
						 | 
					1247e69c78 | ||
| 
						 | 
					aab0e4315d | ||
| 
						 | 
					14f6517733 | ||
| 
						 | 
					5d75effb37 | ||
| 
						 | 
					ab4f18954b | ||
| 
						 | 
					e1460c65e8 | ||
| 
						 | 
					425a0fcf07 | ||
| 
						 | 
					7205ee5e0a | ||
| 
						 | 
					72c5db8910 | ||
| 
						 | 
					3067f4be3a | ||
| 
						 | 
					2aa1ccdd76 | ||
| 
						 | 
					0284df503f | ||
| 
						 | 
					2833a983d8 | ||
| 
						 | 
					39c6be7cb7 | ||
| 
						 | 
					fdc94c1353 | ||
| 
						 | 
					9cc4e48124 | ||
| 
						 | 
					34c7f15d6d | ||
| 
						 | 
					899a9b025e | ||
| 
						 | 
					deb4315383 | ||
| 
						 | 
					9a06660fdb | ||
| 
						 | 
					5c35d24e13 | ||
| 
						 | 
					03f99752a7 | ||
| 
						 | 
					fd37567c18 | ||
| 
						 | 
					6e38bf1578 | ||
| 
						 | 
					8b2d278840 | ||
| 
						 | 
					06aa0a124d | ||
| 
						 | 
					eb4595158d | ||
| 
						 | 
					32103441f1 | ||
| 
						 | 
					7ed0aa6630 | ||
| 
						 | 
					f690229f31 | ||
| 
						 | 
					f3bab72a86 | ||
| 
						 | 
					2bd63c2d27 | ||
| 
						 | 
					545d9e85e9 | ||
| 
						 | 
					21a4ab4ec7 | ||
| 
						 | 
					66fbbeb5ec | ||
| 
						 | 
					55879c7b6d | ||
| 
						 | 
					66c4e5a5e2 | ||
| 
						 | 
					884139e246 | ||
| 
						 | 
					c3d7b1541e | ||
| 
						 | 
					51ada4d70b | ||
| 
						 | 
					e3a5d52c5e | ||
| 
						 | 
					559fd70737 | ||
| 
						 | 
					e0dba85cbb | ||
| 
						 | 
					74c9cf03d0 | ||
| 
						 | 
					0774e79e4f | ||
| 
						 | 
					a3ec37741a | ||
| 
						 | 
					9bf5cd83c3 | ||
| 
						 | 
					f0da793f99 | ||
| 
						 | 
					684f3ac172 | ||
| 
						 | 
					3e5bd460a5 | ||
| 
						 | 
					3b1d787fbe | ||
| 
						 | 
					980f55ff69 | ||
| 
						 | 
					52ed68bfeb | ||
| 
						 | 
					be0d4c28e4 | ||
| 
						 | 
					79807bf2ab | ||
| 
						 | 
					e48ca1a03f | ||
| 
						 | 
					eae18ce973 | ||
| 
						 | 
					591344ca9d | ||
| 
						 | 
					fbe067823e | ||
| 
						 | 
					ffece911e6 | ||
| 
						 | 
					186afa9651 | ||
| 
						 | 
					6b3037106a | ||
| 
						 | 
					1bf22288ee | ||
| 
						 | 
					3cec470f25 | ||
| 
						 | 
					e1ec0d13ae | ||
| 
						 | 
					924fe97fc3 | ||
| 
						 | 
					504411eade | ||
| 
						 | 
					038ca1b9ca | ||
| 
						 | 
					544b192f8c | ||
| 
						 | 
					7748ccdb8e | ||
| 
						 | 
					64e29c6fce | ||
| 
						 | 
					acdf097998 | ||
| 
						 | 
					ba3107c1fa | ||
| 
						 | 
					9985f787eb | ||
| 
						 | 
					d6f41bcf98 | ||
| 
						 | 
					50bced49ad | ||
| 
						 | 
					4fd7470bbf | ||
| 
						 | 
					033c6f1fdb | ||
| 
						 | 
					6c58347916 | ||
| 
						 | 
					cccbdc164c | ||
| 
						 | 
					cea14a6869 | ||
| 
						 | 
					9b4b24edf7 | ||
| 
						 | 
					8b10a5fb7c | ||
| 
						 | 
					b0d0d9cad2 | ||
| 
						 | 
					d5c8eb048a | ||
| 
						 | 
					9abee3f29a | ||
| 
						 | 
					bf9b6b1301 | ||
| 
						 | 
					8cd57025a0 | ||
| 
						 | 
					faf60b6b1f | ||
| 
						 | 
					da2c1be49c | ||
| 
						 | 
					92c02449f4 | ||
| 
						 | 
					e381622a9a | ||
| 
						 | 
					b799223ebc | ||
| 
						 | 
					40ef224a95 | ||
| 
						 | 
					a4c20b6e1c | ||
| 
						 | 
					e6ee867f72 | ||
| 
						 | 
					468a31f515 | ||
| 
						 | 
					4d746794cc | ||
| 
						 | 
					02d2a66ef2 | ||
| 
						 | 
					4638baf545 | ||
| 
						 | 
					2be23d3768 | ||
| 
						 | 
					b39b1746ba | ||
| 
						 | 
					24f97510b0 | ||
| 
						 | 
					325d5399fa | ||
| 
						 | 
					d8f6fbf594 | ||
| 
						 | 
					21b3e4052c | ||
| 
						 | 
					bf2928805e | ||
| 
						 | 
					7d2bf334c8 | ||
| 
						 | 
					7446802a70 | ||
| 
						 | 
					077bf5ebae | ||
| 
						 | 
					c9bef39f96 | ||
| 
						 | 
					3740eadb7d | ||
| 
						 | 
					e29fa66a74 | ||
| 
						 | 
					ca5406c8e4 | ||
| 
						 | 
					7217caacd1 | ||
| 
						 | 
					8081082251 | ||
| 
						 | 
					1597ca0de5 | ||
| 
						 | 
					8c938ceff9 | ||
| 
						 | 
					65a6945ea5 | ||
| 
						 | 
					02640812af | ||
| 
						 | 
					ba761d5c35 | ||
| 
						 | 
					48a3b1f07f | ||
| 
						 | 
					4370cb77e7 | ||
| 
						 | 
					470e8f6fc7 | ||
| 
						 | 
					b270d88427 | ||
| 
						 | 
					66ce247129 | ||
| 
						 | 
					6ad016c587 | ||
| 
						 | 
					532dac1b95 | ||
| 
						 | 
					2a4bcc262f | ||
| 
						 | 
					1ce2361daf | ||
| 
						 | 
					6e8584e8e0 | ||
| 
						 | 
					121aa91139 | ||
| 
						 | 
					bbc07c72d3 | ||
| 
						 | 
					43b48fdbea | ||
| 
						 | 
					604f97aba1 | ||
| 
						 | 
					dc980081cd | ||
| 
						 | 
					981f03fef3 | ||
| 
						 | 
					d40133dc72 | ||
| 
						 | 
					c9fa586fce | ||
| 
						 | 
					b847a7d90b | ||
| 
						 | 
					8b67108dc8 | ||
| 
						 | 
					b559f9625a | ||
| 
						 | 
					1736c9b0f8 | ||
| 
						 | 
					4fb2d8d318 | ||
| 
						 | 
					95891eb0a5 | ||
| 
						 | 
					c133443eb7 | ||
| 
						 | 
					8f0641f36c | ||
| 
						 | 
					f48dbde736 | ||
| 
						 | 
					f2e4c1ae9a | ||
| 
						 | 
					a4aef38cc0 | ||
| 
						 | 
					b445ecde51 | ||
| 
						 | 
					a209a01284 | ||
| 
						 | 
					7037532943 | ||
| 
						 | 
					bb405ee1aa | ||
| 
						 | 
					ef23356309 | ||
| 
						 | 
					1613e2593c | ||
| 
						 | 
					5464a7a379 | ||
| 
						 | 
					bb1331e449 | ||
| 
						 | 
					acbebc5631 | ||
| 
						 | 
					e1c4fc29de | ||
| 
						 | 
					b903433284 | ||
| 
						 | 
					31a7fdc7b6 | ||
| 
						 | 
					9909adb665 | ||
| 
						 | 
					26f8ba48ee | ||
| 
						 | 
					29ea408980 | ||
| 
						 | 
					0bb7ca7441 | ||
| 
						 | 
					a992644c62 | ||
| 
						 | 
					1c15926e6f | ||
| 
						 | 
					c921315b3e | ||
| 
						 | 
					ab740f92db | ||
| 
						 | 
					1d7390fa7c | ||
| 
						 | 
					0ab96b8e47 | ||
| 
						 | 
					6f6edd37ef | ||
| 
						 | 
					f4282de068 | ||
| 
						 | 
					85c85c07b7 | ||
| 
						 | 
					7abcb1579a | ||
| 
						 | 
					7ce733cc16 | ||
| 
						 | 
					41a3c5f846 | ||
| 
						 | 
					7734e77dfc | ||
| 
						 | 
					257c8b65c2 | ||
| 
						 | 
					846c9e5e12 | ||
| 
						 | 
					685d2b460c | ||
| 
						 | 
					bd71e1cd02 | ||
| 
						 | 
					43a5e12449 | ||
| 
						 | 
					ca97510a52 | ||
| 
						 | 
					50b753cb44 | ||
| 
						 | 
					5ca6704c4d | ||
| 
						 | 
					49142fa385 | ||
| 
						 | 
					d631d29cb4 | ||
| 
						 | 
					01b7891347 | ||
| 
						 | 
					c786a4cbeb | ||
| 
						 | 
					1920ecd668 | ||
| 
						 | 
					c8827424e7 | ||
| 
						 | 
					cc066dd6a1 | ||
| 
						 | 
					eb0b37f729 | ||
| 
						 | 
					e552757edc | ||
| 
						 | 
					87b8dffe23 | ||
| 
						 | 
					81b5904188 | ||
| 
						 | 
					894a3b2fe2 | ||
| 
						 | 
					b75b3e3984 | ||
| 
						 | 
					dea4906144 | ||
| 
						 | 
					97e5117a3f | ||
| 
						 | 
					037215f7c4 | ||
| 
						 | 
					0277187fde | ||
| 
						 | 
					c80a3c1401 | ||
| 
						 | 
					5614f85ea1 | ||
| 
						 | 
					1a3c8692e6 | ||
| 
						 | 
					f2e8691ad5 | ||
| 
						 | 
					c94d7574bc | ||
| 
						 | 
					a38cb5df18 | ||
| 
						 | 
					5407868620 | ||
| 
						 | 
					7edf77561b | ||
| 
						 | 
					a78cbd91da | ||
| 
						 | 
					bb5c3773f1 | ||
| 
						 | 
					2e641a266d | ||
| 
						 | 
					3a787afec6 | ||
| 
						 | 
					34019222c2 | ||
| 
						 | 
					5f3378213b | ||
| 
						 | 
					547fda6a40 | ||
| 
						 | 
					2080ac3bda | ||
| 
						 | 
					61769c8f16 | ||
| 
						 | 
					934e091410 | ||
| 
						 | 
					7f7ee75954 | ||
| 
						 | 
					e76b8da269 | ||
| 
						 | 
					7e5f226480 | ||
| 
						 | 
					2f634184f0 | ||
| 
						 | 
					e3e01466ee | ||
| 
						 | 
					025918cfcc | ||
| 
						 | 
					28fb76e602 | ||
| 
						 | 
					b0f97393a3 | ||
| 
						 | 
					2a7041e751 | ||
| 
						 | 
					58c78d0d78 | ||
| 
						 | 
					eed158afdd | ||
| 
						 | 
					1c7505e04a | ||
| 
						 | 
					617da24942 | ||
| 
						 | 
					98bdbfd3d5 | ||
| 
						 | 
					b289f253c7 | ||
| 
						 | 
					aabae03305 | ||
| 
						 | 
					194d645551 | ||
| 
						 | 
					889d6f9e43 | ||
| 
						 | 
					151de093d0 | ||
| 
						 | 
					cc13e45f21 | ||
| 
						 | 
					7492a4c871 | ||
| 
						 | 
					d20543b92c | ||
| 
						 | 
					59aab2ebbd | ||
| 
						 | 
					08f7b1b9e5 | ||
| 
						 | 
					f2ac1c15e6 | ||
| 
						 | 
					eaf8f198c1 | ||
| 
						 | 
					2955286606 | ||
| 
						 | 
					40561340a8 | ||
| 
						 | 
					4f00a7db88 | ||
| 
						 | 
					acc21d0b76 | ||
| 
						 | 
					db5df70d0c | ||
| 
						 | 
					a6073dc237 | ||
| 
						 | 
					92c132381e | ||
| 
						 | 
					d0575e4087 | ||
| 
						 | 
					5ca48b96af | ||
| 
						 | 
					2a9f30fc8a | ||
| 
						 | 
					ba89a81a3e | ||
| 
						 | 
					5f32300592 | ||
| 
						 | 
					15b4d9363b | ||
| 
						 | 
					ceca0e7f0e | ||
| 
						 | 
					700770b883 | ||
| 
						 | 
					8365037be5 | ||
| 
						 | 
					dfaba7daa6 | ||
| 
						 | 
					5756934144 | ||
| 
						 | 
					7b3ab2727f | ||
| 
						 | 
					714ba808dd | ||
| 
						 | 
					6e94e03baa | ||
| 
						 | 
					ac98dbccb8 | ||
| 
						 | 
					6e3355d7f2 | ||
| 
						 | 
					97907906c5 | ||
| 
						 | 
					eb84200f28 | ||
| 
						 | 
					caaa26e153 | ||
| 
						 | 
					030dd747e9 | ||
| 
						 | 
					dccb98bb92 | ||
| 
						 | 
					e356b7141c | ||
| 
						 | 
					4cae7e6d5d | ||
| 
						 | 
					cc07b4a89a | ||
| 
						 | 
					7e8154e648 | ||
| 
						 | 
					dfee997e45 | ||
| 
						 | 
					f6b7cb9c49 | ||
| 
						 | 
					4452d0e0f5 | ||
| 
						 | 
					7fba44ccce | ||
| 
						 | 
					6f1695ecd4 | ||
| 
						 | 
					76acbf9bb6 | ||
| 
						 | 
					2769a62bb3 | ||
| 
						 | 
					160dd830a0 | ||
| 
						 | 
					aafc595e3a | ||
| 
						 | 
					202783c67a | ||
| 
						 | 
					f11b2c5a0d | ||
| 
						 | 
					e8a86013da | ||
| 
						 | 
					a89c377c92 | ||
| 
						 | 
					54d73f6722 | ||
| 
						 | 
					2e58f5f0d4 | ||
| 
						 | 
					e7ea39f410 | ||
| 
						 | 
					a125218d03 | ||
| 
						 | 
					55b8563c08 | ||
| 
						 | 
					aea1f59f6e | ||
| 
						 | 
					ab27b789e4 | ||
| 
						 | 
					3a1a59f1eb | ||
| 
						 | 
					c20a76cddb | ||
| 
						 | 
					1ef6db16ed | ||
| 
						 | 
					230b734663 | ||
| 
						 | 
					dc414f1239 | ||
| 
						 | 
					dafd2329c5 | ||
| 
						 | 
					12cfda1f58 | ||
| 
						 | 
					96b4e71704 | ||
| 
						 | 
					edb415d1a8 | ||
| 
						 | 
					72c1d1c484 | ||
| 
						 | 
					41a7154aa5 | ||
| 
						 | 
					346d024e48 | ||
| 
						 | 
					04a248dc37 | ||
| 
						 | 
					5defc3b914 | ||
| 
						 | 
					04ca945ecf | ||
| 
						 | 
					d687db71e7 | ||
| 
						 | 
					87f8fe14dd | ||
| 
						 | 
					af08124229 | ||
| 
						 | 
					2eadb21eb7 | ||
| 
						 | 
					8b97a0dbbf | ||
| 
						 | 
					69afa2a7a3 | ||
| 
						 | 
					da5328bae5 | ||
| 
						 | 
					a4325372e2 | ||
| 
						 | 
					4b96b73858 | ||
| 
						 | 
					bbae43f259 | ||
| 
						 | 
					14fedbf063 | ||
| 
						 | 
					ab974c409d | ||
| 
						 | 
					2040709585 | ||
| 
						 | 
					60214dc659 | ||
| 
						 | 
					b990d77f16 | ||
| 
						 | 
					d204e06e11 | ||
| 
						 | 
					f6b37dbc77 | ||
| 
						 | 
					fab65d6c40 | ||
| 
						 | 
					ff4d49f556 | ||
| 
						 | 
					dfa5fa1187 | ||
| 
						 | 
					1f4f69a5b6 | ||
| 
						 | 
					84f82f5465 | ||
| 
						 | 
					c911f7c47e | ||
| 
						 | 
					4d983e54b5 | ||
| 
						 | 
					33c000daea | ||
| 
						 | 
					7ff204ec44 | ||
| 
						 | 
					7c757ef3bf | ||
| 
						 | 
					2db7945d6f | ||
| 
						 | 
					81186bf262 | ||
| 
						 | 
					eeef5b0896 | ||
| 
						 | 
					8189b6fc11 | ||
| 
						 | 
					e5a2df93ab | ||
| 
						 | 
					c3f770da27 | ||
| 
						 | 
					49f66a936c | ||
| 
						 | 
					83dda98240 | ||
| 
						 | 
					b4ddbd0097 | ||
| 
						 | 
					cbe92bb985 | ||
| 
						 | 
					60c6a0d334 | ||
| 
						 | 
					1baab5eb61 | ||
| 
						 | 
					8fc8974b60 | ||
| 
						 | 
					ecb49c2e5e | ||
| 
						 | 
					29797b9eb0 | ||
| 
						 | 
					e181ee586b | ||
| 
						 | 
					7b7d742bec | ||
| 
						 | 
					612eaff9ff | ||
| 
						 | 
					d76ef187e8 | ||
| 
						 | 
					e01ab86a89 | ||
| 
						 | 
					89b59b4ffc | ||
| 
						 | 
					e367ecf806 | ||
| 
						 | 
					effc9e0f33 | ||
| 
						 | 
					da06e6c6e3 | ||
| 
						 | 
					c258bee54f | ||
| 
						 | 
					cde4a505cf | ||
| 
						 | 
					2802e66259 | ||
| 
						 | 
					3a3003029a | ||
| 
						 | 
					08bca8fb63 | ||
| 
						 | 
					7c7ff802fa | ||
| 
						 | 
					0945acc780 | ||
| 
						 | 
					64ec9f9cb6 | ||
| 
						 | 
					83f7de33c0 | ||
| 
						 | 
					ec2d7bf349 | ||
| 
						 | 
					f4c9064b79 | ||
| 
						 | 
					8ede16dc26 | ||
| 
						 | 
					27e400fba3 | ||
| 
						 | 
					37d6cb469b | ||
| 
						 | 
					100a82feb2 | ||
| 
						 | 
					90e5828d5d | ||
| 
						 | 
					b3e80308d4 | ||
| 
						 | 
					a7abe11105 | ||
| 
						 | 
					3c63a48df4 | ||
| 
						 | 
					fcb88e5a98 | ||
| 
						 | 
					a467b34de4 | ||
| 
						 | 
					a24cc77ff8 | ||
| 
						 | 
					d6675d9909 | ||
| 
						 | 
					fa163093d2 | ||
| 
						 | 
					e70f64e23d | ||
| 
						 | 
					6f605f8141 | ||
| 
						 | 
					d9419ef994 | ||
| 
						 | 
					7e8639a682 | ||
| 
						 | 
					452b303b4c | ||
| 
						 | 
					b0f1a4967d | ||
| 
						 | 
					9eb4c59c04 | ||
| 
						 | 
					0d42506cde | ||
| 
						 | 
					c8a13ce475 | ||
| 
						 | 
					05e3467d09 | ||
| 
						 | 
					90639e5068 | ||
| 
						 | 
					73c7711c78 | ||
| 
						 | 
					78f6b6a507 | ||
| 
						 | 
					84f0ab5356 | ||
| 
						 | 
					546437d799 | ||
| 
						 | 
					0f05aec563 | ||
| 
						 | 
					c9097623d6 | ||
| 
						 | 
					6392b37c47 | ||
| 
						 | 
					4fcc8075d4 | ||
| 
						 | 
					b2d6a55335 | ||
| 
						 | 
					1fea5f8fe7 | ||
| 
						 | 
					d3e52a2afb | ||
| 
						 | 
					d6ea1989cc | ||
| 
						 | 
					96513665d6 | ||
| 
						 | 
					b795d13f61 | ||
| 
						 | 
					970f9b3981 | ||
| 
						 | 
					be7dab4d17 | ||
| 
						 | 
					0e44ce5cba | ||
| 
						 | 
					1f8c2781dd | ||
| 
						 | 
					f381a9c773 | ||
| 
						 | 
					855a9a01fc | ||
| 
						 | 
					a5f237993d | ||
| 
						 | 
					c68264802a | ||
| 
						 | 
					742469a8bc | ||
| 
						 | 
					92928d5c4f | ||
| 
						 | 
					8320e25d64 | ||
| 
						 | 
					c16a9d8463 | ||
| 
						 | 
					f1819c916a | ||
| 
						 | 
					dd14b24d2a | ||
| 
						 | 
					050d7c12a3 | ||
| 
						 | 
					7e2c433abc | ||
| 
						 | 
					6713b23a65 | ||
| 
						 | 
					60078e7950 | ||
| 
						 | 
					69095fbb48 | ||
| 
						 | 
					c88a3c64e3 | ||
| 
						 | 
					771b0d0ab1 | ||
| 
						 | 
					c85310578b | ||
| 
						 | 
					60e2992158 | ||
| 
						 | 
					2795e8a8b7 | ||
| 
						 | 
					bdf14170a4 | ||
| 
						 | 
					10dcbc639a | ||
| 
						 | 
					6a9bb0f4e4 | ||
| 
						 | 
					c941e5a8f4 | ||
| 
						 | 
					be91414c7a | ||
| 
						 | 
					6839b603c8 | ||
| 
						 | 
					926b68d62e | ||
| 
						 | 
					d374e90033 | ||
| 
						 | 
					b168b0758a | ||
| 
						 | 
					54c66ecfc0 | ||
| 
						 | 
					1c158bd4ff | ||
| 
						 | 
					ff24143f54 | ||
| 
						 | 
					dd117e81c2 | ||
| 
						 | 
					f4744a18c6 | ||
| 
						 | 
					259d5fabd9 | ||
| 
						 | 
					d122a75efd | ||
| 
						 | 
					c9ea3ac304 | ||
| 
						 | 
					c63fe6ef8a | ||
| 
						 | 
					72ec89dfe9 | ||
| 
						 | 
					04805d106e | ||
| 
						 | 
					9aed578466 | ||
| 
						 | 
					77c5279296 | ||
| 
						 | 
					af75bf3b64 | ||
| 
						 | 
					a5157e868b | ||
| 
						 | 
					01a3d8f932 | ||
| 
						 | 
					f22472a644 | ||
| 
						 | 
					5cac8bcf9f | ||
| 
						 | 
					a2801fbef9 | ||
| 
						 | 
					0b14e913da | ||
| 
						 | 
					85155bb2b4 | ||
| 
						 | 
					dd8de1e9ac | ||
| 
						 | 
					c909835b0a | ||
| 
						 | 
					a18aafedfd | ||
| 
						 | 
					317ab6df6b | ||
| 
						 | 
					1fcaffe6b0 | ||
| 
						 | 
					3ae5c410dc | ||
| 
						 | 
					381128364e | ||
| 
						 | 
					0acf167e84 | ||
| 
						 | 
					f7ca6deeb0 | ||
| 
						 | 
					251486e4aa | ||
| 
						 | 
					c6467be60d | ||
| 
						 | 
					4dd512ad28 | ||
| 
						 | 
					28076b9385 | ||
| 
						 | 
					49dcc816ae | ||
| 
						 | 
					fa61c70103 | ||
| 
						 | 
					5ee6dbcdf4 | ||
| 
						 | 
					634219da2c | ||
| 
						 | 
					fbe3849b4b | ||
| 
						 | 
					bd2e335063 | ||
| 
						 | 
					96262e7d87 | ||
| 
						 | 
					c5da87b860 | ||
| 
						 | 
					848d4a1498 | ||
| 
						 | 
					70e23df6f8 | ||
| 
						 | 
					95af205681 | ||
| 
						 | 
					6dfb689d1f | ||
| 
						 | 
					462e74ef87 | ||
| 
						 | 
					c6aa536590 | ||
| 
						 | 
					c79480342b | ||
| 
						 | 
					a1cc5ca045 | ||
| 
						 | 
					7f74ff3dd7 | ||
| 
						 | 
					c4a95e9a1e | ||
| 
						 | 
					71f9e2b1d7 | ||
| 
						 | 
					16fe32215b | ||
| 
						 | 
					dd7342a6cf | ||
| 
						 | 
					35c88d10cd | ||
| 
						 | 
					42532de0eb | ||
| 
						 | 
					122e2a9378 | ||
| 
						 | 
					33c9395d79 | ||
| 
						 | 
					fc49aa359c | ||
| 
						 | 
					fcf37942a7 | ||
| 
						 | 
					9b42d5a5e9 | ||
| 
						 | 
					ba92dfcbe9 | ||
| 
						 | 
					fd03603adb | ||
| 
						 | 
					2008ddf8a8 | ||
| 
						 | 
					c56b876bfe | ||
| 
						 | 
					c4957d5dfb | ||
| 
						 | 
					068bd33afb | ||
| 
						 | 
					e9bd108be9 | ||
| 
						 | 
					4f2d1cdc00 | ||
| 
						 | 
					61cca10cf6 | ||
| 
						 | 
					dfbdd17dce | ||
| 
						 | 
					9078d3bd37 | ||
| 
						 | 
					5e1a8c86f9 | ||
| 
						 | 
					bf01bf631d | ||
| 
						 | 
					80c5ba32b5 | ||
| 
						 | 
					874cc79443 | ||
| 
						 | 
					3883460202 | ||
| 
						 | 
					f0dbc2e404 | ||
| 
						 | 
					4df1ac5b23 | ||
| 
						 | 
					1f6d0d342b | ||
| 
						 | 
					4625c28e6a | ||
| 
						 | 
					5536ba20a8 | ||
| 
						 | 
					ef398e9036 | ||
| 
						 | 
					0c73c3f1cd | ||
| 
						 | 
					7ae7984f3c | ||
| 
						 | 
					8286b33c52 | ||
| 
						 | 
					475775cc9d | ||
| 
						 | 
					11067d7a56 | ||
| 
						 | 
					5b05da65f0 | ||
| 
						 | 
					444e630783 | ||
| 
						 | 
					8951b8de7a | ||
| 
						 | 
					2abb87eb63 | ||
| 
						 | 
					32e8ac912d | ||
| 
						 | 
					e403fb4652 | ||
| 
						 | 
					daa37c22f5 | ||
| 
						 | 
					5a2a134c95 | ||
| 
						 | 
					b9acb6dfa5 | ||
| 
						 | 
					4e7ad3c7ce | ||
| 
						 | 
					ee0e1a2342 | ||
| 
						 | 
					f206b476d1 | ||
| 
						 | 
					dd2595c53f | ||
| 
						 | 
					545df28d71 | ||
| 
						 | 
					16f80b78cf | ||
| 
						 | 
					147bcce01b | ||
| 
						 | 
					f5877ac6d1 | ||
| 
						 | 
					adc41e31f4 | ||
| 
						 | 
					2e555a930f | ||
| 
						 | 
					bcba0c0279 | ||
| 
						 | 
					c7f382add6 | ||
| 
						 | 
					665b1e68d5 | ||
| 
						 | 
					2ca9300bf7 | ||
| 
						 | 
					81f62b246c | ||
| 
						 | 
					87badc71d2 | ||
| 
						 | 
					e5242c67ff | ||
| 
						 | 
					4355420994 | ||
| 
						 | 
					c357af02c2 | ||
| 
						 | 
					19576effbe | ||
| 
						 | 
					ecc6eb7497 | ||
| 
						 | 
					d0ac318980 | ||
| 
						 | 
					7b030fe70d | ||
| 
						 | 
					115556fcf2 | ||
| 
						 | 
					9760cf1f4e | ||
| 
						 | 
					47bb7fd21b | ||
| 
						 | 
					1c7ed8ca48 | ||
| 
						 | 
					6b268c5df4 | ||
| 
						 | 
					62f783f1dc | ||
| 
						 | 
					61c65f3df1 | ||
| 
						 | 
					05166b3673 | ||
| 
						 | 
					0a1c93b869 | ||
| 
						 | 
					788f91a36f | ||
| 
						 | 
					c831ecf5d2 | ||
| 
						 | 
					9e42ee153c | ||
| 
						 | 
					d457aa5951 | ||
| 
						 | 
					ab37ee6ebb | ||
| 
						 | 
					8655530b19 | ||
| 
						 | 
					27b1f59aa9 | ||
| 
						 | 
					cc2cc4db43 | ||
| 
						 | 
					20bcd95279 | ||
| 
						 | 
					d7954be5e5 | ||
| 
						 | 
					0ea77cabfb | ||
| 
						 | 
					0d46352ff4 | ||
| 
						 | 
					ffa0d5fe45 | ||
| 
						 | 
					a2c837a99c | ||
| 
						 | 
					13d8d11011 | ||
| 
						 | 
					2357b6162f | ||
| 
						 | 
					b4f242193d | ||
| 
						 | 
					7242ee0186 | ||
| 
						 | 
					3e742ffc4c | ||
| 
						 | 
					2ec12fe06f | ||
| 
						 | 
					c76e0ae685 | ||
| 
						 | 
					25ded775ad | ||
| 
						 | 
					cae4f19629 | ||
| 
						 | 
					04f6c7b156 | ||
| 
						 | 
					77b79e9899 | ||
| 
						 | 
					a55354357c | ||
| 
						 | 
					392d5d51df | ||
| 
						 | 
					9bc996a630 | ||
| 
						 | 
					7b709d4c68 | ||
| 
						 | 
					eab5f67c5c | ||
| 
						 | 
					6020106000 | ||
| 
						 | 
					12f470ed10 | ||
| 
						 | 
					945cbcfad6 | ||
| 
						 | 
					d53007739e | ||
| 
						 | 
					6eaf8272e1 | ||
| 
						 | 
					6fb83dce06 | ||
| 
						 | 
					52addc877d | ||
| 
						 | 
					53a5f3d2dc | ||
| 
						 | 
					711ee5a36d | ||
| 
						 | 
					cd09b696b5 | ||
| 
						 | 
					df1ca255a9 | ||
| 
						 | 
					811a5d93f4 | ||
| 
						 | 
					adbe361b9b | ||
| 
						 | 
					0f16f21677 | ||
| 
						 | 
					aa0de01e5f | ||
| 
						 | 
					785757f2f6 | ||
| 
						 | 
					01120dfc46 | ||
| 
						 | 
					a119eb4ef0 | ||
| 
						 | 
					0aa4c3d217 | ||
| 
						 | 
					3c0cc59d77 | ||
| 
						 | 
					7e1d095996 | ||
| 
						 | 
					cfa9fb6ee4 | ||
| 
						 | 
					9d23192614 | ||
| 
						 | 
					7c1a52ae65 | ||
| 
						 | 
					9aa1b9c740 | ||
| 
						 | 
					c4a4916055 | ||
| 
						 | 
					b402e0671a | ||
| 
						 | 
					8144f83b66 | ||
| 
						 | 
					cd2a55e268 | ||
| 
						 | 
					f92b5d69c8 | ||
| 
						 | 
					a8c21459c3 | ||
| 
						 | 
					4789b4c9f3 | ||
| 
						 | 
					ee1cd6f151 | ||
| 
						 | 
					dfcda296a3 | ||
| 
						 | 
					4d38fcb289 | ||
| 
						 | 
					cbdea8f331 | ||
| 
						 | 
					51d6a13510 | ||
| 
						 | 
					7b4eeecd9f | ||
| 
						 | 
					82eff7e082 | ||
| 
						 | 
					b922e36071 | ||
| 
						 | 
					7c75aeaad2 | ||
| 
						 | 
					2db9323671 | ||
| 
						 | 
					31ae93de19 | ||
| 
						 | 
					a81e9f23f0 | ||
| 
						 | 
					59f09a4386 | ||
| 
						 | 
					53400ecac1 | ||
| 
						 | 
					1b8928a8ec | ||
| 
						 | 
					e706494893 | ||
| 
						 | 
					894aea7ce7 | ||
| 
						 | 
					87167a21c9 | ||
| 
						 | 
					7c8f5ef811 | ||
| 
						 | 
					7aa4241662 | ||
| 
						 | 
					56a915b5b1 | ||
| 
						 | 
					90a0dfa35f | ||
| 
						 | 
					128d72785f | ||
| 
						 | 
					21a6017547 | ||
| 
						 | 
					a0964d44d5 | ||
| 
						 | 
					fb0859dfe6 | ||
| 
						 | 
					dadd6037bb | ||
| 
						 | 
					6f3eff3258 | ||
| 
						 | 
					02224d5aa9 | ||
| 
						 | 
					bfd2845077 | ||
| 
						 | 
					ba2e0489e6 | ||
| 
						 | 
					ca7c5b8b10 | ||
| 
						 | 
					6c43489fb2 | ||
| 
						 | 
					d76f671d37 | ||
| 
						 | 
					776ce586bc | ||
| 
						 | 
					adc3066dc8 | ||
| 
						 | 
					7fd2da1096 | ||
| 
						 | 
					451340e4c0 | ||
| 
						 | 
					a3e812b86d | ||
| 
						 | 
					a3f98091c4 | ||
| 
						 | 
					6720b34868 | ||
| 
						 | 
					781ed0dc67 | ||
| 
						 | 
					8f00848c7b | ||
| 
						 | 
					53aa19a916 | ||
| 
						 | 
					2dc04d2957 | ||
| 
						 | 
					306bdee673 | ||
| 
						 | 
					cff52ded58 | ||
| 
						 | 
					fbe658a724 | ||
| 
						 | 
					f9d0eb47b7 | ||
| 
						 | 
					078f50d45a | ||
| 
						 | 
					974a45c804 | ||
| 
						 | 
					760e4e3d68 | ||
| 
						 | 
					9ec5689d6b | ||
| 
						 | 
					c8b72431a3 | ||
| 
						 | 
					0eb913fb9a | ||
| 
						 | 
					fce27cb2e8 | ||
| 
						 | 
					1b6272db2e | ||
| 
						 | 
					b1c0ad5e42 | ||
| 
						 | 
					3f7cdcb6a7 | ||
| 
						 | 
					a25b030e36 | ||
| 
						 | 
					717fac02d1 | ||
| 
						 | 
					dcf8ba0edb | ||
| 
						 | 
					3ab2ae130b | ||
| 
						 | 
					6e6900fa3a | ||
| 
						 | 
					d7af4596e1 | ||
| 
						 | 
					1759151875 | ||
| 
						 | 
					a7ed3dea4b | ||
| 
						 | 
					cdcb774dc8 | ||
| 
						 | 
					d199c817dc | ||
| 
						 | 
					dc51bd09f7 | ||
| 
						 | 
					139e3fab25 | ||
| 
						 | 
					7a98f9aa02 | ||
| 
						 | 
					b53dd67e74 | ||
| 
						 | 
					e546731093 | ||
| 
						 | 
					d50c4ef6da | ||
| 
						 | 
					7d0b1955a2 | ||
| 
						 | 
					16cf7681f0 | ||
| 
						 | 
					12f09ad2d7 | ||
| 
						 | 
					b3e88a8d80 | ||
| 
						 | 
					761273bcc4 | ||
| 
						 | 
					1a75f68cb2 | ||
| 
						 | 
					1b0edf54f1 | ||
| 
						 | 
					caa6576719 | ||
| 
						 | 
					93bd2c11fa | ||
| 
						 | 
					2be09790a9 | ||
| 
						 | 
					bf6eae711a | ||
| 
						 | 
					69b68c0091 | ||
| 
						 | 
					6f1d5d3b73 | ||
| 
						 | 
					099a912992 | ||
| 
						 | 
					56b1ea3726 | ||
| 
						 | 
					d6391f2d70 | ||
| 
						 | 
					07910272e2 | ||
| 
						 | 
					1092013c2b | ||
| 
						 | 
					0db83bd787 | ||
| 
						 | 
					f55316eabc | ||
| 
						 | 
					840f59934e | ||
| 
						 | 
					75a9c59ad8 | ||
| 
						 | 
					adfccd33ae | ||
| 
						 | 
					9d41243c15 | ||
| 
						 | 
					e33e182eb0 | ||
| 
						 | 
					4dffd662f0 | ||
| 
						 | 
					5064d579d4 | ||
| 
						 | 
					540425a41b | ||
| 
						 | 
					4d21b582c7 | ||
| 
						 | 
					f288bc1790 | ||
| 
						 | 
					8942e348bd | ||
| 
						 | 
					9f27336827 | ||
| 
						 | 
					f517cccf7b | ||
| 
						 | 
					3a937ace51 | ||
| 
						 | 
					b8661f8bff | ||
| 
						 | 
					51828ab5f8 | ||
| 
						 | 
					84fe5d7f34 | ||
| 
						 | 
					2891d2b260 | ||
| 
						 | 
					edfb861a5f | ||
| 
						 | 
					88c1cf3ee7 | ||
| 
						 | 
					813e3fdcfd | ||
| 
						 | 
					bbe10e4938 | ||
| 
						 | 
					cb4903fa86 | ||
| 
						 | 
					ea45165db8 | ||
| 
						 | 
					1fba699ed4 | ||
| 
						 | 
					ce3d574c41 | ||
| 
						 | 
					7a601a7eb2 | ||
| 
						 | 
					9ec66ab826 | ||
| 
						 | 
					ebfa07f8ce | ||
| 
						 | 
					964a800d51 | ||
| 
						 | 
					5c05dec65a | ||
| 
						 | 
					bf6ebc4a68 | ||
| 
						 | 
					2e944931b3 | ||
| 
						 | 
					db67538311 | ||
| 
						 | 
					307c7e00e2 | ||
| 
						 | 
					45feb55483 | ||
| 
						 | 
					0a1d902f46 | ||
| 
						 | 
					959a577b5f | ||
| 
						 | 
					b91fe8be5a | ||
| 
						 | 
					d1f0a13ddc | ||
| 
						 | 
					c455bdad11 | ||
| 
						 | 
					bc1ef813c2 | ||
| 
						 | 
					603791c0ba | ||
| 
						 | 
					8091b1289f | ||
| 
						 | 
					cc0035b1d7 | ||
| 
						 | 
					ceba1ba4ee | ||
| 
						 | 
					468e13501c | ||
| 
						 | 
					32bf70571a | ||
| 
						 | 
					95f4bd8e23 | ||
| 
						 | 
					4c9624db64 | ||
| 
						 | 
					2cbf4d8ad1 | ||
| 
						 | 
					524c9b50d4 | ||
| 
						 | 
					d3147b661b | ||
| 
						 | 
					d3182dce51 | ||
| 
						 | 
					8763df1cd0 | ||
| 
						 | 
					15e05b692c | ||
| 
						 | 
					2bf5e341d3 | ||
| 
						 | 
					b53890ddae | ||
| 
						 | 
					93602ad9ea | ||
| 
						 | 
					191d0001f4 | ||
| 
						 | 
					1a04ce33f1 | ||
| 
						 | 
					babfe50550 | ||
| 
						 | 
					ff57b3eb72 | ||
| 
						 | 
					1837e89fe4 | ||
| 
						 | 
					24b8b0e382 | ||
| 
						 | 
					321a758ab9 | ||
| 
						 | 
					1a9c14acde | ||
| 
						 | 
					e8734c77b4 | ||
| 
						 | 
					1eb00a9f74 | ||
| 
						 | 
					922a21d359 | ||
| 
						 | 
					4a4f314768 | ||
| 
						 | 
					3c64596ea1 | ||
| 
						 | 
					33283b1b6e | ||
| 
						 | 
					2f89bdc672 | ||
| 
						 | 
					2d275c4782 | ||
| 
						 | 
					25156eb83e | ||
| 
						 | 
					39032b45c9 | ||
| 
						 | 
					821a8dca3b | ||
| 
						 | 
					0145b133a1 | ||
| 
						 | 
					b0b137d7f0 | ||
| 
						 | 
					b0c09153c2 | ||
| 
						 | 
					0485078c6c | ||
| 
						 | 
					7079cc43c9 | ||
| 
						 | 
					e7fca0051e | ||
| 
						 | 
					6273e56886 | ||
| 
						 | 
					8b9ad2dce8 | ||
| 
						 | 
					301cbb0e68 | ||
| 
						 | 
					5313963baf | ||
| 
						 | 
					f60348eee4 | ||
| 
						 | 
					a31e079f93 | ||
| 
						 | 
					556edc9f0d | ||
| 
						 | 
					17d0b7a985 | ||
| 
						 | 
					86e00e865e | ||
| 
						 | 
					5dda83dc73 | ||
| 
						 | 
					28439d822a | ||
| 
						 | 
					b1d8ee19ca | ||
| 
						 | 
					f7c556ed8d | ||
| 
						 | 
					5377e10532 | ||
| 
						 | 
					30522bbf7d | ||
| 
						 | 
					58374623b7 | ||
| 
						 | 
					7e7498350f | ||
| 
						 | 
					06c268c274 | ||
| 
						 | 
					9b36e2b145 | ||
| 
						 | 
					ca75f8dc20 | ||
| 
						 | 
					6f2f3fdb68 | ||
| 
						 | 
					c903e49a4f | ||
| 
						 | 
					9121feb44f | ||
| 
						 | 
					7b42ed66f2 | ||
| 
						 | 
					fb26c9b2c4 | ||
| 
						 | 
					78ffb63429 | ||
| 
						 | 
					1213990b7d | ||
| 
						 | 
					c3af30d520 | ||
| 
						 | 
					2598123140 | ||
| 
						 | 
					40627191f3 | ||
| 
						 | 
					38dc844e85 | ||
| 
						 | 
					abc4405a76 | ||
| 
						 | 
					243c66442d | ||
| 
						 | 
					9afcec77f6 | ||
| 
						 | 
					70ad98cc6f | ||
| 
						 | 
					76cfbde933 | ||
| 
						 | 
					f200bd9594 | ||
| 
						 | 
					4d4ca7bb36 | ||
| 
						 | 
					78c3c6dafa | ||
| 
						 | 
					6d859dec67 | ||
| 
						 | 
					3563e7e1aa | ||
| 
						 | 
					cb898fabf4 | ||
| 
						 | 
					5899671d96 | ||
| 
						 | 
					8c1eb23aa1 | ||
| 
						 | 
					b564087db0 | ||
| 
						 | 
					1748e8510e | ||
| 
						 | 
					742c5bb639 | ||
| 
						 | 
					297de01d95 | ||
| 
						 | 
					fb31c3b46d | ||
| 
						 | 
					ba2beffcd8 | ||
| 
						 | 
					2eb2dddb59 | ||
| 
						 | 
					0601d851d0 | ||
| 
						 | 
					b731f6ab03 | ||
| 
						 | 
					0403e306ed | ||
| 
						 | 
					d393fbf360 | ||
| 
						 | 
					4cc680965c | ||
| 
						 | 
					ba08e487cb | ||
| 
						 | 
					d37eda4e9b | ||
| 
						 | 
					3960d0f6de | ||
| 
						 | 
					5be5e5b58f | ||
| 
						 | 
					04ac9b8e32 | ||
| 
						 | 
					409a8a3a43 | ||
| 
						 | 
					1ba3f72e4c | ||
| 
						 | 
					3e5e9e57e9 | ||
| 
						 | 
					02e5e49de2 | ||
| 
						 | 
					43438d3824 | ||
| 
						 | 
					8f82d19fd1 | ||
| 
						 | 
					ee450bcd77 | ||
| 
						 | 
					553b4d9428 | ||
| 
						 | 
					df145f4bc9 | ||
| 
						 | 
					fa55283f62 | ||
| 
						 | 
					9e163db491 | ||
| 
						 | 
					286230f477 | ||
| 
						 | 
					3ba2c7e7e8 | ||
| 
						 | 
					b4f5e5bc00 | ||
| 
						 | 
					f580d2e41a | ||
| 
						 | 
					cd197e8be3 | ||
| 
						 | 
					51cf6465ff | ||
| 
						 | 
					a1feb32a2f | ||
| 
						 | 
					7478ad115f | ||
| 
						 | 
					9d8e338a11 | ||
| 
						 | 
					ed4163cfde | ||
| 
						 | 
					bd95f742c0 | ||
| 
						 | 
					463e6d9316 | ||
| 
						 | 
					3358811788 | ||
| 
						 | 
					a45509d28e | ||
| 
						 | 
					9ba94d2c6b | ||
| 
						 | 
					a4de83b3a3 | ||
| 
						 | 
					68a12d1d17 | ||
| 
						 | 
					c97d3cf359 | ||
| 
						 | 
					4721337c7c | ||
| 
						 | 
					2b36ed967c | ||
| 
						 | 
					3bb8f1ac8d | ||
| 
						 | 
					617ec7f565 | ||
| 
						 | 
					dc259b9f8e | ||
| 
						 | 
					7b31a87b3c | ||
| 
						 | 
					37a430c97c | ||
| 
						 | 
					f264cb0b18 | ||
| 
						 | 
					6ea530cc48 | ||
| 
						 | 
					a0abf307b4 | ||
| 
						 | 
					55cf9f5e1c | ||
| 
						 | 
					b89f0fac7b | ||
| 
						 | 
					8b3b3182bd | ||
| 
						 | 
					97c64f27ff | ||
| 
						 | 
					e548e1f6e0 | ||
| 
						 | 
					7ea1c7d85a | ||
| 
						 | 
					e08235b575 | ||
| 
						 | 
					783c672130 | ||
| 
						 | 
					5351a6b2ed | ||
| 
						 | 
					a110b103e8 | ||
| 
						 | 
					c26f573620 | ||
| 
						 | 
					f06e9ae30c | ||
| 
						 | 
					f5d208d5d6 | ||
| 
						 | 
					7fb8c4a68d | ||
| 
						 | 
					647fc56d47 | ||
| 
						 | 
					597d84e263 | ||
| 
						 | 
					977b0c3c0c | ||
| 
						 | 
					1b0d6de735 | ||
| 
						 | 
					2f5bb7774e | ||
| 
						 | 
					5565f02dbd | ||
| 
						 | 
					17a131ac21 | ||
| 
						 | 
					9a5cfe9f75 | ||
| 
						 | 
					cc936d9977 | ||
| 
						 | 
					e9911fee4d | ||
| 
						 | 
					aefde67aa2 | ||
| 
						 | 
					a1ea62a923 | ||
| 
						 | 
					7209ced446 | ||
| 
						 | 
					db63d352a2 | ||
| 
						 | 
					289de840fd | ||
| 
						 | 
					cb34a8b620 | ||
| 
						 | 
					95c633914f | ||
| 
						 | 
					d033412b1f | ||
| 
						 | 
					9c5e97144d | ||
| 
						 | 
					8b96289e2f | ||
| 
						 | 
					51ff43e2f2 | ||
| 
						 | 
					1e30f4f973 | ||
| 
						 | 
					36f66661f7 | ||
| 
						 | 
					de27fc15b6 | ||
| 
						 | 
					f9f90ba1d6 | ||
| 
						 | 
					51bf8a3538 | ||
| 
						 | 
					7b033a48a3 | ||
| 
						 | 
					1b420f69aa | ||
| 
						 | 
					6a187a384b | ||
| 
						 | 
					ac5de1f96e | ||
| 
						 | 
					6c917f686a | ||
| 
						 | 
					328ee94412 | ||
| 
						 | 
					de9951594e | ||
| 
						 | 
					561fc15ae9 | ||
| 
						 | 
					d65814c53f | ||
| 
						 | 
					803f17aa90 | ||
| 
						 | 
					08a3687eb5 | ||
| 
						 | 
					c4035b2273 | ||
| 
						 | 
					5c364e0f7c | ||
| 
						 | 
					9cfc3d9d37 | ||
| 
						 | 
					b5fdd30b77 | ||
| 
						 | 
					280292d3f5 | ||
| 
						 | 
					c593d864be | ||
| 
						 | 
					6d17348c72 | ||
| 
						 | 
					536648ec19 | ||
| 
						 | 
					b5e32a9ce5 | ||
| 
						 | 
					4077822e37 | ||
| 
						 | 
					e2d8750625 | ||
| 
						 | 
					79f5751375 | ||
| 
						 | 
					106437bd45 | ||
| 
						 | 
					b7cd13bb0b | ||
| 
						 | 
					b1a4f05b5a | ||
| 
						 | 
					ce2079104a | ||
| 
						 | 
					d64e9b6263 | ||
| 
						 | 
					123710078d | ||
| 
						 | 
					ec0d0ba368 | ||
| 
						 | 
					3f434f2a44 | ||
| 
						 | 
					71d8e6b4cd | ||
| 
						 | 
					a78af0a7fb | ||
| 
						 | 
					117ae196fd | ||
| 
						 | 
					4c211c8dce | ||
| 
						 | 
					c10d9b9d9d | ||
| 
						 | 
					b68b0a256e | 
@@ -9,4 +9,3 @@ tasks:
 | 
			
		||||
    gmake
 | 
			
		||||
    gmake test
 | 
			
		||||
    sudo gmake install
 | 
			
		||||
    gmake test-install
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								.builds/linux.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.builds/linux.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
image: archlinux
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
- meson
 | 
			
		||||
tasks:
 | 
			
		||||
- with-epoll: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup with-epoll --buildtype=release
 | 
			
		||||
    cd with-epoll
 | 
			
		||||
    meson configure -Depoll=true
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
- no-epoll: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup no-epoll --buildtype=release
 | 
			
		||||
    cd no-epoll
 | 
			
		||||
    meson configure -Depoll=false
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
    sudo ninja install
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
image: openbsd/latest
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
- meson
 | 
			
		||||
tasks:
 | 
			
		||||
- build: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build --buildtype=release
 | 
			
		||||
    cd build
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
image: openbsd/latest
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
- meson
 | 
			
		||||
tasks:
 | 
			
		||||
- build: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build --buildtype=release
 | 
			
		||||
    cd build
 | 
			
		||||
    meson configure -Dsingle_threaded=true
 | 
			
		||||
    meson configure -Dnanbox=false
 | 
			
		||||
    meson configure -Ddynamic_modules=false
 | 
			
		||||
    meson configure -Ddocstrings=false
 | 
			
		||||
    meson configure -Dnet=false
 | 
			
		||||
    meson configure -Dsourcemaps=false
 | 
			
		||||
    meson configure -Dpeg=false
 | 
			
		||||
    meson configure -Dassembler=false
 | 
			
		||||
    meson configure -Dint_types=false
 | 
			
		||||
    meson configure -Dtyped_arrays=false
 | 
			
		||||
    meson configure -Dreduced_os=true
 | 
			
		||||
    meson configure -Dprf=false
 | 
			
		||||
    ninja # will not pass tests but should build
 | 
			
		||||
@@ -3,10 +3,30 @@ sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
- gmake
 | 
			
		||||
- meson
 | 
			
		||||
tasks:
 | 
			
		||||
- build: |
 | 
			
		||||
- gmake: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    gmake
 | 
			
		||||
    gmake test
 | 
			
		||||
    doas gmake install
 | 
			
		||||
    gmake test-install
 | 
			
		||||
- 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 -Dreduced_os=true -Dffi=false
 | 
			
		||||
    cd build_meson_min
 | 
			
		||||
    ninja
 | 
			
		||||
- meson_prf: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build_meson_prf --buildtype=release -Dprf=true
 | 
			
		||||
    cd build_meson_prf
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
- meson_default: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build_meson_default --buildtype=release
 | 
			
		||||
    cd build_meson_default
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
    doas ninja install
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
*.janet linguist-language=Janet
 | 
			
		||||
*.janet text eol=lf
 | 
			
		||||
*.c text eol=lf
 | 
			
		||||
*.h text eol=lf
 | 
			
		||||
*.md text eol=lf
 | 
			
		||||
*.yml text eol=lf
 | 
			
		||||
*.build text eol=lf
 | 
			
		||||
*.txt text eol=lf
 | 
			
		||||
*.sh text eol=lf
 | 
			
		||||
							
								
								
									
										41
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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 }}"
 | 
			
		||||
							
								
								
									
										62
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
			
		||||
name: Release
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    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:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ ubuntu-latest, macos-latest ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Set the version
 | 
			
		||||
        run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set the platform
 | 
			
		||||
        run: echo "platform=$(tr '[A-Z]' '[a-z]' <<< $RUNNER_OS)" >> $GITHUB_ENV
 | 
			
		||||
      - name: Compile the project
 | 
			
		||||
        run: make clean && make
 | 
			
		||||
      - name: Build the artifact
 | 
			
		||||
        run: JANET_DIST_DIR=janet-${{ env.version }}-${{ env.platform }} make build/janet-${{ env.version }}-${{ env.platform }}-x64.tar.gz
 | 
			
		||||
      - name: Draft the release
 | 
			
		||||
        uses: softprops/action-gh-release@v1
 | 
			
		||||
        with:
 | 
			
		||||
          draft: true
 | 
			
		||||
          files: |
 | 
			
		||||
            build/*.gz
 | 
			
		||||
            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
 | 
			
		||||
							
								
								
									
										91
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
name: Test
 | 
			
		||||
 | 
			
		||||
on: [push, pull_request]
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
 | 
			
		||||
  test-posix:
 | 
			
		||||
    name: Build and test on POSIX systems
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ ubuntu-latest, macos-latest ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Compile the project
 | 
			
		||||
        run: make clean && make
 | 
			
		||||
      - name: Test the project
 | 
			
		||||
        run: make test
 | 
			
		||||
 | 
			
		||||
  test-windows:
 | 
			
		||||
    name: Build and test on 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
 | 
			
		||||
      - 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 -j CC=gcc
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
							
								
								
									
										17
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -32,6 +32,11 @@ lockfile.janet
 | 
			
		||||
# Local directory for testing
 | 
			
		||||
local
 | 
			
		||||
 | 
			
		||||
# Common test files I use.
 | 
			
		||||
temp.janet
 | 
			
		||||
temp*.janet
 | 
			
		||||
scratch.janet
 | 
			
		||||
 | 
			
		||||
# Emscripten
 | 
			
		||||
*.bc
 | 
			
		||||
janet.js
 | 
			
		||||
@@ -43,6 +48,7 @@ janet.wasm
 | 
			
		||||
 | 
			
		||||
# Generate test files
 | 
			
		||||
*.out
 | 
			
		||||
.orig
 | 
			
		||||
 | 
			
		||||
# Tools
 | 
			
		||||
xxd
 | 
			
		||||
@@ -50,6 +56,7 @@ xxd.exe
 | 
			
		||||
 | 
			
		||||
# VSCode
 | 
			
		||||
.vs
 | 
			
		||||
.clangd
 | 
			
		||||
 | 
			
		||||
# Swap files
 | 
			
		||||
*.swp
 | 
			
		||||
@@ -61,6 +68,13 @@ tags
 | 
			
		||||
vgcore.*
 | 
			
		||||
*.out.*
 | 
			
		||||
 | 
			
		||||
# WiX artifacts
 | 
			
		||||
*.msi
 | 
			
		||||
*.wixpdb
 | 
			
		||||
 | 
			
		||||
# Makefile config
 | 
			
		||||
/config.mk
 | 
			
		||||
 | 
			
		||||
# Created by https://www.gitignore.io/api/c
 | 
			
		||||
 | 
			
		||||
### C ###
 | 
			
		||||
@@ -131,3 +145,6 @@ compile_commands.json
 | 
			
		||||
CTestTestfile.cmake
 | 
			
		||||
 | 
			
		||||
# End of https://www.gitignore.io/api/cmake
 | 
			
		||||
 | 
			
		||||
# Astyle
 | 
			
		||||
*.orig
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,25 +0,0 @@
 | 
			
		||||
language: c
 | 
			
		||||
script:
 | 
			
		||||
- make
 | 
			
		||||
- make test
 | 
			
		||||
- sudo make install
 | 
			
		||||
- make test-install
 | 
			
		||||
- make build/janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME}.tar.gz
 | 
			
		||||
compiler:
 | 
			
		||||
- clang
 | 
			
		||||
- gcc
 | 
			
		||||
os:
 | 
			
		||||
- linux
 | 
			
		||||
- osx
 | 
			
		||||
before_deploy: 
 | 
			
		||||
deploy:
 | 
			
		||||
  provider: releases
 | 
			
		||||
  api_key:
 | 
			
		||||
    secure: JSqAOTH1jmfVlbOuPO3BbY1BhPq+ddiBNPCxuAyKHoVwfO4eNAmq9COI+UwCMWY3dg+YlspufRwkHj//B7QQ6hPbSsKu+Mapu6gr/CAE/jxbfO/E98LkIkUwbGjplwtzw2kiBkHN/Bu6J5X76cwo4D8nwQ1JIcV3nWtoG87t7H4W0R4AYQkbLGAPylgUFr11YMPx2cRBBqCdLAGIrny7kQ/0cRBfkN81R/gUJv/q3OjmUvY7sALXp7mFdZb75QPSilKIDuVUU5hLvPYTeRl6cWI/M+m5SmGZx1rjv5S9Qaw070XoNyt9JAADtbOUnADKvDguDZIP1FCuT1Gb+cnJPzrvk6+OBU9s8UjCTFtgV+LKlhmRZcwV5YQBE94PKRMJNC6VvIWM7UeQ8Zhm1jmQS6ONNWbuoUAlkZP57NtDQa2x0GT2wkubNSQKlaY+6/gwTD9KAJIzaZG7HYXH7b+4g7VbccCyhDAtDZtXgrOIS4WAkNc8rWezRO4H0qHMyON9aCEb0eTE8hWIufbx6ymG4gUxnYO+AkrEYMCwQvU6lS8BsevkaMTVtSShqlQtJ9FRlmJA3MA2ONyqzQXJENqRydyVbpFrKSv+0HbMyhEc5BoKbt0QcTh/slouNV4eASNar/GKN7aP8XKGUeMwIoCcRpP+3ehmwX9SUw7Ah5S42pA=
 | 
			
		||||
  file: build/janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME}.tar.gz
 | 
			
		||||
  draft: true
 | 
			
		||||
  skip_cleanup: true
 | 
			
		||||
  on:
 | 
			
		||||
    tags: true
 | 
			
		||||
    repo: janet-lang/janet
 | 
			
		||||
    condition: "$CC = clang"
 | 
			
		||||
							
								
								
									
										424
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										424
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,7 +1,429 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## 1.10.1 - 2020-06-14
 | 
			
		||||
## 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.
 | 
			
		||||
- Fix `-r` switch in repl.
 | 
			
		||||
 | 
			
		||||
## 1.18.0 - 2021-10-10
 | 
			
		||||
- Allow `ev/cancel` to work on already scheduled fibers.
 | 
			
		||||
- Fix bugs with ev/ module.
 | 
			
		||||
- Add optional `base` argument to scan-number
 | 
			
		||||
- Add `-i` flag to janet binary to make it easier to run image files from the command line
 | 
			
		||||
- Remove `thread/` module.
 | 
			
		||||
- Add `(number ...)` pattern to peg for more efficient number parsing using Janet's
 | 
			
		||||
  scan-number function without immediate string creation.
 | 
			
		||||
 | 
			
		||||
## 1.17.2 - 2021-09-18
 | 
			
		||||
- Remove include of windows.h from janet.h. This caused issues on certain projects.
 | 
			
		||||
- Fix formatting in doc-format to better handle special characters in signatures.
 | 
			
		||||
- Fix some marshalling bugs.
 | 
			
		||||
- Add optional Makefile target to install jpm as well.
 | 
			
		||||
- Supervisor channels in threads will no longer include a wasteful copy of the fiber in every
 | 
			
		||||
  message across a thread.
 | 
			
		||||
- Allow passing a closure to `ev/thread` as well as a whole fiber.
 | 
			
		||||
- Allow passing a closure directly to `ev/go` to spawn fibers on the event loop.
 | 
			
		||||
 | 
			
		||||
## 1.17.1 - 2021-08-29
 | 
			
		||||
- Fix docstring typos
 | 
			
		||||
- Add `make install-jpm-git` to make jpm co-install simpler if using the Makefile.
 | 
			
		||||
- Fix bugs with starting ev/threads and fiber marshaling.
 | 
			
		||||
 | 
			
		||||
## 1.17.0 - 2021-08-21
 | 
			
		||||
- Add the `-E` flag for one-liners with the `short-fn` syntax for argument passing.
 | 
			
		||||
- Add support for threaded abstract types. Threaded abstract types can easily be shared between threads.
 | 
			
		||||
- Deprecate the `thread` library. Use threaded channels and ev instead.
 | 
			
		||||
- Channels can now be marshalled.
 | 
			
		||||
- Add the ability to close channels with `ev/chan-close` (or `:close`).
 | 
			
		||||
- Add threaded channels with `ev/thread-chan`.
 | 
			
		||||
- Add `JANET_FN` and `JANET_REG` macros to more easily define C functions that export their source mapping information.
 | 
			
		||||
- Add `janet_interpreter_interupt` and `janet_loop1_interrupt` to interrupt the interpreter while running.
 | 
			
		||||
- Add `table/clear`
 | 
			
		||||
- Add build option to disable the threading library without disabling all threads.
 | 
			
		||||
- Remove JPM from the main Janet distribution. Instead, JPM must be installed
 | 
			
		||||
  separately like any other package.
 | 
			
		||||
- Fix issue with `ev/go` when called with an initial value and supervisor.
 | 
			
		||||
- Add the C API functions `janet_vm_save` and `janet_vm_load` to allow
 | 
			
		||||
saving and restoring the entire VM state.
 | 
			
		||||
 | 
			
		||||
## 1.16.1 - 2021-06-09
 | 
			
		||||
- Add `maclintf` - a utility for adding linting messages when inside macros.
 | 
			
		||||
- Print source code of offending line on compiler warnings and errors.
 | 
			
		||||
- Fix some issues with linting and re-add missing `make docs`.
 | 
			
		||||
- Allow controlling linting with dynamic bindings `:lint-warn`, `:lint-error`, and `:lint-levels`.
 | 
			
		||||
- Add `-w` and `-x` command line flags to the `janet` binary to set linting thresholds.
 | 
			
		||||
  linting thresholds are as follows:
 | 
			
		||||
    - :none - will never be trigger.
 | 
			
		||||
    - :relaxed - will only trigger on `:relaxed` lints.
 | 
			
		||||
    - :normal - will trigger on `:relaxed` and `:normal` lints.
 | 
			
		||||
    - :strict - will trigger on `:strict`, `:normal`, and `:relaxed` lints. This will catch the most issues
 | 
			
		||||
      but can be distracting.
 | 
			
		||||
 | 
			
		||||
## 1.16.0 - 2021-05-30
 | 
			
		||||
- Add color documentation to the `doc` macro - enable/disable with `(dyn :doc-color)`.
 | 
			
		||||
- Remove simpler HTML docs from distribution - use website or built-in documentation instead.
 | 
			
		||||
- Add compiler warnings and deprecation levels.
 | 
			
		||||
- Add `as-macro` to make using macros within quasiquote easier to do hygienically.
 | 
			
		||||
- Expose `JANET_OUT_OF_MEMORY` as part of the Janet API.
 | 
			
		||||
- Add `native-deps` option to `declare-native` in `jpm`. This lets native libraries link to other
 | 
			
		||||
  native libraries when building with jpm.
 | 
			
		||||
- Remove the `tarray` module. The functionality of typed arrays will be moved to an external module
 | 
			
		||||
  that can be installed via `jpm`.
 | 
			
		||||
- Add `from-pairs` to core.
 | 
			
		||||
- Add `JPM_OS_WHICH` environment variable to jpm to allow changing auto-detection behavior.
 | 
			
		||||
- The flychecker will consider any top-level calls of functions that start with `define-` to
 | 
			
		||||
  be safe to execute and execute them. This allows certain patterns (like spork/path) to be
 | 
			
		||||
  better processed by the flychecker.
 | 
			
		||||
 | 
			
		||||
## 1.15.5 - 2021-04-25
 | 
			
		||||
- Add `declare-headers` to jpm.
 | 
			
		||||
- Fix error using unix pipes on BSDs.
 | 
			
		||||
- Support .cc and .cxx extensions in `jpm` for C++ code.
 | 
			
		||||
- Change networking code to not create as many HUP errors.
 | 
			
		||||
- Add `net/shutdown` to close sockets in one direction without hang ups.
 | 
			
		||||
- Update code for printing the debug repl
 | 
			
		||||
 | 
			
		||||
## 1.15.4 - 2021-03-16
 | 
			
		||||
- Increase default nesting depth of pretty printing to `JANET_RECURSION_GUARD`
 | 
			
		||||
- Update meson.build
 | 
			
		||||
- Add option to automatically add shebang line in installed scripts with `jpm`.
 | 
			
		||||
- Add `partition-by` and `group-by` to the core.
 | 
			
		||||
- Sort keys in pretty printing output.
 | 
			
		||||
 | 
			
		||||
## 1.15.3 - 2021-02-28
 | 
			
		||||
- Fix a fiber bug that occured in deeply nested fibers
 | 
			
		||||
- Add `unref` combinator to pegs.
 | 
			
		||||
- Small docstring changes.
 | 
			
		||||
 | 
			
		||||
## 1.15.2 - 2021-02-15
 | 
			
		||||
- Fix bug in windows version of `os/spawn` and `os/execute` with setting environment variables.
 | 
			
		||||
- Fix documentation typos.
 | 
			
		||||
- Fix peg integer reading combinators when used with capture tags.
 | 
			
		||||
 | 
			
		||||
## 1.15.0 - 2021-02-08
 | 
			
		||||
- Fix `gtim` and `ltim` bytecode instructions on non-integer values.
 | 
			
		||||
- Clean up output of flychecking to be the same as the repl.
 | 
			
		||||
- Change behavior of `debug/stacktrace` with a nil error value.
 | 
			
		||||
- Add optional argument to `parser/produce`.
 | 
			
		||||
- Add `no-core` option to creating standalone binaries to make execution faster.
 | 
			
		||||
- Fix bug where a buffer overflow could be confused with an out of memory error.
 | 
			
		||||
- Change error output to `file:line:column: message`. Column is in bytes - tabs
 | 
			
		||||
  are considered to have width 1 (instead of 8).
 | 
			
		||||
 | 
			
		||||
## 1.14.2 - 2021-01-23
 | 
			
		||||
- Allow `JANET_PROFILE` env variable to load a profile before loading the repl.
 | 
			
		||||
- Update `tracev` macro to allow `def` and `var` inside to work as expected.
 | 
			
		||||
- Use `(dyn :peg-grammar)` for passing a default grammar to `peg/compile` instead of loading
 | 
			
		||||
  `default-peg-grammar` directly from the root environment.
 | 
			
		||||
- Add `ev/thread` for combining threading with the event loop.
 | 
			
		||||
- Add `ev/do-thread` to make `ev/thread` easier to use.
 | 
			
		||||
- Automatically set supervisor channel in `net/accept-loop` and `net/server` correctly.
 | 
			
		||||
 | 
			
		||||
## 1.14.1 - 2021-01-18
 | 
			
		||||
- Add `doc-of` for reverse documentation lookup.
 | 
			
		||||
- Add `ev/give-supervsior` to send a message to the supervising channel.
 | 
			
		||||
- Add `ev/gather` and `chan` argument to `ev/go`. This new argument allows "supervisor channels"
 | 
			
		||||
  for fibers to enable structured concurrency.
 | 
			
		||||
- Make `-k` flag work on stdin if no files are given.
 | 
			
		||||
- Add `flycheck` function to core.
 | 
			
		||||
- Make `backmatch` and `backref` more expressive in pegs.
 | 
			
		||||
- Fix buggy `string/split`.
 | 
			
		||||
- Add `fiber/last-value` to get the value that was last yielded, errored, or signaled
 | 
			
		||||
  by a fiber.
 | 
			
		||||
- Remove `:generate` verb from `loop` macros. Instead, use the `:in` verb
 | 
			
		||||
  which will now work on fibers as well as other data structures.
 | 
			
		||||
- Define `next`, `get`, and `in` for fibers. This lets
 | 
			
		||||
  `each`, `map`, and similar iteration macros can now iterate over fibers.
 | 
			
		||||
- Remove macro `eachy`, which can be replaced by `each`.
 | 
			
		||||
- Add `dflt` argument to find-index.
 | 
			
		||||
- Deprecate `file/popen` in favor of `os/spawn`.
 | 
			
		||||
- Add `:all` keyword to `ev/read` and `net/read` to make them more like `file/read`. However, we
 | 
			
		||||
  do not provide any `:line` option as that requires buffering.
 | 
			
		||||
- Change repl behavior to make Ctrl-C raise SIGINT on posix. The old behavior for Ctrl-C,
 | 
			
		||||
  to clear the current line buffer, has been moved to Ctrl-Q.
 | 
			
		||||
- Importing modules that start with `/` is now the only way to import from project root.
 | 
			
		||||
  Before, this would import from / on disk. Previous imports that did not start with `.` or `/`
 | 
			
		||||
  are now unambiguously importing from the syspath, instead of checking both the syspath and
 | 
			
		||||
  the project root. This is backwards incompatible and dependencies should be updated for this.
 | 
			
		||||
- Change hash function for numbers.
 | 
			
		||||
- Improve error handling of `dofile`.
 | 
			
		||||
- Bug fixes in networking and subprocess code.
 | 
			
		||||
- Use markdown formatting in more places for docstrings.
 | 
			
		||||
 | 
			
		||||
## 1.13.1 - 2020-12-13
 | 
			
		||||
- Pretty printing a table with a prototype will look for `:_name` instead of `:name`
 | 
			
		||||
  in the prototype table to tag the output.
 | 
			
		||||
- `match` macro implementation changed to be tail recursive.
 | 
			
		||||
- Adds a :preload loader which allows one to manually put things into `module/cache`.
 | 
			
		||||
- Add `buffer/push` function.
 | 
			
		||||
- Backtick delimited strings and buffers are now reindented based on the column of the
 | 
			
		||||
  opening delimiter. Whitespace in columns to the left of the starting column is ignored unless
 | 
			
		||||
  there are non-space/non-newline characters in that region, in which case the old behavior is preserved.
 | 
			
		||||
- Argument to `(error)` combinator in PEGs is now optional.
 | 
			
		||||
- Add `(line)` and `(column)` combinators to PEGs to capture source line and column.
 | 
			
		||||
  This should make error reporting a bit easier.
 | 
			
		||||
- Add `merge-module` to core.
 | 
			
		||||
- During installation and release, merge janetconf.h into janet.h for easier install.
 | 
			
		||||
- Add `upscope` special form.
 | 
			
		||||
- `os/execute` and `os/spawn` can take streams for redirecting IO.
 | 
			
		||||
- Add `:parser` and `:read` parameters to `run-context`.
 | 
			
		||||
- Add `os/open` if ev is enabled.
 | 
			
		||||
- Add `os/pipe` if ev is enabled.
 | 
			
		||||
- Add `janet_thread_current(void)` to C API
 | 
			
		||||
- Add integer parsing forms to pegs. This makes parsing many binary protocols easier.
 | 
			
		||||
- Lots of updates to networking code - now can use epoll (or poll) on linux and IOCP on windows.
 | 
			
		||||
- Add `ev/` module. This exposes a fiber scheduler, queues, timeouts, and other functionality to users
 | 
			
		||||
  for single threaded cooperative scheduling and asynchronous IO.
 | 
			
		||||
- Add `net/accept-loop` and `net/listen`. These functions break down `net/server` into it's essential parts
 | 
			
		||||
  and are more flexible. They also allow further improvements to these utility functions.
 | 
			
		||||
- Various small bug fixes.
 | 
			
		||||
 | 
			
		||||
## 1.12.2 - 2020-09-20
 | 
			
		||||
- Add janet\_try and janet\_restore to C API.
 | 
			
		||||
- Fix `os/execute` regression on windows.
 | 
			
		||||
- Add :pipe option to `os/spawn`.
 | 
			
		||||
- Fix docstring typos.
 | 
			
		||||
 | 
			
		||||
## 1.12.1 - 2020-09-07
 | 
			
		||||
- Make `zero?`, `one?`, `pos?`, and `neg?` polymorphic.
 | 
			
		||||
- Add C++ support to jpm and improve C++ interop in janet.h.
 | 
			
		||||
- Add `%t` formatter to `printf`, `string/format`, and other formatter functions.
 | 
			
		||||
- Expose `janet_cfuns_prefix` in C API.
 | 
			
		||||
- Add `os/proc-wait` and `os/proc-kill` for interacting with processes.
 | 
			
		||||
- Add `janet_getjfile` to C API.
 | 
			
		||||
- Allow redirection of stdin, stdout, and stderr by passing keywords in the env table in `os/spawn` and `os/execute`.
 | 
			
		||||
- Add `os/spawn` to get a core/process back instead of an exit code as in `os/execute`.
 | 
			
		||||
  When called like this, `os/execute` returns immediately.
 | 
			
		||||
- Add `:x` flag to os/execute to raise error when exit code is non-zero.
 | 
			
		||||
- Don't run `main` when flychecking.
 | 
			
		||||
- Add `:n` flag to `file/open` to raise an error if file cannot be opened.
 | 
			
		||||
- Fix import macro to not try and coerce everything to a string.
 | 
			
		||||
- Allow passing a second argument to `disasm`.
 | 
			
		||||
- Add `cancel`. Resumes a fiber but makes it immediately error at the yield point.
 | 
			
		||||
- Allow multi-line paste into built in repl.
 | 
			
		||||
- Add `(curenv)`.
 | 
			
		||||
- Change `net/read`, `net/chunk`, and `net/write` to raise errors in the case of failures.
 | 
			
		||||
- Add `janet_continue_signal` to C API. This indirectly enables C functions that yield to the event loop
 | 
			
		||||
  to raise errors or other signals.
 | 
			
		||||
- Update meson build script to fix bug on Debian's version of meson
 | 
			
		||||
- Add `xprint`, `xprin`, `xprintf`, and `xprinf`.
 | 
			
		||||
- `net/write` now raises an error message if write fails.
 | 
			
		||||
- Fix issue with SIGPIPE on macOS and BSDs.
 | 
			
		||||
 | 
			
		||||
## 1.11.3 - 2020-08-03
 | 
			
		||||
- Add `JANET_HASHSEED` environment variable when `JANET_PRF` is enabled.
 | 
			
		||||
- Expose `janet_cryptorand` in C API.
 | 
			
		||||
- Properly initialize PRF in default janet program
 | 
			
		||||
- Add `index-of` to core library.
 | 
			
		||||
- Add `-fPIC` back to core CFLAGS (non-optional when compiling default client with Makefile)
 | 
			
		||||
- Fix defaults on Windows for ARM
 | 
			
		||||
- Fix defaults on NetBSD.
 | 
			
		||||
 | 
			
		||||
## 1.11.1 - 2020-07-25
 | 
			
		||||
- Fix jpm and git with multiple git installs on Windows
 | 
			
		||||
- Fix importing a .so file in the current directory
 | 
			
		||||
- Allow passing byte sequence types directly to typed-array constructors.
 | 
			
		||||
- Fix bug sending files between threads.
 | 
			
		||||
- Disable PRF by default.
 | 
			
		||||
- Update the soname.
 | 
			
		||||
 | 
			
		||||
## 1.11.0 - 2020-07-18
 | 
			
		||||
- Add `forever` macro.
 | 
			
		||||
- Add `any?` predicate to core.
 | 
			
		||||
- Add `jpm list-pkgs` subcommand to see which package aliases are in the listing.
 | 
			
		||||
- Add `jpm list-installed` subcommand to see which packages are installed.
 | 
			
		||||
- Add `math/int-min`, `math/int-max`, `math/int32-min`, and `math/int32-max` for getting integer limits.
 | 
			
		||||
- The gc interval is now autotuned, to prevent very bad gc behavior.
 | 
			
		||||
- Improvements to the bytecode compiler, Janet will now generate more efficient bytecode.
 | 
			
		||||
- Add `peg/find`, `peg/find-all`, `peg/replace`, and `peg/replace-all`
 | 
			
		||||
- Add `math/nan`
 | 
			
		||||
- Add `forv` macro
 | 
			
		||||
- Add `symbol/slice`
 | 
			
		||||
- Add `keyword/slice`
 | 
			
		||||
- Allow cross compilation with Makefile.
 | 
			
		||||
- Change `compare-primitve` to `cmp` and make it more efficient.
 | 
			
		||||
- Add `reverse!` for reversing an array or buffer in place.
 | 
			
		||||
- `janet_dobytes` and `janet_dostring` return parse errors in \*out
 | 
			
		||||
- Add `repeat` macro for iterating something n times.
 | 
			
		||||
- Add `eachy` (each yield) macro for iterating a fiber.
 | 
			
		||||
- Fix `:generate` verb in loop macro to accept non symbols as bindings.
 | 
			
		||||
- Add `:h`, `:h+`, and `:h*` in `default-peg-grammar` for hexidecimal digits.
 | 
			
		||||
- Fix `%j` formatter to print numbers precisely (using the `%.17g` format string to printf).
 | 
			
		||||
 | 
			
		||||
## 1.10.1 - 2020-06-18
 | 
			
		||||
- Expose `janet_table_clear` in API.
 | 
			
		||||
- Respect `JANET_NO_PROCESSES` define when building
 | 
			
		||||
- Fix `jpm` rules having multiple copies of the same dependency.
 | 
			
		||||
- Fix `jpm` install in some cases.
 | 
			
		||||
- Add `array/trim` and `buffer/trim` to shrink the backing capacity of these types
 | 
			
		||||
  to their current length.
 | 
			
		||||
 | 
			
		||||
## 1.10.0 - 2020-06-14
 | 
			
		||||
- Hardcode default jpm paths on install so env variables are needed in fewer cases.
 | 
			
		||||
- Add `:no-compile` to `create-executable` option for jpm.
 | 
			
		||||
- Fix bug with the `trace` function.
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@ Please read this document before making contributions.
 | 
			
		||||
  on how to reproduce it. If it is a compiler or language bug, please try to include a minimal
 | 
			
		||||
  example. This means don't post all 200 lines of code from your project, but spend some time
 | 
			
		||||
  distilling the problem to just the relevant code.
 | 
			
		||||
* Add the `bug` tag to the issue.
 | 
			
		||||
 | 
			
		||||
## Contributing Changes
 | 
			
		||||
 | 
			
		||||
@@ -30,8 +29,7 @@ may require changes before being merged.
 | 
			
		||||
  the test folder and make sure it is run when`make test` is invoked.
 | 
			
		||||
* Be consistent with the style. For C this means follow the indentation and style in
 | 
			
		||||
  other files (files have MIT license at top, 4 spaces indentation, no trailing
 | 
			
		||||
          whitespace, cuddled brackets, etc.) Use `make format` to
 | 
			
		||||
  automatically format your C code with
 | 
			
		||||
  whitespace, cuddled brackets, etc.) Use `make format` to automatically format your C code with
 | 
			
		||||
  [astyle](http://astyle.sourceforge.net/astyle.html). You will probably need
 | 
			
		||||
  to install this, but it can be installed with most package managers.
 | 
			
		||||
 | 
			
		||||
@@ -45,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.
 | 
			
		||||
@@ -66,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
 | 
			
		||||
@@ -75,4 +90,3 @@ timely manner. In short, if you want extra functionality now, then build it.
 | 
			
		||||
 | 
			
		||||
* Include a good description of the problem that is being solved
 | 
			
		||||
* Include descriptions of potential solutions if you have some in mind.
 | 
			
		||||
* Add the appropriate tags to the issue. For new features, add the `enhancement` tag.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
Copyright (c) 2020 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										232
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										232
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2020 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,52 +21,89 @@
 | 
			
		||||
################################
 | 
			
		||||
##### 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 || echo local)\""
 | 
			
		||||
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=build/libjanet.so
 | 
			
		||||
JANET_STATIC_LIBRARY=build/libjanet.a
 | 
			
		||||
JANET_PATH?=$(LIBDIR)/janet
 | 
			
		||||
MANPATH?=$(PREFIX)/share/man/man1/
 | 
			
		||||
PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig
 | 
			
		||||
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
 | 
			
		||||
DEBUGGER=gdb
 | 
			
		||||
SONAME_SETTER=-Wl,-soname,
 | 
			
		||||
 | 
			
		||||
CFLAGS:=$(CFLAGS) -std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fPIC -O2 -fvisibility=hidden
 | 
			
		||||
LDFLAGS:=$(LDFLAGS) -rdynamic
 | 
			
		||||
# For cross compilation
 | 
			
		||||
HOSTCC?=$(CC)
 | 
			
		||||
HOSTAR?=$(AR)
 | 
			
		||||
CFLAGS?=-O2 -g
 | 
			
		||||
LDFLAGS?=-rdynamic
 | 
			
		||||
RUN:=$(RUN)
 | 
			
		||||
 | 
			
		||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
 | 
			
		||||
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/mainclient build/webclient build/boot)
 | 
			
		||||
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY)
 | 
			
		||||
# Mingw
 | 
			
		||||
ifeq ($(findstring MINGW,$(UNAME)), MINGW)
 | 
			
		||||
	CLIBS:=-lws2_32 -lpsapi -lwsock32
 | 
			
		||||
	LDFLAGS:=-Wl,--out-implib,$(JANET_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_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
 | 
			
		||||
 | 
			
		||||
######################
 | 
			
		||||
##### 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 \
 | 
			
		||||
@@ -90,6 +127,8 @@ JANET_CORE_SOURCES=src/core/abstract.c \
 | 
			
		||||
				   src/core/corelib.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 \
 | 
			
		||||
@@ -104,14 +143,13 @@ JANET_CORE_SOURCES=src/core/abstract.c \
 | 
			
		||||
				   src/core/regalloc.c \
 | 
			
		||||
				   src/core/run.c \
 | 
			
		||||
				   src/core/specials.c \
 | 
			
		||||
				   src/core/state.c \
 | 
			
		||||
				   src/core/string.c \
 | 
			
		||||
				   src/core/strtod.c \
 | 
			
		||||
				   src/core/struct.c \
 | 
			
		||||
				   src/core/symcache.c \
 | 
			
		||||
				   src/core/table.c \
 | 
			
		||||
				   src/core/thread.c \
 | 
			
		||||
				   src/core/tuple.c \
 | 
			
		||||
				   src/core/typedarray.c \
 | 
			
		||||
				   src/core/util.c \
 | 
			
		||||
				   src/core/value.c \
 | 
			
		||||
				   src/core/vector.c \
 | 
			
		||||
@@ -131,76 +169,87 @@ JANET_BOOT_HEADERS=src/boot/tests.h
 | 
			
		||||
##########################################################
 | 
			
		||||
 | 
			
		||||
JANET_BOOT_OBJECTS=$(patsubst src/%.c,build/%.boot.o,$(JANET_CORE_SOURCES) $(JANET_BOOT_SOURCES))
 | 
			
		||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) $(CFLAGS)
 | 
			
		||||
 | 
			
		||||
$(JANET_BOOT_OBJECTS): $(JANET_BOOT_HEADERS)
 | 
			
		||||
 | 
			
		||||
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_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/janet.c: build/janet_boot src/boot/boot.janet
 | 
			
		||||
	build/janet_boot . JANET_PATH '$(JANET_PATH)' JANET_HEADERPATH '$(INCLUDEDIR)/janet' > $@
 | 
			
		||||
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.10
 | 
			
		||||
ifeq ($(UNAME), Darwin)
 | 
			
		||||
SONAME=libjanet.1.29.dylib
 | 
			
		||||
else
 | 
			
		||||
SONAME=libjanet.so.1.29
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
build/shell.c: src/mainclient/shell.c
 | 
			
		||||
build/c/shell.c: src/mainclient/shell.c
 | 
			
		||||
	cp $< $@
 | 
			
		||||
 | 
			
		||||
build/janet.h: src/include/janet.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: $(JANETCONF_HEADER)
 | 
			
		||||
	cp $< $@
 | 
			
		||||
 | 
			
		||||
build/janetconf.h: src/conf/janetconf.h
 | 
			
		||||
	cp $< $@
 | 
			
		||||
build/janet.o: build/c/janet.c $(JANETCONF_HEADER) src/include/janet.h
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
build/janet.o: build/janet.c build/janet.h build/janetconf.h
 | 
			
		||||
	$(CC) $(CFLAGS) -c $< -o $@ -I build
 | 
			
		||||
build/shell.o: build/c/shell.c $(JANETCONF_HEADER) src/include/janet.h
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
build/shell.o: build/shell.c build/janet.h build/janetconf.h
 | 
			
		||||
	$(CC) $(CFLAGS) -c $< -o $@ -I build
 | 
			
		||||
$(JANET_TARGET): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_TARGET): build/janet.o build/shell.o
 | 
			
		||||
	$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ $(CLIBS)
 | 
			
		||||
$(JANET_LIBRARY): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_LIBRARY): build/janet.o build/shell.o
 | 
			
		||||
	$(CC) $(LDFLAGS) $(CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_STATIC_LIBRARY): build/janet.o build/shell.o
 | 
			
		||||
	$(AR) rcs $@ $^
 | 
			
		||||
$(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTAR) rcs $@ $^
 | 
			
		||||
 | 
			
		||||
###################
 | 
			
		||||
##### Testing #####
 | 
			
		||||
###################
 | 
			
		||||
 | 
			
		||||
# Testing assumes HOSTCC=CC
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	./$(JANET_TARGET) -k jpm
 | 
			
		||||
	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
 | 
			
		||||
	for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
 | 
			
		||||
	$(VALGRIND_COMMAND) ./$(JANET_TARGET) -k jpm
 | 
			
		||||
 | 
			
		||||
callgrind: $(JANET_TARGET)
 | 
			
		||||
	for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
 | 
			
		||||
@@ -212,13 +261,21 @@ callgrind: $(JANET_TARGET)
 | 
			
		||||
dist: build/janet-dist.tar.gz
 | 
			
		||||
 | 
			
		||||
build/janet-%.tar.gz: $(JANET_TARGET) \
 | 
			
		||||
	src/include/janet.h src/conf/janetconf.h \
 | 
			
		||||
	jpm.1 janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
 | 
			
		||||
	build/doc.html README.md build/janet.c build/shell.c jpm
 | 
			
		||||
	$(eval JANET_DIST_DIR = "janet-$(shell basename $*)")
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)
 | 
			
		||||
	cp -r $^ build/$(JANET_DIST_DIR)/
 | 
			
		||||
	cd build && tar -czvf ../$@ $(JANET_DIST_DIR)
 | 
			
		||||
	build/janet.h \
 | 
			
		||||
	janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(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/
 | 
			
		||||
	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/
 | 
			
		||||
	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)
 | 
			
		||||
 | 
			
		||||
#########################
 | 
			
		||||
##### Documentation #####
 | 
			
		||||
@@ -227,16 +284,12 @@ 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 #####
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
build/jpm: jpm $(JANET_TARGET)
 | 
			
		||||
	$(JANET_TARGET) tools/patch-jpm.janet jpm build/jpm "--libpath=$(LIBDIR)" "--headerpath=$(INCLUDEDIR)/janet" "--binpath=$(BINDIR)"
 | 
			
		||||
	chmod +x build/jpm
 | 
			
		||||
 | 
			
		||||
.INTERMEDIATE: build/janet.pc
 | 
			
		||||
build/janet.pc: $(JANET_TARGET)
 | 
			
		||||
	echo 'prefix=$(PREFIX)' > $@
 | 
			
		||||
@@ -247,38 +300,56 @@ 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)' >> $@
 | 
			
		||||
 | 
			
		||||
install: $(JANET_TARGET) build/janet.pc build/jpm
 | 
			
		||||
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 -rf $(JANET_HEADERS) '$(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)
 | 
			
		||||
	cp -rf build/jpm '$(DESTDIR)$(BINDIR)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(MANPATH)'
 | 
			
		||||
	cp janet.1 '$(DESTDIR)$(MANPATH)'
 | 
			
		||||
	cp jpm.1 '$(DESTDIR)$(MANPATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(PKG_CONFIG_PATH)'
 | 
			
		||||
	cp build/janet.pc '$(DESTDIR)$(PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || true
 | 
			
		||||
	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'
 | 
			
		||||
	cp '$(JANET_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
 | 
			
		||||
	rm -rf build/jpm
 | 
			
		||||
	git clone --depth=1 --branch='$(JPM_TAG)' https://github.com/janet-lang/jpm.git build/jpm
 | 
			
		||||
	cd build/jpm && PREFIX='$(PREFIX)' \
 | 
			
		||||
		DESTDIR=$(DESTDIR) \
 | 
			
		||||
		JANET_MANPATH='$(JANET_MANPATH)' \
 | 
			
		||||
		JANET_HEADERPATH='$(INCLUDEDIR)/janet' \
 | 
			
		||||
		JANET_BINPATH='$(BINDIR)' \
 | 
			
		||||
		JANET_LIBPATH='$(LIBDIR)' \
 | 
			
		||||
		$(RUN) ../../$(JANET_TARGET) ./bootstrap.janet
 | 
			
		||||
 | 
			
		||||
uninstall:
 | 
			
		||||
	-rm '$(DESTDIR)$(BINDIR)/janet'
 | 
			
		||||
	-rm '$(DESTDIR)$(BINDIR)/jpm'
 | 
			
		||||
	-rm -rf '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	-rm -rf '$(DESTDIR)$(INCLUDEDIR)/janet.h'
 | 
			
		||||
	-rm -rf '$(DESTDIR)$(LIBDIR)'/libjanet.*
 | 
			
		||||
	-rm '$(DESTDIR)$(PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	-rm '$(DESTDIR)$(MANPATH)/janet.1'
 | 
			
		||||
	-rm '$(DESTDIR)$(MANPATH)/jpm.1'
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_MANPATH)/janet.1'
 | 
			
		||||
	# -rm -rf '$(DESTDIR)$(JANET_PATH)'/* - err on the side of correctness here
 | 
			
		||||
 | 
			
		||||
#################
 | 
			
		||||
@@ -286,29 +357,22 @@ 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
 | 
			
		||||
	compiledb make
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	-rm -rf build vgcore.* callgrind.*
 | 
			
		||||
	-rm -rf test/install/build test/install/modpath
 | 
			
		||||
 | 
			
		||||
test-install:
 | 
			
		||||
	cd test/install \
 | 
			
		||||
		&& rm -rf build .cache .manifests \
 | 
			
		||||
		&& jpm --verbose build \
 | 
			
		||||
		&& jpm --verbose test \
 | 
			
		||||
		&& build/testexec \
 | 
			
		||||
		&& jpm --verbose quickbin testexec.janet build/testexec2 \
 | 
			
		||||
		&& build/testexec2 \
 | 
			
		||||
		&& mkdir -p modpath \
 | 
			
		||||
		&& jpm --verbose --testdeps --modpath=./modpath install https://github.com/janet-lang/json.git
 | 
			
		||||
	cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/jhydro.git
 | 
			
		||||
	cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/path.git
 | 
			
		||||
	cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/argparse.git
 | 
			
		||||
	echo "JPM has been removed from default install."
 | 
			
		||||
 | 
			
		||||
help:
 | 
			
		||||
	@echo
 | 
			
		||||
@@ -333,4 +397,4 @@ help:
 | 
			
		||||
	@echo
 | 
			
		||||
 | 
			
		||||
.PHONY: clean install repl debug valgrind test \
 | 
			
		||||
	valtest dist uninstall docs grammar format help
 | 
			
		||||
	valtest dist uninstall docs grammar format help compile-commands
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										318
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										318
									
								
								README.md
									
									
									
									
									
								
							@@ -1,72 +1,148 @@
 | 
			
		||||
[](https://gitter.im/janet-language/community)
 | 
			
		||||
 
 | 
			
		||||
[](https://ci.appveyor.com/project/bakpakin/janet/branch/master)
 | 
			
		||||
[](https://travis-ci.org/janet-lang/janet)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/freebsd.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/openbsd.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/meson.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/meson_min.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/openbsd.yml?)
 | 
			
		||||
[](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.
 | 
			
		||||
 | 
			
		||||
There is a repl for trying out the language, as well as the ability
 | 
			
		||||
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 into other programs. Try Janet in your browser at
 | 
			
		||||
[https://janet-lang.org](https://janet-lang.org).
 | 
			
		||||
Janet can be embedded in other programs. Try Janet in your browser at
 | 
			
		||||
<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
 | 
			
		||||
 | 
			
		||||
* Minimal setup - one binary and you are good to go!
 | 
			
		||||
* First class closures
 | 
			
		||||
```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)
 | 
			
		||||
* Python style generators (implemented as a plain macro)
 | 
			
		||||
* 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
 | 
			
		||||
* Byte code interpreter with an assembly interface, as well as bytecode verification
 | 
			
		||||
* Tailcall 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 in to 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.
 | 
			
		||||
Documentation is also available locally in the REPL.
 | 
			
		||||
Use the `(doc symbol-name)` macro to get API
 | 
			
		||||
documentation for symbols in the core library. For example,
 | 
			
		||||
```
 | 
			
		||||
(doc doc)
 | 
			
		||||
(doc apply)
 | 
			
		||||
```
 | 
			
		||||
Shows documentation for the doc macro.
 | 
			
		||||
shows documentation for the `apply` function.
 | 
			
		||||
 | 
			
		||||
To get a list of all bindings in the default
 | 
			
		||||
environment, use the `(all-bindings)` function. You
 | 
			
		||||
can also use the `(doc)` macro with no arguments if you are in the repl
 | 
			
		||||
can also use the `(doc)` macro with no arguments if you are in the REPL
 | 
			
		||||
to show bound symbols.
 | 
			
		||||
 | 
			
		||||
## Source
 | 
			
		||||
@@ -81,61 +157,71 @@ 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`.
 | 
			
		||||
 | 
			
		||||
### 32-bit Haiku
 | 
			
		||||
 | 
			
		||||
32-bit Haiku build instructions are the same as the unix-like build instructions,
 | 
			
		||||
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 instuctions,
 | 
			
		||||
but you need `gmake` to compile. Alternatively, install directly from
 | 
			
		||||
packages, using `pkg install lang/janet`.
 | 
			
		||||
FreeBSD build instructions are the same as the UNIX-like build instructions,
 | 
			
		||||
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 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.
 | 
			
		||||
 | 
			
		||||
### Meson
 | 
			
		||||
 | 
			
		||||
Janet also has a build file for [Meson](https://mesonbuild.com/), a cross platform build
 | 
			
		||||
system. Although Meson has a python dependency, Meson is a very complete build system that
 | 
			
		||||
Janet also has a build file for [Meson](https://mesonbuild.com/), a cross-platform build
 | 
			
		||||
system. Although Meson has a Python dependency, Meson is a very complete build system that
 | 
			
		||||
is maybe more convenient and flexible for integrating into existing pipelines.
 | 
			
		||||
Meson also provides much better IDE integration than Make or batch files, as well as support
 | 
			
		||||
for cross compilation.
 | 
			
		||||
for cross-compilation.
 | 
			
		||||
 | 
			
		||||
For the impatient, building with Meson is as follows. The options provided to
 | 
			
		||||
`meson setup` below emulate Janet's Makefile.
 | 
			
		||||
@@ -161,22 +247,22 @@ 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
 | 
			
		||||
 | 
			
		||||
See [the Introduction](https://janet-lang.org/introduction.html) for more details. If you just want
 | 
			
		||||
See the [Introduction](https://janet-lang.org/docs/index.html) for more details. If you just want
 | 
			
		||||
to try out the language, you don't need to install anything. You can also move the `janet` executable wherever you want on your system and run it.
 | 
			
		||||
 | 
			
		||||
## 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.
 | 
			
		||||
by entering the command `(all-bindings)` into the REPL.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
$ janet
 | 
			
		||||
@@ -188,32 +274,38 @@ 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
 | 
			
		||||
  -r : Enter the repl after running all scripts
 | 
			
		||||
  -p : Keep on executing if there is a top level error (persistent)
 | 
			
		||||
  -q : Hide prompt, logo, and repl output (quiet)
 | 
			
		||||
  -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 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
 | 
			
		||||
  -n : Disable ANSI color output in the repl
 | 
			
		||||
  -l path : Execute code in a file before running the main script
 | 
			
		||||
  -i : Load the script argument as an image file instead of source code
 | 
			
		||||
  -n : Disable ANSI color output in the REPL
 | 
			
		||||
  -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
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If installed, you can also run `man janet` and `man jpm` to get usage information.
 | 
			
		||||
If installed, you can also run `man janet` to get usage information.
 | 
			
		||||
 | 
			
		||||
## Embedding
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@@ -221,27 +313,89 @@ 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 discussion on the [Janet Gitter Channel](https://gitter.im/janet-language/community).
 | 
			
		||||
Alternatively, check out [the #janet channel on Freenode](https://webchat.freenode.net/)
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
## FAQ
 | 
			
		||||
 | 
			
		||||
### Why is my terminal spitting out junk when I run the repl?
 | 
			
		||||
### 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 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>.
 | 
			
		||||
 | 
			
		||||
### Is this Scheme/Common Lisp? Where are the cons cells?
 | 
			
		||||
 | 
			
		||||
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 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?
 | 
			
		||||
 | 
			
		||||
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 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!")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Why can't we add (feature from Clojure) into the core?
 | 
			
		||||
 | 
			
		||||
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, 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 may need some 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
 | 
			
		||||
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` flag if further issues
 | 
			
		||||
ensue.
 | 
			
		||||
 | 
			
		||||
## Why Janet
 | 
			
		||||
## Why is it called "Janet"?
 | 
			
		||||
 | 
			
		||||
Janet is named after the almost omniscient and friendly artificial being in [The Good Place](https://en.wikipedia.org/wiki/The_Good_Place).
 | 
			
		||||
 | 
			
		||||
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-the-good-place.gif" alt="Janet logo" width="115px" align="left">
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								appveyor.yml
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								appveyor.yml
									
									
									
									
									
								
							@@ -1,58 +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
 | 
			
		||||
    - refreshenv
 | 
			
		||||
    # We need to reload vcvars after refreshing
 | 
			
		||||
    - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %platform%
 | 
			
		||||
    - build_win test-install
 | 
			
		||||
    - 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: janetconf.h
 | 
			
		||||
      path: dist\janetconf.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
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 109 KiB  | 
@@ -14,13 +14,18 @@
 | 
			
		||||
@if "%1"=="test" goto TEST
 | 
			
		||||
@if "%1"=="dist" goto DIST
 | 
			
		||||
@if "%1"=="install" goto INSTALL
 | 
			
		||||
@if "%1"=="test-install" goto TESTINSTALL
 | 
			
		||||
@if "%1"=="all" goto ALL
 | 
			
		||||
 | 
			
		||||
@rem Set compile and link options here
 | 
			
		||||
@setlocal
 | 
			
		||||
 | 
			
		||||
@rem Example use asan
 | 
			
		||||
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
 | 
			
		||||
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
 | 
			
		||||
 | 
			
		||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
 | 
			
		||||
@set JANET_LINK=link /nologo
 | 
			
		||||
 | 
			
		||||
@set JANET_LINK_STATIC=lib /nologo
 | 
			
		||||
 | 
			
		||||
@rem Add janet build tag
 | 
			
		||||
@@ -28,10 +33,10 @@ if not "%JANET_BUILD%" == "" (
 | 
			
		||||
    @set JANET_COMPILE=%JANET_COMPILE% /DJANET_BUILD="\"%JANET_BUILD%\""
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
mkdir build
 | 
			
		||||
mkdir build\core
 | 
			
		||||
mkdir build\mainclient
 | 
			
		||||
mkdir build\boot
 | 
			
		||||
if not exist build mkdir build
 | 
			
		||||
if not exist build\core mkdir build\core
 | 
			
		||||
if not exist build\c mkdir build\c
 | 
			
		||||
if not exist build\boot mkdir build\boot
 | 
			
		||||
 | 
			
		||||
@rem Build the bootstrap interpreter
 | 
			
		||||
for %%f in (src\core\*.c) do (
 | 
			
		||||
@@ -44,10 +49,10 @@ for %%f in (src\boot\*.c) do (
 | 
			
		||||
)
 | 
			
		||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
build\janet_boot . > build\janet.c
 | 
			
		||||
build\janet_boot . > build\c\janet.c
 | 
			
		||||
 | 
			
		||||
@rem Build the sources
 | 
			
		||||
%JANET_COMPILE% /Fobuild\janet.obj build\janet.c
 | 
			
		||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
%JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
@@ -82,7 +87,7 @@ exit /b 1
 | 
			
		||||
@echo command prompt.
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
@rem Clean build artifacts 
 | 
			
		||||
@rem Clean build artifacts
 | 
			
		||||
:CLEAN
 | 
			
		||||
del *.exe *.lib *.exp
 | 
			
		||||
rd /s /q build
 | 
			
		||||
@@ -102,8 +107,9 @@ exit /b 0
 | 
			
		||||
mkdir dist
 | 
			
		||||
janet.exe tools\gendoc.janet > dist\doc.html
 | 
			
		||||
janet.exe tools\removecr.janet dist\doc.html
 | 
			
		||||
janet.exe tools\removecr.janet build\c\janet.c
 | 
			
		||||
 | 
			
		||||
copy build\janet.c dist\janet.c
 | 
			
		||||
copy build\c\janet.c dist\janet.c
 | 
			
		||||
copy src\mainclient\shell.c dist\shell.c
 | 
			
		||||
copy janet.exe dist\janet.exe
 | 
			
		||||
copy LICENSE dist\LICENSE
 | 
			
		||||
@@ -112,13 +118,10 @@ copy README.md dist\README.md
 | 
			
		||||
copy janet.lib dist\janet.lib
 | 
			
		||||
copy janet.exp dist\janet.exp
 | 
			
		||||
 | 
			
		||||
copy src\include\janet.h dist\janet.h
 | 
			
		||||
copy src\conf\janetconf.h dist\janetconf.h
 | 
			
		||||
janet.exe tools\patch-header.janet src\include\janet.h src\conf\janetconf.h build\janet.h
 | 
			
		||||
copy build\janet.h dist\janet.h
 | 
			
		||||
copy build\libjanet.lib dist\libjanet.lib
 | 
			
		||||
 | 
			
		||||
copy .\jpm dist\jpm
 | 
			
		||||
copy tools\jpm.bat dist\jpm.bat
 | 
			
		||||
 | 
			
		||||
@rem Create installer
 | 
			
		||||
janet.exe -e "(->> janet/version (peg/match ''(* :d+ `.` :d+ `.` :d+)) first print)" > build\version.txt
 | 
			
		||||
janet.exe -e "(print (os/arch))" > build\arch.txt
 | 
			
		||||
@@ -147,34 +150,6 @@ FOR %%a in (janet-*-windows-*-installer.msi) DO (
 | 
			
		||||
)
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
@rem Test the installation.
 | 
			
		||||
:TESTINSTALL
 | 
			
		||||
pushd test\install
 | 
			
		||||
call jpm clean
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm test
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --modpath=. install https://github.com/janet-lang/json.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call build\testexec
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose quickbin testexec.janet build\testexec2.exe
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call build\testexec2.exe
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/jhydro.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/path.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/argparse.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
popd
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
:TESTINSTALLFAIL
 | 
			
		||||
popd
 | 
			
		||||
goto :TESTFAIL
 | 
			
		||||
 | 
			
		||||
@rem build, test, dist, install. Useful for local dev.
 | 
			
		||||
:ALL
 | 
			
		||||
call %0 build
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,22 @@
 | 
			
		||||
# Example of dst bytecode assembly
 | 
			
		||||
 | 
			
		||||
# Fibonacci sequence, implemented with naive recursion.
 | 
			
		||||
(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
 | 
			
		||||
  ]
 | 
			
		||||
}))
 | 
			
		||||
(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
 | 
			
		||||
]}))
 | 
			
		||||
 | 
			
		||||
# Test it
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								examples/async-execute.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								examples/async-execute.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
(defn dowork [name n]
 | 
			
		||||
  (print name " starting work...")
 | 
			
		||||
  (os/execute [(dyn :executable) "-e" (string "(os/sleep " n ")")] :p)
 | 
			
		||||
  (print name " finished work!"))
 | 
			
		||||
 | 
			
		||||
# Will be done in parallel
 | 
			
		||||
(print "starting group A")
 | 
			
		||||
(ev/call dowork "A 2" 2)
 | 
			
		||||
(ev/call dowork "A 1" 1)
 | 
			
		||||
(ev/call dowork "A 3" 3)
 | 
			
		||||
 | 
			
		||||
(ev/sleep 4)
 | 
			
		||||
 | 
			
		||||
# Will also be done in parallel
 | 
			
		||||
(print "starting group B")
 | 
			
		||||
(ev/call dowork "B 2" 2)
 | 
			
		||||
(ev/call dowork "B 1" 1)
 | 
			
		||||
(ev/call dowork "B 3" 3)
 | 
			
		||||
 | 
			
		||||
(ev/sleep 4)
 | 
			
		||||
 | 
			
		||||
(print "all work done")
 | 
			
		||||
							
								
								
									
										15
									
								
								examples/channel.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/channel.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
(def c (ev/chan 4))
 | 
			
		||||
 | 
			
		||||
(defn writer []
 | 
			
		||||
  (for i 0 10
 | 
			
		||||
    (ev/sleep 0.1)
 | 
			
		||||
    (print "writer giving item " i "...")
 | 
			
		||||
    (ev/give c (string "item " i))))
 | 
			
		||||
 | 
			
		||||
(defn reader [name]
 | 
			
		||||
  (forever
 | 
			
		||||
    (print "reader " name " got " (ev/take c))))
 | 
			
		||||
 | 
			
		||||
(ev/call writer)
 | 
			
		||||
(each letter [:a :b :c :d :e :f :g]
 | 
			
		||||
  (ev/call reader letter))
 | 
			
		||||
							
								
								
									
										5
									
								
								examples/echoclient.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								examples/echoclient.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
(with [conn (net/connect "127.0.0.1" 8000)]
 | 
			
		||||
  (print "writing abcdefg...")
 | 
			
		||||
  (:write conn "abcdefg")
 | 
			
		||||
  (print "reading...")
 | 
			
		||||
  (printf "got: %v" (:read conn 1024)))
 | 
			
		||||
							
								
								
									
										15
									
								
								examples/echoserve.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/echoserve.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
(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)
 | 
			
		||||
							
								
								
									
										45
									
								
								examples/evlocks.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								examples/evlocks.janet
									
									
									
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
							
								
								
									
										22
									
								
								examples/evsleep.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								examples/evsleep.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
(defn worker
 | 
			
		||||
  "Run for a number of iterations."
 | 
			
		||||
  [name iterations]
 | 
			
		||||
  (for i 0 iterations
 | 
			
		||||
    (ev/sleep 1)
 | 
			
		||||
    (print "worker " name " iteration " i)))
 | 
			
		||||
 | 
			
		||||
(ev/call worker :a 10)
 | 
			
		||||
(ev/sleep 0.2)
 | 
			
		||||
(ev/call worker :b 5)
 | 
			
		||||
(ev/sleep 0.3)
 | 
			
		||||
(ev/call worker :c 12)
 | 
			
		||||
 | 
			
		||||
(defn worker2
 | 
			
		||||
  [name]
 | 
			
		||||
  (repeat 10
 | 
			
		||||
    (ev/sleep 0.2)
 | 
			
		||||
    (print name " working")))
 | 
			
		||||
 | 
			
		||||
(ev/go worker2 :bob)
 | 
			
		||||
(ev/go worker2 :joe)
 | 
			
		||||
(ev/go worker2 :sally)
 | 
			
		||||
							
								
								
									
										71
									
								
								examples/ffi/gtk.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								examples/ffi/gtk.janet
									
									
									
									
									
										Normal file
									
								
							@@ -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))
 | 
			
		||||
							
								
								
									
										205
									
								
								examples/ffi/so.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								examples/ffi/so.c
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										134
									
								
								examples/ffi/test.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								examples/ffi/test.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
#
 | 
			
		||||
# 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])
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# 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))
 | 
			
		||||
 | 
			
		||||
(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.")
 | 
			
		||||
							
								
								
									
										7
									
								
								examples/ffi/win32.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/ffi/win32.janet
									
									
									
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								examples/iterate-fiber.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								examples/iterate-fiber.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
(def f
 | 
			
		||||
  (coro
 | 
			
		||||
    (for i 0 10
 | 
			
		||||
      (yield (string "yield " i))
 | 
			
		||||
      (os/sleep 0))))
 | 
			
		||||
 | 
			
		||||
(print "simple yielding")
 | 
			
		||||
(each item f (print "got: " item ", now " (fiber/status f)))
 | 
			
		||||
 | 
			
		||||
(def f
 | 
			
		||||
  (coro
 | 
			
		||||
    (for i 0 10
 | 
			
		||||
      (yield (string "yield " i))
 | 
			
		||||
      (ev/sleep 0))))
 | 
			
		||||
 | 
			
		||||
(print "complex yielding")
 | 
			
		||||
(each item f (print "got: " item ", now " (fiber/status f)))
 | 
			
		||||
 | 
			
		||||
(print (fiber/status f))
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								examples/jitfn/hello.bin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/jitfn/hello.bin
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										17
									
								
								examples/jitfn/hello.nasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								examples/jitfn/hello.nasm
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										13
									
								
								examples/jitfn/jitfn.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								examples/jitfn/jitfn.janet
									
									
									
									
									
										Normal file
									
								
							@@ -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)))
 | 
			
		||||
							
								
								
									
										2
									
								
								examples/lineloop.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								examples/lineloop.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
(while (not (empty? (def line (getline))))
 | 
			
		||||
  (prin "line: " line))
 | 
			
		||||
							
								
								
									
										30
									
								
								examples/marshal-stress.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								examples/marshal-stress.janet
									
									
									
									
									
										Normal file
									
								
							@@ -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)))
 | 
			
		||||
 | 
			
		||||
@@ -7,13 +7,13 @@ typedef struct {
 | 
			
		||||
} num_array;
 | 
			
		||||
 | 
			
		||||
static num_array *num_array_init(num_array *array, size_t size) {
 | 
			
		||||
    array->data = (double *)calloc(size, sizeof(double));
 | 
			
		||||
    array->data = (double *)janet_calloc(size, sizeof(double));
 | 
			
		||||
    array->size = size;
 | 
			
		||||
    return array;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void num_array_deinit(num_array *array) {
 | 
			
		||||
    free(array->data);
 | 
			
		||||
    janet_free(array->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int num_array_gc(void *p, size_t 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))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								examples/select.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								examples/select.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
(def channels
 | 
			
		||||
  (seq [:repeat 5] (ev/chan 4)))
 | 
			
		||||
 | 
			
		||||
(defn writer [c]
 | 
			
		||||
  (for i 0 3
 | 
			
		||||
    (def item (string i ":" (mod (hash c) 999)))
 | 
			
		||||
    (ev/sleep 0.1)
 | 
			
		||||
    (print "writer giving item " item " to " c "...")
 | 
			
		||||
    (ev/give c item))
 | 
			
		||||
  (print "Done!"))
 | 
			
		||||
 | 
			
		||||
(defn reader [name]
 | 
			
		||||
  (forever
 | 
			
		||||
    (def [_ c x] (ev/rselect ;channels))
 | 
			
		||||
    (print "reader " name " got " x " from " c)))
 | 
			
		||||
 | 
			
		||||
# Readers
 | 
			
		||||
(each letter [:a :b :c :d :e :f :g]
 | 
			
		||||
  (ev/call reader letter))
 | 
			
		||||
 | 
			
		||||
# Writers
 | 
			
		||||
(each c channels
 | 
			
		||||
  (ev/call writer c))
 | 
			
		||||
							
								
								
									
										37
									
								
								examples/select2.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								examples/select2.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
###
 | 
			
		||||
### examples/select2.janet
 | 
			
		||||
###
 | 
			
		||||
### Mix reads and writes in select.
 | 
			
		||||
###
 | 
			
		||||
 | 
			
		||||
(def c1 (ev/chan 40))
 | 
			
		||||
(def c2 (ev/chan 40))
 | 
			
		||||
(def c3 (ev/chan 40))
 | 
			
		||||
(def c4 (ev/chan 40))
 | 
			
		||||
 | 
			
		||||
(def c5 (ev/chan 4))
 | 
			
		||||
 | 
			
		||||
(defn worker
 | 
			
		||||
  [c n x]
 | 
			
		||||
  (forever
 | 
			
		||||
    (ev/sleep n)
 | 
			
		||||
    (ev/give c x)))
 | 
			
		||||
 | 
			
		||||
(defn writer-worker
 | 
			
		||||
  [c]
 | 
			
		||||
  (forever
 | 
			
		||||
    (ev/sleep 0.2)
 | 
			
		||||
    (print "writing " (ev/take c))))
 | 
			
		||||
 | 
			
		||||
(ev/call worker c1 1 :item1)
 | 
			
		||||
(ev/sleep 0.2)
 | 
			
		||||
(ev/call worker c2 1 :item2)
 | 
			
		||||
(ev/sleep 0.1)
 | 
			
		||||
(ev/call worker c3 1 :item3)
 | 
			
		||||
(ev/sleep 0.2)
 | 
			
		||||
(ev/call worker c4 1 :item4)
 | 
			
		||||
(ev/sleep 0.1)
 | 
			
		||||
(ev/call worker c4 1 :item5)
 | 
			
		||||
(ev/call writer-worker c5)
 | 
			
		||||
 | 
			
		||||
(forever (pp (ev/rselect c1 c2 c3 c4 [c5 :thing])))
 | 
			
		||||
@@ -1,73 +0,0 @@
 | 
			
		||||
# naive matrix implementation for testing typed array
 | 
			
		||||
 | 
			
		||||
(defn matrix [nrow ncol] {:nrow nrow :ncol ncol :array (tarray/new :float64 (* nrow ncol))})
 | 
			
		||||
 | 
			
		||||
(defn matrix/row [mat i]
 | 
			
		||||
  (def {:nrow nrow :ncol ncol :array array} mat)
 | 
			
		||||
  (tarray/new :float64 ncol 1 (* i ncol)  array))
 | 
			
		||||
 | 
			
		||||
(defn matrix/column [mat j]
 | 
			
		||||
  (def {:nrow nrow :ncol ncol :array array} mat)
 | 
			
		||||
  (tarray/new :float64 nrow ncol j array))
 | 
			
		||||
 | 
			
		||||
(defn matrix/set [mat i j value]
 | 
			
		||||
  (def {:nrow nrow :ncol ncol :array array} mat)
 | 
			
		||||
  (set (array (+ (* i ncol) j)) value))
 | 
			
		||||
 | 
			
		||||
(defn matrix/get [mat i j value]
 | 
			
		||||
  (def {:nrow nrow :ncol ncol :array array} mat)
 | 
			
		||||
  (array (+ (* i ncol) j)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# other variants to test rows and cols views
 | 
			
		||||
 | 
			
		||||
(defn matrix/set* [mat i j value]
 | 
			
		||||
  (set ((matrix/row mat i) j) value))
 | 
			
		||||
 | 
			
		||||
(defn matrix/set** [mat i j value]
 | 
			
		||||
  (set ((matrix/column mat j) i) value))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(defn matrix/get* [mat i j value]
 | 
			
		||||
  ((matrix/row mat i) j))
 | 
			
		||||
 | 
			
		||||
(defn matrix/get** [mat i j value]
 | 
			
		||||
  ((matrix/column mat j) i))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(defn tarray/print [arr]
 | 
			
		||||
  (def size (tarray/length arr))
 | 
			
		||||
  (prinf "[%2i]" size)
 | 
			
		||||
  (for i 0 size
 | 
			
		||||
    (prinf " %+6.3f " (arr i)))
 | 
			
		||||
  (print))
 | 
			
		||||
 | 
			
		||||
(defn matrix/print [mat]
 | 
			
		||||
  (def {:nrow nrow :ncol ncol :array tarray} mat)
 | 
			
		||||
  (printf "matrix %iX%i %p" nrow ncol tarray)
 | 
			
		||||
  (for i 0 nrow
 | 
			
		||||
    (tarray/print (matrix/row mat i))))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(def nr 5)
 | 
			
		||||
(def nc 4)
 | 
			
		||||
(def A (matrix nr nc))
 | 
			
		||||
 | 
			
		||||
(loop (i :range (0 nr) j :range (0 nc)) 
 | 
			
		||||
  (matrix/set A i j i))
 | 
			
		||||
(matrix/print A)
 | 
			
		||||
 | 
			
		||||
(loop (i :range (0 nr) j :range (0 nc)) 
 | 
			
		||||
  (matrix/set* A i j i))
 | 
			
		||||
(matrix/print A)
 | 
			
		||||
 | 
			
		||||
(loop (i :range (0 nr) j :range (0 nc)) 
 | 
			
		||||
  (matrix/set** A i j i))
 | 
			
		||||
(matrix/print A)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(printf "properties:\n%p" (tarray/properties (A :array)))
 | 
			
		||||
(for i 0 nr  
 | 
			
		||||
  (printf "row properties:[%i]\n%p" i (tarray/properties (matrix/row A i))))
 | 
			
		||||
(for i 0 nc  
 | 
			
		||||
  (printf "col properties:[%i]\n%p" i (tarray/properties (matrix/column A i))))
 | 
			
		||||
@@ -6,8 +6,15 @@
 | 
			
		||||
    (def b @"")
 | 
			
		||||
    (print "Connection " id "!")
 | 
			
		||||
    (while (:read stream 1024 b)
 | 
			
		||||
      (repeat 10 (print "work for " id " ...") (ev/sleep 0.1))
 | 
			
		||||
      (:write stream b)
 | 
			
		||||
      (buffer/clear b))
 | 
			
		||||
    (printf "Done %v!" id)))
 | 
			
		||||
 | 
			
		||||
(net/server "127.0.0.1" "8000" handler)
 | 
			
		||||
# Run server.
 | 
			
		||||
(let [server (net/server "127.0.0.1" "8000")]
 | 
			
		||||
  (print "Starting echo server on 127.0.0.1:8000")
 | 
			
		||||
  (forever
 | 
			
		||||
    (if-let [conn (:accept server)]
 | 
			
		||||
      (ev/call handler conn)
 | 
			
		||||
      (print "no new connections"))))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								examples/threaded-channels.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								examples/threaded-channels.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
(def chan (ev/thread-chan 10))
 | 
			
		||||
 | 
			
		||||
(ev/spawn
 | 
			
		||||
  (ev/sleep 0)
 | 
			
		||||
  (print "started fiber!")
 | 
			
		||||
  (ev/give chan (math/random))
 | 
			
		||||
  (ev/give chan (math/random))
 | 
			
		||||
  (ev/give chan (math/random))
 | 
			
		||||
  (ev/sleep 0.5)
 | 
			
		||||
  (for i 0 10
 | 
			
		||||
    (print "giving to channel...")
 | 
			
		||||
    (ev/give chan (math/random))
 | 
			
		||||
    (ev/sleep 1))
 | 
			
		||||
  (print "finished fiber!")
 | 
			
		||||
  (:close chan))
 | 
			
		||||
 | 
			
		||||
(ev/do-thread
 | 
			
		||||
  (print "started thread!")
 | 
			
		||||
  (ev/sleep 1)
 | 
			
		||||
  (while (def x (do (print "taking from channel...") (ev/take chan)))
 | 
			
		||||
    (print "got " x " from thread!"))
 | 
			
		||||
  (print "finished thread!"))
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
(defn worker-main
 | 
			
		||||
  "Sends 11 messages back to parent"
 | 
			
		||||
  [parent]
 | 
			
		||||
  (def name (thread/receive))
 | 
			
		||||
  (def interval (thread/receive))
 | 
			
		||||
  (for i 0 10
 | 
			
		||||
    (os/sleep interval)
 | 
			
		||||
    (:send parent (string/format "thread %s wakeup no. %d" name i)))
 | 
			
		||||
  (:send parent name))
 | 
			
		||||
 | 
			
		||||
(defn make-worker
 | 
			
		||||
  [name interval]
 | 
			
		||||
  (-> (thread/new worker-main)
 | 
			
		||||
      (:send name)
 | 
			
		||||
      (:send interval)))
 | 
			
		||||
 | 
			
		||||
(def bob (make-worker "bob" 0.02))
 | 
			
		||||
(def joe (make-worker "joe" 0.03))
 | 
			
		||||
(def sam (make-worker "sam" 0.05))
 | 
			
		||||
 | 
			
		||||
# Receive out of order
 | 
			
		||||
(for i 0 33
 | 
			
		||||
  (print (thread/receive)))
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Recursive Thread Tree - should pause for a bit, and then print a cool zigzag.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(def rng (math/rng (os/cryptorand 16)))
 | 
			
		||||
 | 
			
		||||
(defn choose [& xs]
 | 
			
		||||
  (in xs (:int rng (length xs))))
 | 
			
		||||
 | 
			
		||||
(defn worker-tree
 | 
			
		||||
  [parent]
 | 
			
		||||
  (def name (thread/receive))
 | 
			
		||||
  (def depth (thread/receive))
 | 
			
		||||
  (if (< depth 5)
 | 
			
		||||
    (do
 | 
			
		||||
    (defn subtree []
 | 
			
		||||
      (-> (thread/new worker-tree)
 | 
			
		||||
          (:send (string name "/" (choose "bob" "marley" "harry" "suki" "anna" "yu")))
 | 
			
		||||
          (:send (inc depth))))
 | 
			
		||||
    (let [l (subtree)
 | 
			
		||||
          r (subtree)
 | 
			
		||||
          lrep (thread/receive)
 | 
			
		||||
          rrep (thread/receive)]
 | 
			
		||||
      (:send parent [name ;lrep ;rrep])))
 | 
			
		||||
    (do
 | 
			
		||||
      (:send parent [name]))))
 | 
			
		||||
 | 
			
		||||
(-> (thread/new worker-tree) (:send "adam") (:send 0))
 | 
			
		||||
(def lines (thread/receive))
 | 
			
		||||
(map print lines)
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Receive timeout
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(def slow (make-worker "slow-loras" 0.5))
 | 
			
		||||
(for i 0 50
 | 
			
		||||
  (try
 | 
			
		||||
    (let [msg (thread/receive 0.1)]
 | 
			
		||||
      (print "\n" msg))
 | 
			
		||||
    ([err] (prin ".") (:flush stdout))))
 | 
			
		||||
 | 
			
		||||
(print "\ndone timing, timeouts ending.")
 | 
			
		||||
(try (while true (print (thread/receive))) ([err] (print "done")))
 | 
			
		||||
							
								
								
									
										5
									
								
								examples/udpclient.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								examples/udpclient.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
(def conn (net/connect "127.0.0.1" "8009" :datagram))
 | 
			
		||||
(:write conn (string/format "%q" (os/cryptorand 16)))
 | 
			
		||||
(def x (:read conn 1024))
 | 
			
		||||
(pp x)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								examples/udpserver.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								examples/udpserver.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
(def server (net/listen "127.0.0.1" "8009" :datagram))
 | 
			
		||||
(while true
 | 
			
		||||
  (def buf @"")
 | 
			
		||||
  (def who (:recv-from server 1024 buf))
 | 
			
		||||
  (printf "got %q from %v, echoing!" buf who)
 | 
			
		||||
  (:send-to server who buf))
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										73
									
								
								janet.1
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								janet.1
									
									
									
									
									
								
							@@ -3,11 +3,14 @@
 | 
			
		||||
janet \- run the Janet language abstract machine
 | 
			
		||||
.SH SYNOPSIS
 | 
			
		||||
.B janet
 | 
			
		||||
[\fB\-hvsrpnqk\fR]
 | 
			
		||||
[\fB\-hvsrpnqik\fR]
 | 
			
		||||
[\fB\-e\fR \fISOURCE\fR]
 | 
			
		||||
[\fB\-E\fR \fISOURCE ...ARGUMENTS\fR]
 | 
			
		||||
[\fB\-l\fR \fIMODULE\fR]
 | 
			
		||||
[\fB\-m\fR \fIPATH\fR]
 | 
			
		||||
[\fB\-c\fR \fIMODULE JIMAGE\fR]
 | 
			
		||||
[\fB\-w\fR \fILEVEL\fR]
 | 
			
		||||
[\fB\-x\fR \fILEVEL\fR]
 | 
			
		||||
[\fB\-\-\fR]
 | 
			
		||||
.BR script
 | 
			
		||||
.BR args ...
 | 
			
		||||
@@ -64,6 +67,10 @@ Move cursor to the beginning of input line.
 | 
			
		||||
.BR Ctrl\-B
 | 
			
		||||
Move cursor one character to the left.
 | 
			
		||||
 | 
			
		||||
.TP 16
 | 
			
		||||
.BR Ctrl\-D
 | 
			
		||||
If on a newline, indicate end of stream and exit the repl.
 | 
			
		||||
 | 
			
		||||
.TP 16
 | 
			
		||||
.BR Ctrl\-E
 | 
			
		||||
Move cursor to the end of input line.
 | 
			
		||||
@@ -100,6 +107,10 @@ Delete one word before the cursor.
 | 
			
		||||
.BR Ctrl\-G
 | 
			
		||||
Show documentation for the current symbol under the cursor.
 | 
			
		||||
 | 
			
		||||
.TP 16
 | 
			
		||||
.BR Ctrl\-Q
 | 
			
		||||
Clear the current command, including already typed lines.
 | 
			
		||||
 | 
			
		||||
.TP 16
 | 
			
		||||
.BR Alt\-B/Alt\-F
 | 
			
		||||
Move cursor backwards and forwards one word.
 | 
			
		||||
@@ -152,6 +163,16 @@ Read raw input from stdin and forgo prompt history and other readline-like featu
 | 
			
		||||
Execute a string of Janet source. Source code is executed in the order it is encountered, so earlier
 | 
			
		||||
arguments are executed before later ones.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.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
 | 
			
		||||
@@ -162,11 +183,19 @@ 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
 | 
			
		||||
arguments, a REPL is opened.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-R
 | 
			
		||||
If using the REPL, disable loading the user profile from the JANET_PROFILE environment variable.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-p
 | 
			
		||||
Turn on the persistent flag. By default, when Janet is executing commands from a file and encounters an error,
 | 
			
		||||
@@ -194,11 +223,27 @@ Source should be a path to the Janet module to compile, and output should be the
 | 
			
		||||
resulting image. Output should usually end with the .jimage extension.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-l\ path
 | 
			
		||||
Load a Janet file before running a script or repl. Multiple files can be loaded
 | 
			
		||||
.BR \-i
 | 
			
		||||
When this flag is passed, a script passed to the interpreter will be treated as a janet image file
 | 
			
		||||
rather than a janet source file.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-l\ lib
 | 
			
		||||
Import a Janet module before running a script or repl. Multiple files can be loaded
 | 
			
		||||
in this manner, and exports from each file will be made available to the script
 | 
			
		||||
or repl.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-w\ level
 | 
			
		||||
Set the warning linting level for Janet.
 | 
			
		||||
This linting level should be one of :relaxed, :none, :strict, :normal, or a
 | 
			
		||||
Janet number. Any linting message that is of a greater lint level than this setting will be displayed as
 | 
			
		||||
a warning, but not stop compilation or execution.
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-x\ level
 | 
			
		||||
Set the error linting level for Janet.
 | 
			
		||||
This linting level should be one of :relaxed, :none, :strict, :normal, or a
 | 
			
		||||
Janet number. Any linting message that is of a greater lint level will cause a compilation error
 | 
			
		||||
and stop compilation.
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-
 | 
			
		||||
Stop parsing command line arguments. All arguments after this one will be considered file names
 | 
			
		||||
@@ -213,5 +258,25 @@ find native and source code modules. If no JANET_PATH is set, Janet will look in
 | 
			
		||||
the default location set at compile time.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_PROFILE
 | 
			
		||||
.RS
 | 
			
		||||
Path to a profile file that the interpreter will load before entering the REPL. This profile file will
 | 
			
		||||
not run for scripts, though. This behavior can be disabled with the -R option.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_HASHSEED
 | 
			
		||||
.RS
 | 
			
		||||
To disable randomization of Janet's PRF on start up, one can set this variable. This can have the
 | 
			
		||||
effect of making programs deterministic that otherwise would depend on the random seed chosen at program start.
 | 
			
		||||
This variable does nothing in the default configuration of Janet, as PRF is disabled by default. Also, JANET_REDUCED_OS
 | 
			
		||||
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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										254
									
								
								jpm.1
									
									
									
									
									
								
							
							
						
						
									
										254
									
								
								jpm.1
									
									
									
									
									
								
							@@ -1,254 +0,0 @@
 | 
			
		||||
.TH JPM 1
 | 
			
		||||
.SH NAME
 | 
			
		||||
jpm \- the Janet Project Manager, a build tool for Janet 
 | 
			
		||||
.SH SYNOPSIS
 | 
			
		||||
.B jpm
 | 
			
		||||
[\fB\-\-flag ...\fR]
 | 
			
		||||
[\fB\-\-option=value ...\fR]
 | 
			
		||||
.IR command
 | 
			
		||||
.IR args ...
 | 
			
		||||
.SH DESCRIPTION
 | 
			
		||||
jpm is the build tool that ships with a standard Janet install. It is
 | 
			
		||||
used for building Janet projects, installing dependencies, installing
 | 
			
		||||
projects, building native modules, and exporting your Janet project to a
 | 
			
		||||
standalone executable. Although not required for working with Janet, it
 | 
			
		||||
removes much of the boilerplate with installing dependencies and
 | 
			
		||||
building native modules. jpm requires only Janet to run, and uses git
 | 
			
		||||
to install dependencies (jpm will work without git installed).
 | 
			
		||||
.SH DOCUMENTATION
 | 
			
		||||
 | 
			
		||||
jpm has several subcommands, each used for managing either a single Janet project or
 | 
			
		||||
all Janet modules installed on the system. Global commands, those that manage modules
 | 
			
		||||
at the system level, do things like install and uninstall packages, as well as clear the cache.
 | 
			
		||||
More interesting are the local commands. For more information on jpm usage, see https://janet-lang.org/docs/index.html
 | 
			
		||||
 | 
			
		||||
.SH FLAGS
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-nocolor
 | 
			
		||||
Disable color in the jpm debug repl.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-verbose
 | 
			
		||||
Print detailed messages of what jpm is doing, including compilation commands and other shell commands.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-test
 | 
			
		||||
If passed to jpm install, runs tests before installing. Will run tests recursively on dependencies.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-offline
 | 
			
		||||
Prevents jpm from going to network to get dependencies - all dependencies should be in the cache or this command will fail.
 | 
			
		||||
Use this flag with the deps and update-pkgs subcommands. This is not a surefire way to prevent a build script from accessing
 | 
			
		||||
the network, for example, a build script that invokes curl will still have network access.
 | 
			
		||||
 | 
			
		||||
.SH OPTIONS
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-modpath=/some/path
 | 
			
		||||
Set the path to install modules to. Defaults to $JANET_MODPATH, $JANET_PATH, or (dyn :syspath) in that order. You most likely don't need this.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-headerpath=/some/path
 | 
			
		||||
Set the path the jpm will include when building C source code. This lets
 | 
			
		||||
you specify the location of janet.h and janetconf.h on your system. On a
 | 
			
		||||
normal install, this option is not needed.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-binpath=/some/path
 | 
			
		||||
Set the path that jpm will install scripts and standalone executables to. Executables
 | 
			
		||||
defined via declare-execuatble or scripts declared via declare-binscript will be installed
 | 
			
		||||
here when jpm install is run. Defaults to $JANET_BINPATH, or a reasonable default for the system.
 | 
			
		||||
See JANET_BINPATH for more.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-libpath=/some/path
 | 
			
		||||
Sets the path jpm will use to look for libjanet.a for building standalone executables. libjanet.so
 | 
			
		||||
is \fBnot\fR used for building native modules or standalone executables, only
 | 
			
		||||
for linking into applications that want to embed janet as a dynamic module.
 | 
			
		||||
Linking statically might be a better idea, even in that case. Defaults to
 | 
			
		||||
$JANET_LIBPATH, or a reasonable default. See JANET_LIBPATH for more.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-compiler=$CC
 | 
			
		||||
Sets the compiler used for compiling native modules and standalone executables. Defaults
 | 
			
		||||
to cc.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-linker
 | 
			
		||||
Sets the linker used to create native modules and executables. Only used on windows, where
 | 
			
		||||
it defaults to link.exe.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-pkglist=https://github.com/janet-lang/pkgs.git
 | 
			
		||||
Sets the git repository for the package listing used to resolve shorthand package names.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-archiver=$AR
 | 
			
		||||
Sets the command used for creating static libraries, use for linking into the standalone executable.
 | 
			
		||||
Native modules are compiled twice, once a normal native module (shared object), and once as an
 | 
			
		||||
archive. Defaults to ar.
 | 
			
		||||
 | 
			
		||||
.SH COMMANDS
 | 
			
		||||
.TP
 | 
			
		||||
.BR help
 | 
			
		||||
Shows the usage text and exits immediately.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR build
 | 
			
		||||
Builds all artifacts specified in the project.janet file in the current directory. Artifacts will
 | 
			
		||||
be created in the ./build/ directory.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR install\ [\fBrepo...\fR]
 | 
			
		||||
 | 
			
		||||
When run with no arguments, installs all installable artifacts in the current project to
 | 
			
		||||
the current JANET_MODPATH for modules and JANET_BINPATH for executables and scripts. Can also
 | 
			
		||||
take an optional git repository URL and will install all artifacts in that repository instead.
 | 
			
		||||
When run with an argument, install does not need to be run from a jpm project directory. Will also
 | 
			
		||||
install multiple dependencies in one command.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR uninstall\ [\fBname...\fR]
 | 
			
		||||
Uninstall a project installed with install. uninstall expects the name of the project, not the
 | 
			
		||||
repository url, path to installed file or executable name. The name of the project must be specified
 | 
			
		||||
at the top of the project.janet file in the declare-project form. If no name is given, uninstalls
 | 
			
		||||
the current project if installed. Will also uninstall multiple packages in one command.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clean
 | 
			
		||||
Remove all artifacts created by jpm. This just deletes the build folder.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR test
 | 
			
		||||
Runs jpm tests. jpm will run all janet source files in the test directory as tests. A test
 | 
			
		||||
is considered failing if it exits with a non-zero exit code.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR deps
 | 
			
		||||
Install all dependencies that this project requires recursively. jpm does not
 | 
			
		||||
resolve dependency issues, like conflicting versions of the same module are required, or
 | 
			
		||||
different modules with the same name. Dependencies are installed with git, so deps requires
 | 
			
		||||
git to be on the PATH.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clear-cache
 | 
			
		||||
jpm caches git repositories that are needed to install modules from a remote
 | 
			
		||||
source in a global cache ($JANET_PATH/.cache). If these dependencies are out of
 | 
			
		||||
date or too large, clear-cache will remove the cache and jpm will rebuild it
 | 
			
		||||
when needed. clear-cache is a global command, so a project.janet is not
 | 
			
		||||
required.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clear-manifest
 | 
			
		||||
jpm creates a manifest directory that contains a list of all installed files.
 | 
			
		||||
By deleting this directory, jpm will think that nothing is installed and will
 | 
			
		||||
try reinstalling everything on the jpm deps or jpm load-lockfile commands. Be careful with
 | 
			
		||||
this command, as it may leave extra files on your system and shouldn't be needed
 | 
			
		||||
most of the time in a healthy install.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR run\ [\fBrule\fR]
 | 
			
		||||
Run a given rule defined in project.janet. Project definitions files (project.janet) usually
 | 
			
		||||
contain a few artifact declarations, which set up rules that jpm can then resolve, or execute.
 | 
			
		||||
A project.janet can also create custom rules to create arbitrary files or run arbitrary code, much
 | 
			
		||||
like make. run will run a single rule or build a single file.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR rules
 | 
			
		||||
List all rules that can be run via run. This is useful for exploring rules in the project.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR rule-tree\ [\fBroot\fR] [\fdepth\fR]
 | 
			
		||||
Show rule dependency tree in a pretty format. Optionally provide a rule to use as the tree
 | 
			
		||||
root, as well as a max depth to print. By default, prints the full tree for all rules. This
 | 
			
		||||
can be quite long, so it is recommended to give a root rule.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR show-paths
 | 
			
		||||
Show all of the paths used when installing and building artifacts.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR update-pkgs
 | 
			
		||||
Update the package listing by installing the 'pkgs' package. Same as jpm install pkgs
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR quickbin [\fBentry\fR] [\fBexecutable\fR]
 | 
			
		||||
Create a standalone, statically linked executable from a Janet source file that contains a main function.
 | 
			
		||||
The main function is the entry point of the program and will receive command line arguments
 | 
			
		||||
as function arguments. The entry file can import other modules, including native C modules, and
 | 
			
		||||
jpm will attempt to include the dependencies into the generated executable.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR debug-repl
 | 
			
		||||
Load the current project.janet file and start a repl in it's environment. This lets a user better
 | 
			
		||||
debug the project file, as well as run rules manually.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR make-lockfile\ [\fBfilename\fR]
 | 
			
		||||
Create a lockfile. A lockfile is a record that describes what dependencies were installed at the
 | 
			
		||||
time of the lockfile's creation, including exact versions. A lockfile can then be later used
 | 
			
		||||
to set up that environment on a different machine via load-lockfile. By default, the lockfile
 | 
			
		||||
is created at lockfile.jdn, although any path can be used.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR load-lockfile\ [\fBfilename\fR]
 | 
			
		||||
Install dependencies from a lockfile previously created with make-lockfile. By default, will look
 | 
			
		||||
for a lockfile at lockfile.jdn, although any path can be used.
 | 
			
		||||
 | 
			
		||||
.SH ENVIRONMENT
 | 
			
		||||
 | 
			
		||||
.B JANET_PATH
 | 
			
		||||
.RS
 | 
			
		||||
The location to look for Janet libraries. This is the only environment variable Janet needs to
 | 
			
		||||
find native and source code modules. If no JANET_PATH is set, Janet will look in
 | 
			
		||||
the default location set at compile time, which can be determined with (dyn :syspath)
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_MODPATH
 | 
			
		||||
.RS
 | 
			
		||||
The location that jpm will use to install libraries to. Defaults to JANET_PATH, but you could
 | 
			
		||||
set this to a different directory if you want to. Doing so would let you import Janet modules
 | 
			
		||||
on the normal system path (JANET_PATH or (dyn :syspath)), but install to a different directory. It is also a more reliable way to install
 | 
			
		||||
This variable is overwritten by the --modpath=/some/path if it is provided.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_HEADERPATH
 | 
			
		||||
.RS
 | 
			
		||||
The location that jpm will look for janet header files (janet.h and janetconf.h) that are used
 | 
			
		||||
to build native modules and standalone executables. If janet.h and janetconf.h are available as
 | 
			
		||||
default includes on your system, this value is not required. If not provided, will default to
 | 
			
		||||
<jpm script location>/../include/janet. The --headerpath=/some/path option will override this
 | 
			
		||||
variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_LIBPATH
 | 
			
		||||
.RS
 | 
			
		||||
Similar to JANET_HEADERPATH, this path is where jpm will look for
 | 
			
		||||
libjanet.a for creating standalong executables. This does not need to be
 | 
			
		||||
set on a normal install. 
 | 
			
		||||
If not provided, this will default to <jpm script location>/../lib.
 | 
			
		||||
The --libpath=/some/path option will override this variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_BINPATH
 | 
			
		||||
.RS
 | 
			
		||||
The directory where jpm will install binary scripts and executables to.
 | 
			
		||||
Defaults to
 | 
			
		||||
(dyn :syspath)/bin
 | 
			
		||||
The --binpath=/some/path will override this variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_PKGLIST
 | 
			
		||||
.RS
 | 
			
		||||
The git repository URL that contains a listing of packages. This allows installing packages with shortnames, which
 | 
			
		||||
is mostly a convenience. However, package dependencies can use short names, package listings
 | 
			
		||||
can be used to choose a particular set of dependency versions for a whole project.
 | 
			
		||||
 | 
			
		||||
.B JANET_GIT
 | 
			
		||||
.RS
 | 
			
		||||
An optional path to a git executable to use to clone git dependencies. By default, uses "git" on the current $PATH. You shouldn't need to set this
 | 
			
		||||
if you have a normal install of git.
 | 
			
		||||
 | 
			
		||||
.SH AUTHOR
 | 
			
		||||
Written by Calvin Rose <calsrose@gmail.com>
 | 
			
		||||
							
								
								
									
										127
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								meson.build
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2020 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
 | 
			
		||||
@@ -19,8 +19,8 @@
 | 
			
		||||
# IN THE SOFTWARE.
 | 
			
		||||
 | 
			
		||||
project('janet', 'c',
 | 
			
		||||
  default_options : ['c_std=c99', 'b_lundef=false', 'default_library=both'],
 | 
			
		||||
  version : '1.10.0')
 | 
			
		||||
  default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
 | 
			
		||||
  version : '1.29.1')
 | 
			
		||||
 | 
			
		||||
# Global settings
 | 
			
		||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
 | 
			
		||||
@@ -30,10 +30,11 @@ header_path = join_paths(get_option('prefix'), get_option('includedir'), 'janet'
 | 
			
		||||
cc = meson.get_compiler('c')
 | 
			
		||||
m_dep = cc.find_library('m', required : false)
 | 
			
		||||
dl_dep = cc.find_library('dl', required : false)
 | 
			
		||||
android_spawn_dep = cc.find_library('android-spawn', required : false)
 | 
			
		||||
thread_dep = dependency('threads')
 | 
			
		||||
 | 
			
		||||
# Link options
 | 
			
		||||
if build_machine.system() != 'windows'
 | 
			
		||||
if get_option('default_library') != 'static' and build_machine.system() != 'windows'
 | 
			
		||||
    add_project_link_arguments('-rdynamic', language : 'c')
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
@@ -60,10 +61,10 @@ 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_EV', not get_option('ev') or get_option('single_threaded'))
 | 
			
		||||
conf.set('JANET_REDUCED_OS', get_option('reduced_os'))
 | 
			
		||||
conf.set('JANET_NO_TYPED_ARRAY', not get_option('typed_array'))
 | 
			
		||||
conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
 | 
			
		||||
conf.set('JANET_NO_PRF', not get_option('prf'))
 | 
			
		||||
conf.set('JANET_PRF', get_option('prf'))
 | 
			
		||||
conf.set('JANET_RECURSION_GUARD', get_option('recursion_guard'))
 | 
			
		||||
conf.set('JANET_MAX_PROTO_DEPTH', get_option('max_proto_depth'))
 | 
			
		||||
conf.set('JANET_MAX_MACRO_EXPAND', get_option('max_macro_expand'))
 | 
			
		||||
@@ -71,6 +72,12 @@ conf.set('JANET_STACK_MAX', get_option('stack_max'))
 | 
			
		||||
conf.set('JANET_NO_UMASK', not get_option('umask'))
 | 
			
		||||
conf.set('JANET_NO_REALPATH', not get_option('realpath'))
 | 
			
		||||
conf.set('JANET_NO_PROCESSES', not get_option('processes'))
 | 
			
		||||
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'))
 | 
			
		||||
if get_option('os_name') != ''
 | 
			
		||||
  conf.set('JANET_OS_NAME', get_option('os_name'))
 | 
			
		||||
endif
 | 
			
		||||
@@ -110,6 +117,8 @@ core_src = [
 | 
			
		||||
  'src/core/corelib.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',
 | 
			
		||||
@@ -124,14 +133,13 @@ core_src = [
 | 
			
		||||
  'src/core/regalloc.c',
 | 
			
		||||
  'src/core/run.c',
 | 
			
		||||
  'src/core/specials.c',
 | 
			
		||||
  'src/core/state.c',
 | 
			
		||||
  'src/core/string.c',
 | 
			
		||||
  'src/core/strtod.c',
 | 
			
		||||
  'src/core/struct.c',
 | 
			
		||||
  'src/core/symcache.c',
 | 
			
		||||
  'src/core/table.c',
 | 
			
		||||
  'src/core/thread.c',
 | 
			
		||||
  'src/core/tuple.c',
 | 
			
		||||
  'src/core/typedarray.c',
 | 
			
		||||
  'src/core/util.c',
 | 
			
		||||
  'src/core/value.c',
 | 
			
		||||
  'src/core/vector.c',
 | 
			
		||||
@@ -156,7 +164,7 @@ mainclient_src = [
 | 
			
		||||
janet_boot = executable('janet-boot', core_src, boot_src,
 | 
			
		||||
  include_directories : incdir,
 | 
			
		||||
  c_args : '-DJANET_BOOTSTRAP',
 | 
			
		||||
  dependencies : [m_dep, dl_dep, thread_dep],
 | 
			
		||||
  dependencies : [m_dep, dl_dep, thread_dep, android_spawn_dep],
 | 
			
		||||
  native : true)
 | 
			
		||||
 | 
			
		||||
# Build janet.c
 | 
			
		||||
@@ -166,42 +174,45 @@ janetc = custom_target('janetc',
 | 
			
		||||
  capture : true,
 | 
			
		||||
  command : [
 | 
			
		||||
    janet_boot, meson.current_source_dir(),
 | 
			
		||||
    'JANET_PATH', janet_path, 'JANET_HEADERPATH', header_path
 | 
			
		||||
    'JANET_PATH', janet_path
 | 
			
		||||
  ])
 | 
			
		||||
 | 
			
		||||
janet_dependencies = [m_dep, dl_dep, android_spawn_dep]
 | 
			
		||||
if not get_option('single_threaded')
 | 
			
		||||
  janet_dependencies += thread_dep
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
libjanet = library('janet', janetc,
 | 
			
		||||
  include_directories : incdir,
 | 
			
		||||
  dependencies : [m_dep, dl_dep, thread_dep],
 | 
			
		||||
  dependencies : janet_dependencies,
 | 
			
		||||
  version: meson.project_version(),
 | 
			
		||||
  soversion: version_parts[0] + '.' + version_parts[1],
 | 
			
		||||
  install : true)
 | 
			
		||||
 | 
			
		||||
# Extra c flags - adding -fvisibility=hidden matches the Makefile and
 | 
			
		||||
# shaves off about 10k on linux x64, likely similar on other platforms.
 | 
			
		||||
native_cc = meson.get_compiler('c', native: true)
 | 
			
		||||
cross_cc = meson.get_compiler('c', native: false)
 | 
			
		||||
if native_cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
  extra_native_cflags = ['-fvisibility=hidden']
 | 
			
		||||
if cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
  extra_cflags = ['-fvisibility=hidden']
 | 
			
		||||
else
 | 
			
		||||
  extra_native_cflags = []
 | 
			
		||||
  extra_cflags = []
 | 
			
		||||
endif
 | 
			
		||||
if cross_cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
  extra_cross_cflags = ['-fvisibility=hidden']
 | 
			
		||||
else
 | 
			
		||||
  extra_cross_cflags = []
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
janet_mainclient = executable('janet', janetc, mainclient_src,
 | 
			
		||||
  include_directories : incdir,
 | 
			
		||||
  dependencies : [m_dep, dl_dep, thread_dep],
 | 
			
		||||
  c_args : extra_native_cflags,
 | 
			
		||||
  dependencies : janet_dependencies,
 | 
			
		||||
  c_args : extra_cflags,
 | 
			
		||||
  install : true)
 | 
			
		||||
 | 
			
		||||
if meson.is_cross_build()
 | 
			
		||||
  native_cc = meson.get_compiler('c', native: true)
 | 
			
		||||
  if native_cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
    extra_native_cflags = ['-fvisibility=hidden']
 | 
			
		||||
  else
 | 
			
		||||
    extra_native_cflags = []
 | 
			
		||||
  endif
 | 
			
		||||
  janet_nativeclient = executable('janet-native', janetc, mainclient_src,
 | 
			
		||||
    include_directories : incdir,
 | 
			
		||||
    dependencies : [m_dep, dl_dep, thread_dep],
 | 
			
		||||
    c_args : extra_cross_cflags,
 | 
			
		||||
    dependencies : janet_dependencies,
 | 
			
		||||
    c_args : extra_native_cflags,
 | 
			
		||||
    native : true)
 | 
			
		||||
else
 | 
			
		||||
  janet_nativeclient = janet_mainclient
 | 
			
		||||
@@ -216,16 +227,34 @@ docs = custom_target('docs',
 | 
			
		||||
 | 
			
		||||
# Tests
 | 
			
		||||
test_files = [
 | 
			
		||||
  'test/suite0.janet',
 | 
			
		||||
  'test/suite1.janet',
 | 
			
		||||
  'test/suite2.janet',
 | 
			
		||||
  'test/suite3.janet',
 | 
			
		||||
  'test/suite4.janet',
 | 
			
		||||
  'test/suite5.janet',
 | 
			
		||||
  'test/suite6.janet',
 | 
			
		||||
  'test/suite7.janet',
 | 
			
		||||
  'test/suite8.janet',
 | 
			
		||||
  'test/suite9.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())
 | 
			
		||||
@@ -241,22 +270,22 @@ janet_dep = declare_dependency(include_directories : incdir,
 | 
			
		||||
# pkgconfig
 | 
			
		||||
pkg = import('pkgconfig')
 | 
			
		||||
pkg.generate(libjanet,
 | 
			
		||||
  subdirs: 'janet',
 | 
			
		||||
  description: 'Library for the Janet programming language.')
 | 
			
		||||
 | 
			
		||||
# Installation
 | 
			
		||||
install_man('janet.1')
 | 
			
		||||
install_headers(['src/include/janet.h', jconf], subdir: 'janet')
 | 
			
		||||
install_data(sources : ['tools/.keep'], install_dir : join_paths(get_option('libdir'), 'janet'))
 | 
			
		||||
if get_option('peg') and not get_option('reduced_os') and get_option('processes')
 | 
			
		||||
  install_man('jpm.1')
 | 
			
		||||
  patched_jpm = custom_target('patched-jpm',
 | 
			
		||||
    input : ['tools/patch-jpm.janet', 'jpm'],
 | 
			
		||||
    install : true,
 | 
			
		||||
    install_dir : get_option('bindir'),
 | 
			
		||||
    build_by_default : true,
 | 
			
		||||
    output : ['jpm'],
 | 
			
		||||
    command : [janet_nativeclient, '@INPUT@', '@OUTPUT@',
 | 
			
		||||
      '--binpath=' + join_paths(get_option('prefix'), get_option('bindir')),
 | 
			
		||||
      '--libpath=' + join_paths(get_option('prefix'), get_option('libdir'), 'janet'),
 | 
			
		||||
      '--headerpath=' + join_paths(get_option('prefix'), get_option('includedir'))])
 | 
			
		||||
patched_janet = custom_target('patched-janeth',
 | 
			
		||||
  input : ['tools/patch-header.janet', 'src/include/janet.h', jconf],
 | 
			
		||||
  install : true,
 | 
			
		||||
  install_dir : join_paths(get_option('includedir'), 'janet'),
 | 
			
		||||
  build_by_default : true,
 | 
			
		||||
  output : ['janet.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.h', install_dir: get_option('includedir'))
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,13 +8,19 @@ option('sourcemaps', type : 'boolean', value : true)
 | 
			
		||||
option('reduced_os', type : 'boolean', value : false)
 | 
			
		||||
option('assembler', type : 'boolean', value : true)
 | 
			
		||||
option('peg', type : 'boolean', value : true)
 | 
			
		||||
option('typed_array', type : 'boolean', value : true)
 | 
			
		||||
option('int_types', type : 'boolean', value : true)
 | 
			
		||||
option('prf', type : 'boolean', value : true)
 | 
			
		||||
option('prf', type : 'boolean', value : false)
 | 
			
		||||
option('net', 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('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)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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) 2020 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,7 +93,7 @@ int main(int argc, const char **argv) {
 | 
			
		||||
    fseek(boot_file, 0, SEEK_END);
 | 
			
		||||
    size_t boot_size = ftell(boot_file);
 | 
			
		||||
    fseek(boot_file, 0, SEEK_SET);
 | 
			
		||||
    unsigned char *boot_buffer = malloc(boot_size);
 | 
			
		||||
    unsigned char *boot_buffer = janet_malloc(boot_size);
 | 
			
		||||
    if (NULL == boot_buffer) {
 | 
			
		||||
        fprintf(stderr, "Failed to allocate boot buffer\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
@@ -105,7 +105,7 @@ int main(int argc, const char **argv) {
 | 
			
		||||
    fclose(boot_file);
 | 
			
		||||
 | 
			
		||||
    status = janet_dobytes(env, boot_buffer, (int32_t) boot_size, boot_filename, NULL);
 | 
			
		||||
    free(boot_buffer);
 | 
			
		||||
    janet_free(boot_buffer);
 | 
			
		||||
 | 
			
		||||
    /* Deinitialize vm */
 | 
			
		||||
    janet_deinit();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3704
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
							
						
						
									
										3704
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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) 2020 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) 2020 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 @@
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#include "tests.h"
 | 
			
		||||
 | 
			
		||||
@@ -44,6 +45,11 @@ int system_test() {
 | 
			
		||||
    assert(janet_equals(janet_wrap_integer(INT32_MIN), janet_wrap_integer(INT32_MIN)));
 | 
			
		||||
    assert(janet_equals(janet_wrap_number(1.4), janet_wrap_number(1.4)));
 | 
			
		||||
    assert(janet_equals(janet_wrap_number(3.14159265), janet_wrap_number(3.14159265)));
 | 
			
		||||
#ifdef NAN
 | 
			
		||||
    assert(janet_checktype(janet_wrap_number(NAN), JANET_NUMBER));
 | 
			
		||||
#else
 | 
			
		||||
    assert(janet_checktype(janet_wrap_number(0.0 / 0.0), JANET_NUMBER));
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    assert(NULL != &janet_wrap_nil);
 | 
			
		||||
 | 
			
		||||
@@ -64,6 +70,5 @@ int system_test() {
 | 
			
		||||
 | 
			
		||||
    assert(janet_equals(tuple1, tuple2));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -61,5 +61,11 @@ int table_test() {
 | 
			
		||||
    assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key1")), janet_wrap_integer(10)));
 | 
			
		||||
    assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key2")), janet_wrap_integer(100)));
 | 
			
		||||
 | 
			
		||||
    assert(t2->count == 4);
 | 
			
		||||
    assert(janet_equals(janet_table_remove(t2, janet_csymbolv("t2key1")), janet_wrap_integer(10)));
 | 
			
		||||
    assert(t2->count == 3);
 | 
			
		||||
    assert(janet_equals(janet_table_remove(t2, janet_csymbolv("t2key2")), janet_wrap_integer(100)));
 | 
			
		||||
    assert(t2->count == 2);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,36 +1,13 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* This is an example janetconf.h file. This will be usually generated
 | 
			
		||||
 * by the build system. */
 | 
			
		||||
/* This will be generated by the build system if this file is not used */
 | 
			
		||||
 | 
			
		||||
#ifndef JANETCONF_H
 | 
			
		||||
#define JANETCONF_H
 | 
			
		||||
 | 
			
		||||
#define JANET_VERSION_MAJOR 1
 | 
			
		||||
#define JANET_VERSION_MINOR 10
 | 
			
		||||
#define JANET_VERSION_PATCH 0
 | 
			
		||||
#define JANET_VERSION_MINOR 29
 | 
			
		||||
#define JANET_VERSION_PATCH 1
 | 
			
		||||
#define JANET_VERSION_EXTRA ""
 | 
			
		||||
#define JANET_VERSION "1.10.0"
 | 
			
		||||
#define JANET_VERSION "1.29.1"
 | 
			
		||||
 | 
			
		||||
/* #define JANET_BUILD "local" */
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +18,8 @@
 | 
			
		||||
/* #define JANET_API __attribute__((visibility ("default"))) */
 | 
			
		||||
 | 
			
		||||
/* These settings should be specified before amalgamation is
 | 
			
		||||
 * built. */
 | 
			
		||||
 * built. Any build with these set should be considered non-standard, and
 | 
			
		||||
 * certain Janet libraries should be expected not to work. */
 | 
			
		||||
/* #define JANET_NO_DOCSTRINGS */
 | 
			
		||||
/* #define JANET_NO_SOURCEMAPS */
 | 
			
		||||
/* #define JANET_REDUCED_OS */
 | 
			
		||||
@@ -49,15 +27,19 @@
 | 
			
		||||
/* #define JANET_NO_ASSEMBLER */
 | 
			
		||||
/* #define JANET_NO_PEG */
 | 
			
		||||
/* #define JANET_NO_NET */
 | 
			
		||||
/* #define JANET_NO_TYPED_ARRAY */
 | 
			
		||||
/* #define JANET_NO_INT_TYPES */
 | 
			
		||||
 | 
			
		||||
/* Other settings */
 | 
			
		||||
/* #define JANET_NO_PRF */
 | 
			
		||||
/* #define JANET_NO_UTC_MKTIME */
 | 
			
		||||
/* #define JANET_NO_EV */
 | 
			
		||||
/* #define JANET_NO_REALPATH */
 | 
			
		||||
/* #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 */
 | 
			
		||||
/* #define JANET_PRF */
 | 
			
		||||
/* #define JANET_NO_UTC_MKTIME */
 | 
			
		||||
/* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */
 | 
			
		||||
/* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */
 | 
			
		||||
/* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */
 | 
			
		||||
@@ -67,5 +49,18 @@
 | 
			
		||||
/* #define JANET_STACK_MAX 16384 */
 | 
			
		||||
/* #define JANET_OS_NAME my-custom-os */
 | 
			
		||||
/* #define JANET_ARCH_NAME pdp-8 */
 | 
			
		||||
/* #define JANET_EV_NO_EPOLL */
 | 
			
		||||
/* #define JANET_EV_NO_KQUEUE */
 | 
			
		||||
/* #define JANET_NO_INTERPRETER_INTERRUPT */
 | 
			
		||||
 | 
			
		||||
/* Custom vm allocator support */
 | 
			
		||||
/* #include <mimalloc.h> */
 | 
			
		||||
/* #define janet_malloc(X) mi_malloc((X)) */
 | 
			
		||||
/* #define janet_realloc(X, Y) mi_realloc((X), (Y)) */
 | 
			
		||||
/* #define janet_calloc(X, Y) mi_calloc((X), (Y)) */
 | 
			
		||||
/* #define janet_free(X) mi_free((X)) */
 | 
			
		||||
 | 
			
		||||
/* Main client settings, does not affect library code */
 | 
			
		||||
/* #define JANET_SIMPLE_GETLINE */
 | 
			
		||||
 | 
			
		||||
#endif /* end of include guard: JANETCONF_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,7 +23,17 @@
 | 
			
		||||
#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>
 | 
			
		||||
#else
 | 
			
		||||
#include <stdatomic.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Create new userdata */
 | 
			
		||||
@@ -43,3 +53,170 @@ void *janet_abstract_end(void *x) {
 | 
			
		||||
void *janet_abstract(const JanetAbstractType *atype, size_t size) {
 | 
			
		||||
    return janet_abstract_end(janet_abstract_begin(atype, size));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Threaded abstracts
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void *janet_abstract_begin_threaded(const JanetAbstractType *atype, size_t size) {
 | 
			
		||||
    JanetAbstractHead *header = janet_malloc(sizeof(JanetAbstractHead) + size);
 | 
			
		||||
    if (NULL == header) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm.next_collection += size + sizeof(JanetAbstractHead);
 | 
			
		||||
    header->gc.flags = JANET_MEMORY_THREADED_ABSTRACT;
 | 
			
		||||
    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);
 | 
			
		||||
    janet_table_put(&janet_vm.threaded_abstracts, janet_wrap_abstract(abstract), janet_wrap_false());
 | 
			
		||||
    return abstract;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_abstract_end_threaded(void *x) {
 | 
			
		||||
    janet_gc_settype((void *)(janet_abstract_head(x)), JANET_MEMORY_THREADED_ABSTRACT);
 | 
			
		||||
    return x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size) {
 | 
			
		||||
    return janet_abstract_end_threaded(janet_abstract_begin_threaded(atype, size));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Refcounting primitives and sync primitives */
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
 | 
			
		||||
size_t janet_os_mutex_size(void) {
 | 
			
		||||
    return sizeof(CRITICAL_SECTION);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t janet_os_rwlock_size(void) {
 | 
			
		||||
    return sizeof(void *);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_incref(JanetAbstractHead *ab) {
 | 
			
		||||
    return InterlockedIncrement((LONG volatile *) &ab->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_decref(JanetAbstractHead *ab) {
 | 
			
		||||
    return InterlockedDecrement((LONG volatile *) &ab->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
 | 
			
		||||
    InitializeCriticalSection((CRITICAL_SECTION *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_deinit(JanetOSMutex *mutex) {
 | 
			
		||||
    DeleteCriticalSection((CRITICAL_SECTION *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_lock(JanetOSMutex *mutex) {
 | 
			
		||||
    EnterCriticalSection((CRITICAL_SECTION *) 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
 | 
			
		||||
 | 
			
		||||
size_t janet_os_mutex_size(void) {
 | 
			
		||||
    return sizeof(pthread_mutex_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t janet_os_rwlock_size(void) {
 | 
			
		||||
    return sizeof(pthread_rwlock_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_incref(JanetAbstractHead *ab) {
 | 
			
		||||
    return __atomic_add_fetch(&ab->gc.data.refcount, 1, __ATOMIC_RELAXED);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_decref(JanetAbstractHead *ab) {
 | 
			
		||||
    return __atomic_add_fetch(&ab->gc.data.refcount, -1, __ATOMIC_RELAXED);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
 | 
			
		||||
    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((pthread_mutex_t *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_lock(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_lock((pthread_mutex_t *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_unlock(JanetOSMutex *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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t janet_abstract_decref(void *abst) {
 | 
			
		||||
    return janet_decref(janet_abstract_head(abst));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										208
									
								
								src/core/array.c
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								src/core/array.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,8 +35,8 @@ JanetArray *janet_array(int32_t capacity) {
 | 
			
		||||
    JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
 | 
			
		||||
    Janet *data = NULL;
 | 
			
		||||
    if (capacity > 0) {
 | 
			
		||||
        janet_vm_next_collection += capacity * sizeof(Janet);
 | 
			
		||||
        data = (Janet *) malloc(sizeof(Janet) * (size_t) capacity);
 | 
			
		||||
        janet_vm.next_collection += capacity * sizeof(Janet);
 | 
			
		||||
        data = (Janet *) janet_malloc(sizeof(Janet) * (size_t) capacity);
 | 
			
		||||
        if (NULL == data) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -52,7 +52,7 @@ JanetArray *janet_array_n(const Janet *elements, int32_t n) {
 | 
			
		||||
    JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
 | 
			
		||||
    array->capacity = n;
 | 
			
		||||
    array->count = n;
 | 
			
		||||
    array->data = malloc(sizeof(Janet) * (size_t) n);
 | 
			
		||||
    array->data = janet_malloc(sizeof(Janet) * (size_t) n);
 | 
			
		||||
    if (!array->data) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -68,11 +68,11 @@ void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth) {
 | 
			
		||||
    int64_t new_capacity = ((int64_t) capacity) * growth;
 | 
			
		||||
    if (new_capacity > INT32_MAX) new_capacity = INT32_MAX;
 | 
			
		||||
    capacity = (int32_t) new_capacity;
 | 
			
		||||
    newData = realloc(old, capacity * sizeof(Janet));
 | 
			
		||||
    newData = janet_realloc(old, capacity * sizeof(Janet));
 | 
			
		||||
    if (NULL == newData) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_next_collection += (capacity - array->capacity) * sizeof(Janet);
 | 
			
		||||
    janet_vm.next_collection += (capacity - array->capacity) * sizeof(Janet);
 | 
			
		||||
    array->data = newData;
 | 
			
		||||
    array->capacity = capacity;
 | 
			
		||||
}
 | 
			
		||||
@@ -122,16 +122,21 @@ Janet janet_array_peek(JanetArray *array) {
 | 
			
		||||
 | 
			
		||||
/* C Functions */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_new(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getinteger(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(cap);
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_new_filled(int32_t argc, Janet *argv) {
 | 
			
		||||
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++) {
 | 
			
		||||
@@ -141,7 +146,10 @@ static Janet cfun_array_new_filled(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_fill,
 | 
			
		||||
              "(array/fill arr &opt value)",
 | 
			
		||||
              "Replace all elements of an array with `value` (defaulting to nil) without changing the length of the array. "
 | 
			
		||||
              "Returns the modified array.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    Janet x = (argc == 2) ? argv[1] : janet_wrap_nil();
 | 
			
		||||
@@ -151,19 +159,26 @@ static Janet cfun_array_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_pop(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_pop,
 | 
			
		||||
              "(array/pop arr)",
 | 
			
		||||
              "Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
 | 
			
		||||
              "the input array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    return janet_array_pop(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_peek(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_peek,
 | 
			
		||||
              "(array/peek arr)",
 | 
			
		||||
              "Returns the last element of the array. Does not modify the array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    return janet_array_peek(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_push(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    if (INT32_MAX - argc + 1 <= array->count) {
 | 
			
		||||
@@ -176,7 +191,12 @@ static Janet cfun_array_push(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
 | 
			
		||||
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. "
 | 
			
		||||
              "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);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    int32_t newcount = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -186,7 +206,13 @@ static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_slice,
 | 
			
		||||
              "(array/slice arrtup &opt start end)",
 | 
			
		||||
              "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 "
 | 
			
		||||
              "negative slice range. Returns a new array.") {
 | 
			
		||||
    JanetView view = janet_getindexed(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    JanetArray *array = janet_array(range.end - range.start);
 | 
			
		||||
@@ -196,7 +222,12 @@ static Janet cfun_array_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_concat,
 | 
			
		||||
              "(array/concat arr & parts)",
 | 
			
		||||
              "Concatenates a variable number of arrays (and tuples) into the first argument, "
 | 
			
		||||
              "which must be an array. If any of the parts are arrays or tuples, their elements will "
 | 
			
		||||
              "be inserted into the array. Otherwise, each part in `parts` will be appended to `arr` in order. "
 | 
			
		||||
              "Return the modified array `arr`.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
@@ -210,6 +241,11 @@ static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
                int32_t j, len = 0;
 | 
			
		||||
                const Janet *vals = NULL;
 | 
			
		||||
                janet_indexed_view(argv[i], &vals, &len);
 | 
			
		||||
                if (array->data == vals) {
 | 
			
		||||
                    int32_t newcount = array->count + len;
 | 
			
		||||
                    janet_array_ensure(array, newcount, 2);
 | 
			
		||||
                    janet_indexed_view(argv[i], &vals, &len);
 | 
			
		||||
                }
 | 
			
		||||
                for (j = 0; j < len; j++)
 | 
			
		||||
                    janet_array_push(array, vals[j]);
 | 
			
		||||
            }
 | 
			
		||||
@@ -219,7 +255,12 @@ static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    size_t chunksize, restsize;
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
@@ -245,7 +286,12 @@ static Janet cfun_array_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_remove(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_remove,
 | 
			
		||||
              "(array/remove arr at &opt n)",
 | 
			
		||||
              "Remove up to `n` elements starting at index `at` in array `arr`. `at` can index from "
 | 
			
		||||
              "the end of the array with a negative index, and `n` must be a non-negative integer. "
 | 
			
		||||
              "By default, `n` is 1. "
 | 
			
		||||
              "Returns the array.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    int32_t at = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -270,85 +316,55 @@ static Janet cfun_array_remove(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg array_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "array/new", cfun_array_new,
 | 
			
		||||
        JDOC("(array/new capacity)\n\n"
 | 
			
		||||
             "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/new-filled", cfun_array_new_filled,
 | 
			
		||||
        JDOC("(array/new-filled count &opt value)\n\n"
 | 
			
		||||
             "Creates a new array of count elements, all set to value, which defaults to nil. Returns the new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/fill", cfun_array_fill,
 | 
			
		||||
        JDOC("(array/fill arr &opt value)\n\n"
 | 
			
		||||
             "Replace all elements of an array with value (defaulting to nil) without changing the length of the array. "
 | 
			
		||||
             "Returns the modified array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/pop", cfun_array_pop,
 | 
			
		||||
        JDOC("(array/pop arr)\n\n"
 | 
			
		||||
             "Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
 | 
			
		||||
             "the input array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/peek", cfun_array_peek,
 | 
			
		||||
        JDOC("(array/peek arr)\n\n"
 | 
			
		||||
             "Returns the last element of the array. Does not modify the array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/push", cfun_array_push,
 | 
			
		||||
        JDOC("(array/push arr x)\n\n"
 | 
			
		||||
             "Insert an element in the end of an array. Modifies the input array and returns it.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/ensure", cfun_array_ensure,
 | 
			
		||||
        JDOC("(array/ensure arr capacity growth)\n\n"
 | 
			
		||||
             "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. "
 | 
			
		||||
             "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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/slice", cfun_array_slice,
 | 
			
		||||
        JDOC("(array/slice arrtup &opt start end)\n\n"
 | 
			
		||||
             "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 "
 | 
			
		||||
             "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 "
 | 
			
		||||
             "negative slice range. Returns a new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/concat", cfun_array_concat,
 | 
			
		||||
        JDOC("(array/concat arr & parts)\n\n"
 | 
			
		||||
             "Concatenates a variadic number of arrays (and tuples) into the first argument "
 | 
			
		||||
             "which must an array. If any of the parts are arrays or tuples, their elements will "
 | 
			
		||||
             "be inserted into the array. Otherwise, each part in parts will be appended to arr in order. "
 | 
			
		||||
             "Return the modified array arr.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/insert", cfun_array_insert,
 | 
			
		||||
        JDOC("(array/insert arr at & xs)\n\n"
 | 
			
		||||
             "Insert all of xs into array arr at index at. at should be an integer "
 | 
			
		||||
             "0 and the length of the array. A negative value for at will index from "
 | 
			
		||||
             "the end of the array, such that inserting at -1 appends to the array. "
 | 
			
		||||
             "Returns the array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/remove", cfun_array_remove,
 | 
			
		||||
        JDOC("(array/remove arr at &opt n)\n\n"
 | 
			
		||||
             "Remove up to n elements starting at index at in array arr. at can index from "
 | 
			
		||||
             "the end of the array with a negative index, and n must be a non-negative integer. "
 | 
			
		||||
             "By default, n is 1. "
 | 
			
		||||
             "Returns the array.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
JANET_CORE_FN(cfun_array_trim,
 | 
			
		||||
              "(array/trim arr)",
 | 
			
		||||
              "Set the backing capacity of an array to its current length. Returns the modified array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    if (array->count) {
 | 
			
		||||
        if (array->count < array->capacity) {
 | 
			
		||||
            Janet *newData = janet_realloc(array->data, array->count * sizeof(Janet));
 | 
			
		||||
            if (NULL == newData) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
            array->data = newData;
 | 
			
		||||
            array->capacity = array->count;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        array->capacity = 0;
 | 
			
		||||
        janet_free(array->data);
 | 
			
		||||
        array->data = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_array_clear,
 | 
			
		||||
              "(array/clear arr)",
 | 
			
		||||
              "Empties an array, setting it's count to 0 but does not free the backing capacity. "
 | 
			
		||||
              "Returns the modified array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    array->count = 0;
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load the array module */
 | 
			
		||||
void janet_lib_array(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, array_cfuns);
 | 
			
		||||
    JanetRegExt array_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("array/new", cfun_array_new),
 | 
			
		||||
        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),
 | 
			
		||||
        JANET_CORE_REG("array/peek", cfun_array_peek),
 | 
			
		||||
        JANET_CORE_REG("array/push", cfun_array_push),
 | 
			
		||||
        JANET_CORE_REG("array/ensure", cfun_array_ensure),
 | 
			
		||||
        JANET_CORE_REG("array/slice", cfun_array_slice),
 | 
			
		||||
        JANET_CORE_REG("array/concat", cfun_array_concat),
 | 
			
		||||
        JANET_CORE_REG("array/insert", cfun_array_insert),
 | 
			
		||||
        JANET_CORE_REG("array/remove", cfun_array_remove),
 | 
			
		||||
        JANET_CORE_REG("array/trim", cfun_array_trim),
 | 
			
		||||
        JANET_CORE_REG("array/clear", cfun_array_clear),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, array_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										341
									
								
								src/core/asm.c
									
									
									
									
									
								
							
							
						
						
									
										341
									
								
								src/core/asm.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -73,7 +73,9 @@ static const JanetInstructionDef janet_ops[] = {
 | 
			
		||||
    {"call", JOP_CALL},
 | 
			
		||||
    {"clo", JOP_CLOSURE},
 | 
			
		||||
    {"cmp", JOP_COMPARE},
 | 
			
		||||
    {"cncl", JOP_CANCEL},
 | 
			
		||||
    {"div", JOP_DIVIDE},
 | 
			
		||||
    {"divf", JOP_DIVIDE_FLOOR},
 | 
			
		||||
    {"divim", JOP_DIVIDE_IMMEDIATE},
 | 
			
		||||
    {"eq", JOP_EQUALS},
 | 
			
		||||
    {"eqim", JOP_EQUALS_IMMEDIATE},
 | 
			
		||||
@@ -112,6 +114,8 @@ static const JanetInstructionDef janet_ops[] = {
 | 
			
		||||
    {"movn", JOP_MOVE_NEAR},
 | 
			
		||||
    {"mul", JOP_MULTIPLY},
 | 
			
		||||
    {"mulim", JOP_MULTIPLY_IMMEDIATE},
 | 
			
		||||
    {"neq", JOP_NOT_EQUALS},
 | 
			
		||||
    {"neqim", JOP_NOT_EQUALS_IMMEDIATE},
 | 
			
		||||
    {"next", JOP_NEXT},
 | 
			
		||||
    {"noop", JOP_NOOP},
 | 
			
		||||
    {"prop", JOP_PROPAGATE},
 | 
			
		||||
@@ -184,7 +188,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)
 | 
			
		||||
@@ -221,7 +229,7 @@ static int32_t janet_asm_addenv(JanetAssembler *a, Janet envname) {
 | 
			
		||||
    janet_table_put(&a->envs, envname, janet_wrap_number(envindex));
 | 
			
		||||
    if (envindex >= a->environments_capacity) {
 | 
			
		||||
        int32_t newcap = 2 * envindex;
 | 
			
		||||
        def->environments = realloc(def->environments, newcap * sizeof(int32_t));
 | 
			
		||||
        def->environments = janet_realloc(def->environments, newcap * sizeof(int32_t));
 | 
			
		||||
        if (NULL == def->environments) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -513,6 +521,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;
 | 
			
		||||
@@ -550,6 +559,10 @@ 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;
 | 
			
		||||
 | 
			
		||||
    /* 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);
 | 
			
		||||
@@ -579,7 +592,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("constants"));
 | 
			
		||||
    if (janet_indexed_view(x, &arr, &count)) {
 | 
			
		||||
        def->constants_length = count;
 | 
			
		||||
        def->constants = malloc(sizeof(Janet) * (size_t) count);
 | 
			
		||||
        def->constants = janet_malloc(sizeof(Janet) * (size_t) count);
 | 
			
		||||
        if (NULL == def->constants) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -594,6 +607,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++) {
 | 
			
		||||
@@ -611,7 +627,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
            newlen = def->defs_length + 1;
 | 
			
		||||
            if (a.defs_capacity < newlen) {
 | 
			
		||||
                int32_t newcap = newlen;
 | 
			
		||||
                def->defs = realloc(def->defs, newcap * sizeof(JanetFuncDef *));
 | 
			
		||||
                def->defs = janet_realloc(def->defs, newcap * sizeof(JanetFuncDef *));
 | 
			
		||||
                if (NULL == def->defs) {
 | 
			
		||||
                    JANET_OUT_OF_MEMORY;
 | 
			
		||||
                }
 | 
			
		||||
@@ -640,7 +656,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
        }
 | 
			
		||||
        /* Allocate bytecode array */
 | 
			
		||||
        def->bytecode_length = blength;
 | 
			
		||||
        def->bytecode = malloc(sizeof(uint32_t) * (size_t) blength);
 | 
			
		||||
        def->bytecode = janet_malloc(sizeof(uint32_t) * (size_t) blength);
 | 
			
		||||
        if (NULL == def->bytecode) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -682,7 +698,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("sourcemap"));
 | 
			
		||||
    if (janet_indexed_view(x, &arr, &count)) {
 | 
			
		||||
        janet_asm_assert(&a, count == def->bytecode_length, "sourcemap must have the same length as the bytecode");
 | 
			
		||||
        def->sourcemap = malloc(sizeof(JanetSourceMapping) * (size_t) count);
 | 
			
		||||
        def->sourcemap = janet_malloc(sizeof(JanetSourceMapping) * (size_t) count);
 | 
			
		||||
        if (NULL == def->sourcemap) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -706,10 +722,63 @@ 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 =
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -718,6 +787,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
        janet_asm_error(&a, "invalid assembly");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Add final flags */
 | 
			
		||||
    janet_def_addflags(def);
 | 
			
		||||
 | 
			
		||||
    /* Finish everything and return funcdef */
 | 
			
		||||
    janet_asm_deinit(&a);
 | 
			
		||||
    result.error = NULL;
 | 
			
		||||
@@ -835,120 +907,201 @@ Janet janet_asm_decode_instruction(uint32_t instr) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_disasm(JanetFuncDef *def) {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    JanetArray *bcode = janet_array(def->bytecode_length);
 | 
			
		||||
    JanetArray *constants;
 | 
			
		||||
    JanetTable *ret = janet_table(10);
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("arity"), janet_wrap_integer(def->arity));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("min-arity"), janet_wrap_integer(def->min_arity));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("max-arity"), janet_wrap_integer(def->max_arity));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("bytecode"), janet_wrap_array(bcode));
 | 
			
		||||
    if (NULL != def->source) {
 | 
			
		||||
        janet_table_put(ret, janet_ckeywordv("source"), janet_wrap_string(def->source));
 | 
			
		||||
    }
 | 
			
		||||
    if (def->flags & JANET_FUNCDEF_FLAG_VARARG) {
 | 
			
		||||
        janet_table_put(ret, janet_ckeywordv("vararg"), janet_wrap_true());
 | 
			
		||||
    }
 | 
			
		||||
    if (NULL != def->name) {
 | 
			
		||||
        janet_table_put(ret, janet_ckeywordv("name"), janet_wrap_string(def->name));
 | 
			
		||||
    }
 | 
			
		||||
/*
 | 
			
		||||
 * Disasm sections
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
    /* Add constants */
 | 
			
		||||
    if (def->constants_length > 0) {
 | 
			
		||||
        constants = janet_array(def->constants_length);
 | 
			
		||||
        janet_table_put(ret, janet_ckeywordv("constants"), janet_wrap_array(constants));
 | 
			
		||||
        for (i = 0; i < def->constants_length; i++) {
 | 
			
		||||
            constants->data[i] = def->constants[i];
 | 
			
		||||
static Janet janet_disasm_arity(JanetFuncDef *def) {
 | 
			
		||||
    return janet_wrap_integer(def->arity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_min_arity(JanetFuncDef *def) {
 | 
			
		||||
    return janet_wrap_integer(def->min_arity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_max_arity(JanetFuncDef *def) {
 | 
			
		||||
    return janet_wrap_integer(def->max_arity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
        }
 | 
			
		||||
        constants->count = def->constants_length;
 | 
			
		||||
        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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /* Add bytecode */
 | 
			
		||||
    for (i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
static Janet janet_disasm_bytecode(JanetFuncDef *def) {
 | 
			
		||||
    JanetArray *bcode = janet_array(def->bytecode_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
        bcode->data[i] = janet_asm_decode_instruction(def->bytecode[i]);
 | 
			
		||||
    }
 | 
			
		||||
    bcode->count = def->bytecode_length;
 | 
			
		||||
    return janet_wrap_array(bcode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /* Add source map */
 | 
			
		||||
    if (NULL != def->sourcemap) {
 | 
			
		||||
        JanetArray *sourcemap = janet_array(def->bytecode_length);
 | 
			
		||||
        for (i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
            Janet *t = janet_tuple_begin(2);
 | 
			
		||||
            JanetSourceMapping mapping = def->sourcemap[i];
 | 
			
		||||
            t[0] = janet_wrap_integer(mapping.line);
 | 
			
		||||
            t[1] = janet_wrap_integer(mapping.column);
 | 
			
		||||
            sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t));
 | 
			
		||||
        }
 | 
			
		||||
        sourcemap->count = def->bytecode_length;
 | 
			
		||||
        janet_table_put(ret, janet_ckeywordv("sourcemap"), janet_wrap_array(sourcemap));
 | 
			
		||||
static Janet janet_disasm_source(JanetFuncDef *def) {
 | 
			
		||||
    if (def->source != NULL) return janet_wrap_string(def->source);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_name(JanetFuncDef *def) {
 | 
			
		||||
    if (def->name != NULL) return janet_wrap_string(def->name);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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++) {
 | 
			
		||||
        constants->data[i] = def->constants[i];
 | 
			
		||||
    }
 | 
			
		||||
    constants->count = def->constants_length;
 | 
			
		||||
    return janet_wrap_array(constants);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /* Add environments */
 | 
			
		||||
    if (NULL != def->environments) {
 | 
			
		||||
        JanetArray *envs = janet_array(def->environments_length);
 | 
			
		||||
        for (i = 0; i < def->environments_length; i++) {
 | 
			
		||||
            envs->data[i] = janet_wrap_integer(def->environments[i]);
 | 
			
		||||
        }
 | 
			
		||||
        envs->count = def->environments_length;
 | 
			
		||||
        janet_table_put(ret, janet_ckeywordv("environments"), janet_wrap_array(envs));
 | 
			
		||||
static Janet janet_disasm_sourcemap(JanetFuncDef *def) {
 | 
			
		||||
    if (NULL == def->sourcemap) return janet_wrap_nil();
 | 
			
		||||
    JanetArray *sourcemap = janet_array(def->bytecode_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
        Janet *t = janet_tuple_begin(2);
 | 
			
		||||
        JanetSourceMapping mapping = def->sourcemap[i];
 | 
			
		||||
        t[0] = janet_wrap_integer(mapping.line);
 | 
			
		||||
        t[1] = janet_wrap_integer(mapping.column);
 | 
			
		||||
        sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t));
 | 
			
		||||
    }
 | 
			
		||||
    sourcemap->count = def->bytecode_length;
 | 
			
		||||
    return janet_wrap_array(sourcemap);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /* Add closures */
 | 
			
		||||
    /* Funcdefs cannot be recursive */
 | 
			
		||||
    if (NULL != def->defs) {
 | 
			
		||||
        JanetArray *defs = janet_array(def->defs_length);
 | 
			
		||||
        for (i = 0; i < def->defs_length; i++) {
 | 
			
		||||
            defs->data[i] = janet_disasm(def->defs[i]);
 | 
			
		||||
        }
 | 
			
		||||
        defs->count = def->defs_length;
 | 
			
		||||
        janet_table_put(ret, janet_ckeywordv("defs"), janet_wrap_array(defs));
 | 
			
		||||
static Janet janet_disasm_environments(JanetFuncDef *def) {
 | 
			
		||||
    JanetArray *envs = janet_array(def->environments_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->environments_length; i++) {
 | 
			
		||||
        envs->data[i] = janet_wrap_integer(def->environments[i]);
 | 
			
		||||
    }
 | 
			
		||||
    envs->count = def->environments_length;
 | 
			
		||||
    return janet_wrap_array(envs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    /* Add slotcount */
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("slotcount"), janet_wrap_integer(def->slotcount));
 | 
			
		||||
static Janet janet_disasm_defs(JanetFuncDef *def) {
 | 
			
		||||
    JanetArray *defs = janet_array(def->defs_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->defs_length; i++) {
 | 
			
		||||
        defs->data[i] = janet_disasm(def->defs[i]);
 | 
			
		||||
    }
 | 
			
		||||
    defs->count = def->defs_length;
 | 
			
		||||
    return janet_wrap_array(defs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_disasm(JanetFuncDef *def) {
 | 
			
		||||
    JanetTable *ret = janet_table(10);
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("arity"), janet_disasm_arity(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("min-arity"), janet_disasm_min_arity(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("max-arity"), janet_disasm_max_arity(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));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("defs"), janet_disasm_defs(def));
 | 
			
		||||
    return janet_wrap_struct(janet_table_to_struct(ret));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* C Function for assembly */
 | 
			
		||||
static Janet cfun_asm(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 1);
 | 
			
		||||
JANET_CORE_FN(cfun_asm,
 | 
			
		||||
              "(asm assembly)",
 | 
			
		||||
              "Returns a new function that is the compiled result of the assembly.\n"
 | 
			
		||||
              "The syntax for the assembly can be found on the Janet website, and should correspond\n"
 | 
			
		||||
              "to the return value of disasm. Will throw an\n"
 | 
			
		||||
              "error on invalid assembly.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_disasm(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 1);
 | 
			
		||||
JANET_CORE_FN(cfun_disasm,
 | 
			
		||||
              "(disasm func &opt field)",
 | 
			
		||||
              "Returns assembly that could be used to compile the given function. "
 | 
			
		||||
              "func must be a function, not a c function. Will throw on error on a badly "
 | 
			
		||||
              "typed argument. If given a field name, will only return that part of the function assembly. "
 | 
			
		||||
              "Possible fields are:\n\n"
 | 
			
		||||
              "* :arity - number of required and optional arguments.\n"
 | 
			
		||||
              "* :min-arity - minimum number of arguments function can be called with.\n"
 | 
			
		||||
              "* :max-arity - maximum number of arguments function can be called with.\n"
 | 
			
		||||
              "* :vararg - true if function can take a variable number of arguments.\n"
 | 
			
		||||
              "* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n"
 | 
			
		||||
              "* :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"
 | 
			
		||||
              "* :defs - other function definitions that this function may instantiate.\n") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetFunction *f = janet_getfunction(argv, 0);
 | 
			
		||||
    return janet_disasm(f->def);
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
        JanetKeyword kw = janet_getkeyword(argv, 1);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "arity")) return janet_disasm_arity(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "min-arity")) return janet_disasm_min_arity(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "max-arity")) return janet_disasm_max_arity(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "bytecode")) return janet_disasm_bytecode(f->def);
 | 
			
		||||
        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);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "environments")) return janet_disasm_environments(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "defs")) return janet_disasm_defs(f->def);
 | 
			
		||||
        janet_panicf("unknown disasm key %v", argv[1]);
 | 
			
		||||
    } else {
 | 
			
		||||
        return janet_disasm(f->def);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg asm_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "asm", cfun_asm,
 | 
			
		||||
        JDOC("(asm assembly)\n\n"
 | 
			
		||||
             "Returns a new function that is the compiled result of the assembly.\n"
 | 
			
		||||
             "The syntax for the assembly can be found on the Janet website. Will throw an\n"
 | 
			
		||||
             "error on invalid assembly.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "disasm", cfun_disasm,
 | 
			
		||||
        JDOC("(disasm func)\n\n"
 | 
			
		||||
             "Returns assembly that could be used be compile the given function.\n"
 | 
			
		||||
             "func must be a function, not a c function. Will throw on error on a badly\n"
 | 
			
		||||
             "typed argument.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Load the library */
 | 
			
		||||
void janet_lib_asm(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, asm_cfuns);
 | 
			
		||||
    JanetRegExt asm_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("asm", cfun_asm),
 | 
			
		||||
        JANET_CORE_REG("disasm", cfun_disasm),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, asm_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,15 +28,21 @@
 | 
			
		||||
#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 > 0) {
 | 
			
		||||
        janet_gcpressure(capacity);
 | 
			
		||||
        data = malloc(sizeof(uint8_t) * (size_t) capacity);
 | 
			
		||||
        if (NULL == data) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
    if (capacity < 4) capacity = 4;
 | 
			
		||||
    janet_gcpressure(capacity);
 | 
			
		||||
    data = janet_malloc(sizeof(uint8_t) * (size_t) capacity);
 | 
			
		||||
    if (NULL == data) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    buffer->count = 0;
 | 
			
		||||
    buffer->capacity = capacity;
 | 
			
		||||
@@ -44,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) {
 | 
			
		||||
    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 */
 | 
			
		||||
@@ -60,10 +88,11 @@ 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);
 | 
			
		||||
    new_data = realloc(old, (size_t) capacity * sizeof(uint8_t));
 | 
			
		||||
    new_data = janet_realloc(old, (size_t) capacity * sizeof(uint8_t));
 | 
			
		||||
    if (NULL == new_data) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -92,8 +121,9 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
 | 
			
		||||
    }
 | 
			
		||||
    int32_t new_size = buffer->count + n;
 | 
			
		||||
    if (new_size > buffer->capacity) {
 | 
			
		||||
        int32_t new_capacity = new_size * 2;
 | 
			
		||||
        uint8_t *new_data = realloc(buffer->data, new_capacity * sizeof(uint8_t));
 | 
			
		||||
        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);
 | 
			
		||||
        if (NULL == new_data) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
@@ -163,28 +193,38 @@ void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x) {
 | 
			
		||||
 | 
			
		||||
/* C functions */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_new,
 | 
			
		||||
              "(buffer/new capacity)",
 | 
			
		||||
              "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);
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(cap);
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_new_filled(int32_t argc, Janet *argv) {
 | 
			
		||||
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. "
 | 
			
		||||
              "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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
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. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int32_t byte = 0;
 | 
			
		||||
@@ -197,7 +237,29 @@ static Janet cfun_buffer_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_trim,
 | 
			
		||||
              "(buffer/trim buffer)",
 | 
			
		||||
              "Set the backing capacity of the buffer to the current length of the buffer. Returns the "
 | 
			
		||||
              "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);
 | 
			
		||||
        if (NULL == newData) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        buffer->data = newData;
 | 
			
		||||
        buffer->capacity = newcap;
 | 
			
		||||
    }
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_u8,
 | 
			
		||||
              "(buffer/push-byte buffer & xs)",
 | 
			
		||||
              "Append bytes to a buffer. Will expand the buffer as necessary. "
 | 
			
		||||
              "Returns the modified buffer. Will throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -207,7 +269,11 @@ static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_word(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_word,
 | 
			
		||||
              "(buffer/push-word buffer & xs)",
 | 
			
		||||
              "Append machine words to a buffer. The 4 bytes of the integer are appended "
 | 
			
		||||
              "in twos complement, little endian order, unsigned for all x. Returns the modified buffer. Will "
 | 
			
		||||
              "throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -221,7 +287,12 @@ static Janet cfun_buffer_word(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_chars(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_chars,
 | 
			
		||||
              "(buffer/push-string buffer & xs)",
 | 
			
		||||
              "Push byte sequences onto the end of a buffer. "
 | 
			
		||||
              "Will accept any of strings, keywords, symbols, and buffers. "
 | 
			
		||||
              "Returns the modified buffer. "
 | 
			
		||||
              "Will throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -236,14 +307,66 @@ static Janet cfun_buffer_chars(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_clear(int32_t argc, Janet *argv) {
 | 
			
		||||
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 {
 | 
			
		||||
            JanetByteView view = janet_getbytes(argv, i);
 | 
			
		||||
            if (view.bytes == buffer->data) {
 | 
			
		||||
                janet_buffer_ensure(buffer, buffer->count + view.len, 2);
 | 
			
		||||
                view.bytes = buffer->data;
 | 
			
		||||
            }
 | 
			
		||||
            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)",
 | 
			
		||||
              "Sets the size of a buffer to 0 and empties it. The buffer retains "
 | 
			
		||||
              "its memory so it can be efficiently refilled. Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    buffer->count = 0;
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_popn(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_popn,
 | 
			
		||||
              "(buffer/popn buffer n)",
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -256,7 +379,12 @@ static Janet cfun_buffer_popn(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
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, "
 | 
			
		||||
              "[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. "
 | 
			
		||||
              "Returns a new buffer.") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(range.end - range.start);
 | 
			
		||||
@@ -280,7 +408,9 @@ static void bitloc(int32_t argc, Janet *argv, JanetBuffer **b, int32_t *index, i
 | 
			
		||||
    *bit = which_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitset(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitset,
 | 
			
		||||
              "(buffer/bit-set buffer index)",
 | 
			
		||||
              "Sets the bit at the given bit-index. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -289,7 +419,9 @@ static Janet cfun_buffer_bitset(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitclear(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitclear,
 | 
			
		||||
              "(buffer/bit-clear buffer index)",
 | 
			
		||||
              "Clears the bit at the given bit-index. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -298,7 +430,9 @@ static Janet cfun_buffer_bitclear(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitget(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitget,
 | 
			
		||||
              "(buffer/bit buffer index)",
 | 
			
		||||
              "Gets the bit at the given bit-index. Returns true if the bit is set, false if not.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -306,7 +440,9 @@ static Janet cfun_buffer_bitget(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_boolean(buffer->data[index] & (1 << bit));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bittoggle(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bittoggle,
 | 
			
		||||
              "(buffer/bit-toggle buffer index)",
 | 
			
		||||
              "Toggles the bit at the given bit index in buffer. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -315,7 +451,11 @@ static Janet cfun_buffer_bittoggle(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_blit(int32_t argc, Janet *argv) {
 | 
			
		||||
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 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);
 | 
			
		||||
@@ -352,7 +492,10 @@ static Janet cfun_buffer_blit(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_format(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_format,
 | 
			
		||||
              "(buffer/format buffer 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);
 | 
			
		||||
    const char *strfrmt = (const char *) janet_getstring(argv, 1);
 | 
			
		||||
@@ -360,100 +503,27 @@ static Janet cfun_buffer_format(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg buffer_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/new", cfun_buffer_new,
 | 
			
		||||
        JDOC("(buffer/new capacity)\n\n"
 | 
			
		||||
             "Creates a new, empty buffer with enough backing memory for capacity bytes. "
 | 
			
		||||
             "Returns a new buffer of length 0.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/new-filled", cfun_buffer_new_filled,
 | 
			
		||||
        JDOC("(buffer/new-filled count &opt byte)\n\n"
 | 
			
		||||
             "Creates a new buffer of length count filled with byte. By default, byte is 0. "
 | 
			
		||||
             "Returns the new buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/fill", cfun_buffer_fill,
 | 
			
		||||
        JDOC("(buffer/fill buffer &opt byte)\n\n"
 | 
			
		||||
             "Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. "
 | 
			
		||||
             "Returns the modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/push-byte", cfun_buffer_u8,
 | 
			
		||||
        JDOC("(buffer/push-byte buffer x)\n\n"
 | 
			
		||||
             "Append a byte to a buffer. Will expand the buffer as necessary. "
 | 
			
		||||
             "Returns the modified buffer. Will throw an error if the buffer overflows.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/push-word", cfun_buffer_word,
 | 
			
		||||
        JDOC("(buffer/push-word buffer x)\n\n"
 | 
			
		||||
             "Append a machine word to a buffer. The 4 bytes of the integer are appended "
 | 
			
		||||
             "in twos complement, little endian order, unsigned. Returns the modified buffer. Will "
 | 
			
		||||
             "throw an error if the buffer overflows.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/push-string", cfun_buffer_chars,
 | 
			
		||||
        JDOC("(buffer/push-string buffer str)\n\n"
 | 
			
		||||
             "Push a string onto the end of a buffer. Non string values will be converted "
 | 
			
		||||
             "to strings before being pushed. Returns the modified buffer. "
 | 
			
		||||
             "Will throw an error if the buffer overflows.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/popn", cfun_buffer_popn,
 | 
			
		||||
        JDOC("(buffer/popn buffer n)\n\n"
 | 
			
		||||
             "Removes the last n bytes from the buffer. Returns the modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/clear", cfun_buffer_clear,
 | 
			
		||||
        JDOC("(buffer/clear buffer)\n\n"
 | 
			
		||||
             "Sets the size of a buffer to 0 and empties it. The buffer retains "
 | 
			
		||||
             "its memory so it can be efficiently refilled. Returns the modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/slice", cfun_buffer_slice,
 | 
			
		||||
        JDOC("(buffer/slice bytes &opt start end)\n\n"
 | 
			
		||||
             "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. "
 | 
			
		||||
             "Returns a new buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/bit-set", cfun_buffer_bitset,
 | 
			
		||||
        JDOC("(buffer/bit-set buffer index)\n\n"
 | 
			
		||||
             "Sets the bit at the given bit-index. Returns the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/bit-clear", cfun_buffer_bitclear,
 | 
			
		||||
        JDOC("(buffer/bit-clear buffer index)\n\n"
 | 
			
		||||
             "Clears the bit at the given bit-index. Returns the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/bit", cfun_buffer_bitget,
 | 
			
		||||
        JDOC("(buffer/bit buffer index)\n\n"
 | 
			
		||||
             "Gets the bit at the given bit-index. Returns true if the bit is set, false if not.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/bit-toggle", cfun_buffer_bittoggle,
 | 
			
		||||
        JDOC("(buffer/bit-toggle buffer index)\n\n"
 | 
			
		||||
             "Toggles the bit at the given bit index in buffer. Returns the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/blit", cfun_buffer_blit,
 | 
			
		||||
        JDOC("(buffer/blit dest src &opt dest-start src-start src-end)\n\n"
 | 
			
		||||
             "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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/format", cfun_buffer_format,
 | 
			
		||||
        JDOC("(buffer/format buffer format & args)\n\n"
 | 
			
		||||
             "Snprintf like functionality for printing values into a buffer. Returns "
 | 
			
		||||
             " the modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void janet_lib_buffer(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, buffer_cfuns);
 | 
			
		||||
    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/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", 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),
 | 
			
		||||
        JANET_CORE_REG("buffer/bit-set", cfun_buffer_bitset),
 | 
			
		||||
        JANET_CORE_REG("buffer/bit-clear", cfun_buffer_bitclear),
 | 
			
		||||
        JANET_CORE_REG("buffer/bit", cfun_buffer_bitget),
 | 
			
		||||
        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_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, buffer_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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 */
 | 
			
		||||
@@ -41,6 +42,7 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
 | 
			
		||||
    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, */
 | 
			
		||||
@@ -101,10 +103,297 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
 | 
			
		||||
    JINT_SSS, /* JOP_GREATER_THAN_EQUAL */
 | 
			
		||||
    JINT_SSS, /* JOP_LESS_THAN_EQUAL */
 | 
			
		||||
    JINT_SSS, /* JOP_NEXT */
 | 
			
		||||
    JINT_SSS, /* JOP_NOT_EQUALS, */
 | 
			
		||||
    JINT_SSI, /* JOP_NOT_EQUALS_IMMEDIATE, */
 | 
			
		||||
    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:
 | 
			
		||||
                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:
 | 
			
		||||
                    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_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 */
 | 
			
		||||
int32_t janet_verify(JanetFuncDef *def) {
 | 
			
		||||
int janet_verify(JanetFuncDef *def) {
 | 
			
		||||
    int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG);
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    int32_t maxslot = def->arity + vargs;
 | 
			
		||||
@@ -215,6 +504,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;
 | 
			
		||||
@@ -226,6 +516,7 @@ JanetFuncDef *janet_funcdef_alloc(void) {
 | 
			
		||||
    def->constants_length = 0;
 | 
			
		||||
    def->bytecode_length = 0;
 | 
			
		||||
    def->environments_length = 0;
 | 
			
		||||
    def->symbolmap_length = 0;
 | 
			
		||||
    return def;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										134
									
								
								src/core/capi.c
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								src/core/capi.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,23 +27,39 @@
 | 
			
		||||
#include "fiber.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_SINGLE_THREADED
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
 | 
			
		||||
#ifdef JANET_TOP_LEVEL_SIGNAL
 | 
			
		||||
    JANET_TOP_LEVEL_SIGNAL(msg);
 | 
			
		||||
#else
 | 
			
		||||
    fputs(msg, stdout);
 | 
			
		||||
    exit(1);
 | 
			
		||||
# ifdef JANET_SINGLE_THREADED
 | 
			
		||||
    exit(-1);
 | 
			
		||||
# elif defined(JANET_WINDOWS)
 | 
			
		||||
    ExitThread(-1);
 | 
			
		||||
# else
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
# endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_signalv(JanetSignal sig, Janet message) {
 | 
			
		||||
    if (janet_vm_return_reg != NULL) {
 | 
			
		||||
        *janet_vm_return_reg = message;
 | 
			
		||||
        janet_vm_fiber->flags |= JANET_FIBER_DID_LONGJUMP;
 | 
			
		||||
    if (janet_vm.return_reg != NULL) {
 | 
			
		||||
        *janet_vm.return_reg = message;
 | 
			
		||||
        if (NULL != janet_vm.fiber) {
 | 
			
		||||
            janet_vm.fiber->flags |= JANET_FIBER_DID_LONGJUMP;
 | 
			
		||||
        }
 | 
			
		||||
#if defined(JANET_BSD) || defined(JANET_APPLE)
 | 
			
		||||
        _longjmp(*janet_vm_jmp_buf, sig);
 | 
			
		||||
        _longjmp(*janet_vm.signal_buf, sig);
 | 
			
		||||
#else
 | 
			
		||||
        longjmp(*janet_vm_jmp_buf, sig);
 | 
			
		||||
        longjmp(*janet_vm.signal_buf, sig);
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        const char *str = (const char *)janet_formatc("janet top level signal - %v\n", message);
 | 
			
		||||
@@ -133,6 +149,23 @@ int janet_getmethod(const uint8_t *method, const JanetMethod *methods, Janet *ou
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_nextmethod(const JanetMethod *methods, Janet key) {
 | 
			
		||||
    if (!janet_checktype(key, JANET_NIL)) {
 | 
			
		||||
        while (methods->name) {
 | 
			
		||||
            if (janet_keyeq(key, methods->name)) {
 | 
			
		||||
                methods++;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            methods++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (methods->name) {
 | 
			
		||||
        return janet_ckeywordv(methods->name);
 | 
			
		||||
    } else {
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_GETTER(number, NUMBER, double)
 | 
			
		||||
DEFINE_GETTER(array, ARRAY, JanetArray *)
 | 
			
		||||
DEFINE_GETTER(tuple, TUPLE, const Janet *)
 | 
			
		||||
@@ -176,12 +209,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_panicf("string %v 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) {
 | 
			
		||||
@@ -226,12 +293,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) {
 | 
			
		||||
@@ -325,20 +416,27 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_dyn(const char *name) {
 | 
			
		||||
    if (!janet_vm_fiber) return janet_wrap_nil();
 | 
			
		||||
    if (janet_vm_fiber->env) {
 | 
			
		||||
        return janet_table_get(janet_vm_fiber->env, janet_ckeywordv(name));
 | 
			
		||||
    if (!janet_vm.fiber) {
 | 
			
		||||
        if (!janet_vm.top_dyns) return janet_wrap_nil();
 | 
			
		||||
        return janet_table_get(janet_vm.top_dyns, janet_ckeywordv(name));
 | 
			
		||||
    }
 | 
			
		||||
    if (janet_vm.fiber->env) {
 | 
			
		||||
        return janet_table_get(janet_vm.fiber->env, janet_ckeywordv(name));
 | 
			
		||||
    } else {
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_setdyn(const char *name, Janet value) {
 | 
			
		||||
    if (!janet_vm_fiber) return;
 | 
			
		||||
    if (!janet_vm_fiber->env) {
 | 
			
		||||
        janet_vm_fiber->env = janet_table(1);
 | 
			
		||||
    if (!janet_vm.fiber) {
 | 
			
		||||
        if (!janet_vm.top_dyns) janet_vm.top_dyns = janet_table(10);
 | 
			
		||||
        janet_table_put(janet_vm.top_dyns, janet_ckeywordv(name), value);
 | 
			
		||||
    } else {
 | 
			
		||||
        if (!janet_vm.fiber->env) {
 | 
			
		||||
            janet_vm.fiber->env = janet_table(1);
 | 
			
		||||
        }
 | 
			
		||||
        janet_table_put(janet_vm.fiber->env, janet_ckeywordv(name), value);
 | 
			
		||||
    }
 | 
			
		||||
    janet_table_put(janet_vm_fiber->env, janet_ckeywordv(name), value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										151
									
								
								src/core/cfuns.c
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								src/core/cfuns.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -33,6 +33,11 @@ static int arity1or2(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    int32_t arity = janet_v_count(args);
 | 
			
		||||
    return arity == 1 || arity == 2;
 | 
			
		||||
}
 | 
			
		||||
static int arity2or3(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    (void) opts;
 | 
			
		||||
    int32_t arity = janet_v_count(args);
 | 
			
		||||
    return arity == 2 || arity == 3;
 | 
			
		||||
}
 | 
			
		||||
static int fixarity1(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    (void) opts;
 | 
			
		||||
    return janet_v_count(args) == 1;
 | 
			
		||||
@@ -90,34 +95,68 @@ static JanetSlot opfunction(
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a value can be coerced to an immediate value */
 | 
			
		||||
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;
 | 
			
		||||
    *out = (int8_t) integer;
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a slot can be coerced to an immediate value */
 | 
			
		||||
static int can_slot_be_imm(JanetSlot s, int8_t *out) {
 | 
			
		||||
    if (!(s.flags & JANET_SLOT_CONSTANT)) return 0;
 | 
			
		||||
    return can_be_imm(s.constant, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Emit a series of instructions instead of a function call to a math op */
 | 
			
		||||
static JanetSlot opreduce(
 | 
			
		||||
    JanetFopts opts,
 | 
			
		||||
    JanetSlot *args,
 | 
			
		||||
    int op,
 | 
			
		||||
    Janet nullary) {
 | 
			
		||||
    int opim,
 | 
			
		||||
    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) {
 | 
			
		||||
        return janetc_cslot(nullary);
 | 
			
		||||
    } else if (len == 1) {
 | 
			
		||||
        t = janetc_gettarget(opts);
 | 
			
		||||
        janetc_emit_sss(c, op, t, janetc_cslot(nullary), args[0], 1);
 | 
			
		||||
        /* Special case subtract to be times -1 */
 | 
			
		||||
        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(unary), args[0], 1);
 | 
			
		||||
        }
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
    t = janetc_gettarget(opts);
 | 
			
		||||
    janetc_emit_sss(c, op, t, args[0], args[1], 1);
 | 
			
		||||
    for (i = 2; i < len; i++)
 | 
			
		||||
        janetc_emit_sss(c, op, t, t, args[i], 1);
 | 
			
		||||
    if (opim && can_slot_be_imm(args[1], &imm)) {
 | 
			
		||||
        janetc_emit_ssi(c, opim, t, args[0], neg ? -imm : 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);
 | 
			
		||||
        } else {
 | 
			
		||||
            janetc_emit_sss(c, op, t, t, args[i], 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Function optimizers */
 | 
			
		||||
 | 
			
		||||
static JanetSlot do_propagate(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_PROPAGATE, 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);
 | 
			
		||||
@@ -134,19 +173,34 @@ static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_in(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_IN, janet_wrap_nil());
 | 
			
		||||
    return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil(), janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_GET, janet_wrap_nil());
 | 
			
		||||
    if (janet_v_count(args) == 3) {
 | 
			
		||||
        JanetCompiler *c = opts.compiler;
 | 
			
		||||
        JanetSlot t = janetc_gettarget(opts);
 | 
			
		||||
        int target_is_default = janetc_sequal(t, args[2]);
 | 
			
		||||
        JanetSlot dflt_slot = args[2];
 | 
			
		||||
        if (target_is_default) {
 | 
			
		||||
            dflt_slot = janetc_farslot(c);
 | 
			
		||||
            janetc_copy(c, dflt_slot, t);
 | 
			
		||||
        }
 | 
			
		||||
        janetc_emit_sss(c, JOP_GET, t, args[0], args[1], 1);
 | 
			
		||||
        int32_t label = janetc_emit_si(c, JOP_JUMP_IF_NOT_NIL, t, 0, 0);
 | 
			
		||||
        janetc_copy(c, t, dflt_slot);
 | 
			
		||||
        if (target_is_default) janetc_freeslot(c, dflt_slot);
 | 
			
		||||
        int32_t current = janet_v_count(c->buffer);
 | 
			
		||||
        c->buffer[label] |= (current - label) << 16;
 | 
			
		||||
        return t;
 | 
			
		||||
    } else {
 | 
			
		||||
        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, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_REMAINDER, janet_wrap_nil());
 | 
			
		||||
static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    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) {
 | 
			
		||||
@@ -172,6 +226,9 @@ static JanetSlot do_yield(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
static JanetSlot do_resume(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opfunction(opts, args, JOP_RESUME, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_cancel(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opfunction(opts, args, JOP_CANCEL, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_apply(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    /* Push phase */
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
@@ -200,34 +257,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, 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, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_SUBTRACT, -JOP_ADD_IMMEDIATE, janet_wrap_integer(0), janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_mul(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_MULTIPLY, 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, 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, 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, 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, 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, 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, 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, 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]);
 | 
			
		||||
@@ -238,9 +304,11 @@ static JanetSlot compreduce(
 | 
			
		||||
    JanetFopts opts,
 | 
			
		||||
    JanetSlot *args,
 | 
			
		||||
    int op,
 | 
			
		||||
    int opim,
 | 
			
		||||
    int invert) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    int32_t i, len;
 | 
			
		||||
    int8_t imm = 0;
 | 
			
		||||
    len = janet_v_count(args);
 | 
			
		||||
    int32_t *labels = NULL;
 | 
			
		||||
    JanetSlot t;
 | 
			
		||||
@@ -251,19 +319,17 @@ static JanetSlot compreduce(
 | 
			
		||||
    }
 | 
			
		||||
    t = janetc_gettarget(opts);
 | 
			
		||||
    for (i = 1; i < len; i++) {
 | 
			
		||||
        janetc_emit_sss(c, op, t, args[i - 1], args[i], 1);
 | 
			
		||||
        if (opim && can_slot_be_imm(args[i], &imm)) {
 | 
			
		||||
            janetc_emit_ssi(c, opim, t, args[i - 1], imm, 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            janetc_emit_sss(c, op, t, args[i - 1], args[i], 1);
 | 
			
		||||
        }
 | 
			
		||||
        if (i != (len - 1)) {
 | 
			
		||||
            int32_t label = janetc_emit_si(c, JOP_JUMP_IF_NOT, t, 0, 1);
 | 
			
		||||
            int32_t label = janetc_emit_si(c, invert ? JOP_JUMP_IF : JOP_JUMP_IF_NOT, t, 0, 1);
 | 
			
		||||
            janet_v_push(labels, label);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    int32_t end = janet_v_count(c->buffer);
 | 
			
		||||
    if (invert) {
 | 
			
		||||
        janetc_emit_si(c, JOP_JUMP_IF, t, 3, 0);
 | 
			
		||||
        janetc_emit_s(c, JOP_LOAD_TRUE, t, 1);
 | 
			
		||||
        janetc_emit(c, JOP_JUMP | (2 << 8));
 | 
			
		||||
        janetc_emit_s(c, JOP_LOAD_FALSE, t, 1);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 0; i < janet_v_count(labels); i++) {
 | 
			
		||||
        int32_t label = labels[i];
 | 
			
		||||
        c->buffer[label] |= ((end - label) << 16);
 | 
			
		||||
@@ -273,22 +339,22 @@ static JanetSlot compreduce(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetSlot do_gt(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_GREATER_THAN, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_GREATER_THAN, JOP_GREATER_THAN_IMMEDIATE, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_lt(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_LESS_THAN, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_LESS_THAN, JOP_LESS_THAN_IMMEDIATE, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_gte(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_GREATER_THAN_EQUAL, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_GREATER_THAN_EQUAL, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_lte(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_LESS_THAN_EQUAL, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_LESS_THAN_EQUAL, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_eq(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_EQUALS, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_EQUALS, JOP_EQUALS_IMMEDIATE, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_neq(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_EQUALS, 1);
 | 
			
		||||
    return compreduce(opts, args, JOP_NOT_EQUALS, JOP_NOT_EQUALS_IMMEDIATE, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Arranged by tag */
 | 
			
		||||
@@ -319,10 +385,13 @@ static const JanetFunOptimizer optimizers[] = {
 | 
			
		||||
    {NULL, do_eq},
 | 
			
		||||
    {NULL, do_neq},
 | 
			
		||||
    {fixarity2, do_propagate},
 | 
			
		||||
    {fixarity2, do_get},
 | 
			
		||||
    {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) 2020 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
 | 
			
		||||
@@ -53,6 +53,36 @@ void janetc_cerror(JanetCompiler *c, const char *m) {
 | 
			
		||||
    janetc_error(c, janet_cstring(m));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *janet_lint_level_names[] = {
 | 
			
		||||
    "relaxed",
 | 
			
		||||
    "normal",
 | 
			
		||||
    "strict"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Emit compiler linter messages */
 | 
			
		||||
void janetc_lintf(JanetCompiler *c, JanetCompileLintLevel level, const char *format, ...) {
 | 
			
		||||
    if (NULL != c->lints) {
 | 
			
		||||
        /* format message */
 | 
			
		||||
        va_list args;
 | 
			
		||||
        JanetBuffer buffer;
 | 
			
		||||
        int32_t len = 0;
 | 
			
		||||
        while (format[len]) len++;
 | 
			
		||||
        janet_buffer_init(&buffer, len);
 | 
			
		||||
        va_start(args, format);
 | 
			
		||||
        janet_formatbv(&buffer, format, args);
 | 
			
		||||
        va_end(args);
 | 
			
		||||
        const uint8_t *str = janet_string(buffer.data, buffer.count);
 | 
			
		||||
        janet_buffer_deinit(&buffer);
 | 
			
		||||
        /* construct linting payload */
 | 
			
		||||
        Janet *payload = janet_tuple_begin(4);
 | 
			
		||||
        payload[0] = janet_ckeywordv(janet_lint_level_names[level]);
 | 
			
		||||
        payload[1] = c->current_mapping.line == -1 ? janet_wrap_nil() : janet_wrap_integer(c->current_mapping.line);
 | 
			
		||||
        payload[2] = c->current_mapping.column == -1 ? janet_wrap_nil() : janet_wrap_integer(c->current_mapping.column);
 | 
			
		||||
        payload[3] = janet_wrap_string(str);
 | 
			
		||||
        janet_array_push(c->lints, janet_wrap_tuple(janet_tuple_end(payload)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Free a slot */
 | 
			
		||||
void janetc_freeslot(JanetCompiler *c, JanetSlot s) {
 | 
			
		||||
    if (s.flags & (JANET_SLOT_CONSTANT | JANET_SLOT_REF | JANET_SLOT_NAMED)) return;
 | 
			
		||||
@@ -63,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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -129,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);
 | 
			
		||||
@@ -167,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,
 | 
			
		||||
@@ -199,24 +272,62 @@ JanetSlot janetc_resolve(
 | 
			
		||||
 | 
			
		||||
    /* Symbol not found - check for global */
 | 
			
		||||
    {
 | 
			
		||||
        Janet check;
 | 
			
		||||
        JanetBindingType btype = janet_resolve(c->env, sym, &check);
 | 
			
		||||
        switch (btype) {
 | 
			
		||||
        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:
 | 
			
		||||
                janetc_error(c, janet_formatc("unknown symbol %q", janet_wrap_symbol(sym)));
 | 
			
		||||
                return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
            case JANET_BINDING_DEF:
 | 
			
		||||
            case JANET_BINDING_MACRO: /* Macro should function like defs when not in calling pos */
 | 
			
		||||
                return janetc_cslot(check);
 | 
			
		||||
                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: {
 | 
			
		||||
                JanetSlot ret = janetc_cslot(check);
 | 
			
		||||
                /* TODO save type info */
 | 
			
		||||
                ret = janetc_cslot(binding.value);
 | 
			
		||||
                ret.flags |= JANET_SLOT_REF | JANET_SLOT_NAMED | JANET_SLOT_MUTABLE | JANET_SLOTTYPE_ANY;
 | 
			
		||||
                ret.flags &= ~JANET_SLOT_CONSTANT;
 | 
			
		||||
                return ret;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        JanetCompileLintLevel depLevel = JANET_C_LINT_RELAXED;
 | 
			
		||||
        switch (binding.deprecation) {
 | 
			
		||||
            case JANET_BINDING_DEP_NONE:
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_BINDING_DEP_RELAXED:
 | 
			
		||||
                depLevel = JANET_C_LINT_RELAXED;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_BINDING_DEP_NORMAL:
 | 
			
		||||
                depLevel = JANET_C_LINT_NORMAL;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_BINDING_DEP_STRICT:
 | 
			
		||||
                depLevel = JANET_C_LINT_STRICT;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        if (binding.deprecation != JANET_BINDING_DEP_NONE) {
 | 
			
		||||
            janetc_lintf(c, depLevel, "%q is deprecated", janet_wrap_symbol(sym));
 | 
			
		||||
        }
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Symbol was found */
 | 
			
		||||
@@ -233,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;
 | 
			
		||||
@@ -254,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;
 | 
			
		||||
@@ -263,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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -307,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]));
 | 
			
		||||
    }
 | 
			
		||||
@@ -317,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);
 | 
			
		||||
@@ -399,6 +516,7 @@ void janetc_throwaway(JanetFopts opts, Janet x) {
 | 
			
		||||
    int32_t mapbufstart = janet_v_count(c->mapbuffer);
 | 
			
		||||
    janetc_scope(&unusedScope, c, JANET_SCOPE_UNUSED, "unusued");
 | 
			
		||||
    janetc_value(opts, x);
 | 
			
		||||
    janetc_lintf(c, JANET_C_LINT_STRICT, "dead code, consider removing %.2q", x);
 | 
			
		||||
    janetc_popscope(c);
 | 
			
		||||
    if (c->buffer) {
 | 
			
		||||
        janet_v__cnt(c->buffer) = bufstart;
 | 
			
		||||
@@ -439,22 +557,22 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun) {
 | 
			
		||||
                        min_arity = -1 - min_arity;
 | 
			
		||||
                        if (min_arity > max && max >= 0) {
 | 
			
		||||
                            const uint8_t *es = janet_formatc(
 | 
			
		||||
                                                    "%v expects at most %d argument, got at least %d",
 | 
			
		||||
                                                    fun.constant, max, min_arity);
 | 
			
		||||
                                                    "%v expects at most %d argument%s, got at least %d",
 | 
			
		||||
                                                    fun.constant, max, max == 1 ? "" : "s", min_arity);
 | 
			
		||||
                            janetc_error(c, es);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        /* Call has no splices */
 | 
			
		||||
                        if (min_arity > max && max >= 0) {
 | 
			
		||||
                            const uint8_t *es = janet_formatc(
 | 
			
		||||
                                                    "%v expects at most %d argument, got %d",
 | 
			
		||||
                                                    fun.constant, max, min_arity);
 | 
			
		||||
                                                    "%v expects at most %d argument%s, got %d",
 | 
			
		||||
                                                    fun.constant, max, max == 1 ? "" : "s", min_arity);
 | 
			
		||||
                            janetc_error(c, es);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (min_arity < min) {
 | 
			
		||||
                            const uint8_t *es = janet_formatc(
 | 
			
		||||
                                                    "%v expects at least %d argument, got %d",
 | 
			
		||||
                                                    fun.constant, min, min_arity);
 | 
			
		||||
                                                    "%v expects at least %d argument%s, got %d",
 | 
			
		||||
                                                    fun.constant, min, min == 1 ? "" : "s", min_arity);
 | 
			
		||||
                            janetc_error(c, es);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
@@ -504,10 +622,40 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun) {
 | 
			
		||||
static JanetSlot janetc_maker(JanetFopts opts, JanetSlot *slots, int op) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    JanetSlot retslot;
 | 
			
		||||
    janetc_pushslots(c, slots);
 | 
			
		||||
    janetc_freeslots(c, slots);
 | 
			
		||||
    retslot = janetc_gettarget(opts);
 | 
			
		||||
    janetc_emit_s(c, op, retslot, 1);
 | 
			
		||||
 | 
			
		||||
    /* Check if this structure is composed entirely of constants */
 | 
			
		||||
    int can_inline = 1;
 | 
			
		||||
    for (int32_t i = 0; i < janet_v_count(slots); i++) {
 | 
			
		||||
        if (!(slots[i].flags & JANET_SLOT_CONSTANT) ||
 | 
			
		||||
                (slots[i].flags & JANET_SLOT_SPLICED)) {
 | 
			
		||||
            can_inline = 0;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (can_inline && (op == JOP_MAKE_STRUCT)) {
 | 
			
		||||
        JanetKV *st = janet_struct_begin(janet_v_count(slots) / 2);
 | 
			
		||||
        for (int32_t i = 0; i < janet_v_count(slots); i += 2) {
 | 
			
		||||
            Janet k = slots[i].constant;
 | 
			
		||||
            Janet v = slots[i + 1].constant;
 | 
			
		||||
            janet_struct_put(st, k, v);
 | 
			
		||||
        }
 | 
			
		||||
        retslot = janetc_cslot(janet_wrap_struct(janet_struct_end(st)));
 | 
			
		||||
        janetc_freeslots(c, slots);
 | 
			
		||||
    } else if (can_inline && (op == JOP_MAKE_TUPLE)) {
 | 
			
		||||
        Janet *tup = janet_tuple_begin(janet_v_count(slots));
 | 
			
		||||
        for (int32_t i = 0; i < janet_v_count(slots); i++) {
 | 
			
		||||
            tup[i] = slots[i].constant;
 | 
			
		||||
        }
 | 
			
		||||
        retslot = janetc_cslot(janet_wrap_tuple(janet_tuple_end(tup)));
 | 
			
		||||
        janetc_freeslots(c, slots);
 | 
			
		||||
    } else {
 | 
			
		||||
        janetc_pushslots(c, slots);
 | 
			
		||||
        janetc_freeslots(c, slots);
 | 
			
		||||
        retslot = janetc_gettarget(opts);
 | 
			
		||||
        janetc_emit_s(c, op, retslot, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return retslot;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -573,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;
 | 
			
		||||
 | 
			
		||||
@@ -598,9 +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());
 | 
			
		||||
    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);
 | 
			
		||||
@@ -698,7 +851,32 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add function flags to janet functions */
 | 
			
		||||
void janet_def_addflags(JanetFuncDef *def) {
 | 
			
		||||
    int32_t set_flags = 0;
 | 
			
		||||
    int32_t unset_flags = 0;
 | 
			
		||||
    /* pos checks */
 | 
			
		||||
    if (def->name)            set_flags |= JANET_FUNCDEF_FLAG_HASNAME;
 | 
			
		||||
    if (def->source)          set_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
 | 
			
		||||
    if (def->defs)            set_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
 | 
			
		||||
    if (def->environments)    set_flags |= JANET_FUNCDEF_FLAG_HASENVS;
 | 
			
		||||
    if (def->sourcemap)       set_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
 | 
			
		||||
    if (def->closure_bitset)  set_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
 | 
			
		||||
    /* negative checks */
 | 
			
		||||
    if (!def->name)           unset_flags |= JANET_FUNCDEF_FLAG_HASNAME;
 | 
			
		||||
    if (!def->source)         unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
 | 
			
		||||
    if (!def->defs)           unset_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
 | 
			
		||||
    if (!def->environments)   unset_flags |= JANET_FUNCDEF_FLAG_HASENVS;
 | 
			
		||||
    if (!def->sourcemap)      unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
 | 
			
		||||
    if (!def->closure_bitset) unset_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
 | 
			
		||||
    /* Update flags */
 | 
			
		||||
    def->flags |= set_flags;
 | 
			
		||||
    def->flags &= ~unset_flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compile a funcdef */
 | 
			
		||||
/* Once the various other settings of the FuncDef have been tweaked,
 | 
			
		||||
 * call janet_def_addflags to set the proper flags for the funcdef */
 | 
			
		||||
JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
    JanetScope *scope = c->scope;
 | 
			
		||||
    JanetFuncDef *def = janet_funcdef_alloc();
 | 
			
		||||
@@ -708,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);
 | 
			
		||||
@@ -720,7 +901,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
    def->bytecode_length = janet_v_count(c->buffer) - scope->bytecode_start;
 | 
			
		||||
    if (def->bytecode_length) {
 | 
			
		||||
        size_t s = sizeof(int32_t) * (size_t) def->bytecode_length;
 | 
			
		||||
        def->bytecode = malloc(s);
 | 
			
		||||
        def->bytecode = janet_malloc(s);
 | 
			
		||||
        if (NULL == def->bytecode) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -728,7 +909,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        janet_v__cnt(c->buffer) = scope->bytecode_start;
 | 
			
		||||
        if (NULL != c->mapbuffer && c->source) {
 | 
			
		||||
            size_t s = sizeof(JanetSourceMapping) * (size_t) def->bytecode_length;
 | 
			
		||||
            def->sourcemap = malloc(s);
 | 
			
		||||
            def->sourcemap = janet_malloc(s);
 | 
			
		||||
            if (NULL == def->sourcemap) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -753,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 = calloc(sizeof(uint32_t), slotchunks);
 | 
			
		||||
        uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks);
 | 
			
		||||
        if (NULL == chunks) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -761,17 +942,73 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        /* Register allocator preallocates some registers [240-255, high 16 bits of chunk index 7], we can ignore those. */
 | 
			
		||||
        if (scope->ua.count > 7) chunks[7] &= 0xFFFFU;
 | 
			
		||||
        def->closure_bitset = chunks;
 | 
			
		||||
        def->flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize a compiler */
 | 
			
		||||
static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where) {
 | 
			
		||||
static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where, JanetArray *lints) {
 | 
			
		||||
    c->scope = NULL;
 | 
			
		||||
    c->buffer = NULL;
 | 
			
		||||
    c->mapbuffer = NULL;
 | 
			
		||||
@@ -780,6 +1017,7 @@ static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where)
 | 
			
		||||
    c->source = where;
 | 
			
		||||
    c->current_mapping.line = -1;
 | 
			
		||||
    c->current_mapping.column = -1;
 | 
			
		||||
    c->lints = lints;
 | 
			
		||||
    /* Init result */
 | 
			
		||||
    c->result.error = NULL;
 | 
			
		||||
    c->result.status = JANET_COMPILE_OK;
 | 
			
		||||
@@ -797,12 +1035,13 @@ static void janetc_deinit(JanetCompiler *c) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compile a form. */
 | 
			
		||||
JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *where) {
 | 
			
		||||
JanetCompileResult janet_compile_lint(Janet source,
 | 
			
		||||
                                      JanetTable *env, const uint8_t *where, JanetArray *lints) {
 | 
			
		||||
    JanetCompiler c;
 | 
			
		||||
    JanetScope rootscope;
 | 
			
		||||
    JanetFopts fopts;
 | 
			
		||||
 | 
			
		||||
    janetc_init(&c, env, where);
 | 
			
		||||
    janetc_init(&c, env, where, lints);
 | 
			
		||||
 | 
			
		||||
    /* Push a function scope */
 | 
			
		||||
    janetc_scope(&rootscope, &c, JANET_SCOPE_FUNCTION | JANET_SCOPE_TOP, "root");
 | 
			
		||||
@@ -818,6 +1057,7 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
 | 
			
		||||
    if (c.result.status == JANET_COMPILE_OK) {
 | 
			
		||||
        JanetFuncDef *def = janetc_pop_funcdef(&c);
 | 
			
		||||
        def->name = janet_cstring("_thunk");
 | 
			
		||||
        janet_def_addflags(def);
 | 
			
		||||
        c.result.funcdef = def;
 | 
			
		||||
    } else {
 | 
			
		||||
        c.result.error_mapping = c.current_mapping;
 | 
			
		||||
@@ -829,26 +1069,51 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
 | 
			
		||||
    return c.result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *where) {
 | 
			
		||||
    return janet_compile_lint(source, env, where, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* C Function for compiling */
 | 
			
		||||
static Janet cfun(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetTable *env = argc > 1 ? janet_gettable(argv, 1) : janet_vm_fiber->env;
 | 
			
		||||
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 "
 | 
			
		||||
              "eval. Returns a new function and does not modify ast. Returns an error "
 | 
			
		||||
              "struct with keys :line, :column, and :error if compilation fails. "
 | 
			
		||||
              "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_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;
 | 
			
		||||
        janet_vm.fiber->env = env;
 | 
			
		||||
    }
 | 
			
		||||
    const uint8_t *source = NULL;
 | 
			
		||||
    if (argc == 3) {
 | 
			
		||||
        source = janet_getstring(argv, 2);
 | 
			
		||||
    if (argc >= 3) {
 | 
			
		||||
        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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetCompileResult res = janet_compile(argv[0], env, source);
 | 
			
		||||
    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));
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetTable *t = janet_table(4);
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("error"), janet_wrap_string(res.error));
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("line"), janet_wrap_integer(res.error_mapping.line));
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("column"), janet_wrap_integer(res.error_mapping.column));
 | 
			
		||||
        if (res.error_mapping.line > 0) {
 | 
			
		||||
            janet_table_put(t, janet_ckeywordv("line"), janet_wrap_integer(res.error_mapping.line));
 | 
			
		||||
        }
 | 
			
		||||
        if (res.error_mapping.column > 0) {
 | 
			
		||||
            janet_table_put(t, janet_ckeywordv("column"), janet_wrap_integer(res.error_mapping.column));
 | 
			
		||||
        }
 | 
			
		||||
        if (res.macrofiber) {
 | 
			
		||||
            janet_table_put(t, janet_ckeywordv("fiber"), janet_wrap_fiber(res.macrofiber));
 | 
			
		||||
        }
 | 
			
		||||
@@ -856,18 +1121,10 @@ static Janet cfun(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg compile_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "compile", cfun,
 | 
			
		||||
        JDOC("(compile ast &opt env source)\n\n"
 | 
			
		||||
             "Compiles an Abstract Syntax Tree (ast) into a function. "
 | 
			
		||||
             "Pair the compile function with parsing functionality to implement "
 | 
			
		||||
             "eval. Returns a new function and does not modify ast. Returns an error "
 | 
			
		||||
             "struct with keys :line, :column, and :error if compilation fails.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void janet_lib_compile(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, compile_cfuns);
 | 
			
		||||
    JanetRegExt cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("compile", cfun_compile),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -29,6 +29,13 @@
 | 
			
		||||
#include "regalloc.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Levels for compiler warnings */
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_C_LINT_RELAXED,
 | 
			
		||||
    JANET_C_LINT_NORMAL,
 | 
			
		||||
    JANET_C_LINT_STRICT
 | 
			
		||||
} JanetCompileLintLevel;
 | 
			
		||||
 | 
			
		||||
/* Tags for some functions for the prepared inliner */
 | 
			
		||||
#define JANET_FUN_DEBUG 1
 | 
			
		||||
#define JANET_FUN_ERROR 2
 | 
			
		||||
@@ -60,6 +67,9 @@
 | 
			
		||||
#define JANET_FUN_NEXT 28
 | 
			
		||||
#define JANET_FUN_MODULO 29
 | 
			
		||||
#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;
 | 
			
		||||
@@ -76,10 +86,10 @@ typedef struct JanetSpecial JanetSpecial;
 | 
			
		||||
#define JANET_SLOT_MUTABLE 0x40000
 | 
			
		||||
#define JANET_SLOT_REF 0x80000
 | 
			
		||||
#define JANET_SLOT_RETURNED 0x100000
 | 
			
		||||
/* Needed for handling single element arrays as global vars. */
 | 
			
		||||
 | 
			
		||||
/* Used for unquote-splicing */
 | 
			
		||||
#define JANET_SLOT_SPLICED 0x200000
 | 
			
		||||
#define JANET_SLOT_DEP_NOTE 0x200000
 | 
			
		||||
#define JANET_SLOT_DEP_WARN 0x400000
 | 
			
		||||
#define JANET_SLOT_DEP_ERROR 0x800000
 | 
			
		||||
#define JANET_SLOT_SPLICED 0x1000000
 | 
			
		||||
 | 
			
		||||
#define JANET_SLOTTYPE_ANY 0xFFFF
 | 
			
		||||
 | 
			
		||||
@@ -102,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 */
 | 
			
		||||
@@ -124,7 +142,7 @@ struct JanetScope {
 | 
			
		||||
    /* FuncDefs */
 | 
			
		||||
    JanetFuncDef **defs;
 | 
			
		||||
 | 
			
		||||
    /* Regsiter allocator */
 | 
			
		||||
    /* Register allocator */
 | 
			
		||||
    JanetcRegisterAllocator ra;
 | 
			
		||||
 | 
			
		||||
    /* Upvalue allocator */
 | 
			
		||||
@@ -133,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;
 | 
			
		||||
@@ -162,11 +180,15 @@ struct JanetCompiler {
 | 
			
		||||
 | 
			
		||||
    /* Prevent unbounded recursion */
 | 
			
		||||
    int recursion_guard;
 | 
			
		||||
 | 
			
		||||
    /* Collect linting results */
 | 
			
		||||
    JanetArray *lints;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#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 {
 | 
			
		||||
@@ -215,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 */
 | 
			
		||||
@@ -228,6 +250,9 @@ JanetSlot janetc_return(JanetCompiler *c, JanetSlot s);
 | 
			
		||||
void janetc_error(JanetCompiler *c, const uint8_t *m);
 | 
			
		||||
void janetc_cerror(JanetCompiler *c, const char *m);
 | 
			
		||||
 | 
			
		||||
/* Linting */
 | 
			
		||||
void janetc_lintf(JanetCompiler *C, JanetCompileLintLevel level, const char *format, ...);
 | 
			
		||||
 | 
			
		||||
/* Dispatch to correct form compiler */
 | 
			
		||||
JanetSlot janetc_value(JanetFopts opts, Janet x);
 | 
			
		||||
 | 
			
		||||
@@ -243,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
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										245
									
								
								src/core/debug.c
									
									
									
									
									
								
							
							
						
						
									
										245
									
								
								src/core/debug.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -55,7 +55,7 @@ void janet_debug_find(
 | 
			
		||||
    JanetFuncDef **def_out, int32_t *pc_out,
 | 
			
		||||
    const uint8_t *source, int32_t sourceLine, int32_t sourceColumn) {
 | 
			
		||||
    /* Scan the heap for right func def */
 | 
			
		||||
    JanetGCObject *current = janet_vm_blocks;
 | 
			
		||||
    JanetGCObject *current = janet_vm.blocks;
 | 
			
		||||
    /* Keep track of the best source mapping we have seen so far */
 | 
			
		||||
    int32_t besti = -1;
 | 
			
		||||
    int32_t best_line = -1;
 | 
			
		||||
@@ -86,7 +86,7 @@ void janet_debug_find(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        current = current->next;
 | 
			
		||||
        current = current->data.next;
 | 
			
		||||
    }
 | 
			
		||||
    if (best_def) {
 | 
			
		||||
        *def_out = best_def;
 | 
			
		||||
@@ -96,13 +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;
 | 
			
		||||
    int wrote_error = 0;
 | 
			
		||||
    int wrote_error = !prefix;
 | 
			
		||||
 | 
			
		||||
    int print_color = janet_truthy(janet_dyn("err-color"));
 | 
			
		||||
    if (print_color) janet_eprintf("\x1b[31m");
 | 
			
		||||
@@ -116,6 +122,7 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
        fiber = fibers[fi];
 | 
			
		||||
        int32_t i = fiber->frame;
 | 
			
		||||
        while (i > 0) {
 | 
			
		||||
            JanetCFunRegistry *reg = NULL;
 | 
			
		||||
            JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
 | 
			
		||||
            JanetFuncDef *def = NULL;
 | 
			
		||||
            i = frame->prevframe;
 | 
			
		||||
@@ -123,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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -142,11 +148,19 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
            } else {
 | 
			
		||||
                JanetCFunction cfun = (JanetCFunction)(frame->pc);
 | 
			
		||||
                if (cfun) {
 | 
			
		||||
                    Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
 | 
			
		||||
                    if (!janet_checktype(name, JANET_NIL))
 | 
			
		||||
                        janet_eprintf(" %s", (const char *)janet_to_string(name));
 | 
			
		||||
                    else
 | 
			
		||||
                    reg = janet_registry_get(cfun);
 | 
			
		||||
                    if (NULL != reg && NULL != reg->name) {
 | 
			
		||||
                        if (reg->name_prefix) {
 | 
			
		||||
                            janet_eprintf(" %s/%s", reg->name_prefix, reg->name);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            janet_eprintf(" %s", reg->name);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (NULL != reg->source_file) {
 | 
			
		||||
                            janet_eprintf(" [%s]", reg->source_file);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        janet_eprintf(" <cfunction>");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (frame->flags & JANET_STACKFRAME_TAILCALL)
 | 
			
		||||
@@ -159,6 +173,11 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf(" pc=%d", off);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (NULL != reg) {
 | 
			
		||||
                /* C Function */
 | 
			
		||||
                if (reg->source_line > 0) {
 | 
			
		||||
                    janet_eprintf(" on line %d", (long) reg->source_line);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            janet_eprintf("\n");
 | 
			
		||||
        }
 | 
			
		||||
@@ -193,7 +212,13 @@ static void helper_find_fun(int32_t argc, Janet *argv, JanetFuncDef **def, int32
 | 
			
		||||
    *bytecode_offset = offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_break(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_break,
 | 
			
		||||
              "(debug/break source line col)",
 | 
			
		||||
              "Sets a breakpoint in `source` at a given line and column. "
 | 
			
		||||
              "Will throw an error if the breakpoint location "
 | 
			
		||||
              "cannot be found. For example\n\n"
 | 
			
		||||
              "\t(debug/break \"core.janet\" 10 4)\n\n"
 | 
			
		||||
              "will set a breakpoint at line 10, 4th column of the file core.janet.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset;
 | 
			
		||||
    helper_find(argc, argv, &def, &offset);
 | 
			
		||||
@@ -201,7 +226,11 @@ static Janet cfun_debug_break(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_unbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_unbreak,
 | 
			
		||||
              "(debug/unbreak source line column)",
 | 
			
		||||
              "Remove a breakpoint with a source key at a given line and column. "
 | 
			
		||||
              "Will throw an error if the breakpoint "
 | 
			
		||||
              "cannot be found.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset = 0;
 | 
			
		||||
    helper_find(argc, argv, &def, &offset);
 | 
			
		||||
@@ -209,7 +238,11 @@ static Janet cfun_debug_unbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_fbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_fbreak,
 | 
			
		||||
              "(debug/fbreak fun &opt pc)",
 | 
			
		||||
              "Set a breakpoint in a given function. pc is an optional offset, which "
 | 
			
		||||
              "is in bytecode instructions. fun is a function value. Will throw an error "
 | 
			
		||||
              "if the offset is too large or negative.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset = 0;
 | 
			
		||||
    helper_find_fun(argc, argv, &def, &offset);
 | 
			
		||||
@@ -217,7 +250,9 @@ static Janet cfun_debug_fbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_unfbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_unfbreak,
 | 
			
		||||
              "(debug/unfbreak fun &opt pc)",
 | 
			
		||||
              "Unset a breakpoint set with debug/fbreak.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset;
 | 
			
		||||
    helper_find_fun(argc, argv, &def, &offset);
 | 
			
		||||
@@ -225,7 +260,12 @@ static Janet cfun_debug_unfbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_lineage(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_lineage,
 | 
			
		||||
              "(debug/lineage fib)",
 | 
			
		||||
              "Returns an array of all child fibers from a root fiber. This function "
 | 
			
		||||
              "is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
 | 
			
		||||
              "the fiber handling the error can see which fiber raised the signal. This function should "
 | 
			
		||||
              "be used mostly for debugging purposes.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(0);
 | 
			
		||||
@@ -250,9 +290,20 @@ static Janet doframe(JanetStackFrame *frame) {
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetCFunction cfun = (JanetCFunction)(frame->pc);
 | 
			
		||||
        if (cfun) {
 | 
			
		||||
            Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
 | 
			
		||||
            if (!janet_checktype(name, JANET_NIL)) {
 | 
			
		||||
                janet_table_put(t, janet_ckeywordv("name"), name);
 | 
			
		||||
            JanetCFunRegistry *reg = janet_registry_get(cfun);
 | 
			
		||||
            if (NULL != reg->name) {
 | 
			
		||||
                if (NULL != reg->name_prefix) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("name"), janet_wrap_string(janet_formatc("%s/%s", reg->name_prefix, reg->name)));
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("name"), janet_cstringv(reg->name));
 | 
			
		||||
                }
 | 
			
		||||
                if (NULL != reg->source_file) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source"), janet_cstringv(reg->source_file));
 | 
			
		||||
                }
 | 
			
		||||
                if (reg->source_line > 0) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source-line"), janet_wrap_integer(reg->source_line));
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source-column"), janet_wrap_integer(1));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("c"), janet_wrap_true());
 | 
			
		||||
@@ -263,6 +314,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) {
 | 
			
		||||
@@ -278,11 +330,46 @@ 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_stack(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_stack,
 | 
			
		||||
              "(debug/stack fib)",
 | 
			
		||||
              "Gets information about the stack as an array of tables. Each table "
 | 
			
		||||
              "in the array contains information about a stack frame. The top-most, current "
 | 
			
		||||
              "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"
 | 
			
		||||
              "* :source-column - the current source column of the stack frame\n\n"
 | 
			
		||||
              "* :function - the function that the stack frame represents\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"
 | 
			
		||||
              "* :slots - array of all values in each slot\n\n"
 | 
			
		||||
              "* :tail - boolean indicating a tail call") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(0);
 | 
			
		||||
@@ -298,14 +385,24 @@ static Janet cfun_debug_stack(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_stacktrace(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
JANET_CORE_FN(cfun_debug_stacktrace,
 | 
			
		||||
              "(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 "
 | 
			
		||||
              "provided, and no prefix is given, will skip the error line. Returns the fiber.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    janet_stacktrace(fiber, argv[1]);
 | 
			
		||||
    Janet x = argc == 1 ? janet_wrap_nil() : argv[1];
 | 
			
		||||
    const char *prefix = janet_optcstring(argv, argc, 2, NULL);
 | 
			
		||||
    janet_stacktrace_ext(fiber, x, prefix);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_argstack(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_argstack,
 | 
			
		||||
              "(debug/arg-stack fiber)",
 | 
			
		||||
              "Gets all values currently on the fiber's argument stack. Normally, "
 | 
			
		||||
              "this should be empty unless the fiber signals while pushing arguments "
 | 
			
		||||
              "to make a function call. Returns a new array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(fiber->stacktop - fiber->stackstart);
 | 
			
		||||
@@ -314,7 +411,11 @@ static Janet cfun_debug_argstack(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_step(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_step,
 | 
			
		||||
              "(debug/step fiber &opt x)",
 | 
			
		||||
              "Run a fiber for one virtual instruction of the Janet machine. Can optionally "
 | 
			
		||||
              "pass in a value that will be passed as the resuming value. Returns the signal value, "
 | 
			
		||||
              "which will usually be nil, as breakpoints raise nil signals.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    Janet out = janet_wrap_nil();
 | 
			
		||||
@@ -322,85 +423,19 @@ static Janet cfun_debug_step(int32_t argc, Janet *argv) {
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg debug_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "debug/break", cfun_debug_break,
 | 
			
		||||
        JDOC("(debug/break source byte-offset)\n\n"
 | 
			
		||||
             "Sets a breakpoint with source a key at a given line and column. "
 | 
			
		||||
             "Will throw an error if the breakpoint location "
 | 
			
		||||
             "cannot be found. For example\n\n"
 | 
			
		||||
             "\t(debug/break \"core.janet\" 1000)\n\n"
 | 
			
		||||
             "wil set a breakpoint at the 1000th byte of the file core.janet.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/unbreak", cfun_debug_unbreak,
 | 
			
		||||
        JDOC("(debug/unbreak source line column)\n\n"
 | 
			
		||||
             "Remove a breakpoint with a source key at a given line and column. "
 | 
			
		||||
             "Will throw an error if the breakpoint "
 | 
			
		||||
             "cannot be found.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/fbreak", cfun_debug_fbreak,
 | 
			
		||||
        JDOC("(debug/fbreak fun &opt pc)\n\n"
 | 
			
		||||
             "Set a breakpoint in a given function. pc is an optional offset, which "
 | 
			
		||||
             "is in bytecode instructions. fun is a function value. Will throw an error "
 | 
			
		||||
             "if the offset is too large or negative.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/unfbreak", cfun_debug_unfbreak,
 | 
			
		||||
        JDOC("(debug/unfbreak fun &opt pc)\n\n"
 | 
			
		||||
             "Unset a breakpoint set with debug/fbreak.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/arg-stack", cfun_debug_argstack,
 | 
			
		||||
        JDOC("(debug/arg-stack fiber)\n\n"
 | 
			
		||||
             "Gets all values currently on the fiber's argument stack. Normally, "
 | 
			
		||||
             "this should be empty unless the fiber signals while pushing arguments "
 | 
			
		||||
             "to make a function call. Returns a new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/stack", cfun_debug_stack,
 | 
			
		||||
        JDOC("(debug/stack fib)\n\n"
 | 
			
		||||
             "Gets information about the stack as an array of tables. Each table "
 | 
			
		||||
             "in the array contains information about a stack frame. The top most, current "
 | 
			
		||||
             "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"
 | 
			
		||||
             "\t:c - true if the stack frame is a c function invocation\n"
 | 
			
		||||
             "\t:column - the current source column of the stack frame\n"
 | 
			
		||||
             "\t:function - the function that the stack frame represents\n"
 | 
			
		||||
             "\t:line - the current source line of the stack frame\n"
 | 
			
		||||
             "\t:name - the human friendly name of the function\n"
 | 
			
		||||
             "\t:pc - integer indicating the location of the program counter\n"
 | 
			
		||||
             "\t:source - string with the file path or other identifier for the source code\n"
 | 
			
		||||
             "\t:slots - array of all values in each slot\n"
 | 
			
		||||
             "\t:tail - boolean indicating a tail call")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/stacktrace", cfun_debug_stacktrace,
 | 
			
		||||
        JDOC("(debug/stacktrace fiber err)\n\n"
 | 
			
		||||
             "Prints a nice looking stacktrace for a fiber. The error message "
 | 
			
		||||
             "err must be passed to the function as fiber's do not keep track of "
 | 
			
		||||
             "the last error they have thrown. Returns the fiber.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/lineage", cfun_debug_lineage,
 | 
			
		||||
        JDOC("(debug/lineage fib)\n\n"
 | 
			
		||||
             "Returns an array of all child fibers from a root fiber. This function "
 | 
			
		||||
             "is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
 | 
			
		||||
             "the fiber handling the error can see which fiber raised the signal. This function should "
 | 
			
		||||
             "be used mostly for debugging purposes.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/step", cfun_debug_step,
 | 
			
		||||
        JDOC("(debug/step fiber &opt x)\n\n"
 | 
			
		||||
             "Run a fiber for one virtual instruction of the Janet machine. Can optionally "
 | 
			
		||||
             "pass in a value that will be passed as the resuming value. Returns the signal value, "
 | 
			
		||||
             "which will usually be nil, as breakpoints raise nil signals.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_debug(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, debug_cfuns);
 | 
			
		||||
    JanetRegExt debug_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("debug/break", cfun_debug_break),
 | 
			
		||||
        JANET_CORE_REG("debug/unbreak", cfun_debug_unbreak),
 | 
			
		||||
        JANET_CORE_REG("debug/fbreak", cfun_debug_fbreak),
 | 
			
		||||
        JANET_CORE_REG("debug/unfbreak", cfun_debug_unfbreak),
 | 
			
		||||
        JANET_CORE_REG("debug/arg-stack", cfun_debug_argstack),
 | 
			
		||||
        JANET_CORE_REG("debug/stack", cfun_debug_stack),
 | 
			
		||||
        JANET_CORE_REG("debug/stacktrace", cfun_debug_stacktrace),
 | 
			
		||||
        JANET_CORE_REG("debug/lineage", cfun_debug_lineage),
 | 
			
		||||
        JANET_CORE_REG("debug/step", cfun_debug_step),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, debug_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,7 +37,7 @@ int32_t janetc_allocfar(JanetCompiler *c) {
 | 
			
		||||
    return reg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a register less than 256 */
 | 
			
		||||
/* Get a register less than 256 for temporary use. */
 | 
			
		||||
int32_t janetc_allocnear(JanetCompiler *c, JanetcRegisterTemp tag) {
 | 
			
		||||
    return janetc_regalloc_temp(&c->scope->ra, tag);
 | 
			
		||||
}
 | 
			
		||||
@@ -205,7 +205,7 @@ static int32_t janetc_regnear(JanetCompiler *c, JanetSlot s, JanetcRegisterTemp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if two slots are equal */
 | 
			
		||||
static int janetc_sequal(JanetSlot lhs, JanetSlot rhs) {
 | 
			
		||||
int janetc_sequal(JanetSlot lhs, JanetSlot rhs) {
 | 
			
		||||
    if ((lhs.flags & ~JANET_SLOTTYPE_ANY) == (rhs.flags & ~JANET_SLOTTYPE_ANY) &&
 | 
			
		||||
            lhs.index == rhs.index &&
 | 
			
		||||
            lhs.envindex == rhs.envindex) {
 | 
			
		||||
@@ -245,8 +245,8 @@ void janetc_copy(
 | 
			
		||||
    janetc_moveback(c, dest, nearreg);
 | 
			
		||||
    /* Cleanup */
 | 
			
		||||
    janetc_regalloc_freetemp(&c->scope->ra, nearreg, JANETC_REGTEMP_3);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Instruction templated emitters */
 | 
			
		||||
 | 
			
		||||
static int32_t emit1s(JanetCompiler *c, uint8_t op, JanetSlot s, int32_t rest, int wr) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,6 +42,9 @@ int32_t janetc_emit_ssi(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2
 | 
			
		||||
int32_t janetc_emit_ssu(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, uint8_t immediate, int wr);
 | 
			
		||||
int32_t janetc_emit_sss(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, JanetSlot s3, int wr);
 | 
			
		||||
 | 
			
		||||
/* Check if two slots are equivalent */
 | 
			
		||||
int janetc_sequal(JanetSlot x, JanetSlot y);
 | 
			
		||||
 | 
			
		||||
/* Move value from one slot to another. Cannot copy to constant slots. */
 | 
			
		||||
void janetc_copy(JanetCompiler *c, JanetSlot dest, JanetSlot src);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3311
									
								
								src/core/ev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3311
									
								
								src/core/ev.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,17 +25,55 @@
 | 
			
		||||
#ifndef JANET_FEATURES_H_defined
 | 
			
		||||
#define JANET_FEATURES_H_defined
 | 
			
		||||
 | 
			
		||||
#ifndef _POSIX_C_SOURCE
 | 
			
		||||
#define _POSIX_C_SOURCE 200809L
 | 
			
		||||
#if defined(__NetBSD__) || defined(__APPLE__) || defined(__OpenBSD__) \
 | 
			
		||||
    || 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
 | 
			
		||||
# define _POSIX_C_SOURCE 200809L
 | 
			
		||||
# 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.
 | 
			
		||||
 * It also defines realpath, etc, which would normally require
 | 
			
		||||
 * _XOPEN_SOURCE >= 500. */
 | 
			
		||||
#if !defined(_NETBSD_SOURCE) && defined(__NetBSD__)
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1593
									
								
								src/core/ffi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1593
									
								
								src/core/ffi.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										278
									
								
								src/core/fiber.c
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								src/core/fiber.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,12 @@ static void fiber_reset(JanetFiber *fiber) {
 | 
			
		||||
    fiber->child = NULL;
 | 
			
		||||
    fiber->flags = JANET_FIBER_MASK_YIELD | JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
 | 
			
		||||
    fiber->env = NULL;
 | 
			
		||||
    fiber->last_value = janet_wrap_nil();
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    fiber->waiting = NULL;
 | 
			
		||||
    fiber->sched_id = 0;
 | 
			
		||||
    fiber->supervisor_channel = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
    janet_fiber_set_status(fiber, JANET_STATUS_NEW);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -47,11 +53,11 @@ static JanetFiber *fiber_alloc(int32_t capacity) {
 | 
			
		||||
        capacity = 32;
 | 
			
		||||
    }
 | 
			
		||||
    fiber->capacity = capacity;
 | 
			
		||||
    data = malloc(sizeof(Janet) * (size_t) capacity);
 | 
			
		||||
    data = janet_malloc(sizeof(Janet) * (size_t) capacity);
 | 
			
		||||
    if (NULL == data) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_next_collection += sizeof(Janet) * capacity;
 | 
			
		||||
    janet_vm.next_collection += sizeof(Janet) * capacity;
 | 
			
		||||
    fiber->data = data;
 | 
			
		||||
    return fiber;
 | 
			
		||||
}
 | 
			
		||||
@@ -75,8 +81,13 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -85,14 +96,33 @@ JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, c
 | 
			
		||||
    return janet_fiber_reset(fiber_alloc(capacity), callee, argc, argv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_DEBUG
 | 
			
		||||
/* Test for memory issues by reallocating fiber every time we push a stack frame */
 | 
			
		||||
static void janet_fiber_refresh_memory(JanetFiber *fiber) {
 | 
			
		||||
    int32_t n = fiber->capacity;
 | 
			
		||||
    if (n) {
 | 
			
		||||
        Janet *newData = janet_malloc(sizeof(Janet) * n);
 | 
			
		||||
        if (NULL == newData) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        memcpy(newData, fiber->data, fiber->capacity * sizeof(Janet));
 | 
			
		||||
        janet_free(fiber->data);
 | 
			
		||||
        fiber->data = newData;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Ensure that the fiber has enough extra capacity */
 | 
			
		||||
void janet_fiber_setcapacity(JanetFiber *fiber, int32_t n) {
 | 
			
		||||
    Janet *newData = realloc(fiber->data, sizeof(Janet) * n);
 | 
			
		||||
    int32_t old_size = fiber->capacity;
 | 
			
		||||
    int32_t diff = n - old_size;
 | 
			
		||||
    Janet *newData = janet_realloc(fiber->data, sizeof(Janet) * n);
 | 
			
		||||
    if (NULL == newData) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    fiber->data = newData;
 | 
			
		||||
    fiber->capacity = n;
 | 
			
		||||
    janet_vm.next_collection += sizeof(Janet) * diff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Grow fiber if needed */
 | 
			
		||||
@@ -173,6 +203,10 @@ int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) {
 | 
			
		||||
 | 
			
		||||
    if (fiber->capacity < nextstacktop) {
 | 
			
		||||
        janet_fiber_setcapacity(fiber, 2 * nextstacktop);
 | 
			
		||||
#ifdef JANET_DEBUG
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_fiber_refresh_memory(fiber);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Nil unset stack arguments (Needed for gc correctness) */
 | 
			
		||||
@@ -221,8 +255,8 @@ static void janet_env_detach(JanetFuncEnv *env) {
 | 
			
		||||
        janet_env_valid(env);
 | 
			
		||||
        int32_t len = env->length;
 | 
			
		||||
        size_t s = sizeof(Janet) * (size_t) len;
 | 
			
		||||
        Janet *vmem = malloc(s);
 | 
			
		||||
        janet_vm_next_collection += (uint32_t) s;
 | 
			
		||||
        Janet *vmem = janet_malloc(s);
 | 
			
		||||
        janet_vm.next_collection += (uint32_t) s;
 | 
			
		||||
        if (NULL == vmem) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -305,6 +339,10 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
 | 
			
		||||
 | 
			
		||||
    if (fiber->capacity < nextstacktop) {
 | 
			
		||||
        janet_fiber_setcapacity(fiber, 2 * nextstacktop);
 | 
			
		||||
#ifdef JANET_DEBUG
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_fiber_refresh_memory(fiber);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Janet *stack = fiber->data + fiber->frame;
 | 
			
		||||
@@ -367,6 +405,10 @@ void janet_fiber_cframe(JanetFiber *fiber, JanetCFunction cfun) {
 | 
			
		||||
 | 
			
		||||
    if (fiber->capacity < nextstacktop) {
 | 
			
		||||
        janet_fiber_setcapacity(fiber, 2 * nextstacktop);
 | 
			
		||||
#ifdef JANET_DEBUG
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_fiber_refresh_memory(fiber);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set the next frame */
 | 
			
		||||
@@ -382,8 +424,7 @@ void janet_fiber_cframe(JanetFiber *fiber, JanetCFunction cfun) {
 | 
			
		||||
    newframe->flags = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Pop a stack frame from the fiber. Returns the new stack frame, or
 | 
			
		||||
 * NULL if there are no more frames */
 | 
			
		||||
/* Pop a stack frame from the fiber. */
 | 
			
		||||
void janet_fiber_popframe(JanetFiber *fiber) {
 | 
			
		||||
    JanetStackFrame *frame = janet_fiber_frame(fiber);
 | 
			
		||||
    if (fiber->frame == 0) return;
 | 
			
		||||
@@ -402,16 +443,19 @@ JanetFiberStatus janet_fiber_status(JanetFiber *f) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetFiber *janet_current_fiber(void) {
 | 
			
		||||
    return janet_vm_fiber;
 | 
			
		||||
    return janet_vm.fiber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetFiber *janet_root_fiber(void) {
 | 
			
		||||
    return janet_vm_root_fiber;
 | 
			
		||||
    return janet_vm.root_fiber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* CFuns */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_getenv(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_getenv,
 | 
			
		||||
              "(fiber/getenv fiber)",
 | 
			
		||||
              "Gets the environment for a fiber. Returns nil if no such table is "
 | 
			
		||||
              "set yet.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    return fiber->env ?
 | 
			
		||||
@@ -419,7 +463,10 @@ static Janet cfun_fiber_getenv(int32_t argc, Janet *argv) {
 | 
			
		||||
           janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_setenv(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_setenv,
 | 
			
		||||
              "(fiber/setenv fiber table)",
 | 
			
		||||
              "Sets the environment table for a fiber. Set to nil to remove the current "
 | 
			
		||||
              "environment.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    if (janet_checktype(argv[1], JANET_NIL)) {
 | 
			
		||||
@@ -430,15 +477,44 @@ static Janet cfun_fiber_setenv(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_new,
 | 
			
		||||
              "(fiber/new func &opt sigmask env)",
 | 
			
		||||
              "Create a new fiber with function body func. Can optionally "
 | 
			
		||||
              "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. "
 | 
			
		||||
              "The default sigmask is :y. "
 | 
			
		||||
              "For example,\n\n"
 | 
			
		||||
              "    (fiber/new myfun :e123)\n\n"
 | 
			
		||||
              "blocks error signals and user signals 1, 2 and 3. The signals are "
 | 
			
		||||
              "as follows:\n\n"
 | 
			
		||||
              "* :a - block all signals\n"
 | 
			
		||||
              "* :d - block debug signals\n"
 | 
			
		||||
              "* :e - block error signals\n"
 | 
			
		||||
              "* :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, 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;
 | 
			
		||||
@@ -449,7 +525,7 @@ static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
            } 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 |=
 | 
			
		||||
@@ -479,18 +555,24 @@ static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
                    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);
 | 
			
		||||
                        if (!janet_vm.fiber->env) {
 | 
			
		||||
                            janet_vm.fiber->env = janet_table(0);
 | 
			
		||||
                        }
 | 
			
		||||
                        fiber->env = janet_vm_fiber->env;
 | 
			
		||||
                        fiber->env = janet_vm.fiber->env;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'p':
 | 
			
		||||
                        if (!janet_vm_fiber->env) {
 | 
			
		||||
                            janet_vm_fiber->env = janet_table(0);
 | 
			
		||||
                        if (!janet_vm.fiber->env) {
 | 
			
		||||
                            janet_vm.fiber->env = janet_table(0);
 | 
			
		||||
                        }
 | 
			
		||||
                        fiber->env = janet_table(0);
 | 
			
		||||
                        fiber->env->proto = janet_vm_fiber->env;
 | 
			
		||||
                        fiber->env->proto = janet_vm.fiber->env;
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -499,32 +581,55 @@ static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_fiber(fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_status(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_status,
 | 
			
		||||
              "(fiber/status fib)",
 | 
			
		||||
              "Get the status of a fiber. The status will be one of:\n\n"
 | 
			
		||||
              "* :dead - the fiber has finished\n"
 | 
			
		||||
              "* :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-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);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    uint32_t s = janet_fiber_status(fiber);
 | 
			
		||||
    return janet_ckeywordv(janet_status_names[s]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_current(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_current,
 | 
			
		||||
              "(fiber/current)",
 | 
			
		||||
              "Returns the currently running fiber.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    return janet_wrap_fiber(janet_vm_fiber);
 | 
			
		||||
    return janet_wrap_fiber(janet_vm.fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_root(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_root,
 | 
			
		||||
              "(fiber/root)",
 | 
			
		||||
              "Returns the current root fiber. The root fiber is the oldest ancestor "
 | 
			
		||||
              "that does not have a parent.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    return janet_wrap_fiber(janet_vm_root_fiber);
 | 
			
		||||
    return janet_wrap_fiber(janet_vm.root_fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_maxstack(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_maxstack,
 | 
			
		||||
              "(fiber/maxstack fib)",
 | 
			
		||||
              "Gets the maximum stack size in janet values allowed for a fiber. While memory for "
 | 
			
		||||
              "the fiber's stack is not allocated up front, the fiber will not allocated more "
 | 
			
		||||
              "than this amount and will throw a stack-overflow error if more memory is needed. ") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    return janet_wrap_integer(fiber->maxstack);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_setmaxstack(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_setmaxstack,
 | 
			
		||||
              "(fiber/setmaxstack fib maxstack)",
 | 
			
		||||
              "Sets the maximum stack size in janet values for a fiber. By default, the "
 | 
			
		||||
              "maximum stack size is usually 8192.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    int32_t maxs = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -535,9 +640,7 @@ static Janet cfun_fiber_setmaxstack(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_can_resume(int32_t argc, Janet *argv) {
 | 
			
		||||
    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 ||
 | 
			
		||||
@@ -546,90 +649,39 @@ static Janet cfun_fiber_can_resume(int32_t argc, Janet *argv) {
 | 
			
		||||
                     s == JANET_STATUS_USER2 ||
 | 
			
		||||
                     s == JANET_STATUS_USER3 ||
 | 
			
		||||
                     s == JANET_STATUS_USER4;
 | 
			
		||||
    return janet_wrap_boolean(!isFinished);
 | 
			
		||||
    return !isFinished;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg fiber_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/new", cfun_fiber_new,
 | 
			
		||||
        JDOC("(fiber/new func &opt sigmask)\n\n"
 | 
			
		||||
             "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 "
 | 
			
		||||
             "is used to indicate a signal to block. The default sigmask is :y. "
 | 
			
		||||
             "For example, \n\n"
 | 
			
		||||
             "\t(fiber/new myfun :e123)\n\n"
 | 
			
		||||
             "blocks error signals and user signals 1, 2 and 3. The signals are "
 | 
			
		||||
             "as follows: \n\n"
 | 
			
		||||
             "\ta - block all signals\n"
 | 
			
		||||
             "\td - block debug signals\n"
 | 
			
		||||
             "\te - block error signals\n"
 | 
			
		||||
             "\tt - block termination signals: error + user[0-4]\n"
 | 
			
		||||
             "\tu - block user signals\n"
 | 
			
		||||
             "\ty - block yield signals\n"
 | 
			
		||||
             "\t0-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"
 | 
			
		||||
             "\ti - inherit the environment from the current fiber\n"
 | 
			
		||||
             "\tp - the environment table's prototype is the current environment table")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/status", cfun_fiber_status,
 | 
			
		||||
        JDOC("(fiber/status fib)\n\n"
 | 
			
		||||
             "Get the status of a fiber. The status will be one of:\n\n"
 | 
			
		||||
             "\t:dead - the fiber has finished\n"
 | 
			
		||||
             "\t:error - the fiber has errored out\n"
 | 
			
		||||
             "\t:debug - the fiber is suspended in debug mode\n"
 | 
			
		||||
             "\t:pending - the fiber has been yielded\n"
 | 
			
		||||
             "\t:user(0-9) - the fiber is suspended by a user signal\n"
 | 
			
		||||
             "\t:alive - the fiber is currently running and cannot be resumed\n"
 | 
			
		||||
             "\t:new - the fiber has just been created and not yet run")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/root", cfun_fiber_root,
 | 
			
		||||
        JDOC("(fiber/root)\n\n"
 | 
			
		||||
             "Returns the current root fiber. The root fiber is the oldest ancestor "
 | 
			
		||||
             "that does not have a parent.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/current", cfun_fiber_current,
 | 
			
		||||
        JDOC("(fiber/current)\n\n"
 | 
			
		||||
             "Returns the currently running fiber.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/maxstack", cfun_fiber_maxstack,
 | 
			
		||||
        JDOC("(fiber/maxstack fib)\n\n"
 | 
			
		||||
             "Gets the maximum stack size in janet values allowed for a fiber. While memory for "
 | 
			
		||||
             "the fiber's stack is not allocated up front, the fiber will not allocated more "
 | 
			
		||||
             "than this amount and will throw a stack-overflow error if more memory is needed. ")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/setmaxstack", cfun_fiber_setmaxstack,
 | 
			
		||||
        JDOC("(fiber/setmaxstack fib maxstack)\n\n"
 | 
			
		||||
             "Sets the maximum stack size in janet values for a fiber. By default, the "
 | 
			
		||||
             "maximum stack size is usually 8192.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/getenv", cfun_fiber_getenv,
 | 
			
		||||
        JDOC("(fiber/getenv fiber)\n\n"
 | 
			
		||||
             "Gets the environment for a fiber. Returns nil if no such table is "
 | 
			
		||||
             "set yet.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/setenv", cfun_fiber_setenv,
 | 
			
		||||
        JDOC("(fiber/setenv fiber table)\n\n"
 | 
			
		||||
             "Sets the environment table for a fiber. Set to nil to remove the current "
 | 
			
		||||
             "environment.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/can-resume?", cfun_fiber_can_resume,
 | 
			
		||||
        JDOC("(fiber/can-resume? fiber)\n\n"
 | 
			
		||||
             "Check if a fiber is finished and cannot be resumed.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
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)",
 | 
			
		||||
              "Get the last value returned or signaled from the fiber.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    return fiber->last_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_fiber(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, fiber_cfuns);
 | 
			
		||||
    JanetRegExt fiber_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("fiber/new", cfun_fiber_new),
 | 
			
		||||
        JANET_CORE_REG("fiber/status", cfun_fiber_status),
 | 
			
		||||
        JANET_CORE_REG("fiber/root", cfun_fiber_root),
 | 
			
		||||
        JANET_CORE_REG("fiber/current", cfun_fiber_current),
 | 
			
		||||
        JANET_CORE_REG("fiber/maxstack", cfun_fiber_maxstack),
 | 
			
		||||
        JANET_CORE_REG("fiber/setmaxstack", cfun_fiber_setmaxstack),
 | 
			
		||||
        JANET_CORE_REG("fiber/getenv", cfun_fiber_getenv),
 | 
			
		||||
        JANET_CORE_REG("fiber/setenv", cfun_fiber_setenv),
 | 
			
		||||
        JANET_CORE_REG("fiber/can-resume?", cfun_fiber_can_resume),
 | 
			
		||||
        JANET_CORE_REG("fiber/last-value", cfun_fiber_last_value),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, fiber_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -46,7 +46,8 @@
 | 
			
		||||
#define JANET_FIBER_MASK_USERN(N) (16 << (N))
 | 
			
		||||
#define JANET_FIBER_MASK_USER 0x3FF0
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_STATUS_MASK 0xFF0000
 | 
			
		||||
#define JANET_FIBER_STATUS_MASK 0x3F0000
 | 
			
		||||
#define JANET_FIBER_RESUME_SIGNAL 0x400000
 | 
			
		||||
#define JANET_FIBER_STATUS_OFFSET 16
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_BREAKPOINT       0x1000000
 | 
			
		||||
@@ -55,7 +56,9 @@
 | 
			
		||||
#define JANET_FIBER_DID_LONGJUMP     0x8000000
 | 
			
		||||
#define JANET_FIBER_FLAG_MASK        0xF000000
 | 
			
		||||
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_CANCELED 0x10000
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000
 | 
			
		||||
#define JANET_FIBER_FLAG_ROOT 0x40000
 | 
			
		||||
 | 
			
		||||
#define janet_fiber_set_status(f, s) do {\
 | 
			
		||||
    (f)->flags &= ~JANET_FIBER_STATUS_MASK;\
 | 
			
		||||
@@ -76,4 +79,8 @@ void janet_fiber_popframe(JanetFiber *fiber);
 | 
			
		||||
void janet_env_maybe_detach(JanetFuncEnv *env);
 | 
			
		||||
int janet_env_valid(JanetFuncEnv *env);
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
void janet_fiber_did_resume(JanetFiber *fiber);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										263
									
								
								src/core/gc.c
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								src/core/gc.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,29 +28,9 @@
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "fiber.h"
 | 
			
		||||
#include "vector.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct JanetScratch {
 | 
			
		||||
    JanetScratchFinalizer finalize;
 | 
			
		||||
    long long mem[]; /* for proper alignment */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* GC State */
 | 
			
		||||
JANET_THREAD_LOCAL void *janet_vm_blocks;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_gc_interval;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_next_collection;
 | 
			
		||||
JANET_THREAD_LOCAL int janet_vm_gc_suspend = 0;
 | 
			
		||||
 | 
			
		||||
/* Roots */
 | 
			
		||||
JANET_THREAD_LOCAL Janet *janet_vm_roots;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_root_count;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_root_capacity;
 | 
			
		||||
 | 
			
		||||
/* Scratch Memory */
 | 
			
		||||
JANET_THREAD_LOCAL JanetScratch **janet_scratch_mem;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_scratch_cap;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_scratch_len;
 | 
			
		||||
 | 
			
		||||
/* Helpers for marking the various gc types */
 | 
			
		||||
static void janet_mark_funcenv(JanetFuncEnv *env);
 | 
			
		||||
static void janet_mark_funcdef(JanetFuncDef *def);
 | 
			
		||||
@@ -70,7 +50,7 @@ static JANET_THREAD_LOCAL size_t orig_rootcount;
 | 
			
		||||
 | 
			
		||||
/* Hint to the GC that we may need to collect */
 | 
			
		||||
void janet_gcpressure(size_t s) {
 | 
			
		||||
    janet_vm_next_collection += s;
 | 
			
		||||
    janet_vm.next_collection += s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark a value */
 | 
			
		||||
@@ -125,6 +105,14 @@ static void janet_mark_buffer(JanetBuffer *buffer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_abstract(void *adata) {
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Check if abstract type is a threaded abstract type. If it is, marking means
 | 
			
		||||
     * updating the threaded_abstract table. */
 | 
			
		||||
    if ((janet_abstract_head(adata)->gc.flags & JANET_MEM_TYPEBITS) == JANET_MEMORY_THREADED_ABSTRACT) {
 | 
			
		||||
        janet_table_put(&janet_vm.threaded_abstracts, janet_wrap_abstract(adata), janet_wrap_true());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (janet_gc_reachable(janet_abstract_head(adata)))
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(janet_abstract_head(adata));
 | 
			
		||||
@@ -135,6 +123,8 @@ static void janet_mark_abstract(void *adata) {
 | 
			
		||||
 | 
			
		||||
/* Mark a bunch of items in memory */
 | 
			
		||||
static void janet_mark_many(const Janet *values, int32_t n) {
 | 
			
		||||
    if (values == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
    const Janet *end = values + n;
 | 
			
		||||
    while (values < end) {
 | 
			
		||||
        janet_mark(*values);
 | 
			
		||||
@@ -172,10 +162,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) {
 | 
			
		||||
@@ -216,6 +209,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) {
 | 
			
		||||
@@ -224,11 +223,14 @@ static void janet_mark_function(JanetFunction *func) {
 | 
			
		||||
    if (janet_gc_reachable(func))
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(func);
 | 
			
		||||
    numenvs = func->def->environments_length;
 | 
			
		||||
    for (i = 0; i < numenvs; ++i) {
 | 
			
		||||
        janet_mark_funcenv(func->envs[i]);
 | 
			
		||||
    if (NULL != func->def) {
 | 
			
		||||
        /* this should always be true, except if function is only partially constructed */
 | 
			
		||||
        numenvs = func->def->environments_length;
 | 
			
		||||
        for (i = 0; i < numenvs; ++i) {
 | 
			
		||||
            janet_mark_funcenv(func->envs[i]);
 | 
			
		||||
        }
 | 
			
		||||
        janet_mark_funcdef(func->def);
 | 
			
		||||
    }
 | 
			
		||||
    janet_mark_funcdef(func->def);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_fiber(JanetFiber *fiber) {
 | 
			
		||||
@@ -239,6 +241,8 @@ recur:
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(fiber);
 | 
			
		||||
 | 
			
		||||
    janet_mark(fiber->last_value);
 | 
			
		||||
 | 
			
		||||
    /* Mark values on the argument stack */
 | 
			
		||||
    janet_mark_many(fiber->data + fiber->stackstart,
 | 
			
		||||
                    fiber->stacktop - fiber->stackstart);
 | 
			
		||||
@@ -260,6 +264,12 @@ recur:
 | 
			
		||||
    if (fiber->env)
 | 
			
		||||
        janet_mark_table(fiber->env);
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    if (fiber->supervisor_channel) {
 | 
			
		||||
        janet_mark_abstract(fiber->supervisor_channel);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Explicit tail recursion */
 | 
			
		||||
    if (fiber->child) {
 | 
			
		||||
        fiber = fiber->child;
 | 
			
		||||
@@ -277,13 +287,13 @@ static void janet_deinit_block(JanetGCObject *mem) {
 | 
			
		||||
            janet_symbol_deinit(((JanetStringHead *) mem)->data);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_MEMORY_ARRAY:
 | 
			
		||||
            free(((JanetArray *) mem)->data);
 | 
			
		||||
            janet_free(((JanetArray *) mem)->data);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_MEMORY_TABLE:
 | 
			
		||||
            free(((JanetTable *) mem)->data);
 | 
			
		||||
            janet_free(((JanetTable *) mem)->data);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_MEMORY_FIBER:
 | 
			
		||||
            free(((JanetFiber *)mem)->data);
 | 
			
		||||
            janet_free(((JanetFiber *)mem)->data);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_MEMORY_BUFFER:
 | 
			
		||||
            janet_buffer_deinit((JanetBuffer *) mem);
 | 
			
		||||
@@ -298,18 +308,19 @@ static void janet_deinit_block(JanetGCObject *mem) {
 | 
			
		||||
        case JANET_MEMORY_FUNCENV: {
 | 
			
		||||
            JanetFuncEnv *env = (JanetFuncEnv *)mem;
 | 
			
		||||
            if (0 == env->offset)
 | 
			
		||||
                free(env->as.values);
 | 
			
		||||
                janet_free(env->as.values);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
        case JANET_MEMORY_FUNCDEF: {
 | 
			
		||||
            JanetFuncDef *def = (JanetFuncDef *)mem;
 | 
			
		||||
            /* TODO - get this all with one alloc and one free */
 | 
			
		||||
            free(def->defs);
 | 
			
		||||
            free(def->environments);
 | 
			
		||||
            free(def->constants);
 | 
			
		||||
            free(def->bytecode);
 | 
			
		||||
            free(def->sourcemap);
 | 
			
		||||
            free(def->closure_bitset);
 | 
			
		||||
            janet_free(def->defs);
 | 
			
		||||
            janet_free(def->environments);
 | 
			
		||||
            janet_free(def->constants);
 | 
			
		||||
            janet_free(def->bytecode);
 | 
			
		||||
            janet_free(def->sourcemap);
 | 
			
		||||
            janet_free(def->closure_bitset);
 | 
			
		||||
            janet_free(def->symbolmap);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -319,24 +330,61 @@ static void janet_deinit_block(JanetGCObject *mem) {
 | 
			
		||||
 * 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.blocks;
 | 
			
		||||
    JanetGCObject *next;
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        next = current->next;
 | 
			
		||||
        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->next = next;
 | 
			
		||||
                previous->data.next = next;
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_vm_blocks = next;
 | 
			
		||||
                janet_vm.blocks = next;
 | 
			
		||||
            }
 | 
			
		||||
            free(current);
 | 
			
		||||
            janet_free(current);
 | 
			
		||||
        }
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Sweep threaded abstract types for references to decrement */
 | 
			
		||||
    JanetKV *items = janet_vm.threaded_abstracts.data;
 | 
			
		||||
    for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
 | 
			
		||||
        if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
 | 
			
		||||
 | 
			
		||||
            /* If item was not visited during the mark phase, then this
 | 
			
		||||
             * abstract type isn't present in the heap and needs its refcount
 | 
			
		||||
             * decremented, and shouuld be removed from table. If the refcount is
 | 
			
		||||
             * then 0, the item will be collected. This ensures that only one interpreter
 | 
			
		||||
             * will clean up the threaded abstract. */
 | 
			
		||||
 | 
			
		||||
            /* If not visited... */
 | 
			
		||||
            if (!janet_truthy(items[i].value)) {
 | 
			
		||||
                void *abst = janet_unwrap_abstract(items[i].key);
 | 
			
		||||
                if (0 == janet_abstract_decref(abst)) {
 | 
			
		||||
                    /* Run finalizer */
 | 
			
		||||
                    JanetAbstractHead *head = janet_abstract_head(abst);
 | 
			
		||||
                    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));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Reset for next sweep */
 | 
			
		||||
            items[i].value = janet_wrap_false();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Allocate some memory that is tracked for garbage collection */
 | 
			
		||||
@@ -344,8 +392,8 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
 | 
			
		||||
    JanetGCObject *mem;
 | 
			
		||||
 | 
			
		||||
    /* Make sure everything is inited */
 | 
			
		||||
    janet_assert(NULL != janet_vm_cache, "please initialize janet before use");
 | 
			
		||||
    mem = malloc(size);
 | 
			
		||||
    janet_assert(NULL != janet_vm.cache, "please initialize janet before use");
 | 
			
		||||
    mem = janet_malloc(size);
 | 
			
		||||
 | 
			
		||||
    /* Check for bad malloc */
 | 
			
		||||
    if (NULL == mem) {
 | 
			
		||||
@@ -356,9 +404,10 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
 | 
			
		||||
    mem->flags = type;
 | 
			
		||||
 | 
			
		||||
    /* Prepend block to heap list */
 | 
			
		||||
    janet_vm_next_collection += size;
 | 
			
		||||
    mem->next = janet_vm_blocks;
 | 
			
		||||
    janet_vm_blocks = mem;
 | 
			
		||||
    janet_vm.next_collection += size;
 | 
			
		||||
    mem->data.next = janet_vm.blocks;
 | 
			
		||||
    janet_vm.blocks = mem;
 | 
			
		||||
    janet_vm.block_count++;
 | 
			
		||||
 | 
			
		||||
    return (void *)mem;
 | 
			
		||||
}
 | 
			
		||||
@@ -367,15 +416,15 @@ static void free_one_scratch(JanetScratch *s) {
 | 
			
		||||
    if (NULL != s->finalize) {
 | 
			
		||||
        s->finalize((char *) s->mem);
 | 
			
		||||
    }
 | 
			
		||||
    free(s);
 | 
			
		||||
    janet_free(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Free all allocated scratch memory */
 | 
			
		||||
static void janet_free_all_scratch(void) {
 | 
			
		||||
    for (size_t i = 0; i < janet_scratch_len; i++) {
 | 
			
		||||
        free_one_scratch(janet_scratch_mem[i]);
 | 
			
		||||
    for (size_t i = 0; i < janet_vm.scratch_len; i++) {
 | 
			
		||||
        free_one_scratch(janet_vm.scratch_mem[i]);
 | 
			
		||||
    }
 | 
			
		||||
    janet_scratch_len = 0;
 | 
			
		||||
    janet_vm.scratch_len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetScratch *janet_mem2scratch(void *mem) {
 | 
			
		||||
@@ -386,20 +435,29 @@ static JanetScratch *janet_mem2scratch(void *mem) {
 | 
			
		||||
/* Run garbage collection */
 | 
			
		||||
void janet_collect(void) {
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
    if (janet_vm_gc_suspend) return;
 | 
			
		||||
    if (janet_vm.gc_suspend) return;
 | 
			
		||||
    depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    orig_rootcount = janet_vm_root_count;
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    janet_net_markloop();
 | 
			
		||||
    /* Try and 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
 | 
			
		||||
     * heuristic for automatically changing the gc interval */
 | 
			
		||||
    if (janet_vm.block_count * 8 > janet_vm.gc_interval) {
 | 
			
		||||
        janet_vm.gc_interval = janet_vm.block_count * sizeof(JanetGCObject);
 | 
			
		||||
    }
 | 
			
		||||
    orig_rootcount = janet_vm.root_count;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_ev_mark();
 | 
			
		||||
#endif
 | 
			
		||||
    janet_mark_fiber(janet_vm.root_fiber);
 | 
			
		||||
    for (i = 0; i < orig_rootcount; i++)
 | 
			
		||||
        janet_mark(janet_vm_roots[i]);
 | 
			
		||||
    while (orig_rootcount < janet_vm_root_count) {
 | 
			
		||||
        Janet x = janet_vm_roots[--janet_vm_root_count];
 | 
			
		||||
        janet_mark(janet_vm.roots[i]);
 | 
			
		||||
    while (orig_rootcount < janet_vm.root_count) {
 | 
			
		||||
        Janet x = janet_vm.roots[--janet_vm.root_count];
 | 
			
		||||
        janet_mark(x);
 | 
			
		||||
    }
 | 
			
		||||
    janet_sweep();
 | 
			
		||||
    janet_vm_next_collection = 0;
 | 
			
		||||
    janet_vm.next_collection = 0;
 | 
			
		||||
    janet_free_all_scratch();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -407,17 +465,17 @@ void janet_collect(void) {
 | 
			
		||||
 * and all of its children. If gcroot is called on a value n times, unroot
 | 
			
		||||
 * must also be called n times to remove it as a gc root. */
 | 
			
		||||
void janet_gcroot(Janet root) {
 | 
			
		||||
    size_t newcount = janet_vm_root_count + 1;
 | 
			
		||||
    if (newcount > janet_vm_root_capacity) {
 | 
			
		||||
    size_t newcount = janet_vm.root_count + 1;
 | 
			
		||||
    if (newcount > janet_vm.root_capacity) {
 | 
			
		||||
        size_t newcap = 2 * newcount;
 | 
			
		||||
        janet_vm_roots = realloc(janet_vm_roots, sizeof(Janet) * newcap);
 | 
			
		||||
        if (NULL == janet_vm_roots) {
 | 
			
		||||
        janet_vm.roots = janet_realloc(janet_vm.roots, sizeof(Janet) * newcap);
 | 
			
		||||
        if (NULL == janet_vm.roots) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        janet_vm_root_capacity = newcap;
 | 
			
		||||
        janet_vm.root_capacity = newcap;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_roots[janet_vm_root_count] = root;
 | 
			
		||||
    janet_vm_root_count = newcount;
 | 
			
		||||
    janet_vm.roots[janet_vm.root_count] = root;
 | 
			
		||||
    janet_vm.root_count = newcount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Identity equality for GC purposes */
 | 
			
		||||
@@ -438,11 +496,11 @@ static int janet_gc_idequals(Janet lhs, Janet rhs) {
 | 
			
		||||
/* Remove a root value from the GC. This allows the gc to potentially reclaim
 | 
			
		||||
 * a value and all its children. */
 | 
			
		||||
int janet_gcunroot(Janet root) {
 | 
			
		||||
    Janet *vtop = janet_vm_roots + janet_vm_root_count;
 | 
			
		||||
    Janet *vtop = janet_vm.roots + janet_vm.root_count;
 | 
			
		||||
    /* Search from top to bottom as access is most likely LIFO */
 | 
			
		||||
    for (Janet *v = janet_vm_roots; v < vtop; v++) {
 | 
			
		||||
    for (Janet *v = janet_vm.roots; v < vtop; v++) {
 | 
			
		||||
        if (janet_gc_idequals(root, *v)) {
 | 
			
		||||
            *v = janet_vm_roots[--janet_vm_root_count];
 | 
			
		||||
            *v = janet_vm.roots[--janet_vm.root_count];
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -451,12 +509,12 @@ int janet_gcunroot(Janet root) {
 | 
			
		||||
 | 
			
		||||
/* Remove a root value from the GC. This sets the effective reference count to 0. */
 | 
			
		||||
int janet_gcunrootall(Janet root) {
 | 
			
		||||
    Janet *vtop = janet_vm_roots + janet_vm_root_count;
 | 
			
		||||
    Janet *vtop = janet_vm.roots + janet_vm.root_count;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    /* Search from top to bottom as access is most likely LIFO */
 | 
			
		||||
    for (Janet *v = janet_vm_roots; v < vtop; v++) {
 | 
			
		||||
    for (Janet *v = janet_vm.roots; v < vtop; v++) {
 | 
			
		||||
        if (janet_gc_idequals(root, *v)) {
 | 
			
		||||
            *v = janet_vm_roots[--janet_vm_root_count];
 | 
			
		||||
            *v = janet_vm.roots[--janet_vm.root_count];
 | 
			
		||||
            vtop--;
 | 
			
		||||
            ret = 1;
 | 
			
		||||
        }
 | 
			
		||||
@@ -466,44 +524,59 @@ int janet_gcunrootall(Janet root) {
 | 
			
		||||
 | 
			
		||||
/* Free all allocated memory */
 | 
			
		||||
void janet_clear_memory(void) {
 | 
			
		||||
    JanetGCObject *current = janet_vm_blocks;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    JanetKV *items = janet_vm.threaded_abstracts.data;
 | 
			
		||||
    for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
 | 
			
		||||
        if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
 | 
			
		||||
            void *abst = janet_unwrap_abstract(items[i].key);
 | 
			
		||||
            if (0 == janet_abstract_decref(abst)) {
 | 
			
		||||
                JanetAbstractHead *head = janet_abstract_head(abst);
 | 
			
		||||
                if (head->type->gc) {
 | 
			
		||||
                    janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
 | 
			
		||||
                }
 | 
			
		||||
                janet_free(janet_abstract_head(abst));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    JanetGCObject *current = janet_vm.blocks;
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        janet_deinit_block(current);
 | 
			
		||||
        JanetGCObject *next = current->next;
 | 
			
		||||
        free(current);
 | 
			
		||||
        JanetGCObject *next = current->data.next;
 | 
			
		||||
        janet_free(current);
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_blocks = NULL;
 | 
			
		||||
    janet_vm.blocks = NULL;
 | 
			
		||||
    janet_free_all_scratch();
 | 
			
		||||
    free(janet_scratch_mem);
 | 
			
		||||
    janet_free(janet_vm.scratch_mem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Primitives for suspending GC. */
 | 
			
		||||
int janet_gclock(void) {
 | 
			
		||||
    return janet_vm_gc_suspend++;
 | 
			
		||||
    return janet_vm.gc_suspend++;
 | 
			
		||||
}
 | 
			
		||||
void janet_gcunlock(int handle) {
 | 
			
		||||
    janet_vm_gc_suspend = handle;
 | 
			
		||||
    janet_vm.gc_suspend = handle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Scratch memory API */
 | 
			
		||||
 | 
			
		||||
void *janet_smalloc(size_t size) {
 | 
			
		||||
    JanetScratch *s = malloc(sizeof(JanetScratch) + size);
 | 
			
		||||
    JanetScratch *s = janet_malloc(sizeof(JanetScratch) + size);
 | 
			
		||||
    if (NULL == s) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    s->finalize = NULL;
 | 
			
		||||
    if (janet_scratch_len == janet_scratch_cap) {
 | 
			
		||||
        size_t newcap = 2 * janet_scratch_cap + 2;
 | 
			
		||||
        JanetScratch **newmem = (JanetScratch **) realloc(janet_scratch_mem, newcap * sizeof(JanetScratch));
 | 
			
		||||
    if (janet_vm.scratch_len == janet_vm.scratch_cap) {
 | 
			
		||||
        size_t newcap = 2 * janet_vm.scratch_cap + 2;
 | 
			
		||||
        JanetScratch **newmem = (JanetScratch **) janet_realloc(janet_vm.scratch_mem, newcap * sizeof(JanetScratch));
 | 
			
		||||
        if (NULL == newmem) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        janet_scratch_cap = newcap;
 | 
			
		||||
        janet_scratch_mem = newmem;
 | 
			
		||||
        janet_vm.scratch_cap = newcap;
 | 
			
		||||
        janet_vm.scratch_mem = newmem;
 | 
			
		||||
    }
 | 
			
		||||
    janet_scratch_mem[janet_scratch_len++] = s;
 | 
			
		||||
    janet_vm.scratch_mem[janet_vm.scratch_len++] = s;
 | 
			
		||||
    return (char *)(s->mem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -520,14 +593,14 @@ void *janet_scalloc(size_t nmemb, size_t size) {
 | 
			
		||||
void *janet_srealloc(void *mem, size_t size) {
 | 
			
		||||
    if (NULL == mem) return janet_smalloc(size);
 | 
			
		||||
    JanetScratch *s = janet_mem2scratch(mem);
 | 
			
		||||
    if (janet_scratch_len) {
 | 
			
		||||
        for (size_t i = janet_scratch_len - 1; ; i--) {
 | 
			
		||||
            if (janet_scratch_mem[i] == s) {
 | 
			
		||||
                JanetScratch *news = realloc(s, size + sizeof(JanetScratch));
 | 
			
		||||
    if (janet_vm.scratch_len) {
 | 
			
		||||
        for (size_t i = janet_vm.scratch_len - 1; ; i--) {
 | 
			
		||||
            if (janet_vm.scratch_mem[i] == s) {
 | 
			
		||||
                JanetScratch *news = janet_realloc(s, size + sizeof(JanetScratch));
 | 
			
		||||
                if (NULL == news) {
 | 
			
		||||
                    JANET_OUT_OF_MEMORY;
 | 
			
		||||
                }
 | 
			
		||||
                janet_scratch_mem[i] = news;
 | 
			
		||||
                janet_vm.scratch_mem[i] = news;
 | 
			
		||||
                return (char *)(news->mem);
 | 
			
		||||
            }
 | 
			
		||||
            if (i == 0) break;
 | 
			
		||||
@@ -544,10 +617,10 @@ void janet_sfinalizer(void *mem, JanetScratchFinalizer finalizer) {
 | 
			
		||||
void janet_sfree(void *mem) {
 | 
			
		||||
    if (NULL == mem) return;
 | 
			
		||||
    JanetScratch *s = janet_mem2scratch(mem);
 | 
			
		||||
    if (janet_scratch_len) {
 | 
			
		||||
        for (size_t i = janet_scratch_len - 1; ; i--) {
 | 
			
		||||
            if (janet_scratch_mem[i] == s) {
 | 
			
		||||
                janet_scratch_mem[i] = janet_scratch_mem[--janet_scratch_len];
 | 
			
		||||
    if (janet_vm.scratch_len) {
 | 
			
		||||
        for (size_t i = janet_vm.scratch_len - 1; ; i--) {
 | 
			
		||||
            if (janet_vm.scratch_mem[i] == s) {
 | 
			
		||||
                janet_vm.scratch_mem[i] = janet_vm.scratch_mem[--janet_vm.scratch_len];
 | 
			
		||||
                free_one_scratch(s);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -55,10 +55,11 @@ enum JanetMemoryType {
 | 
			
		||||
    JANET_MEMORY_FUNCTION,
 | 
			
		||||
    JANET_MEMORY_ABSTRACT,
 | 
			
		||||
    JANET_MEMORY_FUNCENV,
 | 
			
		||||
    JANET_MEMORY_FUNCDEF
 | 
			
		||||
    JANET_MEMORY_FUNCDEF,
 | 
			
		||||
    JANET_MEMORY_THREADED_ABSTRACT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* To allocate collectable memory, one must calk janet_alloc, initialize the memory,
 | 
			
		||||
/* To allocate collectable memory, one must call janet_alloc, initialize the memory,
 | 
			
		||||
 * and then call when janet_enablegc when it is initailize and reachable by the gc (on the JANET stack) */
 | 
			
		||||
void *janet_gcalloc(enum JanetMemoryType type, size_t size);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -20,18 +20,18 @@
 | 
			
		||||
* IN THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
/* Conditional compilation */
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +39,8 @@
 | 
			
		||||
 | 
			
		||||
static int it_s64_get(void *p, Janet key, Janet *out);
 | 
			
		||||
static int it_u64_get(void *p, Janet key, Janet *out);
 | 
			
		||||
static Janet janet_int64_next(void *p, Janet key);
 | 
			
		||||
static Janet janet_uint64_next(void *p, Janet key);
 | 
			
		||||
 | 
			
		||||
static int32_t janet_int64_hash(void *p1, size_t size) {
 | 
			
		||||
    (void) size;
 | 
			
		||||
@@ -92,7 +94,8 @@ const JanetAbstractType janet_s64_type = {
 | 
			
		||||
    it_s64_tostring,
 | 
			
		||||
    janet_int64_compare,
 | 
			
		||||
    janet_int64_hash,
 | 
			
		||||
    JANET_ATEND_HASH
 | 
			
		||||
    janet_int64_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_u64_type = {
 | 
			
		||||
@@ -106,7 +109,8 @@ const JanetAbstractType janet_u64_type = {
 | 
			
		||||
    it_u64_tostring,
 | 
			
		||||
    janet_uint64_compare,
 | 
			
		||||
    janet_int64_hash,
 | 
			
		||||
    JANET_ATEND_HASH
 | 
			
		||||
    janet_uint64_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int64_t janet_unwrap_s64(Janet x) {
 | 
			
		||||
@@ -134,7 +138,7 @@ int64_t janet_unwrap_s64(Janet x) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_panic("bad s64 initializer");
 | 
			
		||||
    janet_panicf("can not convert %t %q to 64 bit signed integer", x, x);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -144,7 +148,9 @@ uint64_t janet_unwrap_u64(Janet x) {
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_NUMBER : {
 | 
			
		||||
            double dbl = janet_unwrap_number(x);
 | 
			
		||||
            if ((dbl >= 0) && (dbl <= MAX_INT_IN_DBL))
 | 
			
		||||
            /* 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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -163,7 +169,7 @@ uint64_t janet_unwrap_u64(Janet x) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_panic("bad u64 initializer");
 | 
			
		||||
    janet_panicf("can not convert %t %q to a 64 bit unsigned integer", x, x);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -187,25 +193,114 @@ Janet janet_wrap_u64(uint64_t x) {
 | 
			
		||||
    return janet_wrap_abstract(box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_it_s64_new,
 | 
			
		||||
              "(int/s64 value)",
 | 
			
		||||
              "Create a boxed signed 64 bit integer from a string value.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_s64(janet_unwrap_s64(argv[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_u64_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_it_u64_new,
 | 
			
		||||
              "(int/u64 value)",
 | 
			
		||||
              "Create a boxed unsigned 64 bit integer from a string value.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_u64(janet_unwrap_u64(argv[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Code to support polymorphic comparison.
 | 
			
		||||
//
 | 
			
		||||
// int/u64 and int/s64 support a "compare" method that allows
 | 
			
		||||
// comparison to each other, and to Janet numbers, using the
 | 
			
		||||
// "compare" "compare<" ... functions.
 | 
			
		||||
//
 | 
			
		||||
// In the following code explicit casts are sometimes used to help
 | 
			
		||||
// make it clear when int/float conversions are happening.
 | 
			
		||||
//
 | 
			
		||||
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` paramater 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
 | 
			
		||||
 * comparison to each other, and to Janet numbers, using the
 | 
			
		||||
 * "compare" "compare<" ... functions.
 | 
			
		||||
 * In the following code explicit casts are sometimes used to help
 | 
			
		||||
 * make it clear when int/float conversions are happening.
 | 
			
		||||
 */
 | 
			
		||||
static int compare_double_double(double x, double y) {
 | 
			
		||||
    return (x < y) ? -1 : ((x > y) ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
@@ -242,7 +337,6 @@ 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)
 | 
			
		||||
@@ -313,25 +407,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);                       \
 | 
			
		||||
@@ -339,19 +460,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); \
 | 
			
		||||
} \
 | 
			
		||||
@@ -363,7 +484,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; \
 | 
			
		||||
    } \
 | 
			
		||||
@@ -371,29 +492,49 @@ 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_mod(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
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));
 | 
			
		||||
    *box = janet_unwrap_s64(argv[0]);
 | 
			
		||||
    for (int32_t i = 1; i < argc; i++) {
 | 
			
		||||
        int64_t value = janet_unwrap_s64(argv[i]);
 | 
			
		||||
        if (value == 0) janet_panic("division by zero");
 | 
			
		||||
        int64_t x = *box % value;
 | 
			
		||||
        if (x < 0) {
 | 
			
		||||
            x = (*box < 0) ? x - *box : x + *box;
 | 
			
		||||
        }
 | 
			
		||||
        *box = x;
 | 
			
		||||
    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]);
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
@@ -401,40 +542,45 @@ static Janet cfun_it_s64_mod(int32_t argc, Janet *argv) {
 | 
			
		||||
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 op1 = janet_unwrap_s64(argv[0]);
 | 
			
		||||
    int64_t op2 = janet_unwrap_s64(argv[1]);
 | 
			
		||||
    int64_t x = op1 % op2;
 | 
			
		||||
    if (x < 0) {
 | 
			
		||||
        x = (op1 < 0) ? x - op1 : x + op1;
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
    *box = 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, remi, %)
 | 
			
		||||
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, modi, %)
 | 
			
		||||
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, >>)
 | 
			
		||||
 | 
			
		||||
@@ -443,7 +589,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},
 | 
			
		||||
@@ -453,6 +598,8 @@ 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_modi},
 | 
			
		||||
    {"%", cfun_it_s64_rem},
 | 
			
		||||
@@ -463,10 +610,10 @@ static JanetMethod it_s64_methods[] = {
 | 
			
		||||
    {"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}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -479,23 +626,35 @@ 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_modi},
 | 
			
		||||
    {"%", cfun_it_u64_mod},
 | 
			
		||||
    {"r%", 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}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static Janet janet_int64_next(void *p, Janet key) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    return janet_nextmethod(it_s64_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_uint64_next(void *p, Janet key) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    return janet_nextmethod(it_u64_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int it_s64_get(void *p, Janet key, Janet *out) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    if (!janet_checktype(key, JANET_KEYWORD))
 | 
			
		||||
@@ -510,23 +669,16 @@ static int it_u64_get(void *p, Janet key, Janet *out) {
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), it_u64_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg it_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "int/s64", cfun_it_s64_new,
 | 
			
		||||
        JDOC("(int/s64 value)\n\n"
 | 
			
		||||
             "Create a boxed signed 64 bit integer from a string value.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "int/u64", cfun_it_u64_new,
 | 
			
		||||
        JDOC("(int/u64 value)\n\n"
 | 
			
		||||
             "Create a boxed unsigned 64 bit integer from a string value.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_inttypes(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, it_cfuns);
 | 
			
		||||
    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);
 | 
			
		||||
    janet_register_abstract_type(&janet_s64_type);
 | 
			
		||||
    janet_register_abstract_type(&janet_u64_type);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										605
									
								
								src/core/io.c
									
									
									
									
									
								
							
							
						
						
									
										605
									
								
								src/core/io.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,55 +37,73 @@
 | 
			
		||||
 | 
			
		||||
static int cfun_io_gc(void *p, size_t len);
 | 
			
		||||
static int io_file_get(void *p, Janet key, Janet *out);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_file_type = {
 | 
			
		||||
    "core/file",
 | 
			
		||||
    cfun_io_gc,
 | 
			
		||||
    NULL,
 | 
			
		||||
    io_file_get,
 | 
			
		||||
    JANET_ATEND_GET
 | 
			
		||||
    NULL,
 | 
			
		||||
    io_file_marshal,
 | 
			
		||||
    io_file_unmarshal,
 | 
			
		||||
    NULL, /* tostring */
 | 
			
		||||
    NULL, /* compare */
 | 
			
		||||
    NULL, /* hash */
 | 
			
		||||
    io_file_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Check arguments to fopen */
 | 
			
		||||
static int checkflags(const uint8_t *str) {
 | 
			
		||||
    int flags = 0;
 | 
			
		||||
static int32_t checkflags(const uint8_t *str) {
 | 
			
		||||
    int32_t flags = 0;
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    int32_t len = janet_string_length(str);
 | 
			
		||||
    if (!len || len > 3)
 | 
			
		||||
        janet_panic("file mode must have a length between 1 and 3");
 | 
			
		||||
    if (!len || len > 10)
 | 
			
		||||
        janet_panic("file mode must have a length between 1 and 10");
 | 
			
		||||
    switch (*str) {
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panicf("invalid flag %c, expected w, a, or r", *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++) {
 | 
			
		||||
        switch (str[i]) {
 | 
			
		||||
            default:
 | 
			
		||||
                janet_panicf("invalid flag %c, expected + or b", str[i]);
 | 
			
		||||
                janet_panicf("invalid flag %c, expected +, b, or n", str[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            case '+':
 | 
			
		||||
                if (flags & JANET_FILE_UPDATE) return -1;
 | 
			
		||||
                janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
 | 
			
		||||
                flags |= JANET_FILE_UPDATE;
 | 
			
		||||
                break;
 | 
			
		||||
            case 'b':
 | 
			
		||||
                if (flags & JANET_FILE_BINARY) return -1;
 | 
			
		||||
                flags |= JANET_FILE_BINARY;
 | 
			
		||||
                break;
 | 
			
		||||
            case 'n':
 | 
			
		||||
                if (flags & JANET_FILE_NONIL) return -1;
 | 
			
		||||
                flags |= JANET_FILE_NONIL;
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet makef(FILE *f, int flags) {
 | 
			
		||||
static void *makef(FILE *f, int32_t flags) {
 | 
			
		||||
    JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
 | 
			
		||||
    iof->file = f;
 | 
			
		||||
    iof->flags = flags;
 | 
			
		||||
@@ -95,39 +113,14 @@ static Janet makef(FILE *f, int flags) {
 | 
			
		||||
    if (!(flags & JANET_FILE_NOT_CLOSEABLE))
 | 
			
		||||
        fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
 | 
			
		||||
#endif
 | 
			
		||||
    return janet_wrap_abstract(iof);
 | 
			
		||||
    return iof;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Open a process */
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    const uint8_t *fname = janet_getstring(argv, 0);
 | 
			
		||||
    const uint8_t *fmode = NULL;
 | 
			
		||||
    int flags;
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
        fmode = janet_getkeyword(argv, 1);
 | 
			
		||||
        if (janet_string_length(fmode) != 1 ||
 | 
			
		||||
                !(fmode[0] == 'r' || fmode[0] == 'w')) {
 | 
			
		||||
            janet_panicf("invalid file mode :%S, expected :r or :w", fmode);
 | 
			
		||||
        }
 | 
			
		||||
        flags = JANET_FILE_PIPED | (fmode[0] == 'r' ? JANET_FILE_READ : JANET_FILE_WRITE);
 | 
			
		||||
    } 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) {
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    return makef(f, flags);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_temp(int32_t argc, Janet *argv) {
 | 
			
		||||
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.
 | 
			
		||||
@@ -137,20 +130,36 @@ static Janet cfun_io_temp(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_fopen(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fopen,
 | 
			
		||||
              "(file/open path &opt mode)",
 | 
			
		||||
              "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 "
 | 
			
		||||
              "cannot be opened, returns nil, otherwise returns the new file handle. "
 | 
			
		||||
              "Mode flags:\n\n"
 | 
			
		||||
              "* r - allow reading from the file\n\n"
 | 
			
		||||
              "* w - allow writing to the file\n\n"
 | 
			
		||||
              "* a - append to the file\n\n"
 | 
			
		||||
              "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);
 | 
			
		||||
    const uint8_t *fname = janet_getstring(argv, 0);
 | 
			
		||||
    const uint8_t *fmode;
 | 
			
		||||
    int flags;
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
        fmode = janet_getkeyword(argv, 1);
 | 
			
		||||
        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);
 | 
			
		||||
    return f ? makef(f, flags) : janet_wrap_nil();
 | 
			
		||||
    return f ? janet_makefile(f, flags)
 | 
			
		||||
           : (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil())
 | 
			
		||||
           : janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Read up to n bytes into buffer. */
 | 
			
		||||
@@ -166,7 +175,16 @@ static void read_chunk(JanetFile *iof, JanetBuffer *buffer, int32_t nBytesMax) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Read a certain number of bytes into memory */
 | 
			
		||||
static Janet cfun_io_fread(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fread,
 | 
			
		||||
              "(file/read f what &opt buf)",
 | 
			
		||||
              "Read a number of bytes from a file `f` into a buffer. A buffer `buf` can "
 | 
			
		||||
              "be provided as an optional third argument, otherwise a new buffer "
 | 
			
		||||
              "is created. `what` can either be an integer or a keyword. Returns the "
 | 
			
		||||
              "buffer with file contents. "
 | 
			
		||||
              "Values for `what`:\n\n"
 | 
			
		||||
              "* :all - read the whole file\n\n"
 | 
			
		||||
              "* :line - read up to and including the next newline character\n\n"
 | 
			
		||||
              "* n (integer) - read up to n bytes from the file") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED) janet_panic("file is closed");
 | 
			
		||||
@@ -206,7 +224,10 @@ static Janet cfun_io_fread(int32_t argc, Janet *argv) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write bytes to a file */
 | 
			
		||||
static Janet cfun_io_fwrite(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fwrite,
 | 
			
		||||
              "(file/write f bytes)",
 | 
			
		||||
              "Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
 | 
			
		||||
              "file.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
@@ -228,68 +249,80 @@ static Janet cfun_io_fwrite(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Flush the bytes in the file */
 | 
			
		||||
static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
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)",
 | 
			
		||||
              "Flush any buffered bytes to the file system. In most files, writes are "
 | 
			
		||||
              "buffered for efficiency reasons. Returns the file handle.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
/* For closing files from C API */
 | 
			
		||||
int janet_file_close(JanetFile *file) {
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    if (!(file->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
 | 
			
		||||
        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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Cleanup a file */
 | 
			
		||||
static int cfun_io_gc(void *p, size_t len) {
 | 
			
		||||
    (void) len;
 | 
			
		||||
    JanetFile *iof = (JanetFile *)p;
 | 
			
		||||
    if (!(iof->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
 | 
			
		||||
        /* We can't panic inside a gc, so just ignore bad statuses here */
 | 
			
		||||
        if (iof->flags & JANET_FILE_PIPED) {
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
            pclose(iof->file);
 | 
			
		||||
#endif
 | 
			
		||||
        } else {
 | 
			
		||||
            fclose(iof->file);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_file_close(iof);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Close a file */
 | 
			
		||||
static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    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));
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        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();
 | 
			
		||||
    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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Seek a file */
 | 
			
		||||
static Janet cfun_io_fseek(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fseek,
 | 
			
		||||
              "(file/seek f &opt whence n)",
 | 
			
		||||
              "Jump to a relative location in the file `f`. `whence` must be one of:\n\n"
 | 
			
		||||
              "* :cur - jump relative to the current file location\n\n"
 | 
			
		||||
              "* :set - jump relative to the beginning of the file\n\n"
 | 
			
		||||
              "* :end - jump relative to the end of the file\n\n"
 | 
			
		||||
              "By default, `whence` is :cur. Optionally a value `n` may be passed "
 | 
			
		||||
              "for the relative number of bytes to seek in the file. `n` may be a real "
 | 
			
		||||
              "number to handle large files of more than 4GB. Returns the file handle.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
@@ -315,11 +348,24 @@ static Janet cfun_io_fseek(int32_t argc, Janet *argv) {
 | 
			
		||||
    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");
 | 
			
		||||
    long 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}
 | 
			
		||||
};
 | 
			
		||||
@@ -331,6 +377,55 @@ static int io_file_get(void *p, Janet key, Janet *out) {
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), io_file_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet io_file_next(void *p, Janet key) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    return janet_nextmethod(io_file_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void io_file_marshal(void *p, JanetMarshalContext *ctx) {
 | 
			
		||||
    JanetFile *iof = (JanetFile *)p;
 | 
			
		||||
    if (ctx->flags & JANET_MARSHAL_UNSAFE) {
 | 
			
		||||
        janet_marshal_abstract(ctx, p);
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
        janet_marshal_int(ctx, _fileno(iof->file));
 | 
			
		||||
#else
 | 
			
		||||
        janet_marshal_int(ctx, fileno(iof->file));
 | 
			
		||||
#endif
 | 
			
		||||
        janet_marshal_int(ctx, iof->flags);
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panic("cannot marshal file in safe mode");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *io_file_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
    if (ctx->flags & JANET_MARSHAL_UNSAFE) {
 | 
			
		||||
        JanetFile *iof = janet_unmarshal_abstract(ctx, sizeof(JanetFile));
 | 
			
		||||
        int32_t fd = janet_unmarshal_int(ctx);
 | 
			
		||||
        int32_t flags = janet_unmarshal_int(ctx);
 | 
			
		||||
        char fmt[4] = {0};
 | 
			
		||||
        int index = 0;
 | 
			
		||||
        if (flags & JANET_FILE_READ) fmt[index++] = 'r';
 | 
			
		||||
        if (flags & JANET_FILE_APPEND) {
 | 
			
		||||
            fmt[index++] = 'a';
 | 
			
		||||
        } else if (flags & JANET_FILE_WRITE) {
 | 
			
		||||
            fmt[index++] = 'w';
 | 
			
		||||
        }
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
        iof->file = _fdopen(fd, fmt);
 | 
			
		||||
#else
 | 
			
		||||
        iof->file = fdopen(fd, fmt);
 | 
			
		||||
#endif
 | 
			
		||||
        if (iof->file == NULL) {
 | 
			
		||||
            iof->flags = JANET_FILE_CLOSED;
 | 
			
		||||
        } else {
 | 
			
		||||
            iof->flags = flags;
 | 
			
		||||
        }
 | 
			
		||||
        return iof;
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panic("cannot unmarshal file in safe mode");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FILE *janet_dynfile(const char *name, FILE *def) {
 | 
			
		||||
    Janet x = janet_dyn(name);
 | 
			
		||||
    if (!janet_checktype(x, JANET_ABSTRACT)) return def;
 | 
			
		||||
@@ -340,37 +435,50 @@ FILE *janet_dynfile(const char *name, FILE *def) {
 | 
			
		||||
    return iofile->file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
 | 
			
		||||
                                int newline, const char *name, FILE *dflt_file) {
 | 
			
		||||
static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
                                  FILE *dflt_file, int32_t offset, Janet x) {
 | 
			
		||||
    FILE *f;
 | 
			
		||||
    Janet x = janet_dyn(name);
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        default:
 | 
			
		||||
            /* Other values simply do nothing */
 | 
			
		||||
            return janet_wrap_nil();
 | 
			
		||||
            janet_panicf("cannot print to %v", x);
 | 
			
		||||
        case JANET_BUFFER: {
 | 
			
		||||
            /* Special case buffer */
 | 
			
		||||
            JanetBuffer *buf = janet_unwrap_buffer(x);
 | 
			
		||||
            for (int32_t i = 0; i < argc; ++i) {
 | 
			
		||||
            for (int32_t i = offset; i < argc; ++i) {
 | 
			
		||||
                janet_to_string_b(buf, argv[i]);
 | 
			
		||||
            }
 | 
			
		||||
            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);
 | 
			
		||||
            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");
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ABSTRACT: {
 | 
			
		||||
            void *abstract = janet_unwrap_abstract(x);
 | 
			
		||||
            if (janet_abstract_type(abstract) != &janet_file_type)
 | 
			
		||||
                return janet_wrap_nil();
 | 
			
		||||
            JanetFile *iofile = abstract;
 | 
			
		||||
            io_assert_writeable(iofile);
 | 
			
		||||
            f = iofile->file;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    for (int32_t i = 0; i < argc; ++i) {
 | 
			
		||||
    for (int32_t i = offset; i < argc; ++i) {
 | 
			
		||||
        int32_t len;
 | 
			
		||||
        const uint8_t *vstr;
 | 
			
		||||
        if (janet_checktype(argv[i], JANET_BUFFER)) {
 | 
			
		||||
@@ -383,7 +491,11 @@ static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
 | 
			
		||||
        }
 | 
			
		||||
        if (len) {
 | 
			
		||||
            if (1 != fwrite(vstr, len, 1, f)) {
 | 
			
		||||
                janet_panicf("could not print %d bytes to (dyn :%s)", len, name);
 | 
			
		||||
                if (f == dflt_file) {
 | 
			
		||||
                    janet_panicf("cannot print %d bytes", len);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_panicf("cannot print %d bytes to %v", len, x);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -392,83 +504,160 @@ static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_print(int32_t argc, Janet *argv) {
 | 
			
		||||
static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
 | 
			
		||||
                                int newline, const char *name, FILE *dflt_file) {
 | 
			
		||||
    Janet x = janet_dyn(name);
 | 
			
		||||
    return cfun_io_print_impl_x(argc, argv, newline, dflt_file, 0, x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 "
 | 
			
		||||
              "a buffer. Returns nil.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 1, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_prin(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_prin,
 | 
			
		||||
              "(prin & xs)",
 | 
			
		||||
              "Same as `print`, but does not add trailing newline.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 0, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eprint(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprint,
 | 
			
		||||
              "(eprint & xs)",
 | 
			
		||||
              "Same as `print`, but uses `(dyn :err stderr)` instead of `(dyn :out stdout)`.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 1, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eprin(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprin,
 | 
			
		||||
              "(eprin & xs)",
 | 
			
		||||
              "Same as `prin`, but uses `(dyn :err stderr)` instead of `(dyn :out stdout)`.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 0, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
                                 const char *name, FILE *dflt_file) {
 | 
			
		||||
    FILE *f;
 | 
			
		||||
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.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    const char *fmt = janet_getcstring(argv, 0);
 | 
			
		||||
    Janet x = janet_dyn(name);
 | 
			
		||||
    return cfun_io_print_impl_x(argc, argv, 1, NULL, 1, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    return cfun_io_print_impl_x(argc, argv, 0, NULL, 1, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
                                   FILE *dflt_file, int32_t offset, Janet x) {
 | 
			
		||||
    FILE *f;
 | 
			
		||||
    const char *fmt = janet_getcstring(argv, offset);
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        default:
 | 
			
		||||
            /* Other values simply do nothing */
 | 
			
		||||
            return janet_wrap_nil();
 | 
			
		||||
            janet_panicf("cannot print to %v", x);
 | 
			
		||||
        case JANET_BUFFER: {
 | 
			
		||||
            /* Special case buffer */
 | 
			
		||||
            JanetBuffer *buf = janet_unwrap_buffer(x);
 | 
			
		||||
            janet_buffer_format(buf, fmt, 0, argc, argv);
 | 
			
		||||
            janet_buffer_format(buf, fmt, offset, argc, argv);
 | 
			
		||||
            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");
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ABSTRACT: {
 | 
			
		||||
            void *abstract = janet_unwrap_abstract(x);
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetBuffer *buf = janet_buffer(10);
 | 
			
		||||
    janet_buffer_format(buf, fmt, 0, argc, argv);
 | 
			
		||||
    janet_buffer_format(buf, fmt, offset, argc, argv);
 | 
			
		||||
    if (newline) janet_buffer_push_u8(buf, '\n');
 | 
			
		||||
    if (buf->count) {
 | 
			
		||||
        if (1 != fwrite(buf->data, buf->count, 1, f)) {
 | 
			
		||||
            janet_panicf("could not print %d bytes to file", buf->count, name);
 | 
			
		||||
            janet_panicf("could not print %d bytes to file", buf->count);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* Clear buffer to make things easier for GC */
 | 
			
		||||
    buf->count = 0;
 | 
			
		||||
    buf->capacity = 0;
 | 
			
		||||
    free(buf->data);
 | 
			
		||||
    janet_free(buf->data);
 | 
			
		||||
    buf->data = NULL;
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_printf(int32_t argc, Janet *argv) {
 | 
			
		||||
static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
                                 const char *name, FILE *dflt_file) {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    Janet x = janet_dyn(name);
 | 
			
		||||
    return cfun_io_printf_impl_x(argc, argv, newline, dflt_file, 0, x);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 1, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_prinf(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_prinf,
 | 
			
		||||
              "(prinf fmt & xs)",
 | 
			
		||||
              "Like `printf` but with no trailing newline.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 0, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eprintf(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 1, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eprinf(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprinf,
 | 
			
		||||
              "(eprinf fmt & xs)",
 | 
			
		||||
              "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.") {
 | 
			
		||||
    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.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    return cfun_io_printf_impl_x(argc, argv, 0, NULL, 1, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_flusher(const char *name, FILE *dflt_file) {
 | 
			
		||||
    Janet x = janet_dyn(name);
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
@@ -487,14 +676,18 @@ static void janet_flusher(const char *name, FILE *dflt_file) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_flush(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_flush,
 | 
			
		||||
              "(flush)",
 | 
			
		||||
              "Flush `(dyn :out stdout)` if it is a file, otherwise do nothing.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_flusher("out", stdout);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eflush(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eflush,
 | 
			
		||||
              "(eflush)",
 | 
			
		||||
              "Flush `(dyn :err stderr)` if it is a file, otherwise do nothing.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_flusher("err", stderr);
 | 
			
		||||
@@ -523,12 +716,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;
 | 
			
		||||
@@ -537,154 +741,31 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg io_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "print", cfun_io_print,
 | 
			
		||||
        JDOC("(print & xs)\n\n"
 | 
			
		||||
             "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 "
 | 
			
		||||
             "a buffer. Returns nil.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "prin", cfun_io_prin,
 | 
			
		||||
        JDOC("(prin & xs)\n\n"
 | 
			
		||||
             "Same as print, but does not add trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "printf", cfun_io_printf,
 | 
			
		||||
        JDOC("(printf fmt & xs)\n\n"
 | 
			
		||||
             "Prints output formatted as if with (string/format fmt ;xs) to (dyn :out stdout) with a trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "prinf", cfun_io_prinf,
 | 
			
		||||
        JDOC("(prinf fmt & xs)\n\n"
 | 
			
		||||
             "Like printf but with no trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eprin", cfun_io_eprin,
 | 
			
		||||
        JDOC("(eprin & xs)\n\n"
 | 
			
		||||
             "Same as prin, but uses (dyn :err stderr) instead of (dyn :out stdout).")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eprint", cfun_io_eprint,
 | 
			
		||||
        JDOC("(eprint & xs)\n\n"
 | 
			
		||||
             "Same as print, but uses (dyn :err stderr) instead of (dyn :out stdout).")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eprintf", cfun_io_eprintf,
 | 
			
		||||
        JDOC("(eprintf fmt & xs)\n\n"
 | 
			
		||||
             "Prints output formatted as if with (string/format fmt ;xs) to (dyn :err stderr) with a trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eprinf", cfun_io_eprinf,
 | 
			
		||||
        JDOC("(eprinf fmt & xs)\n\n"
 | 
			
		||||
             "Like eprintf but with no trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "flush", cfun_io_flush,
 | 
			
		||||
        JDOC("(flush)\n\n"
 | 
			
		||||
             "Flush (dyn :out stdout) if it is a file, otherwise do nothing.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eflush", cfun_io_eflush,
 | 
			
		||||
        JDOC("(eflush)\n\n"
 | 
			
		||||
             "Flush (dyn :err stderr) if it is a file, otherwise do nothing.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/temp", cfun_io_temp,
 | 
			
		||||
        JDOC("(file/temp)\n\n"
 | 
			
		||||
             "Open an anonymous temporary file that is removed on close."
 | 
			
		||||
             "Raises an error on failure.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/open", cfun_io_fopen,
 | 
			
		||||
        JDOC("(file/open path &opt mode)\n\n"
 | 
			
		||||
             "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 "
 | 
			
		||||
             "cannot be opened, returns nil, otherwise returns the new file handle. "
 | 
			
		||||
             "Mode flags:\n\n"
 | 
			
		||||
             "\tr - allow reading from the file\n"
 | 
			
		||||
             "\tw - allow writing to the file\n"
 | 
			
		||||
             "\ta - append to the file\n"
 | 
			
		||||
             "\tb - open the file in binary mode (rather than text mode)\n"
 | 
			
		||||
             "\t+ - append to the file instead of overwriting it")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/close", cfun_io_fclose,
 | 
			
		||||
        JDOC("(file/close f)\n\n"
 | 
			
		||||
             "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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/read", cfun_io_fread,
 | 
			
		||||
        JDOC("(file/read f what &opt buf)\n\n"
 | 
			
		||||
             "Read a number of bytes from a file into a buffer. A buffer can "
 | 
			
		||||
             "be provided as an optional fourth argument, otherwise a new buffer "
 | 
			
		||||
             "is created. 'what' can either be an integer or a keyword. Returns the "
 | 
			
		||||
             "buffer with file contents. "
 | 
			
		||||
             "Values for 'what':\n\n"
 | 
			
		||||
             "\t:all - read the whole file\n"
 | 
			
		||||
             "\t:line - read up to and including the next newline character\n"
 | 
			
		||||
             "\tn (integer) - read up to n bytes from the file")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/write", cfun_io_fwrite,
 | 
			
		||||
        JDOC("(file/write f bytes)\n\n"
 | 
			
		||||
             "Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
 | 
			
		||||
             "file.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/flush", cfun_io_fflush,
 | 
			
		||||
        JDOC("(file/flush f)\n\n"
 | 
			
		||||
             "Flush any buffered bytes to the file system. In most files, writes are "
 | 
			
		||||
             "buffered for efficiency reasons. Returns the file handle.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/seek", cfun_io_fseek,
 | 
			
		||||
        JDOC("(file/seek f &opt whence n)\n\n"
 | 
			
		||||
             "Jump to a relative location in the file. 'whence' must be one of\n\n"
 | 
			
		||||
             "\t:cur - jump relative to the current file location\n"
 | 
			
		||||
             "\t:set - jump relative to the beginning of the file\n"
 | 
			
		||||
             "\t:end - jump relative to the end of the file\n\n"
 | 
			
		||||
             "By default, 'whence' is :cur. Optionally a value n may be passed "
 | 
			
		||||
             "for the relative number of bytes to seek in the file. n may be a real "
 | 
			
		||||
             "number to handle large files of more the 4GB. Returns the file handle.")
 | 
			
		||||
    },
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
    {
 | 
			
		||||
        "file/popen", cfun_io_popen,
 | 
			
		||||
        JDOC("(file/popen path &opt mode)\n\n"
 | 
			
		||||
             "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.")
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* C API */
 | 
			
		||||
 | 
			
		||||
FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) {
 | 
			
		||||
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, int32_t *flags) {
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, n, &janet_file_type);
 | 
			
		||||
    if (NULL != flags) *flags = iof->flags;
 | 
			
		||||
    return iof->file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_makefile(FILE *f, int flags) {
 | 
			
		||||
JanetFile *janet_makejfile(FILE *f, int32_t flags) {
 | 
			
		||||
    return makef(f, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_makefile(FILE *f, int32_t flags) {
 | 
			
		||||
    return janet_wrap_abstract(makef(f, flags));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
@@ -692,19 +773,45 @@ FILE *janet_unwrapfile(Janet j, int *flags) {
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_io(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, io_cfuns);
 | 
			
		||||
 | 
			
		||||
    JanetRegExt io_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("print", cfun_io_print),
 | 
			
		||||
        JANET_CORE_REG("prin", cfun_io_prin),
 | 
			
		||||
        JANET_CORE_REG("printf", cfun_io_printf),
 | 
			
		||||
        JANET_CORE_REG("prinf", cfun_io_prinf),
 | 
			
		||||
        JANET_CORE_REG("eprin", cfun_io_eprin),
 | 
			
		||||
        JANET_CORE_REG("eprint", cfun_io_eprint),
 | 
			
		||||
        JANET_CORE_REG("eprintf", cfun_io_eprintf),
 | 
			
		||||
        JANET_CORE_REG("eprinf", cfun_io_eprinf),
 | 
			
		||||
        JANET_CORE_REG("xprint", cfun_io_xprint),
 | 
			
		||||
        JANET_CORE_REG("xprin", cfun_io_xprin),
 | 
			
		||||
        JANET_CORE_REG("xprintf", cfun_io_xprintf),
 | 
			
		||||
        JANET_CORE_REG("xprinf", cfun_io_xprinf),
 | 
			
		||||
        JANET_CORE_REG("flush", cfun_io_flush),
 | 
			
		||||
        JANET_CORE_REG("eflush", cfun_io_eflush),
 | 
			
		||||
        JANET_CORE_REG("file/temp", cfun_io_temp),
 | 
			
		||||
        JANET_CORE_REG("file/open", cfun_io_fopen),
 | 
			
		||||
        JANET_CORE_REG("file/close", cfun_io_fclose),
 | 
			
		||||
        JANET_CORE_REG("file/read", cfun_io_fread),
 | 
			
		||||
        JANET_CORE_REG("file/write", cfun_io_fwrite),
 | 
			
		||||
        JANET_CORE_REG("file/flush", cfun_io_fflush),
 | 
			
		||||
        JANET_CORE_REG("file/seek", cfun_io_fseek),
 | 
			
		||||
        JANET_CORE_REG("file/tell", cfun_io_ftell),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, io_cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_file_type);
 | 
			
		||||
    int default_flags = JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE;
 | 
			
		||||
    /* stdout */
 | 
			
		||||
    janet_core_def(env, "stdout",
 | 
			
		||||
                   makef(stdout, JANET_FILE_APPEND | JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE),
 | 
			
		||||
                   JDOC("The standard output file."));
 | 
			
		||||
    JANET_CORE_DEF(env, "stdout",
 | 
			
		||||
                   janet_makefile(stdout, JANET_FILE_APPEND | default_flags),
 | 
			
		||||
                   "The standard output file.");
 | 
			
		||||
    /* stderr */
 | 
			
		||||
    janet_core_def(env, "stderr",
 | 
			
		||||
                   makef(stderr, JANET_FILE_APPEND | JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE),
 | 
			
		||||
                   JDOC("The standard error file."));
 | 
			
		||||
    JANET_CORE_DEF(env, "stderr",
 | 
			
		||||
                   janet_makefile(stderr, JANET_FILE_APPEND | default_flags),
 | 
			
		||||
                   "The standard error file.");
 | 
			
		||||
    /* stdin */
 | 
			
		||||
    janet_core_def(env, "stdin",
 | 
			
		||||
                   makef(stdin, JANET_FILE_READ | JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE),
 | 
			
		||||
                   JDOC("The standard input file."));
 | 
			
		||||
    JANET_CORE_DEF(env, "stdin",
 | 
			
		||||
                   janet_makefile(stdin, JANET_FILE_READ | default_flags),
 | 
			
		||||
                   "The standard input file.");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										405
									
								
								src/core/marsh.c
									
									
									
									
									
								
							
							
						
						
									
										405
									
								
								src/core/marsh.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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 */
 | 
			
		||||
@@ -63,7 +64,12 @@ enum {
 | 
			
		||||
    LB_FUNCENV_REF, /* 219 */
 | 
			
		||||
    LB_FUNCDEF_REF, /* 220 */
 | 
			
		||||
    LB_UNSAFE_CFUNCTION, /* 221 */
 | 
			
		||||
    LB_UNSAFE_POINTER /* 222 */
 | 
			
		||||
    LB_UNSAFE_POINTER, /* 222 */
 | 
			
		||||
    LB_STRUCT_PROTO, /* 223 */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    LB_THREADED_ABSTRACT, /* 224 */
 | 
			
		||||
    LB_POINTER_BUFFER, /* 224 */
 | 
			
		||||
#endif
 | 
			
		||||
} LeadBytes;
 | 
			
		||||
 | 
			
		||||
/* Helper to look inside an entry in an environment */
 | 
			
		||||
@@ -148,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) {
 | 
			
		||||
@@ -214,15 +224,6 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add function flags to janet functions */
 | 
			
		||||
static void janet_func_addflags(JanetFuncDef *def) {
 | 
			
		||||
    if (def->name) def->flags |= JANET_FUNCDEF_FLAG_HASNAME;
 | 
			
		||||
    if (def->source) def->flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
 | 
			
		||||
    if (def->defs) def->flags |= JANET_FUNCDEF_FLAG_HASDEFS;
 | 
			
		||||
    if (def->environments) def->flags |= JANET_FUNCDEF_FLAG_HASENVS;
 | 
			
		||||
    if (def->sourcemap) def->flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Marshal a sequence of u32s */
 | 
			
		||||
static void janet_marshal_u32s(MarshalState *st, const uint32_t *u32s, int32_t n) {
 | 
			
		||||
    for (int32_t i = 0; i < n; i++) {
 | 
			
		||||
@@ -243,9 +244,9 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_func_addflags(def);
 | 
			
		||||
    /* Add to lookup */
 | 
			
		||||
    janet_v_push(st->seen_defs, def);
 | 
			
		||||
 | 
			
		||||
    pushint(st, def->flags);
 | 
			
		||||
    pushint(st, def->slotcount);
 | 
			
		||||
    pushint(st, def->arity);
 | 
			
		||||
@@ -257,6 +258,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)
 | 
			
		||||
@@ -264,7 +267,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);
 | 
			
		||||
@@ -275,7 +286,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) {
 | 
			
		||||
@@ -295,8 +306,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_FLAG_HASCHILD (1 << 29)
 | 
			
		||||
#define JANET_FIBER_FLAG_HASENV (1 << 28)
 | 
			
		||||
#define JANET_STACKFRAME_HASENV (1 << 30)
 | 
			
		||||
#define JANET_FIBER_FLAG_HASENV   (1 << 30)
 | 
			
		||||
#define JANET_STACKFRAME_HASENV   (INT32_MIN)
 | 
			
		||||
 | 
			
		||||
/* Marshal a fiber */
 | 
			
		||||
static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
 | 
			
		||||
@@ -335,6 +346,7 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
 | 
			
		||||
    }
 | 
			
		||||
    if (fiber->child)
 | 
			
		||||
        marshal_one(st, janet_wrap_fiber(fiber->child), flags + 1);
 | 
			
		||||
    marshal_one(st, fiber->last_value, flags + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_marshal_size(JanetMarshalContext *ctx, size_t value) {
 | 
			
		||||
@@ -351,6 +363,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);
 | 
			
		||||
@@ -367,26 +388,52 @@ 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
 | 
			
		||||
    /* Threaded abstract types get passed through as pointers in the unsafe mode */
 | 
			
		||||
    if ((flags & JANET_MARSHAL_UNSAFE) &&
 | 
			
		||||
            (JANET_MEMORY_THREADED_ABSTRACT == (janet_abstract_head(abstract)->gc.flags & JANET_MEM_TYPEBITS))) {
 | 
			
		||||
 | 
			
		||||
        /* Increment refcount before sending message. This prevents a "death in transit" problem
 | 
			
		||||
         * where a message is garbage collected while in transit between two threads - i.e., the sending threads
 | 
			
		||||
         * loses the reference and runs a garbage collection before the receiving thread gets the message. */
 | 
			
		||||
        janet_abstract_incref(abstract);
 | 
			
		||||
        pushbyte(st, LB_THREADED_ABSTRACT);
 | 
			
		||||
        pushbytes(st, (uint8_t *) &abstract, sizeof(abstract));
 | 
			
		||||
        MARK_SEEN();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    const JanetAbstractType *at = janet_abstract_type(abstract);
 | 
			
		||||
    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("try to marshal unregistered abstract type, cannot marshal %p", x);
 | 
			
		||||
        janet_panicf("cannot marshal %p", x);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -418,11 +465,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);
 | 
			
		||||
@@ -485,6 +535,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);
 | 
			
		||||
@@ -533,8 +593,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;
 | 
			
		||||
@@ -552,9 +614,10 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
        case JANET_FUNCTION: {
 | 
			
		||||
            pushbyte(st, LB_FUNCTION);
 | 
			
		||||
            JanetFunction *func = janet_unwrap_function(x);
 | 
			
		||||
            marshal_one_def(st, func->def, flags);
 | 
			
		||||
            /* Mark seen after reading def, but before envs */
 | 
			
		||||
            pushint(st, func->def->environments_length);
 | 
			
		||||
            /* Mark seen before reading def */
 | 
			
		||||
            MARK_SEEN();
 | 
			
		||||
            marshal_one_def(st, func->def, flags);
 | 
			
		||||
            for (int32_t i = 0; i < func->def->environments_length; i++)
 | 
			
		||||
                marshal_one_env(st, func->envs[i], flags + 1);
 | 
			
		||||
            return;
 | 
			
		||||
@@ -577,8 +640,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:
 | 
			
		||||
@@ -600,6 +662,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);
 | 
			
		||||
@@ -684,9 +747,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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -738,7 +814,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;
 | 
			
		||||
@@ -747,7 +823,7 @@ static const uint8_t *unmarshal_one_env(
 | 
			
		||||
            if (length == 0) {
 | 
			
		||||
                janet_panic("invalid funcenv length");
 | 
			
		||||
            }
 | 
			
		||||
            env->as.values = malloc(sizeof(Janet) * (size_t) length);
 | 
			
		||||
            env->as.values = janet_malloc(sizeof(Janet) * (size_t) length);
 | 
			
		||||
            if (!env->as.values) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -804,6 +880,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 */
 | 
			
		||||
@@ -811,6 +889,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);
 | 
			
		||||
@@ -826,24 +905,26 @@ 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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal constants */
 | 
			
		||||
        if (constants_length) {
 | 
			
		||||
            def->constants = malloc(sizeof(Janet) * constants_length);
 | 
			
		||||
            def->constants = janet_malloc(sizeof(Janet) * constants_length);
 | 
			
		||||
            if (!def->constants) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -854,8 +935,29 @@ 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 = malloc(sizeof(uint32_t) * bytecode_length);
 | 
			
		||||
        def->bytecode = janet_malloc(sizeof(uint32_t) * bytecode_length);
 | 
			
		||||
        if (!def->bytecode) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -864,7 +966,7 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal environments */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASENVS) {
 | 
			
		||||
            def->environments = calloc(1, sizeof(int32_t) * (size_t) environments_length);
 | 
			
		||||
            def->environments = janet_calloc(1, sizeof(int32_t) * (size_t) environments_length);
 | 
			
		||||
            if (!def->environments) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -878,7 +980,7 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal sub funcdefs */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS) {
 | 
			
		||||
            def->defs = calloc(1, sizeof(JanetFuncDef *) * (size_t) defs_length);
 | 
			
		||||
            def->defs = janet_calloc(1, sizeof(JanetFuncDef *) * (size_t) defs_length);
 | 
			
		||||
            if (!def->defs) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -893,14 +995,14 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
        /* Unmarshal source maps if needed */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
 | 
			
		||||
            int32_t current = 0;
 | 
			
		||||
            def->sourcemap = malloc(sizeof(JanetSourceMapping) * (size_t) bytecode_length);
 | 
			
		||||
            def->sourcemap = janet_malloc(sizeof(JanetSourceMapping) * (size_t) bytecode_length);
 | 
			
		||||
            if (!def->sourcemap) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
            for (int32_t i = 0; i < bytecode_length; i++) {
 | 
			
		||||
                current += readint(st, &data);
 | 
			
		||||
                def->sourcemap[i].line = current;
 | 
			
		||||
                def->sourcemap[i].column = readnat(st, &data);
 | 
			
		||||
                def->sourcemap[i].column = readint(st, &data);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            def->sourcemap = NULL;
 | 
			
		||||
@@ -908,11 +1010,12 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal closure bitset if needed */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASCLOBITSET) {
 | 
			
		||||
            def->closure_bitset = malloc(sizeof(uint32_t) * def->slotcount);
 | 
			
		||||
            int32_t n = (def->slotcount + 31) >> 5;
 | 
			
		||||
            def->closure_bitset = janet_malloc(sizeof(uint32_t) * (size_t) n);
 | 
			
		||||
            if (NULL == def->closure_bitset) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
            data = janet_unmarshal_u32s(st, data, def->closure_bitset, (def->slotcount + 31) >> 5);
 | 
			
		||||
            data = janet_unmarshal_u32s(st, data, def->closure_bitset, n);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Validate */
 | 
			
		||||
@@ -943,6 +1046,12 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
    fiber->data = NULL;
 | 
			
		||||
    fiber->child = NULL;
 | 
			
		||||
    fiber->env = NULL;
 | 
			
		||||
    fiber->last_value = janet_wrap_nil();
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    fiber->waiting = NULL;
 | 
			
		||||
    fiber->sched_id = 0;
 | 
			
		||||
    fiber->supervisor_channel = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Push fiber to seen stack */
 | 
			
		||||
    janet_v_push(st->lookup, janet_wrap_fiber(fiber));
 | 
			
		||||
@@ -964,7 +1073,7 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
 | 
			
		||||
    /* Allocate stack memory */
 | 
			
		||||
    fiber->capacity = fiber_stacktop + 10;
 | 
			
		||||
    fiber->data = malloc(sizeof(Janet) * fiber->capacity);
 | 
			
		||||
    fiber->data = janet_malloc(sizeof(Janet) * fiber->capacity);
 | 
			
		||||
    if (!fiber->data) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -990,7 +1099,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;
 | 
			
		||||
 | 
			
		||||
@@ -1036,7 +1145,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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1045,10 +1154,13 @@ 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Get the fiber last value */
 | 
			
		||||
    data = unmarshal_one(st, data, &fiber->last_value, flags + 1);
 | 
			
		||||
 | 
			
		||||
    /* We have valid fiber, finally construct remaining fields. */
 | 
			
		||||
    fiber->frame = frame;
 | 
			
		||||
    fiber->flags = fiber_flags;
 | 
			
		||||
@@ -1057,6 +1169,11 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
    fiber->maxstack = fiber_maxstack;
 | 
			
		||||
    fiber->env = fiber_env;
 | 
			
		||||
 | 
			
		||||
    int status = janet_fiber_status(fiber);
 | 
			
		||||
    if (status < 0 || status > JANET_STATUS_ALIVE) {
 | 
			
		||||
        janet_panic("invalid fiber status");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Return data */
 | 
			
		||||
    *out = fiber;
 | 
			
		||||
    return data;
 | 
			
		||||
@@ -1081,6 +1198,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);
 | 
			
		||||
@@ -1101,14 +1230,18 @@ Janet janet_unmarshal_janet(JanetMarshalContext *ctx) {
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
 | 
			
		||||
void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p) {
 | 
			
		||||
    UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
 | 
			
		||||
    if (ctx->at == NULL) {
 | 
			
		||||
        janet_panicf("janet_unmarshal_abstract called more than once");
 | 
			
		||||
    }
 | 
			
		||||
    void *p = janet_abstract(ctx->at, size);
 | 
			
		||||
    janet_v_push(st->lookup, janet_wrap_abstract(p));
 | 
			
		||||
    ctx->at = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
 | 
			
		||||
    void *p = janet_abstract(ctx->at, size);
 | 
			
		||||
    janet_unmarshal_abstract_reuse(ctx, p);
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1116,17 +1249,18 @@ static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *
 | 
			
		||||
    Janet key;
 | 
			
		||||
    data = unmarshal_one(st, data, &key, flags + 1);
 | 
			
		||||
    const JanetAbstractType *at = janet_get_abstract_type(key);
 | 
			
		||||
    if (at == NULL) goto oops;
 | 
			
		||||
    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_panicf("janet_unmarshal_abstract not called");
 | 
			
		||||
            janet_panic("janet_unmarshal_abstract not called");
 | 
			
		||||
        }
 | 
			
		||||
        return context.data;
 | 
			
		||||
    }
 | 
			
		||||
oops:
 | 
			
		||||
    janet_panic("invalid abstract type");
 | 
			
		||||
    janet_panic("invalid abstract type - no unmarshal function pointer");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const uint8_t *unmarshal_one(
 | 
			
		||||
@@ -1221,20 +1355,29 @@ 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;
 | 
			
		||||
        }
 | 
			
		||||
        case LB_FUNCTION: {
 | 
			
		||||
            JanetFunction *func;
 | 
			
		||||
            JanetFuncDef *def;
 | 
			
		||||
            data = unmarshal_one_def(st, data + 1, &def, flags + 1);
 | 
			
		||||
            data++;
 | 
			
		||||
            int32_t len = readnat(st, &data);
 | 
			
		||||
            if (len > 255) {
 | 
			
		||||
                janet_panicf("invalid function - too many environments (%d)", len);
 | 
			
		||||
            }
 | 
			
		||||
            func = janet_gcalloc(JANET_MEMORY_FUNCTION, sizeof(JanetFunction) +
 | 
			
		||||
                                 def->environments_length * sizeof(JanetFuncEnv));
 | 
			
		||||
            func->def = def;
 | 
			
		||||
                                 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);
 | 
			
		||||
            for (int32_t i = 0; i < def->environments_length; i++) {
 | 
			
		||||
            data = unmarshal_one_def(st, data, &def, flags + 1);
 | 
			
		||||
            func->def = def;
 | 
			
		||||
            for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
                data = unmarshal_one_env(st, data, &(func->envs[i]), flags + 1);
 | 
			
		||||
            }
 | 
			
		||||
            return data;
 | 
			
		||||
@@ -1247,6 +1390,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 */
 | 
			
		||||
@@ -1276,9 +1420,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);
 | 
			
		||||
@@ -1299,7 +1449,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++) {
 | 
			
		||||
@@ -1329,6 +1479,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++;
 | 
			
		||||
@@ -1347,6 +1520,42 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
        case LB_THREADED_ABSTRACT: {
 | 
			
		||||
            MARSH_EOS(st, data + sizeof(void *));
 | 
			
		||||
            data++;
 | 
			
		||||
            if (!(flags & JANET_MARSHAL_UNSAFE)) {
 | 
			
		||||
                janet_panicf("unsafe flag not given, "
 | 
			
		||||
                             "will not unmarshal threaded abstract pointer at index %d",
 | 
			
		||||
                             (int)(data - st->start));
 | 
			
		||||
            }
 | 
			
		||||
            union {
 | 
			
		||||
                void *ptr;
 | 
			
		||||
                uint8_t bytes[sizeof(void *)];
 | 
			
		||||
            } u;
 | 
			
		||||
            memcpy(u.bytes, data, sizeof(void *));
 | 
			
		||||
            data += sizeof(void *);
 | 
			
		||||
 | 
			
		||||
            if (flags & JANET_MARSHAL_DECREF) {
 | 
			
		||||
                /* Decrement immediately and don't bother putting into heap */
 | 
			
		||||
                janet_abstract_decref(u.ptr);
 | 
			
		||||
                *out = janet_wrap_nil();
 | 
			
		||||
            } else {
 | 
			
		||||
                *out = janet_wrap_abstract(u.ptr);
 | 
			
		||||
                Janet check = janet_table_get(&janet_vm.threaded_abstracts, *out);
 | 
			
		||||
                if (janet_checktype(check, JANET_NIL)) {
 | 
			
		||||
                    /* Transfers reference from threaded channel buffer to current heap */
 | 
			
		||||
                    janet_table_put(&janet_vm.threaded_abstracts, *out, janet_wrap_false());
 | 
			
		||||
                } else {
 | 
			
		||||
                    /* Heap reference already accounted for, remove threaded channel reference. */
 | 
			
		||||
                    janet_abstract_decref(u.ptr);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        default: {
 | 
			
		||||
            janet_panicf("unknown byte %x at index %d",
 | 
			
		||||
                         *data,
 | 
			
		||||
@@ -1354,7 +1563,6 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#undef EXTRA
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_unmarshal(
 | 
			
		||||
@@ -1381,16 +1589,28 @@ Janet janet_unmarshal(
 | 
			
		||||
 | 
			
		||||
/* C functions */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_env_lookup(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_env_lookup,
 | 
			
		||||
              "(env-lookup env)",
 | 
			
		||||
              "Creates a forward lookup table for unmarshalling from an environment. "
 | 
			
		||||
              "To create a reverse lookup table, use the invert function to swap keys "
 | 
			
		||||
              "and values in the returned table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTable *env = janet_gettable(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_env_lookup(env));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_marshal(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
JANET_CORE_FN(cfun_marshal,
 | 
			
		||||
              "(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, 4);
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
    JanetTable *rreg = NULL;
 | 
			
		||||
    uint32_t flags = 0;
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        rreg = janet_gettable(argv, 1);
 | 
			
		||||
    }
 | 
			
		||||
@@ -1399,11 +1619,18 @@ static Janet cfun_marshal(int32_t argc, Janet *argv) {
 | 
			
		||||
    } 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_unmarshal(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_unmarshal,
 | 
			
		||||
              "(unmarshal buffer &opt lookup)",
 | 
			
		||||
              "Unmarshal a value from a buffer. An optional lookup table "
 | 
			
		||||
              "can be provided to allow for aliases to be resolved. Returns the value "
 | 
			
		||||
              "unmarshalled from the buffer.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetTable *reg = NULL;
 | 
			
		||||
@@ -1413,35 +1640,13 @@ static Janet cfun_unmarshal(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_unmarshal(view.bytes, (size_t) view.len, 0, reg, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg marsh_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "marshal", cfun_marshal,
 | 
			
		||||
        JDOC("(marshal x &opt reverse-lookup buffer)\n\n"
 | 
			
		||||
             "Marshal a value into a buffer and return the buffer. The buffer "
 | 
			
		||||
             "can the 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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "unmarshal", cfun_unmarshal,
 | 
			
		||||
        JDOC("(unmarshal buffer &opt lookup)\n\n"
 | 
			
		||||
             "Unmarshal a value from a buffer. An optional lookup table "
 | 
			
		||||
             "can be provided to allow for aliases to be resolved. Returns the value "
 | 
			
		||||
             "unmarshalled from the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "env-lookup", cfun_env_lookup,
 | 
			
		||||
        JDOC("(env-lookup env)\n\n"
 | 
			
		||||
             "Creates a forward lookup table for unmarshalling from an environment. "
 | 
			
		||||
             "To create a reverse lookup table, use the invert function to swap keys "
 | 
			
		||||
             "and values in the returned table.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_marsh(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, marsh_cfuns);
 | 
			
		||||
    JanetRegExt marsh_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("marshal", cfun_marshal),
 | 
			
		||||
        JANET_CORE_REG("unmarshal", cfun_unmarshal),
 | 
			
		||||
        JANET_CORE_REG("env-lookup", cfun_env_lookup),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, marsh_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										460
									
								
								src/core/math.c
									
									
									
									
									
								
							
							
						
						
									
										460
									
								
								src/core/math.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,14 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
static JANET_THREAD_LOCAL JanetRNG janet_vm_rng = {0, 0, 0, 0, 0};
 | 
			
		||||
 | 
			
		||||
static int janet_rng_get(void *p, Janet key, Janet *out);
 | 
			
		||||
static Janet janet_rng_next(void *p, Janet key);
 | 
			
		||||
 | 
			
		||||
static void janet_rng_marshal(void *p, JanetMarshalContext *ctx) {
 | 
			
		||||
    JanetRNG *rng = (JanetRNG *)p;
 | 
			
		||||
@@ -60,11 +60,15 @@ const JanetAbstractType janet_rng_type = {
 | 
			
		||||
    NULL,
 | 
			
		||||
    janet_rng_marshal,
 | 
			
		||||
    janet_rng_unmarshal,
 | 
			
		||||
    JANET_ATEND_UNMARSHAL
 | 
			
		||||
    NULL, /* tostring */
 | 
			
		||||
    NULL, /* compare */
 | 
			
		||||
    NULL, /* hash */
 | 
			
		||||
    janet_rng_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JanetRNG *janet_default_rng(void) {
 | 
			
		||||
    return &janet_vm_rng;
 | 
			
		||||
    return &janet_vm.rng;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_rng_seed(JanetRNG *rng, uint32_t seed) {
 | 
			
		||||
@@ -113,7 +117,12 @@ double janet_rng_double(JanetRNG *rng) {
 | 
			
		||||
    return ldexp((double)(big >> (64 - 52)), -52);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_rng_make(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_rng_make,
 | 
			
		||||
              "(math/rng &opt seed)",
 | 
			
		||||
              "Creates a Psuedo-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."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_arity(argc, 0, 1);
 | 
			
		||||
    JanetRNG *rng = janet_abstract(&janet_rng_type, sizeof(JanetRNG));
 | 
			
		||||
    if (argc == 1) {
 | 
			
		||||
@@ -130,13 +139,20 @@ static Janet cfun_rng_make(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_abstract(rng);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_rng_uniform(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_rng_uniform,
 | 
			
		||||
              "(math/rng-uniform rng)",
 | 
			
		||||
              "Extract a random number in the range [0, 1) from the RNG."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type);
 | 
			
		||||
    return janet_wrap_number(janet_rng_double(rng));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_rng_int(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_rng_int,
 | 
			
		||||
              "(math/rng-int rng &opt max)",
 | 
			
		||||
              "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);
 | 
			
		||||
    if (argc == 1) {
 | 
			
		||||
@@ -164,7 +180,11 @@ static void rng_get_4bytes(JanetRNG *rng, uint8_t *buf) {
 | 
			
		||||
    buf[3] = (word >> 24) & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_rng_buffer(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_rng_buffer,
 | 
			
		||||
              "(math/rng-buffer rng n &opt buf)",
 | 
			
		||||
              "Get n random bytes and put them in a buffer. Creates a new buffer if no buffer is "
 | 
			
		||||
              "provided, otherwise appends to the given buffer. Returns the buffer."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type);
 | 
			
		||||
    int32_t n = janet_getnat(argv, 1);
 | 
			
		||||
@@ -203,301 +223,203 @@ static int janet_rng_get(void *p, Janet key, Janet *out) {
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), rng_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_rng_next(void *p, Janet key) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    return janet_nextmethod(rng_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a random number */
 | 
			
		||||
static Janet janet_rand(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_rand,
 | 
			
		||||
              "(math/random)",
 | 
			
		||||
              "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));
 | 
			
		||||
    return janet_wrap_number(janet_rng_double(&janet_vm.rng));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Seed the random number generator */
 | 
			
		||||
static Janet janet_srand(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_srand,
 | 
			
		||||
              "(math/seedrandom seed)",
 | 
			
		||||
              "Set the seed for the random number generator. `seed` should be "
 | 
			
		||||
              "an integer or a buffer."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (janet_checkint(argv[0])) {
 | 
			
		||||
        uint32_t seed = (uint32_t)(janet_getinteger(argv, 0));
 | 
			
		||||
        janet_rng_seed(&janet_vm_rng, seed);
 | 
			
		||||
        janet_rng_seed(&janet_vm.rng, seed);
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetByteView bytes = janet_getbytes(argv, 0);
 | 
			
		||||
        janet_rng_longseed(&janet_vm_rng, bytes.bytes, bytes.len);
 | 
			
		||||
        janet_rng_longseed(&janet_vm.rng, bytes.bytes, bytes.len);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define JANET_DEFINE_MATHOP(name, fop)\
 | 
			
		||||
static Janet janet_##name(int32_t argc, Janet *argv) {\
 | 
			
		||||
#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)
 | 
			
		||||
JANET_DEFINE_MATHOP(asin, asin)
 | 
			
		||||
JANET_DEFINE_MATHOP(atan, atan)
 | 
			
		||||
JANET_DEFINE_MATHOP(cos, cos)
 | 
			
		||||
JANET_DEFINE_MATHOP(cosh, cosh)
 | 
			
		||||
JANET_DEFINE_MATHOP(acosh, acosh)
 | 
			
		||||
JANET_DEFINE_MATHOP(sin, sin)
 | 
			
		||||
JANET_DEFINE_MATHOP(sinh, sinh)
 | 
			
		||||
JANET_DEFINE_MATHOP(asinh, asinh)
 | 
			
		||||
JANET_DEFINE_MATHOP(tan, tan)
 | 
			
		||||
JANET_DEFINE_MATHOP(tanh, tanh)
 | 
			
		||||
JANET_DEFINE_MATHOP(atanh, atanh)
 | 
			
		||||
JANET_DEFINE_MATHOP(exp, exp)
 | 
			
		||||
JANET_DEFINE_MATHOP(exp2, exp2)
 | 
			
		||||
JANET_DEFINE_MATHOP(expm1, expm1)
 | 
			
		||||
JANET_DEFINE_MATHOP(log, log)
 | 
			
		||||
JANET_DEFINE_MATHOP(log10, log10)
 | 
			
		||||
JANET_DEFINE_MATHOP(log2, log2)
 | 
			
		||||
JANET_DEFINE_MATHOP(sqrt, sqrt)
 | 
			
		||||
JANET_DEFINE_MATHOP(cbrt, cbrt)
 | 
			
		||||
JANET_DEFINE_MATHOP(ceil, ceil)
 | 
			
		||||
JANET_DEFINE_MATHOP(fabs, fabs)
 | 
			
		||||
JANET_DEFINE_MATHOP(floor, floor)
 | 
			
		||||
JANET_DEFINE_MATHOP(trunc, trunc)
 | 
			
		||||
JANET_DEFINE_MATHOP(round, round)
 | 
			
		||||
JANET_DEFINE_MATHOP(gamma, lgamma)
 | 
			
		||||
JANET_DEFINE_MATHOP(log1p, log1p)
 | 
			
		||||
JANET_DEFINE_MATHOP(erf, erf)
 | 
			
		||||
JANET_DEFINE_MATHOP(erfc, erfc)
 | 
			
		||||
#define JANET_DEFINE_MATHOP(fop, doc) JANET_DEFINE_NAMED_MATHOP(#fop, fop, doc)
 | 
			
		||||
 | 
			
		||||
#define JANET_DEFINE_MATH2OP(name, fop)\
 | 
			
		||||
static Janet janet_##name(int32_t argc, Janet *argv) {\
 | 
			
		||||
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) {\
 | 
			
		||||
    janet_fixarity(argc, 2); \
 | 
			
		||||
    double lhs = janet_getnumber(argv, 0); \
 | 
			
		||||
    double rhs = janet_getnumber(argv, 1); \
 | 
			
		||||
    return janet_wrap_number(fop(lhs, rhs)); \
 | 
			
		||||
}\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_DEFINE_MATH2OP(atan2, atan2)
 | 
			
		||||
JANET_DEFINE_MATH2OP(pow, pow)
 | 
			
		||||
JANET_DEFINE_MATH2OP(hypot, hypot)
 | 
			
		||||
JANET_DEFINE_MATH2OP(nextafter, nextafter)
 | 
			
		||||
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 value after x in the direction of y.")
 | 
			
		||||
 | 
			
		||||
static Janet janet_not(int32_t argc, Janet *argv) {
 | 
			
		||||
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 const JanetReg math_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "not", janet_not,
 | 
			
		||||
        JDOC("(not x)\n\nReturns the boolean inverse of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/random", janet_rand,
 | 
			
		||||
        JDOC("(math/random)\n\n"
 | 
			
		||||
             "Returns a uniformly distributed random number between 0 and 1.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/seedrandom", janet_srand,
 | 
			
		||||
        JDOC("(math/seedrandom seed)\n\n"
 | 
			
		||||
             "Set the seed for the random number generator. seed should be "
 | 
			
		||||
             "an integer or a buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/cos", janet_cos,
 | 
			
		||||
        JDOC("(math/cos x)\n\n"
 | 
			
		||||
             "Returns the cosine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/sin", janet_sin,
 | 
			
		||||
        JDOC("(math/sin x)\n\n"
 | 
			
		||||
             "Returns the sine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/tan", janet_tan,
 | 
			
		||||
        JDOC("(math/tan x)\n\n"
 | 
			
		||||
             "Returns the tangent of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/acos", janet_acos,
 | 
			
		||||
        JDOC("(math/acos x)\n\n"
 | 
			
		||||
             "Returns the arccosine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/asin", janet_asin,
 | 
			
		||||
        JDOC("(math/asin x)\n\n"
 | 
			
		||||
             "Returns the arcsine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/atan", janet_atan,
 | 
			
		||||
        JDOC("(math/atan x)\n\n"
 | 
			
		||||
             "Returns the arctangent of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/exp", janet_exp,
 | 
			
		||||
        JDOC("(math/exp x)\n\n"
 | 
			
		||||
             "Returns e to the power of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/log", janet_log,
 | 
			
		||||
        JDOC("(math/log x)\n\n"
 | 
			
		||||
             "Returns log base natural number of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/log10", janet_log10,
 | 
			
		||||
        JDOC("(math/log10 x)\n\n"
 | 
			
		||||
             "Returns log base 10 of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/log2", janet_log2,
 | 
			
		||||
        JDOC("(math/log2 x)\n\n"
 | 
			
		||||
             "Returns log base 2 of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/sqrt", janet_sqrt,
 | 
			
		||||
        JDOC("(math/sqrt x)\n\n"
 | 
			
		||||
             "Returns the square root of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/cbrt", janet_cbrt,
 | 
			
		||||
        JDOC("(math/cbrt x)\n\n"
 | 
			
		||||
             "Returns the cube root of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/floor", janet_floor,
 | 
			
		||||
        JDOC("(math/floor x)\n\n"
 | 
			
		||||
             "Returns the largest integer value number that is not greater than x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/ceil", janet_ceil,
 | 
			
		||||
        JDOC("(math/ceil x)\n\n"
 | 
			
		||||
             "Returns the smallest integer value number that is not less than x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/pow", janet_pow,
 | 
			
		||||
        JDOC("(math/pow a x)\n\n"
 | 
			
		||||
             "Return a to the power of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/abs", janet_fabs,
 | 
			
		||||
        JDOC("(math/abs x)\n\n"
 | 
			
		||||
             "Return the absolute value of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/sinh", janet_sinh,
 | 
			
		||||
        JDOC("(math/sinh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic sine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/cosh", janet_cosh,
 | 
			
		||||
        JDOC("(math/cosh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic cosine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/tanh", janet_tanh,
 | 
			
		||||
        JDOC("(math/tanh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic tangent of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/atanh", janet_atanh,
 | 
			
		||||
        JDOC("(math/atanh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic arctangent of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/asinh", janet_asinh,
 | 
			
		||||
        JDOC("(math/asinh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic arcsine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/acosh", janet_acosh,
 | 
			
		||||
        JDOC("(math/acosh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic arccosine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/atan2", janet_atan2,
 | 
			
		||||
        JDOC("(math/atan2 y x)\n\n"
 | 
			
		||||
             "Return the arctangent of y/x. Works even when x is 0.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/rng", cfun_rng_make,
 | 
			
		||||
        JDOC("(math/rng &opt seed)\n\n"
 | 
			
		||||
             "Creates a Psuedo-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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/rng-uniform", cfun_rng_uniform,
 | 
			
		||||
        JDOC("(math/rng-seed rng seed)\n\n"
 | 
			
		||||
             "Extract a random number in the range [0, 1) from the RNG.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/rng-int", cfun_rng_int,
 | 
			
		||||
        JDOC("(math/rng-int rng &opt max)\n\n"
 | 
			
		||||
             "Extract a random random integer in the range [0, max] from the RNG. If "
 | 
			
		||||
             "no max is given, the default is 2^31 - 1.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/rng-buffer", cfun_rng_buffer,
 | 
			
		||||
        JDOC("(math/rng-buffer rng n &opt buf)\n\n"
 | 
			
		||||
             "Get n random bytes and put them in a buffer. Creates a new buffer if no buffer is "
 | 
			
		||||
             "provided, otherwise appends to the given buffer. Returns the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/hypot", janet_hypot,
 | 
			
		||||
        JDOC("(math/hypot a b)\n\n"
 | 
			
		||||
             "Returns the c from the equation c^2 = a^2 + b^2")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/exp2", janet_exp2,
 | 
			
		||||
        JDOC("(math/exp2 x)\n\n"
 | 
			
		||||
             "Returns 2 to the power of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/log1p", janet_log1p,
 | 
			
		||||
        JDOC("(math/log1p x)\n\n"
 | 
			
		||||
             "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/gamma", janet_gamma,
 | 
			
		||||
        JDOC("(math/gamma x)\n\n"
 | 
			
		||||
             "Returns gamma(x).")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/erfc", janet_erfc,
 | 
			
		||||
        JDOC("(math/erfc x)\n\n"
 | 
			
		||||
             "Returns the complementary error function of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/erf", janet_erf,
 | 
			
		||||
        JDOC("(math/erf x)\n\n"
 | 
			
		||||
             "Returns the error function of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/expm1", janet_expm1,
 | 
			
		||||
        JDOC("(math/expm1 x)\n\n"
 | 
			
		||||
             "Returns e to the power of x minus 1.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/trunc", janet_trunc,
 | 
			
		||||
        JDOC("(math/trunc x)\n\n"
 | 
			
		||||
             "Returns the integer between x and 0 nearest to x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/round", janet_round,
 | 
			
		||||
        JDOC("(math/round x)\n\n"
 | 
			
		||||
             "Returns the integer nearest to x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/next", janet_nextafter,
 | 
			
		||||
        JDOC("(math/next x y)\n\n"
 | 
			
		||||
             "Returns the next representable floating point value after x in the direction of y.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_math(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, math_cfuns);
 | 
			
		||||
    JanetRegExt math_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("not", janet_not),
 | 
			
		||||
        JANET_CORE_REG("math/random", janet_rand),
 | 
			
		||||
        JANET_CORE_REG("math/seedrandom", janet_srand),
 | 
			
		||||
        JANET_CORE_REG("math/cos", janet_cos),
 | 
			
		||||
        JANET_CORE_REG("math/sin", janet_sin),
 | 
			
		||||
        JANET_CORE_REG("math/tan", janet_tan),
 | 
			
		||||
        JANET_CORE_REG("math/acos", janet_acos),
 | 
			
		||||
        JANET_CORE_REG("math/asin", janet_asin),
 | 
			
		||||
        JANET_CORE_REG("math/atan", janet_atan),
 | 
			
		||||
        JANET_CORE_REG("math/exp", janet_exp),
 | 
			
		||||
        JANET_CORE_REG("math/log", janet_log),
 | 
			
		||||
        JANET_CORE_REG("math/log10", janet_log10),
 | 
			
		||||
        JANET_CORE_REG("math/log2", janet_log2),
 | 
			
		||||
        JANET_CORE_REG("math/sqrt", janet_sqrt),
 | 
			
		||||
        JANET_CORE_REG("math/cbrt", janet_cbrt),
 | 
			
		||||
        JANET_CORE_REG("math/floor", janet_floor),
 | 
			
		||||
        JANET_CORE_REG("math/ceil", janet_ceil),
 | 
			
		||||
        JANET_CORE_REG("math/pow", janet_pow),
 | 
			
		||||
        JANET_CORE_REG("math/abs", janet_fabs),
 | 
			
		||||
        JANET_CORE_REG("math/sinh", janet_sinh),
 | 
			
		||||
        JANET_CORE_REG("math/cosh", janet_cosh),
 | 
			
		||||
        JANET_CORE_REG("math/tanh", janet_tanh),
 | 
			
		||||
        JANET_CORE_REG("math/atanh", janet_atanh),
 | 
			
		||||
        JANET_CORE_REG("math/asinh", janet_asinh),
 | 
			
		||||
        JANET_CORE_REG("math/acosh", janet_acosh),
 | 
			
		||||
        JANET_CORE_REG("math/atan2", janet_atan2),
 | 
			
		||||
        JANET_CORE_REG("math/rng", cfun_rng_make),
 | 
			
		||||
        JANET_CORE_REG("math/rng-uniform", cfun_rng_uniform),
 | 
			
		||||
        JANET_CORE_REG("math/rng-int", cfun_rng_int),
 | 
			
		||||
        JANET_CORE_REG("math/rng-buffer", cfun_rng_buffer),
 | 
			
		||||
        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_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_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, math_cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_rng_type);
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
    janet_def(env, "math/pi", janet_wrap_number(3.1415926535897931),
 | 
			
		||||
              JDOC("The value pi."));
 | 
			
		||||
    janet_def(env, "math/e", janet_wrap_number(2.7182818284590451),
 | 
			
		||||
              JDOC("The base of the natural log."));
 | 
			
		||||
    janet_def(env, "math/inf", janet_wrap_number(INFINITY),
 | 
			
		||||
              JDOC("The number representing positive infinity"));
 | 
			
		||||
    janet_def(env, "math/-inf", janet_wrap_number(-INFINITY),
 | 
			
		||||
              JDOC("The number representing negative infinity"));
 | 
			
		||||
    JANET_CORE_DEF(env, "math/pi", janet_wrap_number(3.1415926535897931),
 | 
			
		||||
                   "The value pi.");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/e", janet_wrap_number(2.7182818284590451),
 | 
			
		||||
                   "The base of the natural log.");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/inf", janet_wrap_number(INFINITY),
 | 
			
		||||
                   "The number representing positive infinity");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/-inf", janet_wrap_number(-INFINITY),
 | 
			
		||||
                   "The number representing negative infinity");
 | 
			
		||||
    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");
 | 
			
		||||
    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))");
 | 
			
		||||
#ifdef NAN
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#else
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(0.0 / 0.0), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1393
									
								
								src/core/net.c
									
									
									
									
									
								
							
							
						
						
									
										1393
									
								
								src/core/net.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1955
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										1955
									
								
								src/core/os.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										418
									
								
								src/core/parse.c
									
									
									
									
									
								
							
							
						
						
									
										418
									
								
								src/core/parse.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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) {
 | 
			
		||||
@@ -123,7 +123,7 @@ static void NAME(JanetParser *p, T x) { \
 | 
			
		||||
    if (newcount > p->STACKCAP) { \
 | 
			
		||||
        T *next; \
 | 
			
		||||
        size_t newcap = 2 * newcount; \
 | 
			
		||||
        next = realloc(p->STACK, sizeof(T) * newcap); \
 | 
			
		||||
        next = janet_realloc(p->STACK, sizeof(T) * newcap); \
 | 
			
		||||
        if (NULL == next) { \
 | 
			
		||||
            JANET_OUT_OF_MEMORY; \
 | 
			
		||||
        } \
 | 
			
		||||
@@ -175,7 +175,14 @@ static void popstate(JanetParser *p, Janet val) {
 | 
			
		||||
        if (newtop->flags & PFLAG_CONTAINER) {
 | 
			
		||||
            newtop->argn++;
 | 
			
		||||
            /* Keep track of number of values in the root state */
 | 
			
		||||
            if (p->statecount == 1) p->pending++;
 | 
			
		||||
            if (p->statecount == 1) {
 | 
			
		||||
                p->pending++;
 | 
			
		||||
                /* Root items are always wrapped in a tuple for source map info. */
 | 
			
		||||
                const Janet *tup = janet_tuple_n(&val, 1);
 | 
			
		||||
                janet_tuple_sm_line(tup) = (int32_t) top.line;
 | 
			
		||||
                janet_tuple_sm_column(tup) = (int32_t) top.column;
 | 
			
		||||
                val = janet_wrap_tuple(tup);
 | 
			
		||||
            }
 | 
			
		||||
            push_arg(p, val);
 | 
			
		||||
            return;
 | 
			
		||||
        } else if (newtop->flags & PFLAG_READERMAC) {
 | 
			
		||||
@@ -199,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:
 | 
			
		||||
@@ -221,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 '"':
 | 
			
		||||
@@ -313,11 +359,48 @@ static int stringend(JanetParser *p, JanetParseState *state) {
 | 
			
		||||
    uint8_t *bufstart = p->buf;
 | 
			
		||||
    int32_t buflen = (int32_t) p->bufcount;
 | 
			
		||||
    if (state->flags & PFLAG_LONGSTRING) {
 | 
			
		||||
        /* Check for leading newline character so we can remove it */
 | 
			
		||||
        if (bufstart[0] == '\n') {
 | 
			
		||||
            bufstart++;
 | 
			
		||||
            buflen--;
 | 
			
		||||
        /* Post process to remove leading whitespace */
 | 
			
		||||
        JanetParseState top = p->states[p->statecount - 1];
 | 
			
		||||
        int32_t indent_col = (int32_t) top.column - 1;
 | 
			
		||||
        uint8_t *r = bufstart, *end = r + buflen;
 | 
			
		||||
        /* Check if there are any characters before the start column -
 | 
			
		||||
         * if so, do not reindent. */
 | 
			
		||||
        int reindent = 1;
 | 
			
		||||
        while (reindent && (r < end)) {
 | 
			
		||||
            if (*r++ == '\n') {
 | 
			
		||||
                for (int32_t j = 0; (r < end) && (*r != '\n') && (j < indent_col); j++, r++) {
 | 
			
		||||
                    if (*r != ' ') {
 | 
			
		||||
                        reindent = 0;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Now reindent if able to, otherwise just drop leading newline. */
 | 
			
		||||
        if (!reindent) {
 | 
			
		||||
            if (buflen > 0 && bufstart[0] == '\n') {
 | 
			
		||||
                buflen--;
 | 
			
		||||
                bufstart++;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            uint8_t *w = bufstart;
 | 
			
		||||
            r = bufstart;
 | 
			
		||||
            while (r < end) {
 | 
			
		||||
                if (*r == '\n') {
 | 
			
		||||
                    if (r == bufstart) {
 | 
			
		||||
                        /* Skip leading newline */
 | 
			
		||||
                        r++;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        *w++ = *r++;
 | 
			
		||||
                    }
 | 
			
		||||
                    for (int32_t j = 0; (r < end) && (*r != '\n') && (j < indent_col); j++, r++);
 | 
			
		||||
                } else {
 | 
			
		||||
                    *w++ = *r++;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            buflen = (int32_t)(w - bufstart);
 | 
			
		||||
        }
 | 
			
		||||
        /* Check for trailing newline character so we can remove it */
 | 
			
		||||
        if (buflen > 0 && bufstart[buflen - 1] == '\n') {
 | 
			
		||||
            buflen--;
 | 
			
		||||
        }
 | 
			
		||||
@@ -367,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;
 | 
			
		||||
@@ -378,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;
 | 
			
		||||
@@ -398,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;
 | 
			
		||||
@@ -538,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -568,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)) ||
 | 
			
		||||
@@ -589,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);
 | 
			
		||||
@@ -640,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;
 | 
			
		||||
@@ -693,6 +757,20 @@ const char *janet_parser_error(JanetParser *parser) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_parser_produce(JanetParser *parser) {
 | 
			
		||||
    Janet ret;
 | 
			
		||||
    size_t i;
 | 
			
		||||
    if (parser->pending == 0) return janet_wrap_nil();
 | 
			
		||||
    ret = janet_unwrap_tuple(parser->args[0])[0];
 | 
			
		||||
    for (i = 1; i < parser->argcount; i++) {
 | 
			
		||||
        parser->args[i - 1] = parser->args[i];
 | 
			
		||||
    }
 | 
			
		||||
    parser->pending--;
 | 
			
		||||
    parser->argcount--;
 | 
			
		||||
    parser->states[0].argn--;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_parser_produce_wrapped(JanetParser *parser) {
 | 
			
		||||
    Janet ret;
 | 
			
		||||
    size_t i;
 | 
			
		||||
    if (parser->pending == 0) return janet_wrap_nil();
 | 
			
		||||
@@ -702,6 +780,7 @@ Janet janet_parser_produce(JanetParser *parser) {
 | 
			
		||||
    }
 | 
			
		||||
    parser->pending--;
 | 
			
		||||
    parser->argcount--;
 | 
			
		||||
    parser->states[0].argn--;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -726,9 +805,9 @@ void janet_parser_init(JanetParser *parser) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_parser_deinit(JanetParser *parser) {
 | 
			
		||||
    free(parser->args);
 | 
			
		||||
    free(parser->buf);
 | 
			
		||||
    free(parser->states);
 | 
			
		||||
    janet_free(parser->args);
 | 
			
		||||
    janet_free(parser->buf);
 | 
			
		||||
    janet_free(parser->states);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_parser_clone(const JanetParser *src, JanetParser *dest) {
 | 
			
		||||
@@ -755,17 +834,17 @@ void janet_parser_clone(const JanetParser *src, JanetParser *dest) {
 | 
			
		||||
    dest->states = NULL;
 | 
			
		||||
    dest->buf = NULL;
 | 
			
		||||
    if (dest->bufcap) {
 | 
			
		||||
        dest->buf = malloc(dest->bufcap);
 | 
			
		||||
        dest->buf = janet_malloc(dest->bufcap);
 | 
			
		||||
        if (!dest->buf) goto nomem;
 | 
			
		||||
        memcpy(dest->buf, src->buf, dest->bufcap);
 | 
			
		||||
    }
 | 
			
		||||
    if (dest->argcap) {
 | 
			
		||||
        dest->args = malloc(sizeof(Janet) * dest->argcap);
 | 
			
		||||
        dest->args = janet_malloc(sizeof(Janet) * dest->argcap);
 | 
			
		||||
        if (!dest->args) goto nomem;
 | 
			
		||||
        memcpy(dest->args, src->args, dest->argcap * sizeof(Janet));
 | 
			
		||||
    }
 | 
			
		||||
    if (dest->statecap) {
 | 
			
		||||
        dest->states = malloc(sizeof(JanetParseState) * dest->statecap);
 | 
			
		||||
        dest->states = janet_malloc(sizeof(JanetParseState) * dest->statecap);
 | 
			
		||||
        if (!dest->states) goto nomem;
 | 
			
		||||
        memcpy(dest->states, src->states, dest->statecap * sizeof(JanetParseState));
 | 
			
		||||
    }
 | 
			
		||||
@@ -803,17 +882,28 @@ static int parsergc(void *p, size_t size) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int parserget(void *p, Janet key, Janet *out);
 | 
			
		||||
static Janet parsernext(void *p, Janet key);
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_parser_type = {
 | 
			
		||||
    "core/parser",
 | 
			
		||||
    parsergc,
 | 
			
		||||
    parsermark,
 | 
			
		||||
    parserget,
 | 
			
		||||
    JANET_ATEND_GET
 | 
			
		||||
    NULL, /* put */
 | 
			
		||||
    NULL, /* marshal */
 | 
			
		||||
    NULL, /* unmarshal */
 | 
			
		||||
    NULL, /* tostring */
 | 
			
		||||
    NULL, /* compare */
 | 
			
		||||
    NULL, /* hash */
 | 
			
		||||
    parsernext,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* C Function parser */
 | 
			
		||||
static Janet cfun_parse_parser(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    JanetParser *p = janet_abstract(&janet_parser_type, sizeof(JanetParser));
 | 
			
		||||
@@ -821,7 +911,11 @@ static Janet cfun_parse_parser(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_abstract(p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_consume(int32_t argc, Janet *argv) {
 | 
			
		||||
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 "
 | 
			
		||||
              "the number of bytes read.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -846,14 +940,20 @@ static Janet cfun_parse_consume(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_integer(i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_eof(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_eof,
 | 
			
		||||
              "(parser/eof parser)",
 | 
			
		||||
              "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);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_insert,
 | 
			
		||||
              "(parser/insert parser value)",
 | 
			
		||||
              "Insert a value into the parser. This means that the parser state can be manipulated "
 | 
			
		||||
              "in between chunks of bytes. This would allow a user to add extra elements to arrays "
 | 
			
		||||
              "and tuples, for example. Returns the parser.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    JanetParseState *s = p->states + p->statecount - 1;
 | 
			
		||||
@@ -865,15 +965,20 @@ static Janet cfun_parse_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
    if (s->flags & PFLAG_COMMENT) s--;
 | 
			
		||||
    if (s->flags & PFLAG_CONTAINER) {
 | 
			
		||||
        s->argn++;
 | 
			
		||||
        if (p->statecount == 1) p->pending++;
 | 
			
		||||
        push_arg(p, argv[1]);
 | 
			
		||||
        if (p->statecount == 1) {
 | 
			
		||||
            p->pending++;
 | 
			
		||||
            Janet tup = janet_wrap_tuple(janet_tuple_n(argv + 1, 1));
 | 
			
		||||
            push_arg(p, tup);
 | 
			
		||||
        } else {
 | 
			
		||||
            push_arg(p, argv[1]);
 | 
			
		||||
        }
 | 
			
		||||
    } else if (s->flags & (PFLAG_STRING | PFLAG_LONGSTRING)) {
 | 
			
		||||
        const uint8_t *str = janet_to_string(argv[1]);
 | 
			
		||||
        int32_t slen = janet_string_length(str);
 | 
			
		||||
        size_t newcount = p->bufcount + slen;
 | 
			
		||||
        if (p->bufcap < newcount) {
 | 
			
		||||
            size_t newcap = 2 * newcount;
 | 
			
		||||
            p->buf = realloc(p->buf, newcap);
 | 
			
		||||
            p->buf = janet_realloc(p->buf, newcap);
 | 
			
		||||
            if (p->buf == NULL) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -887,13 +992,17 @@ static Janet cfun_parse_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_has_more(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_has_more,
 | 
			
		||||
              "(parser/has-more parser)",
 | 
			
		||||
              "Check if the parser has more values in the value queue.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    return janet_wrap_boolean(janet_parser_has_more(p));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_byte(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_byte,
 | 
			
		||||
              "(parser/byte parser b)",
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -901,7 +1010,13 @@ static Janet cfun_parse_byte(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_status(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_status,
 | 
			
		||||
              "(parser/status parser)",
 | 
			
		||||
              "Gets the current status of the parser state machine. The status will "
 | 
			
		||||
              "be one of:\n\n"
 | 
			
		||||
              "* :pending - a value is being parsed.\n\n"
 | 
			
		||||
              "* :error - a parsing error was encountered.\n\n"
 | 
			
		||||
              "* :root - the parser can either read more values or safely terminate.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    const char *stat = NULL;
 | 
			
		||||
@@ -922,7 +1037,12 @@ static Janet cfun_parse_status(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_ckeywordv(stat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_error(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_error,
 | 
			
		||||
              "(parser/error parser)",
 | 
			
		||||
              "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`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    const char *err = janet_parser_error(p);
 | 
			
		||||
@@ -934,22 +1054,52 @@ static Janet cfun_parse_error(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_produce(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
JANET_CORE_FN(cfun_parse_produce,
 | 
			
		||||
              "(parser/produce parser &opt wrap)",
 | 
			
		||||
              "Dequeue the next value in the parse queue. Will return nil if "
 | 
			
		||||
              "no parsed values are in the queue, otherwise will dequeue the "
 | 
			
		||||
              "next value. If `wrap` is truthy, will return a 1-element tuple that "
 | 
			
		||||
              "wraps the result. This tuple can be used for source-mapping "
 | 
			
		||||
              "purposes.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    return janet_parser_produce(p);
 | 
			
		||||
    if (argc == 2 && janet_truthy(argv[1])) {
 | 
			
		||||
        return janet_parser_produce_wrapped(p);
 | 
			
		||||
    } else {
 | 
			
		||||
        return janet_parser_produce(p);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_flush(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_flush,
 | 
			
		||||
              "(parser/flush parser)",
 | 
			
		||||
              "Clears the parser state and parse queue. Can be used to reset the parser "
 | 
			
		||||
              "if an error was encountered. Does not reset the line and column counter, so "
 | 
			
		||||
              "to begin parsing in a new context, create a new parser.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    janet_parser_flush(p);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_where(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
JANET_CORE_FN(cfun_parse_where,
 | 
			
		||||
              "(parser/where parser &opt line col)",
 | 
			
		||||
              "Returns the current line number and column of the parser's internal state. If line is "
 | 
			
		||||
              "provided, the current line number of the parser is first set to that value. If column is "
 | 
			
		||||
              "also provided, the current column number of the parser is also first set to that value.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        int32_t line = janet_getinteger(argv, 1);
 | 
			
		||||
        if (line < 1)
 | 
			
		||||
            janet_panicf("invalid line number %d", line);
 | 
			
		||||
        p->line = (size_t) line;
 | 
			
		||||
    }
 | 
			
		||||
    if (argc > 2) {
 | 
			
		||||
        int32_t column = janet_getinteger(argv, 2);
 | 
			
		||||
        if (column < 0)
 | 
			
		||||
            janet_panicf("invalid column number %d", column);
 | 
			
		||||
        p->column = (size_t) column;
 | 
			
		||||
    }
 | 
			
		||||
    Janet *tup = janet_tuple_begin(2);
 | 
			
		||||
    tup[0] = janet_wrap_integer(p->line);
 | 
			
		||||
    tup[1] = janet_wrap_integer(p->column);
 | 
			
		||||
@@ -965,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));
 | 
			
		||||
    }
 | 
			
		||||
@@ -1051,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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1061,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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1076,7 +1232,16 @@ static const struct ParserStateGetter parser_state_getters[] = {
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_state(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_state,
 | 
			
		||||
              "(parser/state parser &opt key)",
 | 
			
		||||
              "Returns a representation of the internal state of the parser. If a key is passed, "
 | 
			
		||||
              "only that information about the state is returned. Allowed keys are:\n\n"
 | 
			
		||||
              "* :delimiters - Each byte in the string represents a nested data structure. For example, "
 | 
			
		||||
              "if the parser state is '([\"', then the parser is in the middle of parsing a "
 | 
			
		||||
              "string inside of square brackets inside parentheses. Can be used to augment a REPL prompt.\n\n"
 | 
			
		||||
              "* :frames - Each table in the array represents a 'frame' in the parser state. Frames "
 | 
			
		||||
              "contain information about the start of the expression being parsed as well as the "
 | 
			
		||||
              "type of that expression and some type-specific information.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    const uint8_t *key = NULL;
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
@@ -1104,7 +1269,11 @@ static Janet cfun_parse_state(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_clone(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_clone,
 | 
			
		||||
              "(parser/clone p)",
 | 
			
		||||
              "Creates a deep clone of a parser that is identical to the input parser. "
 | 
			
		||||
              "This cloned parser can be used to continue parsing from a good checkpoint "
 | 
			
		||||
              "if parsing later fails. Returns a new parser.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *src = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    JanetParser *dest = janet_abstract(&janet_parser_type, sizeof(JanetParser));
 | 
			
		||||
@@ -1134,101 +1303,28 @@ static int parserget(void *p, Janet key, Janet *out) {
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), parser_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg parse_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "parser/new", cfun_parse_parser,
 | 
			
		||||
        JDOC("(parser/new)\n\n"
 | 
			
		||||
             "Creates and returns a new parser object. Parsers are state machines "
 | 
			
		||||
             "that can receive bytes, and generate a stream of values.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/clone", cfun_parse_clone,
 | 
			
		||||
        JDOC("(parser/clone p)\n\n"
 | 
			
		||||
             "Creates a deep clone of a parser that is identical to the input parser. "
 | 
			
		||||
             "This cloned parser can be used to continue parsing from a good checkpoint "
 | 
			
		||||
             "if parsing later fails. Returns a new parser.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/has-more", cfun_parse_has_more,
 | 
			
		||||
        JDOC("(parser/has-more parser)\n\n"
 | 
			
		||||
             "Check if the parser has more values in the value queue.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/produce", cfun_parse_produce,
 | 
			
		||||
        JDOC("(parser/produce parser)\n\n"
 | 
			
		||||
             "Dequeue the next value in the parse queue. Will return nil if "
 | 
			
		||||
             "no parsed values are in the queue, otherwise will dequeue the "
 | 
			
		||||
             "next value.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/consume", cfun_parse_consume,
 | 
			
		||||
        JDOC("(parser/consume parser bytes &opt index)\n\n"
 | 
			
		||||
             "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 "
 | 
			
		||||
             "the number of bytes read.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/byte", cfun_parse_byte,
 | 
			
		||||
        JDOC("(parser/byte parser b)\n\n"
 | 
			
		||||
             "Input a single byte into the parser byte stream. Returns the parser.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/error", cfun_parse_error,
 | 
			
		||||
        JDOC("(parser/error parser)\n\n"
 | 
			
		||||
             "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/status", cfun_parse_status,
 | 
			
		||||
        JDOC("(parser/status parser)\n\n"
 | 
			
		||||
             "Gets the current status of the parser state machine. The status will "
 | 
			
		||||
             "be one of:\n\n"
 | 
			
		||||
             "\t:pending - a value is being parsed.\n"
 | 
			
		||||
             "\t:error - a parsing error was encountered.\n"
 | 
			
		||||
             "\t:root - the parser can either read more values or safely terminate.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/flush", cfun_parse_flush,
 | 
			
		||||
        JDOC("(parser/flush parser)\n\n"
 | 
			
		||||
             "Clears the parser state and parse queue. Can be used to reset the parser "
 | 
			
		||||
             "if an error was encountered. Does not reset the line and column counter, so "
 | 
			
		||||
             "to begin parsing in a new context, create a new parser.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/state", cfun_parse_state,
 | 
			
		||||
        JDOC("(parser/state parser &opt key)\n\n"
 | 
			
		||||
             "Returns a representation of the internal state of the parser. If a key is passed, "
 | 
			
		||||
             "only that information about the state is returned. Allowed keys are:\n\n"
 | 
			
		||||
             "\t:delimiters - Each byte in the string represents a nested data structure. For example, "
 | 
			
		||||
             "if the parser state is '([\"', then the parser is in the middle of parsing a "
 | 
			
		||||
             "string inside of square brackets inside parentheses. Can be used to augment a REPL prompt."
 | 
			
		||||
             "\t:frames - Each table in the array represents a 'frame' in the parser state. Frames "
 | 
			
		||||
             "contain information about the start of the expression being parsed as well as the "
 | 
			
		||||
             "type of that expression and some type-specific information.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/where", cfun_parse_where,
 | 
			
		||||
        JDOC("(parser/where parser)\n\n"
 | 
			
		||||
             "Returns the current line number and column of the parser's internal state.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/eof", cfun_parse_eof,
 | 
			
		||||
        JDOC("(parser/eof parser)\n\n"
 | 
			
		||||
             "Indicate that the end of file was reached to the parser. This puts the parser in the :dead state.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/insert", cfun_parse_insert,
 | 
			
		||||
        JDOC("(parser/insert parser value)\n\n"
 | 
			
		||||
             "Insert a value into the parser. This means that the parser state can be manipulated "
 | 
			
		||||
             "in between chunks of bytes. This would allow a user to add extra elements to arrays "
 | 
			
		||||
             "and tuples, for example. Returns the parser.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
static Janet parsernext(void *p, Janet key) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    return janet_nextmethod(parser_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load the library */
 | 
			
		||||
void janet_lib_parse(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, parse_cfuns);
 | 
			
		||||
    JanetRegExt parse_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("parser/new", cfun_parse_parser),
 | 
			
		||||
        JANET_CORE_REG("parser/clone", cfun_parse_clone),
 | 
			
		||||
        JANET_CORE_REG("parser/has-more", cfun_parse_has_more),
 | 
			
		||||
        JANET_CORE_REG("parser/produce", cfun_parse_produce),
 | 
			
		||||
        JANET_CORE_REG("parser/consume", cfun_parse_consume),
 | 
			
		||||
        JANET_CORE_REG("parser/byte", cfun_parse_byte),
 | 
			
		||||
        JANET_CORE_REG("parser/error", cfun_parse_error),
 | 
			
		||||
        JANET_CORE_REG("parser/status", cfun_parse_status),
 | 
			
		||||
        JANET_CORE_REG("parser/flush", cfun_parse_flush),
 | 
			
		||||
        JANET_CORE_REG("parser/state", cfun_parse_state),
 | 
			
		||||
        JANET_CORE_REG("parser/where", cfun_parse_where),
 | 
			
		||||
        JANET_CORE_REG("parser/eof", cfun_parse_eof),
 | 
			
		||||
        JANET_CORE_REG("parser/insert", cfun_parse_insert),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, parse_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										603
									
								
								src/core/peg.c
									
									
									
									
									
								
							
							
						
						
									
										603
									
								
								src/core/peg.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -44,9 +44,13 @@ typedef struct {
 | 
			
		||||
    JanetArray *captures;
 | 
			
		||||
    JanetBuffer *scratch;
 | 
			
		||||
    JanetBuffer *tags;
 | 
			
		||||
    JanetArray *tagged_captures;
 | 
			
		||||
    const Janet *extrav;
 | 
			
		||||
    int32_t *linemap;
 | 
			
		||||
    int32_t extrac;
 | 
			
		||||
    int32_t depth;
 | 
			
		||||
    int32_t linemaplen;
 | 
			
		||||
    int32_t has_backref;
 | 
			
		||||
    enum {
 | 
			
		||||
        PEG_MODE_NORMAL,
 | 
			
		||||
        PEG_MODE_ACCUMULATE
 | 
			
		||||
@@ -58,6 +62,7 @@ typedef struct {
 | 
			
		||||
 * if one branch fails and try a new branch. */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int32_t cap;
 | 
			
		||||
    int32_t tcap;
 | 
			
		||||
    int32_t scratch;
 | 
			
		||||
} CapState;
 | 
			
		||||
 | 
			
		||||
@@ -66,6 +71,7 @@ static CapState cap_save(PegState *s) {
 | 
			
		||||
    CapState cs;
 | 
			
		||||
    cs.scratch = s->scratch->count;
 | 
			
		||||
    cs.cap = s->captures->count;
 | 
			
		||||
    cs.tcap = s->tagged_captures->count;
 | 
			
		||||
    return cs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -73,7 +79,15 @@ static CapState cap_save(PegState *s) {
 | 
			
		||||
static void cap_load(PegState *s, CapState cs) {
 | 
			
		||||
    s->scratch->count = cs.scratch;
 | 
			
		||||
    s->captures->count = cs.cap;
 | 
			
		||||
    s->tags->count = cs.cap;
 | 
			
		||||
    s->tags->count = cs.tcap;
 | 
			
		||||
    s->tagged_captures->count = cs.tcap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load a saved capture state in the case of success. Keeps
 | 
			
		||||
 * tagged captures around for backref. */
 | 
			
		||||
static void cap_load_keept(PegState *s, CapState cs) {
 | 
			
		||||
    s->scratch->count = cs.scratch;
 | 
			
		||||
    s->captures->count = cs.cap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add a capture */
 | 
			
		||||
@@ -81,12 +95,72 @@ static void pushcap(PegState *s, Janet capture, uint32_t tag) {
 | 
			
		||||
    if (s->mode == PEG_MODE_ACCUMULATE) {
 | 
			
		||||
        janet_to_string_b(s->scratch, capture);
 | 
			
		||||
    }
 | 
			
		||||
    if (tag || s->mode == PEG_MODE_NORMAL) {
 | 
			
		||||
    if (s->mode == PEG_MODE_NORMAL) {
 | 
			
		||||
        janet_array_push(s->captures, capture);
 | 
			
		||||
    }
 | 
			
		||||
    if (s->has_backref) {
 | 
			
		||||
        janet_array_push(s->tagged_captures, capture);
 | 
			
		||||
        janet_buffer_push_u8(s->tags, tag);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lazily generate line map to get line and column information for PegState.
 | 
			
		||||
 * line and column are 1-indexed. */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int32_t line;
 | 
			
		||||
    int32_t col;
 | 
			
		||||
} LineCol;
 | 
			
		||||
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++) {
 | 
			
		||||
            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++) {
 | 
			
		||||
            if (*c == '\n') mem[index++] = (int32_t)(c - s->text_start);
 | 
			
		||||
        }
 | 
			
		||||
        s->linemaplen = newline_count;
 | 
			
		||||
        s->linemap = mem;
 | 
			
		||||
    }
 | 
			
		||||
    /* Do binary search for line. Slightly modified from classic binary search:
 | 
			
		||||
     * - if we find that our current character is a line break, just return immediately.
 | 
			
		||||
     *   a newline character is consider to be on the same line as the character before
 | 
			
		||||
     *   (\n is line terminator, not line separator).
 | 
			
		||||
     * - in the not-found case, we still want to find the greatest-indexed newline that
 | 
			
		||||
     *   is before position. we use that to calcuate the line and column.
 | 
			
		||||
     * - in the case that lo = 0 and s->linemap[0] is still greater than position, we
 | 
			
		||||
     *   are on the first line and our column is position + 1. */
 | 
			
		||||
    int32_t hi = s->linemaplen; /* hi is greater than the actual line */
 | 
			
		||||
    int32_t lo = 0; /* lo is less than or equal to the actual line */
 | 
			
		||||
    LineCol ret;
 | 
			
		||||
    while (lo + 1 < hi) {
 | 
			
		||||
        int32_t mid = lo + (hi - lo) / 2;
 | 
			
		||||
        if (s->linemap[mid] >= position) {
 | 
			
		||||
            hi = mid;
 | 
			
		||||
        } else {
 | 
			
		||||
            lo = mid;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* first line case */
 | 
			
		||||
    if (s->linemaplen == 0 || (lo == 0 && s->linemap[0] >= position)) {
 | 
			
		||||
        ret.line = 1;
 | 
			
		||||
        ret.col = position + 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        ret.line = lo + 2;
 | 
			
		||||
        ret.col = position - s->linemap[lo];
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert a uint64_t to a int64_t by wrapping to a maximum number of bytes */
 | 
			
		||||
static int64_t peg_convert_u64_s64(uint64_t from, int width) {
 | 
			
		||||
    int shift = 8 * (8 - width);
 | 
			
		||||
    return ((int64_t)(from << shift)) >> shift;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Prevent stack overflow */
 | 
			
		||||
#define down1(s) do { \
 | 
			
		||||
    if (0 == --((s)->depth)) janet_panic("peg/match recursed too deeply"); \
 | 
			
		||||
@@ -137,9 +211,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -186,43 +261,66 @@ 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) {
 | 
			
		||||
            while (text <= s->text_end) {
 | 
			
		||||
                CapState cs2 = cap_save(s);
 | 
			
		||||
                next_text = peg_rule(s, rule_a, text);
 | 
			
		||||
                if (next_text) {
 | 
			
		||||
                    if (rule[0] == RULE_TO) cap_load(s, cs2);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                cap_load(s, cs2);
 | 
			
		||||
                text++;
 | 
			
		||||
            }
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (text >= s->text_end) {
 | 
			
		||||
            if (text > s->text_end) {
 | 
			
		||||
                cap_load(s, cs);
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
@@ -262,7 +360,7 @@ tail:
 | 
			
		||||
            uint32_t tag = rule[2];
 | 
			
		||||
            for (int32_t i = s->tags->count - 1; i >= 0; i--) {
 | 
			
		||||
                if (s->tags->data[i] == search) {
 | 
			
		||||
                    pushcap(s, s->captures->data[i], tag);
 | 
			
		||||
                    pushcap(s, s->tagged_captures->data[i], tag);
 | 
			
		||||
                    return text;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -274,6 +372,18 @@ tail:
 | 
			
		||||
            return text;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_LINE: {
 | 
			
		||||
            LineCol lc = get_linecol_from_position(s, (int32_t)(text - s->text_start));
 | 
			
		||||
            pushcap(s, janet_wrap_number((double)(lc.line)), rule[1]);
 | 
			
		||||
            return text;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_COLUMN: {
 | 
			
		||||
            LineCol lc = get_linecol_from_position(s, (int32_t)(text - s->text_start));
 | 
			
		||||
            pushcap(s, janet_wrap_number((double)(lc.col)), rule[1]);
 | 
			
		||||
            return text;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_ARGUMENT: {
 | 
			
		||||
            int32_t index = ((int32_t *)rule)[1];
 | 
			
		||||
            Janet capture = (index >= s->extrac) ? janet_wrap_nil() : s->extrav[index];
 | 
			
		||||
@@ -287,20 +397,39 @@ tail:
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_CAPTURE: {
 | 
			
		||||
            uint32_t tag = rule[2];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            /* Specialized pushcap - avoid intermediate string creation */
 | 
			
		||||
            if (!tag && s->mode == PEG_MODE_ACCUMULATE) {
 | 
			
		||||
            if (!s->has_backref && s->mode == PEG_MODE_ACCUMULATE) {
 | 
			
		||||
                janet_buffer_push_bytes(s->scratch, text, (int32_t)(result - text));
 | 
			
		||||
            } else {
 | 
			
		||||
                uint32_t tag = rule[2];
 | 
			
		||||
                pushcap(s, janet_stringv(text, (int32_t)(result - text)), tag);
 | 
			
		||||
            }
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_CAPTURE_NUM: {
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            /* check number parsing */
 | 
			
		||||
            double x = 0.0;
 | 
			
		||||
            int32_t base = (int32_t) rule[2];
 | 
			
		||||
            if (janet_scan_number_base(text, (int32_t)(result - text), base, &x)) return NULL;
 | 
			
		||||
            /* Specialized pushcap - avoid intermediate string creation */
 | 
			
		||||
            if (!s->has_backref && s->mode == PEG_MODE_ACCUMULATE) {
 | 
			
		||||
                janet_buffer_push_bytes(s->scratch, text, (int32_t)(result - text));
 | 
			
		||||
            } else {
 | 
			
		||||
                uint32_t tag = rule[3];
 | 
			
		||||
                pushcap(s, janet_wrap_number(x), tag);
 | 
			
		||||
            }
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_ACCUMULATE: {
 | 
			
		||||
            uint32_t tag = rule[2];
 | 
			
		||||
            int oldmode = s->mode;
 | 
			
		||||
@@ -317,7 +446,7 @@ tail:
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            Janet cap = janet_stringv(s->scratch->data + cs.scratch,
 | 
			
		||||
                                      s->scratch->count - cs.scratch);
 | 
			
		||||
            cap_load(s, cs);
 | 
			
		||||
            cap_load_keept(s, cs);
 | 
			
		||||
            pushcap(s, cap, tag);
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
@@ -348,7 +477,7 @@ tail:
 | 
			
		||||
                        s->captures->data + cs.cap,
 | 
			
		||||
                        sizeof(Janet) * num_sub_captures);
 | 
			
		||||
            sub_captures->count = num_sub_captures;
 | 
			
		||||
            cap_load(s, cs);
 | 
			
		||||
            cap_load_keept(s, cs);
 | 
			
		||||
            pushcap(s, janet_wrap_array(sub_captures), tag);
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
@@ -393,7 +522,7 @@ tail:
 | 
			
		||||
                                     s->captures->data + cs.cap);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            cap_load(s, cs);
 | 
			
		||||
            cap_load_keept(s, cs);
 | 
			
		||||
            if (rule[0] == RULE_MATCHTIME && !janet_truthy(cap)) return NULL;
 | 
			
		||||
            pushcap(s, cap, tag);
 | 
			
		||||
            return result;
 | 
			
		||||
@@ -414,8 +543,8 @@ tail:
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Throw generic error */
 | 
			
		||||
                int32_t start = (int32_t)(text - s->text_start);
 | 
			
		||||
                int32_t end = (int32_t)(result - s->text_start);
 | 
			
		||||
                janet_panicf("match error in range (%d:%d)", start, end);
 | 
			
		||||
                LineCol lc = get_linecol_from_position(s, start);
 | 
			
		||||
                janet_panicf("match error at line %d, column %d", lc.line, lc.col);
 | 
			
		||||
            }
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
@@ -424,7 +553,7 @@ tail:
 | 
			
		||||
            uint32_t search = rule[1];
 | 
			
		||||
            for (int32_t i = s->tags->count - 1; i >= 0; i--) {
 | 
			
		||||
                if (s->tags->data[i] == search) {
 | 
			
		||||
                    Janet capture = s->captures->data[i];
 | 
			
		||||
                    Janet capture = s->tagged_captures->data[i];
 | 
			
		||||
                    if (!janet_checktype(capture, JANET_STRING))
 | 
			
		||||
                        return NULL;
 | 
			
		||||
                    const uint8_t *bytes = janet_unwrap_string(capture);
 | 
			
		||||
@@ -469,6 +598,71 @@ tail:
 | 
			
		||||
            return next_text;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_READINT: {
 | 
			
		||||
            uint32_t tag = rule[2];
 | 
			
		||||
            uint32_t signedness = rule[1] & 0x10;
 | 
			
		||||
            uint32_t endianess = rule[1] & 0x20;
 | 
			
		||||
            int width = (int)(rule[1] & 0xF);
 | 
			
		||||
            if (text + width > s->text_end) return NULL;
 | 
			
		||||
            uint64_t accum = 0;
 | 
			
		||||
            if (endianess) {
 | 
			
		||||
                /* BE */
 | 
			
		||||
                for (int i = 0; i < width; i++) accum = (accum << 8) | text[i];
 | 
			
		||||
            } else {
 | 
			
		||||
                /* LE */
 | 
			
		||||
                for (int i = width - 1; i >= 0; i--) accum = (accum << 8) | text[i];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Janet capture_value;
 | 
			
		||||
            /* We can only parse integeres of greater than 6 bytes reliable if int-types are enabled.
 | 
			
		||||
             * Otherwise, we may lose precision, so 6 is the maximum size when int-types are disabled. */
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
            if (width > 6) {
 | 
			
		||||
                if (signedness) {
 | 
			
		||||
                    capture_value = janet_wrap_s64(peg_convert_u64_s64(accum, width));
 | 
			
		||||
                } else {
 | 
			
		||||
                    capture_value = janet_wrap_u64(accum);
 | 
			
		||||
                }
 | 
			
		||||
            } else
 | 
			
		||||
#endif
 | 
			
		||||
            {
 | 
			
		||||
                double double_value;
 | 
			
		||||
                if (signedness) {
 | 
			
		||||
                    double_value = (double)(peg_convert_u64_s64(accum, width));
 | 
			
		||||
                } else {
 | 
			
		||||
                    double_value = (double)accum;
 | 
			
		||||
                }
 | 
			
		||||
                capture_value = janet_wrap_number(double_value);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            pushcap(s, capture_value, tag);
 | 
			
		||||
            return text + width;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_UNREF: {
 | 
			
		||||
            int32_t tcap = s->tags->count;
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            int32_t final_tcap = s->tags->count;
 | 
			
		||||
            /* Truncate tagged captures to not include items of the given tag */
 | 
			
		||||
            int32_t w = tcap;
 | 
			
		||||
            /* If no tag is given, drop ALL tagged captures */
 | 
			
		||||
            if (rule[2]) {
 | 
			
		||||
                for (int32_t i = tcap; i < final_tcap; i++) {
 | 
			
		||||
                    if (s->tags->data[i] != (0xFF & rule[2])) {
 | 
			
		||||
                        s->tags->data[w] = s->tags->data[i];
 | 
			
		||||
                        s->tagged_captures->data[w] = s->tagged_captures->data[i];
 | 
			
		||||
                        w++;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            s->tags->count = w;
 | 
			
		||||
            s->tagged_captures->count = w;
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -485,6 +679,7 @@ typedef struct {
 | 
			
		||||
    Janet form;
 | 
			
		||||
    int depth;
 | 
			
		||||
    uint32_t nexttag;
 | 
			
		||||
    int has_backref;
 | 
			
		||||
} Builder;
 | 
			
		||||
 | 
			
		||||
/* Forward declaration to allow recursion */
 | 
			
		||||
@@ -783,10 +978,13 @@ static void spec_not(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_onerule(b, argc, argv, RULE_NOT);
 | 
			
		||||
}
 | 
			
		||||
static void spec_error(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_onerule(b, argc, argv, RULE_ERROR);
 | 
			
		||||
}
 | 
			
		||||
static void spec_drop(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_onerule(b, argc, argv, RULE_DROP);
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
        Reserve r = reserve(b, 2);
 | 
			
		||||
        uint32_t rule = peg_compile1(b, janet_wrap_number(0));
 | 
			
		||||
        emit_1(r, RULE_ERROR, rule);
 | 
			
		||||
    } else {
 | 
			
		||||
        spec_onerule(b, argc, argv, RULE_ERROR);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
static void spec_to(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_onerule(b, argc, argv, RULE_TO);
 | 
			
		||||
@@ -794,6 +992,9 @@ static void spec_to(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
static void spec_thru(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_onerule(b, argc, argv, RULE_THRU);
 | 
			
		||||
}
 | 
			
		||||
static void spec_drop(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_onerule(b, argc, argv, RULE_DROP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Rule of the form [rule, tag] */
 | 
			
		||||
static void spec_cap1(Builder *b, int32_t argc, const Janet *argv, uint32_t op) {
 | 
			
		||||
@@ -813,12 +1014,35 @@ static void spec_accumulate(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
static void spec_group(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_cap1(b, argc, argv, RULE_GROUP);
 | 
			
		||||
}
 | 
			
		||||
static void spec_unref(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_cap1(b, argc, argv, RULE_UNREF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_arity(b, argc, 1, 3);
 | 
			
		||||
    Reserve r = reserve(b, 4);
 | 
			
		||||
    uint32_t base = 0;
 | 
			
		||||
    if (argc >= 2) {
 | 
			
		||||
        if (!janet_checktype(argv[1], JANET_NIL)) {
 | 
			
		||||
            if (!janet_checkint(argv[1])) goto error;
 | 
			
		||||
            base = (uint32_t) janet_unwrap_integer(argv[1]);
 | 
			
		||||
            if (base < 2 || base > 36) goto error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
 | 
			
		||||
    uint32_t rule = peg_compile1(b, argv[0]);
 | 
			
		||||
    emit_3(r, RULE_CAPTURE_NUM, rule, base, tag);
 | 
			
		||||
    return;
 | 
			
		||||
error:
 | 
			
		||||
    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) {
 | 
			
		||||
    peg_arity(b, argc, 1, 2);
 | 
			
		||||
    Reserve r = reserve(b, 3);
 | 
			
		||||
    uint32_t search = emit_tag(b, argv[0]);
 | 
			
		||||
    uint32_t tag = (argc == 2) ? emit_tag(b, argv[1]) : 0;
 | 
			
		||||
    b->has_backref = 1;
 | 
			
		||||
    emit_2(r, RULE_GETTAG, search, tag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -833,8 +1057,15 @@ static void spec_tag1(Builder *b, int32_t argc, const Janet *argv, uint32_t op)
 | 
			
		||||
static void spec_position(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_tag1(b, argc, argv, RULE_POSITION);
 | 
			
		||||
}
 | 
			
		||||
static void spec_line(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_tag1(b, argc, argv, RULE_LINE);
 | 
			
		||||
}
 | 
			
		||||
static void spec_column(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_tag1(b, argc, argv, RULE_COLUMN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_backmatch(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    b->has_backref = 1;
 | 
			
		||||
    spec_tag1(b, argc, argv, RULE_BACKMATCH);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -869,13 +1100,43 @@ 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
#define JANET_MAX_READINT_WIDTH 8
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_MAX_READINT_WIDTH 6
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void spec_readint(Builder *b, int32_t argc, const Janet *argv, uint32_t mask) {
 | 
			
		||||
    peg_arity(b, argc, 1, 2);
 | 
			
		||||
    Reserve r = reserve(b, 3);
 | 
			
		||||
    uint32_t tag = (argc == 2) ? emit_tag(b, argv[1]) : 0;
 | 
			
		||||
    int32_t width = peg_getnat(b, argv[0]);
 | 
			
		||||
    if ((width < 0) || (width > JANET_MAX_READINT_WIDTH)) {
 | 
			
		||||
        peg_panicf(b, "width must be between 0 and %d, got %d", JANET_MAX_READINT_WIDTH, width);
 | 
			
		||||
    }
 | 
			
		||||
    emit_2(r, RULE_READINT, mask | ((uint32_t) width), tag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_uint_le(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_readint(b, argc, argv, 0x0u);
 | 
			
		||||
}
 | 
			
		||||
static void spec_int_le(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_readint(b, argc, argv, 0x10u);
 | 
			
		||||
}
 | 
			
		||||
static void spec_uint_be(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_readint(b, argc, argv, 0x20u);
 | 
			
		||||
}
 | 
			
		||||
static void spec_int_be(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_readint(b, argc, argv, 0x30u);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Special compiler form */
 | 
			
		||||
typedef void (*Special)(Builder *b, int32_t argc, const Janet *argv);
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -906,15 +1167,20 @@ static const SpecialPair peg_specials[] = {
 | 
			
		||||
    {"capture", spec_capture},
 | 
			
		||||
    {"choice", spec_choice},
 | 
			
		||||
    {"cmt", spec_matchtime},
 | 
			
		||||
    {"column", spec_column},
 | 
			
		||||
    {"constant", spec_constant},
 | 
			
		||||
    {"drop", spec_drop},
 | 
			
		||||
    {"error", spec_error},
 | 
			
		||||
    {"group", spec_group},
 | 
			
		||||
    {"if", spec_if},
 | 
			
		||||
    {"if-not", spec_ifnot},
 | 
			
		||||
    {"int", spec_int_le},
 | 
			
		||||
    {"int-be", spec_int_be},
 | 
			
		||||
    {"lenprefix", spec_lenprefix},
 | 
			
		||||
    {"line", spec_line},
 | 
			
		||||
    {"look", spec_look},
 | 
			
		||||
    {"not", spec_not},
 | 
			
		||||
    {"number", spec_capture_number},
 | 
			
		||||
    {"opt", spec_opt},
 | 
			
		||||
    {"position", spec_position},
 | 
			
		||||
    {"quote", spec_capture},
 | 
			
		||||
@@ -926,6 +1192,9 @@ static const SpecialPair peg_specials[] = {
 | 
			
		||||
    {"some", spec_some},
 | 
			
		||||
    {"thru", spec_thru},
 | 
			
		||||
    {"to", spec_to},
 | 
			
		||||
    {"uint", spec_uint_le},
 | 
			
		||||
    {"uint-be", spec_uint_be},
 | 
			
		||||
    {"unref", spec_unref},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Compile a janet value into a rule and return the rule index. */
 | 
			
		||||
@@ -942,7 +1211,9 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
 | 
			
		||||
    for (; i > 0 && janet_checktype(peg, JANET_KEYWORD); --i) {
 | 
			
		||||
        Janet nextPeg = janet_table_get_ex(grammar, peg, &grammar);
 | 
			
		||||
        if (!grammar || janet_checktype(nextPeg, JANET_NIL)) {
 | 
			
		||||
            nextPeg = janet_table_get(b->default_grammar, peg);
 | 
			
		||||
            nextPeg = (b->default_grammar == NULL)
 | 
			
		||||
                      ? janet_wrap_nil()
 | 
			
		||||
                      : janet_table_get(b->default_grammar, peg);
 | 
			
		||||
            if (janet_checktype(nextPeg, JANET_NIL)) {
 | 
			
		||||
                peg_panic(b, "unknown rule");
 | 
			
		||||
            }
 | 
			
		||||
@@ -990,6 +1261,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);
 | 
			
		||||
@@ -1006,6 +1284,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);
 | 
			
		||||
@@ -1129,12 +1419,13 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
     * bytecode. */
 | 
			
		||||
    uint32_t blen = (int32_t) peg->bytecode_len;
 | 
			
		||||
    uint32_t clen = peg->num_constants;
 | 
			
		||||
    uint8_t *op_flags = calloc(1, blen);
 | 
			
		||||
    uint8_t *op_flags = janet_calloc(1, blen);
 | 
			
		||||
    if (NULL == op_flags) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* verify peg bytecode */
 | 
			
		||||
    int32_t has_backref = 0;
 | 
			
		||||
    uint32_t i = 0;
 | 
			
		||||
    while (i < blen) {
 | 
			
		||||
        uint32_t instr = bytecode[i];
 | 
			
		||||
@@ -1148,9 +1439,15 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
            case RULE_NOTNCHAR:
 | 
			
		||||
            case RULE_RANGE:
 | 
			
		||||
            case RULE_POSITION:
 | 
			
		||||
            case RULE_LINE:
 | 
			
		||||
            case RULE_COLUMN:
 | 
			
		||||
                /* [1 word] */
 | 
			
		||||
                i += 2;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_BACKMATCH:
 | 
			
		||||
                /* [1 word] */
 | 
			
		||||
                i += 2;
 | 
			
		||||
                has_backref = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_SET:
 | 
			
		||||
                /* [8 words] */
 | 
			
		||||
@@ -1191,18 +1488,29 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
                i += 4;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_ARGUMENT:
 | 
			
		||||
                /* [searchtag, tag] */
 | 
			
		||||
                i += 3;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_GETTAG:
 | 
			
		||||
                /* [searchtag, tag] */
 | 
			
		||||
                i += 3;
 | 
			
		||||
                has_backref = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_CONSTANT:
 | 
			
		||||
                /* [constant, tag] */
 | 
			
		||||
                if (rule[1] >= clen) goto bad;
 | 
			
		||||
                i += 3;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_CAPTURE_NUM:
 | 
			
		||||
                /* [rule, base, tag] */
 | 
			
		||||
                if (rule[1] >= blen) goto bad;
 | 
			
		||||
                op_flags[rule[1]] |= 0x01;
 | 
			
		||||
                i += 4;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_ACCUMULATE:
 | 
			
		||||
            case RULE_GROUP:
 | 
			
		||||
            case RULE_CAPTURE:
 | 
			
		||||
            case RULE_UNREF:
 | 
			
		||||
                /* [rule, tag] */
 | 
			
		||||
                if (rule[1] >= blen) goto bad;
 | 
			
		||||
                op_flags[rule[1]] |= 0x01;
 | 
			
		||||
@@ -1226,6 +1534,11 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
                op_flags[rule[1]] |= 0x01;
 | 
			
		||||
                i += 2;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_READINT:
 | 
			
		||||
                /* [ width | (endianess << 5) | (signedness << 6), tag ] */
 | 
			
		||||
                if (rule[1] > JANET_MAX_READINT_WIDTH) goto bad;
 | 
			
		||||
                i += 3;
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                goto bad;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1242,25 +1555,31 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
    /* Good return */
 | 
			
		||||
    peg->bytecode = bytecode;
 | 
			
		||||
    peg->constants = constants;
 | 
			
		||||
    free(op_flags);
 | 
			
		||||
    peg->has_backref = has_backref;
 | 
			
		||||
    janet_free(op_flags);
 | 
			
		||||
    return peg;
 | 
			
		||||
 | 
			
		||||
bad:
 | 
			
		||||
    free(op_flags);
 | 
			
		||||
    janet_free(op_flags);
 | 
			
		||||
    janet_panic("invalid peg bytecode");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int cfun_peg_getter(JanetAbstract a, Janet key, Janet *out);
 | 
			
		||||
static Janet peg_next(void *p, Janet key);
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_peg_type = {
 | 
			
		||||
    "core/peg",
 | 
			
		||||
    NULL,
 | 
			
		||||
    peg_mark,
 | 
			
		||||
    cfun_peg_getter,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL, /* put */
 | 
			
		||||
    peg_marshal,
 | 
			
		||||
    peg_unmarshal,
 | 
			
		||||
    JANET_ATEND_UNMARSHAL
 | 
			
		||||
    NULL, /* tostring */
 | 
			
		||||
    NULL, /* compare */
 | 
			
		||||
    NULL, /* hash */
 | 
			
		||||
    peg_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Convert Builder to JanetPeg (Janet Abstract Value) */
 | 
			
		||||
@@ -1278,6 +1597,7 @@ static JanetPeg *make_peg(Builder *b) {
 | 
			
		||||
    safe_memcpy(peg->bytecode, b->bytecode, bytecode_size);
 | 
			
		||||
    safe_memcpy(peg->constants, b->constants, constants_size);
 | 
			
		||||
    peg->bytecode_len = janet_v_count(b->bytecode);
 | 
			
		||||
    peg->has_backref = b->has_backref;
 | 
			
		||||
    return peg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1285,13 +1605,20 @@ static JanetPeg *make_peg(Builder *b) {
 | 
			
		||||
static JanetPeg *compile_peg(Janet x) {
 | 
			
		||||
    Builder builder;
 | 
			
		||||
    builder.grammar = janet_table(0);
 | 
			
		||||
    builder.default_grammar = janet_get_core_table("default-peg-grammar");
 | 
			
		||||
    builder.default_grammar = NULL;
 | 
			
		||||
    {
 | 
			
		||||
        Janet default_grammarv = janet_dyn("peg-grammar");
 | 
			
		||||
        if (janet_checktype(default_grammarv, JANET_TABLE)) {
 | 
			
		||||
            builder.default_grammar = janet_unwrap_table(default_grammarv);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    builder.tags = janet_table(0);
 | 
			
		||||
    builder.constants = NULL;
 | 
			
		||||
    builder.bytecode = NULL;
 | 
			
		||||
    builder.nexttag = 1;
 | 
			
		||||
    builder.form = x;
 | 
			
		||||
    builder.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    builder.has_backref = 0;
 | 
			
		||||
    peg_compile1(&builder, x);
 | 
			
		||||
    JanetPeg *peg = make_peg(&builder);
 | 
			
		||||
    builder_cleanup(&builder);
 | 
			
		||||
@@ -1302,74 +1629,190 @@ static JanetPeg *compile_peg(Janet x) {
 | 
			
		||||
 * C Functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_compile(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_compile,
 | 
			
		||||
              "(peg/compile peg)",
 | 
			
		||||
              "Compiles a peg source data structure into a <core/peg>. This will speed up matching "
 | 
			
		||||
              "if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to suppliment "
 | 
			
		||||
              "the grammar of the peg for otherwise undefined peg keywords.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetPeg *peg = compile_peg(argv[0]);
 | 
			
		||||
    return janet_wrap_abstract(peg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_match(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
/* Common data for peg cfunctions */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    JanetPeg *peg;
 | 
			
		||||
    PegState s;
 | 
			
		||||
    JanetByteView bytes;
 | 
			
		||||
    Janet subst;
 | 
			
		||||
    int32_t start;
 | 
			
		||||
} PegCall;
 | 
			
		||||
 | 
			
		||||
/* Initialize state for peg cfunctions */
 | 
			
		||||
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);
 | 
			
		||||
    if (janet_checktype(argv[0], JANET_ABSTRACT) &&
 | 
			
		||||
            janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) {
 | 
			
		||||
        peg = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
        ret.peg = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
    } else {
 | 
			
		||||
        peg = compile_peg(argv[0]);
 | 
			
		||||
        ret.peg = compile_peg(argv[0]);
 | 
			
		||||
    }
 | 
			
		||||
    JanetByteView bytes = janet_getbytes(argv, 1);
 | 
			
		||||
    int32_t start;
 | 
			
		||||
    PegState s;
 | 
			
		||||
    if (argc > 2) {
 | 
			
		||||
        start = janet_gethalfrange(argv, 2, bytes.len, "offset");
 | 
			
		||||
        s.extrac = argc - 3;
 | 
			
		||||
        s.extrav = janet_tuple_n(argv + 3, argc - 3);
 | 
			
		||||
    if (get_replace) {
 | 
			
		||||
        ret.subst = argv[1];
 | 
			
		||||
        ret.bytes = janet_getbytes(argv, 2);
 | 
			
		||||
    } else {
 | 
			
		||||
        start = 0;
 | 
			
		||||
        s.extrac = 0;
 | 
			
		||||
        s.extrav = NULL;
 | 
			
		||||
        ret.bytes = janet_getbytes(argv, 1);
 | 
			
		||||
    }
 | 
			
		||||
    s.mode = PEG_MODE_NORMAL;
 | 
			
		||||
    s.text_start = bytes.bytes;
 | 
			
		||||
    s.text_end = bytes.bytes + bytes.len;
 | 
			
		||||
    s.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    s.captures = janet_array(0);
 | 
			
		||||
    s.scratch = janet_buffer(10);
 | 
			
		||||
    s.tags = janet_buffer(10);
 | 
			
		||||
    s.constants = peg->constants;
 | 
			
		||||
    s.bytecode = peg->bytecode;
 | 
			
		||||
    const uint8_t *result = peg_rule(&s, s.bytecode, bytes.bytes + start);
 | 
			
		||||
    return result ? janet_wrap_array(s.captures) : janet_wrap_nil();
 | 
			
		||||
    if (argc > min) {
 | 
			
		||||
        ret.start = janet_gethalfrange(argv, min, ret.bytes.len, "offset");
 | 
			
		||||
        ret.s.extrac = argc - min - 1;
 | 
			
		||||
        ret.s.extrav = janet_tuple_n(argv + min + 1, argc - min - 1);
 | 
			
		||||
    } else {
 | 
			
		||||
        ret.start = 0;
 | 
			
		||||
        ret.s.extrac = 0;
 | 
			
		||||
        ret.s.extrav = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    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.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    ret.s.captures = janet_array(0);
 | 
			
		||||
    ret.s.tagged_captures = janet_array(0);
 | 
			
		||||
    ret.s.scratch = janet_buffer(10);
 | 
			
		||||
    ret.s.tags = janet_buffer(10);
 | 
			
		||||
    ret.s.constants = ret.peg->constants;
 | 
			
		||||
    ret.s.bytecode = ret.peg->bytecode;
 | 
			
		||||
    ret.s.linemap = NULL;
 | 
			
		||||
    ret.s.linemaplen = -1;
 | 
			
		||||
    ret.s.has_backref = ret.peg->has_backref;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_match,
 | 
			
		||||
              "(peg/match peg text &opt start & args)",
 | 
			
		||||
              "Match a Parsing Expression Grammar to a byte string and return an array of captured values. "
 | 
			
		||||
              "Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + c.start);
 | 
			
		||||
    return result ? janet_wrap_array(c.s.captures) : janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_find,
 | 
			
		||||
              "(peg/find peg text &opt start & args)",
 | 
			
		||||
              "Find first index where the peg matches in text. Returns an integer, or nil if not found.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    for (int32_t i = c.start; i < c.bytes.len; i++) {
 | 
			
		||||
        peg_call_reset(&c);
 | 
			
		||||
        if (peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i))
 | 
			
		||||
            return janet_wrap_integer(i);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_find_all,
 | 
			
		||||
              "(peg/find-all peg text &opt start & args)",
 | 
			
		||||
              "Find all indexes where the peg matches in text. Returns an array of integers.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    JanetArray *ret = janet_array(0);
 | 
			
		||||
    for (int32_t i = c.start; i < c.bytes.len; i++) {
 | 
			
		||||
        peg_call_reset(&c);
 | 
			
		||||
        if (peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i))
 | 
			
		||||
            janet_array_push(ret, janet_wrap_integer(i));
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_array(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 1);
 | 
			
		||||
    JanetBuffer *ret = janet_buffer(0);
 | 
			
		||||
    int32_t trail = 0;
 | 
			
		||||
    for (int32_t i = c.start; i < c.bytes.len;) {
 | 
			
		||||
        peg_call_reset(&c);
 | 
			
		||||
        const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i);
 | 
			
		||||
        if (NULL != result) {
 | 
			
		||||
            if (trail < i) {
 | 
			
		||||
                janet_buffer_push_bytes(ret, c.bytes.bytes + trail, (i - trail));
 | 
			
		||||
                trail = i;
 | 
			
		||||
            }
 | 
			
		||||
            int32_t nexti = (int32_t)(result - c.bytes.bytes);
 | 
			
		||||
            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;
 | 
			
		||||
            if (only_one) break;
 | 
			
		||||
        } else {
 | 
			
		||||
            i++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (trail < c.bytes.len) {
 | 
			
		||||
        janet_buffer_push_bytes(ret, c.bytes.bytes + trail, (c.bytes.len - trail));
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_buffer(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace_all,
 | 
			
		||||
              "(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 `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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetMethod peg_methods[] = {
 | 
			
		||||
    {"match", cfun_peg_match},
 | 
			
		||||
    {"find", cfun_peg_find},
 | 
			
		||||
    {"find-all", cfun_peg_find_all},
 | 
			
		||||
    {"replace", cfun_peg_replace},
 | 
			
		||||
    {"replace-all", cfun_peg_replace_all},
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int cfun_peg_getter(JanetAbstract a, Janet key, Janet *out) {
 | 
			
		||||
    (void) a;
 | 
			
		||||
    if (janet_keyeq(key, "match")) {
 | 
			
		||||
        *out = janet_wrap_cfunction(cfun_peg_match);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
    if (!janet_checktype(key, JANET_KEYWORD))
 | 
			
		||||
        return 0;
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), peg_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg peg_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "peg/compile", cfun_peg_compile,
 | 
			
		||||
        JDOC("(peg/compile peg)\n\n"
 | 
			
		||||
             "Compiles a peg source data structure into a <core/peg>. This will speed up matching "
 | 
			
		||||
             "if the same peg will be used multiple times.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/match", cfun_peg_match,
 | 
			
		||||
        JDOC("(peg/match peg text &opt start & args)\n\n"
 | 
			
		||||
             "Match a Parsing Expression Grammar to a byte string and return an array of captured values. "
 | 
			
		||||
             "Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
static Janet peg_next(void *p, Janet key) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    return janet_nextmethod(peg_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load the peg module */
 | 
			
		||||
void janet_lib_peg(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, peg_cfuns);
 | 
			
		||||
    JanetRegExt cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("peg/compile", cfun_peg_compile),
 | 
			
		||||
        JANET_CORE_REG("peg/match", cfun_peg_match),
 | 
			
		||||
        JANET_CORE_REG("peg/find", cfun_peg_find),
 | 
			
		||||
        JANET_CORE_REG("peg/find-all", cfun_peg_find_all),
 | 
			
		||||
        JANET_CORE_REG("peg/replace", cfun_peg_replace),
 | 
			
		||||
        JANET_CORE_REG("peg/replace-all", cfun_peg_replace_all),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_peg_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										256
									
								
								src/core/pp.c
									
									
									
									
									
								
							
							
						
						
									
										256
									
								
								src/core/pp.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,7 @@
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
/* Implements a pretty printer for Janet. The pretty printer
 | 
			
		||||
 * is simple and not that flexible, but fast. */
 | 
			
		||||
@@ -39,12 +40,17 @@
 | 
			
		||||
 | 
			
		||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
 | 
			
		||||
    janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
 | 
			
		||||
    /* Use int32_t range for valid integers because that is the
 | 
			
		||||
     * range most integer-expecting functions in the C api use. */
 | 
			
		||||
    const char *fmt = (x == floor(x) &&
 | 
			
		||||
                       x <= ((double) INT32_MAX) &&
 | 
			
		||||
                       x >= ((double) INT32_MIN)) ? "%.0f" : "%g";
 | 
			
		||||
    int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, fmt, x);
 | 
			
		||||
                       x <= JANET_INTMAX_DOUBLE &&
 | 
			
		||||
                       x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g";
 | 
			
		||||
    int count;
 | 
			
		||||
    if (x == 0.0) {
 | 
			
		||||
        /* Prevent printing of '-0' */
 | 
			
		||||
        count = 1;
 | 
			
		||||
        buffer->data[buffer->count] = '0';
 | 
			
		||||
    } else {
 | 
			
		||||
        count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, fmt, x);
 | 
			
		||||
    }
 | 
			
		||||
    buffer->count += count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -103,7 +109,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';
 | 
			
		||||
@@ -123,9 +129,6 @@ static void string_description_b(JanetBuffer *buffer, const char *title, void *p
 | 
			
		||||
#undef POINTSIZE
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef HEX
 | 
			
		||||
#undef BUFSIZE
 | 
			
		||||
 | 
			
		||||
static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
 | 
			
		||||
    janet_buffer_push_u8(buffer, '"');
 | 
			
		||||
    for (int32_t i = 0; i < len; ++i) {
 | 
			
		||||
@@ -149,6 +152,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;
 | 
			
		||||
@@ -191,7 +200,7 @@ static void janet_escape_buffer_b(JanetBuffer *buffer, JanetBuffer *bx) {
 | 
			
		||||
void janet_to_string_b(JanetBuffer *buffer, Janet x) {
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        case JANET_NIL:
 | 
			
		||||
            janet_buffer_push_cstring(buffer, "nil");
 | 
			
		||||
            janet_buffer_push_cstring(buffer, "");
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_BOOLEAN:
 | 
			
		||||
            janet_buffer_push_cstring(buffer,
 | 
			
		||||
@@ -225,12 +234,14 @@ void janet_to_string_b(JanetBuffer *buffer, Janet x) {
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
        case JANET_CFUNCTION: {
 | 
			
		||||
            Janet check = janet_table_get(janet_vm_registry, x);
 | 
			
		||||
            if (janet_checktype(check, JANET_SYMBOL)) {
 | 
			
		||||
            JanetCFunRegistry *reg = janet_registry_get(janet_unwrap_cfunction(x));
 | 
			
		||||
            if (NULL != reg) {
 | 
			
		||||
                janet_buffer_push_cstring(buffer, "<cfunction ");
 | 
			
		||||
                janet_buffer_push_bytes(buffer,
 | 
			
		||||
                                        janet_unwrap_symbol(check),
 | 
			
		||||
                                        janet_string_length(janet_unwrap_symbol(check)));
 | 
			
		||||
                if (NULL != reg->name_prefix) {
 | 
			
		||||
                    janet_buffer_push_cstring(buffer, reg->name_prefix);
 | 
			
		||||
                    janet_buffer_push_u8(buffer, '/');
 | 
			
		||||
                }
 | 
			
		||||
                janet_buffer_push_cstring(buffer, reg->name);
 | 
			
		||||
                janet_buffer_push_u8(buffer, '>');
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -239,6 +250,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 ");
 | 
			
		||||
@@ -257,21 +272,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;
 | 
			
		||||
}
 | 
			
		||||
@@ -280,6 +287,9 @@ void janet_description_b(JanetBuffer *buffer, Janet x) {
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_NIL:
 | 
			
		||||
            janet_buffer_push_cstring(buffer, "nil");
 | 
			
		||||
            return;
 | 
			
		||||
        case JANET_KEYWORD:
 | 
			
		||||
            janet_buffer_push_u8(buffer, ':');
 | 
			
		||||
            break;
 | 
			
		||||
@@ -346,6 +356,9 @@ struct pretty {
 | 
			
		||||
    int indent;
 | 
			
		||||
    int flags;
 | 
			
		||||
    int32_t bufstartlen;
 | 
			
		||||
    int32_t *keysort_buffer;
 | 
			
		||||
    int32_t keysort_capacity;
 | 
			
		||||
    int32_t keysort_start;
 | 
			
		||||
    JanetTable seen;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -354,12 +367,16 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
 | 
			
		||||
    if (depth == 0) return 1;
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        case JANET_NIL:
 | 
			
		||||
        case JANET_NUMBER:
 | 
			
		||||
        case JANET_BOOLEAN:
 | 
			
		||||
        case JANET_BUFFER:
 | 
			
		||||
        case JANET_STRING:
 | 
			
		||||
            janet_description_b(S->buffer, x);
 | 
			
		||||
            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;
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
        case JANET_KEYWORD:
 | 
			
		||||
            if (contains_bad_chars(janet_unwrap_keyword(x), janet_type(x) == JANET_SYMBOL)) return 1;
 | 
			
		||||
@@ -452,7 +469,7 @@ static const char *janet_pretty_colors[] = {
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m"
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[35m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
@@ -556,14 +573,31 @@ 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"));
 | 
			
		||||
                    Janet name = janet_table_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");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } 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)) {
 | 
			
		||||
@@ -576,8 +610,8 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                janet_buffer_push_cstring(S->buffer, "{");
 | 
			
		||||
            }
 | 
			
		||||
            janet_buffer_push_cstring(S->buffer, "{");
 | 
			
		||||
 | 
			
		||||
            S->depth--;
 | 
			
		||||
            S->indent += 2;
 | 
			
		||||
@@ -585,31 +619,55 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
 | 
			
		||||
                janet_buffer_push_cstring(S->buffer, "...");
 | 
			
		||||
            } else {
 | 
			
		||||
                int32_t i = 0, len = 0, cap = 0;
 | 
			
		||||
                int first_kv_pair = 1;
 | 
			
		||||
                const JanetKV *kvs = NULL;
 | 
			
		||||
                int counter = 0;
 | 
			
		||||
                janet_dictionary_view(x, &kvs, &len, &cap);
 | 
			
		||||
                if (!istable && !(S->flags & JANET_PRETTY_ONELINE) && len >= JANET_PRETTY_DICT_ONELINE)
 | 
			
		||||
                    janet_buffer_push_u8(S->buffer, ' ');
 | 
			
		||||
                if (is_dict_value && len >= JANET_PRETTY_DICT_ONELINE) print_newline(S, 0);
 | 
			
		||||
                for (i = 0; i < cap; i++) {
 | 
			
		||||
                    if (!janet_checktype(kvs[i].key, JANET_NIL)) {
 | 
			
		||||
                        if (counter == JANET_PRETTY_DICT_LIMIT && !(S->flags & JANET_PRETTY_NOTRUNC)) {
 | 
			
		||||
                            print_newline(S, 0);
 | 
			
		||||
                            janet_buffer_push_cstring(S->buffer, "...");
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        if (first_kv_pair) {
 | 
			
		||||
                            first_kv_pair = 0;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
 | 
			
		||||
                        }
 | 
			
		||||
                        janet_pretty_one(S, kvs[i].key, 0);
 | 
			
		||||
                        janet_buffer_push_u8(S->buffer, ' ');
 | 
			
		||||
                        janet_pretty_one(S, kvs[i].value, 1);
 | 
			
		||||
                        counter++;
 | 
			
		||||
                int32_t ks_start = S->keysort_start;
 | 
			
		||||
 | 
			
		||||
                /* Ensure buffer is large enough to sort keys. */
 | 
			
		||||
                int truncated = 0;
 | 
			
		||||
                int64_t mincap = (int64_t) len + (int64_t) ks_start;
 | 
			
		||||
                if (mincap > INT32_MAX) {
 | 
			
		||||
                    truncated = 1;
 | 
			
		||||
                    len = 0;
 | 
			
		||||
                    mincap = ks_start;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (S->keysort_capacity < mincap) {
 | 
			
		||||
                    if (mincap >= INT32_MAX / 2) {
 | 
			
		||||
                        S->keysort_capacity = INT32_MAX;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        S->keysort_capacity = (int32_t)(mincap * 2);
 | 
			
		||||
                    }
 | 
			
		||||
                    S->keysort_buffer = janet_srealloc(S->keysort_buffer, sizeof(int32_t) * S->keysort_capacity);
 | 
			
		||||
                    if (NULL == S->keysort_buffer) {
 | 
			
		||||
                        JANET_OUT_OF_MEMORY;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                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;
 | 
			
		||||
                    truncated = 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                for (i = 0; i < len; i++) {
 | 
			
		||||
                    if (i) print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
 | 
			
		||||
                    int32_t j = S->keysort_buffer[i + ks_start];
 | 
			
		||||
                    janet_pretty_one(S, kvs[j].key, 0);
 | 
			
		||||
                    janet_buffer_push_u8(S->buffer, ' ');
 | 
			
		||||
                    janet_pretty_one(S, kvs[j].value, 1);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (truncated) {
 | 
			
		||||
                    print_newline(S, 0);
 | 
			
		||||
                    janet_buffer_push_cstring(S->buffer, "...");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                S->keysort_start = ks_start;
 | 
			
		||||
            }
 | 
			
		||||
            S->indent -= 2;
 | 
			
		||||
            S->depth++;
 | 
			
		||||
@@ -632,6 +690,9 @@ static JanetBuffer *janet_pretty_(JanetBuffer *buffer, int depth, int flags, Jan
 | 
			
		||||
    S.indent = 0;
 | 
			
		||||
    S.flags = flags;
 | 
			
		||||
    S.bufstartlen = startlen;
 | 
			
		||||
    S.keysort_capacity = 0;
 | 
			
		||||
    S.keysort_buffer = NULL;
 | 
			
		||||
    S.keysort_start = 0;
 | 
			
		||||
    janet_table_init(&S.seen, 10);
 | 
			
		||||
    janet_pretty_one(&S, x, 0);
 | 
			
		||||
    janet_table_deinit(&S.seen);
 | 
			
		||||
@@ -654,6 +715,9 @@ static JanetBuffer *janet_jdn_(JanetBuffer *buffer, int depth, Janet x, int32_t
 | 
			
		||||
    S.indent = 0;
 | 
			
		||||
    S.flags = 0;
 | 
			
		||||
    S.bufstartlen = startlen;
 | 
			
		||||
    S.keysort_capacity = 0;
 | 
			
		||||
    S.keysort_buffer = NULL;
 | 
			
		||||
    S.keysort_start = 0;
 | 
			
		||||
    janet_table_init(&S.seen, 10);
 | 
			
		||||
    int res = print_jdn_one(&S, x, depth);
 | 
			
		||||
    janet_table_deinit(&S.seen);
 | 
			
		||||
@@ -682,7 +746,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]);
 | 
			
		||||
        }
 | 
			
		||||
@@ -697,20 +761,46 @@ 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},
 | 
			
		||||
    {'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)))
 | 
			
		||||
@@ -724,10 +814,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);
 | 
			
		||||
            strcpy(form, mapping);
 | 
			
		||||
            form += len;
 | 
			
		||||
        } else {
 | 
			
		||||
            *(form++) = *(p2++);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    *form = '\0';
 | 
			
		||||
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -752,11 +855,16 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'd':
 | 
			
		||||
                case 'i':
 | 
			
		||||
                case 'o':
 | 
			
		||||
                case 'i': {
 | 
			
		||||
                    int64_t n = va_arg(args, int);
 | 
			
		||||
                    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, unsigned int);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -813,7 +921,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
                case 'P':
 | 
			
		||||
                case 'p': { /* janet pretty , precision = depth */
 | 
			
		||||
                    int depth = atoi(precision);
 | 
			
		||||
                    if (depth < 1) depth = 4;
 | 
			
		||||
                    if (depth < 1) depth = JANET_RECURSION_GUARD;
 | 
			
		||||
                    char d = c[-1];
 | 
			
		||||
                    int has_color = (d == 'P') || (d == 'Q') || (d == 'M') || (d == 'N');
 | 
			
		||||
                    int has_oneline = (d == 'Q') || (d == 'q') || (d == 'N') || (d == 'n');
 | 
			
		||||
@@ -839,7 +947,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (nb >= MAX_ITEM)
 | 
			
		||||
                janet_panicf("format buffer overflow", form);
 | 
			
		||||
                janet_panic("format buffer overflow");
 | 
			
		||||
            if (nb > 0)
 | 
			
		||||
                janet_buffer_push_bytes(b, (uint8_t *) item, nb);
 | 
			
		||||
        }
 | 
			
		||||
@@ -910,11 +1018,16 @@ void janet_buffer_format(
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                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;
 | 
			
		||||
                }
 | 
			
		||||
@@ -930,8 +1043,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 {
 | 
			
		||||
@@ -953,6 +1067,9 @@ void janet_buffer_format(
 | 
			
		||||
                    janet_description_b(b, argv[arg]);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 't':
 | 
			
		||||
                    janet_buffer_push_cstring(b, typestr(argv[arg]));
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'M':
 | 
			
		||||
                case 'm':
 | 
			
		||||
                case 'N':
 | 
			
		||||
@@ -962,7 +1079,7 @@ void janet_buffer_format(
 | 
			
		||||
                case 'P':
 | 
			
		||||
                case 'p': { /* janet pretty , precision = depth */
 | 
			
		||||
                    int depth = atoi(precision);
 | 
			
		||||
                    if (depth < 1) depth = 4;
 | 
			
		||||
                    if (depth < 1) depth = JANET_RECURSION_GUARD;
 | 
			
		||||
                    char d = strfrmt[-1];
 | 
			
		||||
                    int has_color = (d == 'P') || (d == 'Q') || (d == 'M') || (d == 'N');
 | 
			
		||||
                    int has_oneline = (d == 'Q') || (d == 'q') || (d == 'N') || (d == 'n');
 | 
			
		||||
@@ -988,9 +1105,12 @@ void janet_buffer_format(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (nb >= MAX_ITEM)
 | 
			
		||||
                janet_panicf("format buffer overflow", form);
 | 
			
		||||
                janet_panic("format buffer overflow");
 | 
			
		||||
            if (nb > 0)
 | 
			
		||||
                janet_buffer_push_bytes(b, (uint8_t *) item, nb);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef HEX
 | 
			
		||||
#undef BUFSIZE
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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;
 | 
			
		||||
@@ -36,7 +38,7 @@ void janetc_regalloc_init(JanetcRegisterAllocator *ra) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janetc_regalloc_deinit(JanetcRegisterAllocator *ra) {
 | 
			
		||||
    free(ra->chunks);
 | 
			
		||||
    janet_free(ra->chunks);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Fallbacks for when ctz not available */
 | 
			
		||||
@@ -70,7 +72,7 @@ void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocato
 | 
			
		||||
    size = sizeof(uint32_t) * (size_t) dest->capacity;
 | 
			
		||||
    dest->regtemps = 0;
 | 
			
		||||
    if (size) {
 | 
			
		||||
        dest->chunks = malloc(size);
 | 
			
		||||
        dest->chunks = janet_malloc(size);
 | 
			
		||||
        if (!dest->chunks) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -87,7 +89,7 @@ static void pushchunk(JanetcRegisterAllocator *ra) {
 | 
			
		||||
    int32_t newcount = ra->count + 1;
 | 
			
		||||
    if (newcount > ra->capacity) {
 | 
			
		||||
        int32_t newcapacity = newcount * 2;
 | 
			
		||||
        ra->chunks = realloc(ra->chunks, (size_t) newcapacity * sizeof(uint32_t));
 | 
			
		||||
        ra->chunks = janet_realloc(ra->chunks, (size_t) newcapacity * sizeof(uint32_t));
 | 
			
		||||
        if (!ra->chunks) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -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) 2020 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) 2020 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,14 +51,15 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
                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);
 | 
			
		||||
                if (cres.macrofiber) {
 | 
			
		||||
                    janet_eprintf("compile error in %s: ", sourcePath);
 | 
			
		||||
                    janet_stacktrace(cres.macrofiber, janet_wrap_string(cres.error));
 | 
			
		||||
                    janet_stacktrace_ext(cres.macrofiber, ret, "");
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf("compile error in %s: %s\n", sourcePath,
 | 
			
		||||
                                  (const char *)cres.error);
 | 
			
		||||
@@ -68,25 +69,25 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (done) break;
 | 
			
		||||
 | 
			
		||||
        /* Dispatch based on parse state */
 | 
			
		||||
        switch (janet_parser_status(&parser)) {
 | 
			
		||||
            case JANET_PARSE_DEAD:
 | 
			
		||||
                done = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_PARSE_ERROR:
 | 
			
		||||
            case JANET_PARSE_ERROR: {
 | 
			
		||||
                const char *e = janet_parser_error(&parser);
 | 
			
		||||
                errflags |= 0x04;
 | 
			
		||||
                janet_eprintf("parse error in %s: %s\n",
 | 
			
		||||
                              sourcePath, janet_parser_error(&parser));
 | 
			
		||||
                ret = janet_cstringv(e);
 | 
			
		||||
                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;
 | 
			
		||||
            case JANET_PARSE_PENDING:
 | 
			
		||||
                if (index == len) {
 | 
			
		||||
                    janet_parser_eof(&parser);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_parser_consume(&parser, bytes[index++]);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case JANET_PARSE_ROOT:
 | 
			
		||||
            case JANET_PARSE_PENDING:
 | 
			
		||||
                if (index >= len) {
 | 
			
		||||
                    janet_parser_eof(&parser);
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -100,6 +101,14 @@ 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) {
 | 
			
		||||
        janet_gcroot(ret);
 | 
			
		||||
        janet_loop();
 | 
			
		||||
        janet_gcunroot(ret);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (out) *out = ret;
 | 
			
		||||
    return errflags;
 | 
			
		||||
}
 | 
			
		||||
@@ -110,3 +119,19 @@ int janet_dostring(JanetTable *env, const char *str, const char *sourcePath, Jan
 | 
			
		||||
    return janet_dobytes(env, (const uint8_t *)str, len, sourcePath, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Run a fiber to completion (use event loop if enabled). Return the status. */
 | 
			
		||||
int janet_loop_fiber(JanetFiber *fiber) {
 | 
			
		||||
    int status;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
    janet_loop();
 | 
			
		||||
    status = janet_fiber_status(fiber);
 | 
			
		||||
#else
 | 
			
		||||
    Janet out;
 | 
			
		||||
    status = janet_continue(fiber, janet_wrap_nil(), &out);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
 | 
			
		||||
        janet_stacktrace_ext(fiber, out, "");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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 destruction, 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());
 | 
			
		||||
@@ -251,23 +330,60 @@ static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv)
 | 
			
		||||
            case JANET_STRING:
 | 
			
		||||
                janet_table_put(tab, janet_ckeywordv("doc"), attr);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_STRUCT:
 | 
			
		||||
                janet_table_merge_struct(tab, janet_unwrap_struct(attr));
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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 */
 | 
			
		||||
@@ -275,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);
 | 
			
		||||
@@ -295,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)));
 | 
			
		||||
@@ -311,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -328,28 +478,55 @@ 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 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        return namelocal(c, sym, 0, s);
 | 
			
		||||
    }
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -386,6 +563,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)
 | 
			
		||||
@@ -409,7 +587,9 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
        right = janetc_value(bodyopts, truebody);
 | 
			
		||||
        if (!drop && !tail) janetc_copy(c, target, right);
 | 
			
		||||
        janetc_popscope(c);
 | 
			
		||||
        janetc_throwaway(bodyopts, falsebody);
 | 
			
		||||
        if (!janet_checktype(falsebody, JANET_NIL)) {
 | 
			
		||||
            janetc_throwaway(bodyopts, falsebody);
 | 
			
		||||
        }
 | 
			
		||||
        janetc_popscope(c);
 | 
			
		||||
        return target;
 | 
			
		||||
    }
 | 
			
		||||
@@ -460,6 +640,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) {
 | 
			
		||||
@@ -470,6 +651,28 @@ 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) {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    JanetSlot ret = janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    JanetFopts subopts = janetc_fopts_default(c);
 | 
			
		||||
    for (i = 0; i < argn; i++) {
 | 
			
		||||
        if (i != argn - 1) {
 | 
			
		||||
            subopts.flags = JANET_FOPTS_DROP;
 | 
			
		||||
        } else {
 | 
			
		||||
            subopts = opts;
 | 
			
		||||
            subopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
        }
 | 
			
		||||
        ret = janetc_value(subopts, argv[i]);
 | 
			
		||||
        if (i != argn - 1) {
 | 
			
		||||
            janetc_freeslot(c, ret);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add a funcdef to the top most function scope */
 | 
			
		||||
static int32_t janetc_addfuncdef(JanetCompiler *c, JanetFuncDef *def) {
 | 
			
		||||
    JanetScope *scope = c->scope;
 | 
			
		||||
@@ -572,8 +775,8 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
    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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -649,6 +852,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
        /* Compile function */
 | 
			
		||||
        JanetFuncDef *def = janetc_pop_funcdef(c);
 | 
			
		||||
        def->name = janet_cstring("_while");
 | 
			
		||||
        janet_def_addflags(def);
 | 
			
		||||
        int32_t defindex = janetc_addfuncdef(c, def);
 | 
			
		||||
        /* And then load the closure and call it. */
 | 
			
		||||
        int32_t cloreg = janetc_regalloc_temp(&c->scope->ra, JANETC_REGTEMP_0);
 | 
			
		||||
@@ -687,7 +891,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;
 | 
			
		||||
@@ -699,6 +903,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    int selfref = 0;
 | 
			
		||||
    int seenamp = 0;
 | 
			
		||||
    int seenopt = 0;
 | 
			
		||||
    int namedargs = 0;
 | 
			
		||||
 | 
			
		||||
    /* Begin function */
 | 
			
		||||
    c->scope->flags |= JANET_SCOPE_CLOSURE;
 | 
			
		||||
@@ -723,6 +928,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]);
 | 
			
		||||
@@ -730,49 +938,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));
 | 
			
		||||
@@ -784,6 +1018,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);
 | 
			
		||||
@@ -791,15 +1026,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 */
 | 
			
		||||
@@ -823,6 +1080,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
 | 
			
		||||
 | 
			
		||||
    if (selfref) def->name = janet_unwrap_symbol(head);
 | 
			
		||||
    janet_def_addflags(def);
 | 
			
		||||
    defindex = janetc_addfuncdef(c, def);
 | 
			
		||||
 | 
			
		||||
    /* Ensure enough slots for vararg function. */
 | 
			
		||||
@@ -852,6 +1110,7 @@ static const JanetSpecial janetc_specials[] = {
 | 
			
		||||
    {"set", janetc_varset},
 | 
			
		||||
    {"splice", janetc_splice},
 | 
			
		||||
    {"unquote", janetc_unquote},
 | 
			
		||||
    {"upscope", janetc_upscope},
 | 
			
		||||
    {"var", janetc_var},
 | 
			
		||||
    {"while", janetc_while}
 | 
			
		||||
};
 | 
			
		||||
@@ -864,4 +1123,3 @@ const JanetSpecial *janetc_special(const uint8_t *name) {
 | 
			
		||||
               sizeof(JanetSpecial),
 | 
			
		||||
               name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose and contributors
 | 
			
		||||
* 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
 | 
			
		||||
@@ -20,21 +20,42 @@
 | 
			
		||||
* IN THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* A very simple native module */
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static Janet cfun_get_five(int32_t argc, Janet *argv) {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    return janet_wrap_number(5.0);
 | 
			
		||||
JANET_THREAD_LOCAL JanetVM janet_vm;
 | 
			
		||||
 | 
			
		||||
JanetVM *janet_local_vm(void) {
 | 
			
		||||
    return &janet_vm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg array_cfuns[] = {
 | 
			
		||||
    {"get5", cfun_get_five, NULL},
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JANET_MODULE_ENTRY(JanetTable *env) {
 | 
			
		||||
    janet_cfuns(env, NULL, array_cfuns);
 | 
			
		||||
JanetVM *janet_vm_alloc(void) {
 | 
			
		||||
    JanetVM *mem = janet_malloc(sizeof(JanetVM));
 | 
			
		||||
    if (NULL == mem) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    return mem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_vm_free(JanetVM *vm) {
 | 
			
		||||
    janet_free(vm);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_vm_save(JanetVM *into) {
 | 
			
		||||
    *into = janet_vm;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_vm_load(JanetVM *from) {
 | 
			
		||||
    janet_vm = *from;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Trigger suspension of the Janet vm by trying to
 | 
			
		||||
 * exit the interpeter loop when convenient. You can optionally
 | 
			
		||||
 * use NULL to interrupt the current VM when convenient */
 | 
			
		||||
void janet_interpreter_interrupt(JanetVM *vm) {
 | 
			
		||||
    vm = vm ? vm : &janet_vm;
 | 
			
		||||
    vm->auto_suspend = 1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										220
									
								
								src/core/state.h
									
									
									
									
									
								
							
							
						
						
									
										220
									
								
								src/core/state.h
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,78 +23,176 @@
 | 
			
		||||
#ifndef JANET_STATE_H_defined
 | 
			
		||||
#define JANET_STATE_H_defined
 | 
			
		||||
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/* The VM state. Rather than a struct that is passed
 | 
			
		||||
 * around, the vm state is global for simplicity. If
 | 
			
		||||
 * at some point a global state object, or context,
 | 
			
		||||
 * is required to be passed around, this is what would
 | 
			
		||||
 * be in it. However, thread local global variables for interpreter
 | 
			
		||||
 * state should allow easy multi-threading. */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct JanetScratch JanetScratch;
 | 
			
		||||
typedef int64_t JanetTimestamp;
 | 
			
		||||
 | 
			
		||||
/* Cache the core environment */
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetTable *janet_vm_core_env;
 | 
			
		||||
typedef struct JanetScratch {
 | 
			
		||||
    JanetScratchFinalizer finalize;
 | 
			
		||||
    long long mem[]; /* for proper alignment */
 | 
			
		||||
} JanetScratch;
 | 
			
		||||
 | 
			
		||||
/* How many VM stacks have been entered */
 | 
			
		||||
extern JANET_THREAD_LOCAL int janet_vm_stackn;
 | 
			
		||||
 | 
			
		||||
/* The current running fiber on the current thread.
 | 
			
		||||
 * Set and unset by janet_run. */
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_root_fiber;
 | 
			
		||||
 | 
			
		||||
/* The current pointer to the inner most jmp_buf. The current
 | 
			
		||||
 * return point for panics. */
 | 
			
		||||
extern JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf;
 | 
			
		||||
extern JANET_THREAD_LOCAL Janet *janet_vm_return_reg;
 | 
			
		||||
 | 
			
		||||
/* The global registry for c functions. Used to store meta-data
 | 
			
		||||
 * along with otherwise bare c function pointers. */
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetTable *janet_vm_registry;
 | 
			
		||||
 | 
			
		||||
/* Registry for abstract abstract types that can be marshalled.
 | 
			
		||||
 * We need this to look up the constructors when unmarshalling. */
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetTable *janet_vm_abstract_registry;
 | 
			
		||||
 | 
			
		||||
/* Immutable value cache */
 | 
			
		||||
extern JANET_THREAD_LOCAL const uint8_t **janet_vm_cache;
 | 
			
		||||
extern JANET_THREAD_LOCAL uint32_t janet_vm_cache_capacity;
 | 
			
		||||
extern JANET_THREAD_LOCAL uint32_t janet_vm_cache_count;
 | 
			
		||||
extern JANET_THREAD_LOCAL uint32_t janet_vm_cache_deleted;
 | 
			
		||||
 | 
			
		||||
/* Garbage collection */
 | 
			
		||||
extern JANET_THREAD_LOCAL void *janet_vm_blocks;
 | 
			
		||||
extern JANET_THREAD_LOCAL size_t janet_vm_gc_interval;
 | 
			
		||||
extern JANET_THREAD_LOCAL size_t janet_vm_next_collection;
 | 
			
		||||
extern JANET_THREAD_LOCAL int janet_vm_gc_suspend;
 | 
			
		||||
 | 
			
		||||
/* GC roots */
 | 
			
		||||
extern JANET_THREAD_LOCAL Janet *janet_vm_roots;
 | 
			
		||||
extern JANET_THREAD_LOCAL size_t janet_vm_root_count;
 | 
			
		||||
extern JANET_THREAD_LOCAL size_t janet_vm_root_capacity;
 | 
			
		||||
 | 
			
		||||
/* Scratch memory */
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetScratch **janet_scratch_mem;
 | 
			
		||||
extern JANET_THREAD_LOCAL size_t janet_scratch_cap;
 | 
			
		||||
extern JANET_THREAD_LOCAL size_t janet_scratch_len;
 | 
			
		||||
 | 
			
		||||
/* Recursionless traversal of data structures */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    JanetGCObject *self;
 | 
			
		||||
    JanetGCObject *other;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    int32_t index2;
 | 
			
		||||
} JanetTraversalNode;
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetTraversalNode *janet_vm_traversal;
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetTraversalNode *janet_vm_traversal_top;
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetTraversalNode *janet_vm_traversal_base;
 | 
			
		||||
 | 
			
		||||
/* Setup / teardown */
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
void janet_threads_init(void);
 | 
			
		||||
void janet_threads_deinit(void);
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int32_t capacity;
 | 
			
		||||
    int32_t head;
 | 
			
		||||
    int32_t tail;
 | 
			
		||||
    void *data;
 | 
			
		||||
} JanetQueue;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    JanetTimestamp when;
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
    JanetFiber *curr_fiber;
 | 
			
		||||
    uint32_t sched_id;
 | 
			
		||||
    int is_error;
 | 
			
		||||
} JanetTimeout;
 | 
			
		||||
 | 
			
		||||
/* 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 {
 | 
			
		||||
    JanetCFunction cfun;
 | 
			
		||||
    const char *name;
 | 
			
		||||
    const char *name_prefix;
 | 
			
		||||
    const char *source_file;
 | 
			
		||||
    int32_t source_line;
 | 
			
		||||
    /* int32_t min_arity; */
 | 
			
		||||
    /* int32_t max_arity; */
 | 
			
		||||
} JanetCFunRegistry;
 | 
			
		||||
 | 
			
		||||
struct JanetVM {
 | 
			
		||||
    /* Place for user data */
 | 
			
		||||
    void *user;
 | 
			
		||||
 | 
			
		||||
    /* Top level dynamic bindings */
 | 
			
		||||
    JanetTable *top_dyns;
 | 
			
		||||
 | 
			
		||||
    /* Cache the core environment */
 | 
			
		||||
    JanetTable *core_env;
 | 
			
		||||
 | 
			
		||||
    /* How many VM stacks have been entered */
 | 
			
		||||
    int stackn;
 | 
			
		||||
 | 
			
		||||
    /* 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;
 | 
			
		||||
 | 
			
		||||
    /* The current running fiber on the current thread.
 | 
			
		||||
     * Set and unset by functions in vm.c */
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
    JanetFiber *root_fiber;
 | 
			
		||||
 | 
			
		||||
    /* The current pointer to the inner most jmp_buf. The current
 | 
			
		||||
     * return point for panics. */
 | 
			
		||||
    jmp_buf *signal_buf;
 | 
			
		||||
    Janet *return_reg;
 | 
			
		||||
 | 
			
		||||
    /* The global registry for c functions. Used to store meta-data
 | 
			
		||||
     * along with otherwise bare c function pointers. */
 | 
			
		||||
    JanetCFunRegistry *registry;
 | 
			
		||||
    size_t registry_cap;
 | 
			
		||||
    size_t registry_count;
 | 
			
		||||
    int registry_dirty;
 | 
			
		||||
 | 
			
		||||
    /* Registry for abstract types that can be marshalled.
 | 
			
		||||
     * We need this to look up the constructors when unmarshalling. */
 | 
			
		||||
    JanetTable *abstract_registry;
 | 
			
		||||
 | 
			
		||||
    /* Immutable value cache */
 | 
			
		||||
    const uint8_t **cache;
 | 
			
		||||
    uint32_t cache_capacity;
 | 
			
		||||
    uint32_t cache_count;
 | 
			
		||||
    uint32_t cache_deleted;
 | 
			
		||||
    uint8_t gensym_counter[8];
 | 
			
		||||
 | 
			
		||||
    /* Garbage collection */
 | 
			
		||||
    void *blocks;
 | 
			
		||||
    size_t gc_interval;
 | 
			
		||||
    size_t next_collection;
 | 
			
		||||
    size_t block_count;
 | 
			
		||||
    int gc_suspend;
 | 
			
		||||
 | 
			
		||||
    /* GC roots */
 | 
			
		||||
    Janet *roots;
 | 
			
		||||
    size_t root_count;
 | 
			
		||||
    size_t root_capacity;
 | 
			
		||||
 | 
			
		||||
    /* Scratch memory */
 | 
			
		||||
    JanetScratch **scratch_mem;
 | 
			
		||||
    size_t scratch_cap;
 | 
			
		||||
    size_t scratch_len;
 | 
			
		||||
 | 
			
		||||
    /* Sandbox flags */
 | 
			
		||||
    uint32_t sandbox_flags;
 | 
			
		||||
 | 
			
		||||
    /* Random number generator */
 | 
			
		||||
    JanetRNG rng;
 | 
			
		||||
 | 
			
		||||
    /* Traversal pointers */
 | 
			
		||||
    JanetTraversalNode *traversal;
 | 
			
		||||
    JanetTraversalNode *traversal_top;
 | 
			
		||||
    JanetTraversalNode *traversal_base;
 | 
			
		||||
 | 
			
		||||
    /* Event loop and scheduler globals */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    size_t tq_count;
 | 
			
		||||
    size_t tq_capacity;
 | 
			
		||||
    JanetQueue spawn;
 | 
			
		||||
    JanetTimeout *tq;
 | 
			
		||||
    JanetRNG ev_rng;
 | 
			
		||||
    JanetListenerState **listeners;
 | 
			
		||||
    size_t listener_count;
 | 
			
		||||
    size_t listener_cap;
 | 
			
		||||
    size_t extra_listeners;
 | 
			
		||||
    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 */
 | 
			
		||||
#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
 | 
			
		||||
    pthread_attr_t new_thread_attr;
 | 
			
		||||
    JanetHandle selfpipe[2];
 | 
			
		||||
    struct pollfd *fds;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetVM janet_vm;
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
void janet_net_init(void);
 | 
			
		||||
void janet_net_deinit(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
void janet_ev_init(void);
 | 
			
		||||
void janet_ev_deinit(void);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* JANET_STATE_H_defined */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
@@ -62,7 +62,7 @@ int janet_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
 | 
			
		||||
    int32_t ylen = janet_string_length(rhs);
 | 
			
		||||
    int32_t len = xlen > ylen ? ylen : xlen;
 | 
			
		||||
    int res = memcmp(lhs, rhs, len);
 | 
			
		||||
    if (res) return res;
 | 
			
		||||
    if (res) return res > 0 ? 1 : -1;
 | 
			
		||||
    if (xlen == ylen) return 0;
 | 
			
		||||
    return xlen < ylen ? -1 : 1;
 | 
			
		||||
}
 | 
			
		||||
@@ -108,7 +108,7 @@ static void kmp_init(
 | 
			
		||||
    if (patlen == 0) {
 | 
			
		||||
        janet_panic("expected non-empty pattern");
 | 
			
		||||
    }
 | 
			
		||||
    int32_t *lookup = calloc(patlen, sizeof(int32_t));
 | 
			
		||||
    int32_t *lookup = janet_calloc(patlen, sizeof(int32_t));
 | 
			
		||||
    if (!lookup) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -131,7 +131,7 @@ static void kmp_init(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void kmp_deinit(struct kmp_state *state) {
 | 
			
		||||
    free(state->lookup);
 | 
			
		||||
    janet_free(state->lookup);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void kmp_seti(struct kmp_state *state, int32_t i) {
 | 
			
		||||
@@ -170,13 +170,37 @@ static int32_t kmp_next(struct kmp_state *state) {
 | 
			
		||||
 | 
			
		||||
/* CFuns */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
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. ") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_stringv(view.bytes + range.start, range.end - range.start);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_repeat(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_symbol_slice,
 | 
			
		||||
              "(symbol/slice bytes &opt start end)",
 | 
			
		||||
              "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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_keyword_slice,
 | 
			
		||||
              "(keyword/slice bytes &opt start end)",
 | 
			
		||||
              "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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_repeat,
 | 
			
		||||
              "(string/repeat bytes n)",
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -192,7 +216,9 @@ static Janet cfun_string_repeat(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(newbuf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_bytes(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_bytes,
 | 
			
		||||
              "(string/bytes str)",
 | 
			
		||||
              "Returns a tuple of integers that are the byte values of the string.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    Janet *tup = janet_tuple_begin(view.len);
 | 
			
		||||
@@ -203,7 +229,10 @@ static Janet cfun_string_bytes(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_end(tup));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_frombytes(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_frombytes,
 | 
			
		||||
              "(string/from-bytes & byte-vals)",
 | 
			
		||||
              "Creates a string from integer parameters with byte values. All integers "
 | 
			
		||||
              "will be coerced to the range of 1 byte 0-255.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    uint8_t *buf = janet_string_begin(argc);
 | 
			
		||||
    for (i = 0; i < argc; i++) {
 | 
			
		||||
@@ -213,7 +242,11 @@ static Janet cfun_string_frombytes(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_asciilower(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_asciilower,
 | 
			
		||||
              "(string/ascii-lower str)",
 | 
			
		||||
              "Returns a new string where all bytes are replaced with the "
 | 
			
		||||
              "lowercase version of themselves in ASCII. Does only a very simple "
 | 
			
		||||
              "case check, meaning no unicode support.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    uint8_t *buf = janet_string_begin(view.len);
 | 
			
		||||
@@ -228,7 +261,11 @@ static Janet cfun_string_asciilower(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_asciiupper(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_asciiupper,
 | 
			
		||||
              "(string/ascii-upper str)",
 | 
			
		||||
              "Returns a new string where all bytes are replaced with the "
 | 
			
		||||
              "uppercase version of themselves in ASCII. Does only a very simple "
 | 
			
		||||
              "case check, meaning no unicode support.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    uint8_t *buf = janet_string_begin(view.len);
 | 
			
		||||
@@ -243,7 +280,9 @@ static Janet cfun_string_asciiupper(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_reverse(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_reverse,
 | 
			
		||||
              "(string/reverse 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);
 | 
			
		||||
@@ -267,7 +306,11 @@ static void findsetup(int32_t argc, Janet *argv, struct kmp_state *s, int32_t ex
 | 
			
		||||
    s->i = start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_find(int32_t argc, Janet *argv) {
 | 
			
		||||
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, "
 | 
			
		||||
              "otherwise returns nil.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct kmp_state state;
 | 
			
		||||
    findsetup(argc, argv, &state, 0);
 | 
			
		||||
@@ -278,7 +321,9 @@ static Janet cfun_string_find(int32_t argc, Janet *argv) {
 | 
			
		||||
           : janet_wrap_integer(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_hasprefix(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_hasprefix,
 | 
			
		||||
              "(string/has-prefix? pfx str)",
 | 
			
		||||
              "Tests whether `str` starts with `pfx`.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView prefix = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView str = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -287,7 +332,9 @@ static Janet cfun_string_hasprefix(int32_t argc, Janet *argv) {
 | 
			
		||||
           : janet_wrap_boolean(memcmp(prefix.bytes, str.bytes, prefix.len) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_hassuffix(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_hassuffix,
 | 
			
		||||
              "(string/has-suffix? sfx str)",
 | 
			
		||||
              "Tests whether `str` ends with `sfx`.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView suffix = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView str = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -298,7 +345,12 @@ static Janet cfun_string_hassuffix(int32_t argc, Janet *argv) {
 | 
			
		||||
                                       suffix.len) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_findall(int32_t argc, Janet *argv) {
 | 
			
		||||
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` "
 | 
			
		||||
              "may contribute to multiple found patterns.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct kmp_state state;
 | 
			
		||||
    findsetup(argc, argv, &state, 0);
 | 
			
		||||
@@ -312,14 +364,13 @@ static Janet cfun_string_findall(int32_t argc, Janet *argv) {
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
@@ -328,11 +379,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_replace(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_replace,
 | 
			
		||||
              "(string/replace patt subst 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;
 | 
			
		||||
@@ -342,17 +397,24 @@ static Janet cfun_string_replace(int32_t argc, Janet *argv) {
 | 
			
		||||
        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);
 | 
			
		||||
    return janet_wrap_string(janet_string_end(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_replaceall(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_replaceall,
 | 
			
		||||
              "(string/replace-all patt subst str)",
 | 
			
		||||
              "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. "
 | 
			
		||||
              "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;
 | 
			
		||||
@@ -360,8 +422,9 @@ static Janet cfun_string_replaceall(int32_t argc, Janet *argv) {
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
@@ -372,7 +435,13 @@ static Janet cfun_string_replaceall(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_split(int32_t argc, Janet *argv) {
 | 
			
		||||
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` "
 | 
			
		||||
              "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).") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    JanetArray *array;
 | 
			
		||||
    struct kmp_state state;
 | 
			
		||||
@@ -386,6 +455,7 @@ static Janet cfun_string_split(int32_t argc, Janet *argv) {
 | 
			
		||||
        const uint8_t *slice = janet_string(state.text + lastindex, result - lastindex);
 | 
			
		||||
        janet_array_push(array, janet_wrap_string(slice));
 | 
			
		||||
        lastindex = result + state.patlen;
 | 
			
		||||
        kmp_seti(&state, lastindex);
 | 
			
		||||
    }
 | 
			
		||||
    const uint8_t *slice = janet_string(state.text + lastindex, state.textlen - lastindex);
 | 
			
		||||
    janet_array_push(array, janet_wrap_string(slice));
 | 
			
		||||
@@ -393,7 +463,11 @@ static Janet cfun_string_split(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_checkset(int32_t argc, Janet *argv) {
 | 
			
		||||
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`.") {
 | 
			
		||||
    uint32_t bitset[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView set = janet_getbytes(argv, 0);
 | 
			
		||||
@@ -415,7 +489,10 @@ static Janet cfun_string_checkset(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_true();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_join(int32_t argc, Janet *argv) {
 | 
			
		||||
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`.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetView parts = janet_getindexed(argv, 0);
 | 
			
		||||
    JanetByteView joiner;
 | 
			
		||||
@@ -455,7 +532,33 @@ static Janet cfun_string_join(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_format(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_format,
 | 
			
		||||
              "(string/format format & values)",
 | 
			
		||||
              "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)"
 | 
			
		||||
              "- `V`: format with (string x)"
 | 
			
		||||
              "- `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 speficiers 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);
 | 
			
		||||
@@ -495,7 +598,10 @@ static void trim_help_args(int32_t argc, Janet *argv, JanetByteView *str, JanetB
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_trim(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    JanetByteView str, set;
 | 
			
		||||
    trim_help_args(argc, argv, &str, &set);
 | 
			
		||||
    int32_t left_edge = trim_help_leftedge(str, set);
 | 
			
		||||
@@ -505,153 +611,52 @@ static Janet cfun_string_trim(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_stringv(str.bytes + left_edge, right_edge - left_edge);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_triml(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    JanetByteView str, set;
 | 
			
		||||
    trim_help_args(argc, argv, &str, &set);
 | 
			
		||||
    int32_t left_edge = trim_help_leftedge(str, set);
 | 
			
		||||
    return janet_stringv(str.bytes + left_edge, str.len - left_edge);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_trimr(int32_t argc, Janet *argv) {
 | 
			
		||||
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.") {
 | 
			
		||||
    JanetByteView str, set;
 | 
			
		||||
    trim_help_args(argc, argv, &str, &set);
 | 
			
		||||
    int32_t right_edge = trim_help_rightedge(str, set);
 | 
			
		||||
    return janet_stringv(str.bytes, right_edge);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg string_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "string/slice", cfun_string_slice,
 | 
			
		||||
        JDOC("(string/slice bytes &opt start end)\n\n"
 | 
			
		||||
             "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. ")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/repeat", cfun_string_repeat,
 | 
			
		||||
        JDOC("(string/repeat bytes n)\n\n"
 | 
			
		||||
             "Returns a string that is n copies of bytes concatenated.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/bytes", cfun_string_bytes,
 | 
			
		||||
        JDOC("(string/bytes str)\n\n"
 | 
			
		||||
             "Returns an array of integers that are the byte values of the string.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/from-bytes", cfun_string_frombytes,
 | 
			
		||||
        JDOC("(string/from-bytes & byte-vals)\n\n"
 | 
			
		||||
             "Creates a string from integer parameters with byte values. All integers "
 | 
			
		||||
             "will be coerced to the range of 1 byte 0-255.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/ascii-lower", cfun_string_asciilower,
 | 
			
		||||
        JDOC("(string/ascii-lower str)\n\n"
 | 
			
		||||
             "Returns a new string where all bytes are replaced with the "
 | 
			
		||||
             "lowercase version of themselves in ASCII. Does only a very simple "
 | 
			
		||||
             "case check, meaning no unicode support.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/ascii-upper", cfun_string_asciiupper,
 | 
			
		||||
        JDOC("(string/ascii-upper str)\n\n"
 | 
			
		||||
             "Returns a new string where all bytes are replaced with the "
 | 
			
		||||
             "uppercase version of themselves in ASCII. Does only a very simple "
 | 
			
		||||
             "case check, meaning no unicode support.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/reverse", cfun_string_reverse,
 | 
			
		||||
        JDOC("(string/reverse str)\n\n"
 | 
			
		||||
             "Returns a string that is the reversed version of str.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/find", cfun_string_find,
 | 
			
		||||
        JDOC("(string/find patt str)\n\n"
 | 
			
		||||
             "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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/find-all", cfun_string_findall,
 | 
			
		||||
        JDOC("(string/find-all patt str)\n\n"
 | 
			
		||||
             "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 not counted, meaning a byte in string "
 | 
			
		||||
             "will only contribute to finding at most on occurrence of pattern. If no "
 | 
			
		||||
             "occurrences are found, will return an empty array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/has-prefix?", cfun_string_hasprefix,
 | 
			
		||||
        JDOC("(string/has-prefix? pfx str)\n\n"
 | 
			
		||||
             "Tests whether str starts with pfx.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/has-suffix?", cfun_string_hassuffix,
 | 
			
		||||
        JDOC("(string/has-suffix? sfx str)\n\n"
 | 
			
		||||
             "Tests whether str ends with sfx.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/replace", cfun_string_replace,
 | 
			
		||||
        JDOC("(string/replace patt subst str)\n\n"
 | 
			
		||||
             "Replace the first occurrence of patt with subst in the string str. "
 | 
			
		||||
             "Will return the new string if patt is found, otherwise returns str.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/replace-all", cfun_string_replaceall,
 | 
			
		||||
        JDOC("(string/replace-all patt subst str)\n\n"
 | 
			
		||||
             "Replace all instances of patt with subst in the string str. "
 | 
			
		||||
             "Will return the new string if patt is found, otherwise returns str.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/split", cfun_string_split,
 | 
			
		||||
        JDOC("(string/split delim str &opt start limit)\n\n"
 | 
			
		||||
             "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).")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/check-set", cfun_string_checkset,
 | 
			
		||||
        JDOC("(string/check-set set str)\n\n"
 | 
			
		||||
             "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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/join", cfun_string_join,
 | 
			
		||||
        JDOC("(string/join parts &opt sep)\n\n"
 | 
			
		||||
             "Joins an array of strings into one string, optionally separated by "
 | 
			
		||||
             "a separator string sep.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/format", cfun_string_format,
 | 
			
		||||
        JDOC("(string/format format & values)\n\n"
 | 
			
		||||
             "Similar to snprintf, but specialized for operating with Janet values. Returns "
 | 
			
		||||
             "a new string.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/trim", cfun_string_trim,
 | 
			
		||||
        JDOC("(string/trim str &opt set)\n\n"
 | 
			
		||||
             "Trim leading and trailing whitespace from a byte sequence. If the argument "
 | 
			
		||||
             "set is provided, consider only characters in set to be whitespace.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/triml", cfun_string_triml,
 | 
			
		||||
        JDOC("(string/triml str &opt set)\n\n"
 | 
			
		||||
             "Trim leading whitespace from a byte sequence. If the argument "
 | 
			
		||||
             "set is provided, consider only characters in set to be whitespace.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string/trimr", cfun_string_trimr,
 | 
			
		||||
        JDOC("(string/trimr str &opt set)\n\n"
 | 
			
		||||
             "Trim trailing whitespace from a byte sequence. If the argument "
 | 
			
		||||
             "set is provided, consider only characters in set to be whitespace.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_string(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, string_cfuns);
 | 
			
		||||
    JanetRegExt string_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("string/slice", cfun_string_slice),
 | 
			
		||||
        JANET_CORE_REG("keyword/slice", cfun_keyword_slice),
 | 
			
		||||
        JANET_CORE_REG("symbol/slice", cfun_symbol_slice),
 | 
			
		||||
        JANET_CORE_REG("string/repeat", cfun_string_repeat),
 | 
			
		||||
        JANET_CORE_REG("string/bytes", cfun_string_bytes),
 | 
			
		||||
        JANET_CORE_REG("string/from-bytes", cfun_string_frombytes),
 | 
			
		||||
        JANET_CORE_REG("string/ascii-lower", cfun_string_asciilower),
 | 
			
		||||
        JANET_CORE_REG("string/ascii-upper", cfun_string_asciiupper),
 | 
			
		||||
        JANET_CORE_REG("string/reverse", cfun_string_reverse),
 | 
			
		||||
        JANET_CORE_REG("string/find", cfun_string_find),
 | 
			
		||||
        JANET_CORE_REG("string/find-all", cfun_string_findall),
 | 
			
		||||
        JANET_CORE_REG("string/has-prefix?", cfun_string_hasprefix),
 | 
			
		||||
        JANET_CORE_REG("string/has-suffix?", cfun_string_hassuffix),
 | 
			
		||||
        JANET_CORE_REG("string/replace", cfun_string_replace),
 | 
			
		||||
        JANET_CORE_REG("string/replace-all", cfun_string_replaceall),
 | 
			
		||||
        JANET_CORE_REG("string/split", cfun_string_split),
 | 
			
		||||
        JANET_CORE_REG("string/check-set", cfun_string_checkset),
 | 
			
		||||
        JANET_CORE_REG("string/join", cfun_string_join),
 | 
			
		||||
        JANET_CORE_REG("string/format", cfun_string_format),
 | 
			
		||||
        JANET_CORE_REG("string/trim", cfun_string_trim),
 | 
			
		||||
        JANET_CORE_REG("string/triml", cfun_string_triml),
 | 
			
		||||
        JANET_CORE_REG("string/trimr", cfun_string_trimr),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, string_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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,7 +87,7 @@ static uint32_t *bignat_extra(struct BigNat *mant, int32_t n) {
 | 
			
		||||
    int32_t newn = oldn + n;
 | 
			
		||||
    if (mant->cap < newn) {
 | 
			
		||||
        int32_t newcap = 2 * newn;
 | 
			
		||||
        uint32_t *mem = realloc(mant->digits, (size_t) newcap * sizeof(uint32_t));
 | 
			
		||||
        uint32_t *mem = janet_realloc(mant->digits, (size_t) newcap * sizeof(uint32_t));
 | 
			
		||||
        if (NULL == mem) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -246,15 +246,15 @@ static double convert(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Scan a real (double) from a string. If the string cannot be converted into
 | 
			
		||||
 * and integer, set *err to 1 and return 0. */
 | 
			
		||||
int janet_scan_number(
 | 
			
		||||
 * and integer, return 0. */
 | 
			
		||||
int janet_scan_number_base(
 | 
			
		||||
    const uint8_t *str,
 | 
			
		||||
    int32_t len,
 | 
			
		||||
    int32_t base,
 | 
			
		||||
    double *out) {
 | 
			
		||||
    const uint8_t *end = str + len;
 | 
			
		||||
    int seenadigit = 0;
 | 
			
		||||
    int ex = 0;
 | 
			
		||||
    int base = 10;
 | 
			
		||||
    int seenpoint = 0;
 | 
			
		||||
    int foundexp = 0;
 | 
			
		||||
    int neg = 0;
 | 
			
		||||
@@ -278,21 +278,28 @@ int janet_scan_number(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Check for leading 0x or digit digit r */
 | 
			
		||||
    if (str + 1 < end && str[0] == '0' && str[1] == 'x') {
 | 
			
		||||
        base = 16;
 | 
			
		||||
        str += 2;
 | 
			
		||||
    } else if (str + 1 < end  &&
 | 
			
		||||
               str[0] >= '0' && str[0] <= '9' &&
 | 
			
		||||
               str[1] == 'r') {
 | 
			
		||||
        base = str[0] - '0';
 | 
			
		||||
        str += 2;
 | 
			
		||||
    } else if (str + 2 < end  &&
 | 
			
		||||
               str[0] >= '0' && str[0] <= '9' &&
 | 
			
		||||
               str[1] >= '0' && str[1] <= '9' &&
 | 
			
		||||
               str[2] == 'r') {
 | 
			
		||||
        base = 10 * (str[0] - '0') + (str[1] - '0');
 | 
			
		||||
        if (base < 2 || base > 36) goto error;
 | 
			
		||||
        str += 3;
 | 
			
		||||
    if (base == 0) {
 | 
			
		||||
        if (str + 1 < end && str[0] == '0' && str[1] == 'x') {
 | 
			
		||||
            base = 16;
 | 
			
		||||
            str += 2;
 | 
			
		||||
        } else if (str + 1 < end  &&
 | 
			
		||||
                   str[0] >= '0' && str[0] <= '9' &&
 | 
			
		||||
                   str[1] == 'r') {
 | 
			
		||||
            base = str[0] - '0';
 | 
			
		||||
            str += 2;
 | 
			
		||||
        } else if (str + 2 < end  &&
 | 
			
		||||
                   str[0] >= '0' && str[0] <= '9' &&
 | 
			
		||||
                   str[1] >= '0' && str[1] <= '9' &&
 | 
			
		||||
                   str[2] == 'r') {
 | 
			
		||||
            base = 10 * (str[0] - '0') + (str[1] - '0');
 | 
			
		||||
            if (base < 2 || base > 36) goto error;
 | 
			
		||||
            str += 3;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If still base is 0, set to default (10) */
 | 
			
		||||
    if (base == 0) {
 | 
			
		||||
        base = 10;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Skip leading zeros */
 | 
			
		||||
@@ -368,14 +375,21 @@ int janet_scan_number(
 | 
			
		||||
        goto error;
 | 
			
		||||
 | 
			
		||||
    *out = convert(neg, &mant, base, ex);
 | 
			
		||||
    free(mant.digits);
 | 
			
		||||
    janet_free(mant.digits);
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
    free(mant.digits);
 | 
			
		||||
    janet_free(mant.digits);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_scan_number(
 | 
			
		||||
    const uint8_t *str,
 | 
			
		||||
    int32_t len,
 | 
			
		||||
    double *out) {
 | 
			
		||||
    return janet_scan_number_base(str, len, 0, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
 | 
			
		||||
static int scan_uint64(
 | 
			
		||||
@@ -447,7 +461,7 @@ int janet_scan_int64(const uint8_t *str, int32_t len, int64_t *out) {
 | 
			
		||||
    int neg;
 | 
			
		||||
    uint64_t bi;
 | 
			
		||||
    if (scan_uint64(str, len, &bi, &neg)) {
 | 
			
		||||
        if (neg && bi <= (UINT64_MAX / 2)) {
 | 
			
		||||
        if (neg && bi <= ((UINT64_MAX / 2) + 1)) {
 | 
			
		||||
            if (bi > INT64_MAX) {
 | 
			
		||||
                *out = INT64_MIN;
 | 
			
		||||
            } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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) 2020 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,30 +36,26 @@
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/* Cache state */
 | 
			
		||||
JANET_THREAD_LOCAL const uint8_t **janet_vm_cache = NULL;
 | 
			
		||||
JANET_THREAD_LOCAL uint32_t janet_vm_cache_capacity = 0;
 | 
			
		||||
JANET_THREAD_LOCAL uint32_t janet_vm_cache_count = 0;
 | 
			
		||||
JANET_THREAD_LOCAL uint32_t janet_vm_cache_deleted = 0;
 | 
			
		||||
 | 
			
		||||
/* Initialize the cache (allocate cache memory) */
 | 
			
		||||
void janet_symcache_init() {
 | 
			
		||||
    janet_vm_cache_capacity = 1024;
 | 
			
		||||
    janet_vm_cache = calloc(1, (size_t) janet_vm_cache_capacity * sizeof(const uint8_t *));
 | 
			
		||||
    if (NULL == janet_vm_cache) {
 | 
			
		||||
    janet_vm.cache_capacity = 1024;
 | 
			
		||||
    janet_vm.cache = janet_calloc(1, (size_t) janet_vm.cache_capacity * sizeof(const uint8_t *));
 | 
			
		||||
    if (NULL == janet_vm.cache) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_cache_count = 0;
 | 
			
		||||
    janet_vm_cache_deleted = 0;
 | 
			
		||||
    memset(&janet_vm.gensym_counter, '0', sizeof(janet_vm.gensym_counter));
 | 
			
		||||
    janet_vm.gensym_counter[0] = '_';
 | 
			
		||||
    janet_vm.cache_count = 0;
 | 
			
		||||
    janet_vm.cache_deleted = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Deinitialize the cache (free the cache memory) */
 | 
			
		||||
void janet_symcache_deinit() {
 | 
			
		||||
    free((void *)janet_vm_cache);
 | 
			
		||||
    janet_vm_cache = NULL;
 | 
			
		||||
    janet_vm_cache_capacity = 0;
 | 
			
		||||
    janet_vm_cache_count = 0;
 | 
			
		||||
    janet_vm_cache_deleted = 0;
 | 
			
		||||
    janet_free((void *)janet_vm.cache);
 | 
			
		||||
    janet_vm.cache = NULL;
 | 
			
		||||
    janet_vm.cache_capacity = 0;
 | 
			
		||||
    janet_vm.cache_count = 0;
 | 
			
		||||
    janet_vm.cache_deleted = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark an entry in the table as deleted. */
 | 
			
		||||
@@ -79,24 +75,24 @@ static const uint8_t **janet_symcache_findmem(
 | 
			
		||||
 | 
			
		||||
    /* We will search two ranges - index to the end,
 | 
			
		||||
     * and 0 to the index. */
 | 
			
		||||
    index = (uint32_t)hash & (janet_vm_cache_capacity - 1);
 | 
			
		||||
    index = (uint32_t)hash & (janet_vm.cache_capacity - 1);
 | 
			
		||||
    bounds[0] = index;
 | 
			
		||||
    bounds[1] = janet_vm_cache_capacity;
 | 
			
		||||
    bounds[1] = janet_vm.cache_capacity;
 | 
			
		||||
    bounds[2] = 0;
 | 
			
		||||
    bounds[3] = index;
 | 
			
		||||
    for (j = 0; j < 4; j += 2)
 | 
			
		||||
        for (i = bounds[j]; i < bounds[j + 1]; ++i) {
 | 
			
		||||
            const uint8_t *test = janet_vm_cache[i];
 | 
			
		||||
            const uint8_t *test = janet_vm.cache[i];
 | 
			
		||||
            /* Check empty spots */
 | 
			
		||||
            if (NULL == test) {
 | 
			
		||||
                if (NULL == firstEmpty)
 | 
			
		||||
                    firstEmpty = janet_vm_cache + i;
 | 
			
		||||
                    firstEmpty = janet_vm.cache + i;
 | 
			
		||||
                goto notfound;
 | 
			
		||||
            }
 | 
			
		||||
            /* Check for marked deleted */
 | 
			
		||||
            if (JANET_SYMCACHE_DELETED == test) {
 | 
			
		||||
                if (firstEmpty == NULL)
 | 
			
		||||
                    firstEmpty = janet_vm_cache + i;
 | 
			
		||||
                    firstEmpty = janet_vm.cache + i;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (janet_string_equalconst(test, str, len, hash)) {
 | 
			
		||||
@@ -104,14 +100,15 @@ static const uint8_t **janet_symcache_findmem(
 | 
			
		||||
                *success = 1;
 | 
			
		||||
                if (firstEmpty != NULL) {
 | 
			
		||||
                    *firstEmpty = test;
 | 
			
		||||
                    janet_vm_cache[i] = JANET_SYMCACHE_DELETED;
 | 
			
		||||
                    janet_vm.cache[i] = JANET_SYMCACHE_DELETED;
 | 
			
		||||
                    return firstEmpty;
 | 
			
		||||
                }
 | 
			
		||||
                return janet_vm_cache + i;
 | 
			
		||||
                return janet_vm.cache + i;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
notfound:
 | 
			
		||||
    *success = 0;
 | 
			
		||||
    janet_assert(firstEmpty != NULL, "symcache failed to get memory");
 | 
			
		||||
    return firstEmpty;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -121,15 +118,15 @@ notfound:
 | 
			
		||||
/* Resize the cache. */
 | 
			
		||||
static void janet_cache_resize(uint32_t newCapacity) {
 | 
			
		||||
    uint32_t i, oldCapacity;
 | 
			
		||||
    const uint8_t **oldCache = janet_vm_cache;
 | 
			
		||||
    const uint8_t **newCache = calloc(1, (size_t) newCapacity * sizeof(const uint8_t *));
 | 
			
		||||
    const uint8_t **oldCache = janet_vm.cache;
 | 
			
		||||
    const uint8_t **newCache = janet_calloc(1, (size_t) newCapacity * sizeof(const uint8_t *));
 | 
			
		||||
    if (newCache == NULL) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    oldCapacity = janet_vm_cache_capacity;
 | 
			
		||||
    janet_vm_cache = newCache;
 | 
			
		||||
    janet_vm_cache_capacity = newCapacity;
 | 
			
		||||
    janet_vm_cache_deleted = 0;
 | 
			
		||||
    oldCapacity = janet_vm.cache_capacity;
 | 
			
		||||
    janet_vm.cache = newCache;
 | 
			
		||||
    janet_vm.cache_capacity = newCapacity;
 | 
			
		||||
    janet_vm.cache_deleted = 0;
 | 
			
		||||
    /* Add all of the old cache entries back */
 | 
			
		||||
    for (i = 0; i < oldCapacity; ++i) {
 | 
			
		||||
        int status;
 | 
			
		||||
@@ -145,18 +142,18 @@ static void janet_cache_resize(uint32_t newCapacity) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* Free the old cache */
 | 
			
		||||
    free((void *)oldCache);
 | 
			
		||||
    janet_free((void *)oldCache);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add an item to the cache */
 | 
			
		||||
static void janet_symcache_put(const uint8_t *x, const uint8_t **bucket) {
 | 
			
		||||
    if ((janet_vm_cache_count + janet_vm_cache_deleted) * 2 > janet_vm_cache_capacity) {
 | 
			
		||||
    if ((janet_vm.cache_count + janet_vm.cache_deleted) * 2 > janet_vm.cache_capacity) {
 | 
			
		||||
        int status;
 | 
			
		||||
        janet_cache_resize(janet_tablen((2 * janet_vm_cache_count + 1)));
 | 
			
		||||
        janet_cache_resize(janet_tablen((2 * janet_vm.cache_count + 1)));
 | 
			
		||||
        bucket = janet_symcache_find(x, &status);
 | 
			
		||||
    }
 | 
			
		||||
    /* Add x to the cache */
 | 
			
		||||
    janet_vm_cache_count++;
 | 
			
		||||
    janet_vm.cache_count++;
 | 
			
		||||
    *bucket = x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -165,8 +162,8 @@ void janet_symbol_deinit(const uint8_t *sym) {
 | 
			
		||||
    int status = 0;
 | 
			
		||||
    const uint8_t **bucket = janet_symcache_find(sym, &status);
 | 
			
		||||
    if (status) {
 | 
			
		||||
        janet_vm_cache_count--;
 | 
			
		||||
        janet_vm_cache_deleted++;
 | 
			
		||||
        janet_vm.cache_count--;
 | 
			
		||||
        janet_vm.cache_deleted++;
 | 
			
		||||
        *bucket = JANET_SYMCACHE_DELETED;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -194,22 +191,19 @@ const uint8_t *janet_csymbol(const char *cstr) {
 | 
			
		||||
    return janet_symbol((const uint8_t *)cstr, (int32_t) strlen(cstr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Store counter for genysm to avoid quadratic behavior */
 | 
			
		||||
JANET_THREAD_LOCAL uint8_t gensym_counter[8] = {'_', '0', '0', '0', '0', '0', '0', 0};
 | 
			
		||||
 | 
			
		||||
/* Increment the gensym buffer */
 | 
			
		||||
static void inc_gensym(void) {
 | 
			
		||||
    for (int i = sizeof(gensym_counter) - 2; i; i--) {
 | 
			
		||||
        if (gensym_counter[i] == '9') {
 | 
			
		||||
            gensym_counter[i] = 'a';
 | 
			
		||||
    for (int i = sizeof(janet_vm.gensym_counter) - 2; i; i--) {
 | 
			
		||||
        if (janet_vm.gensym_counter[i] == '9') {
 | 
			
		||||
            janet_vm.gensym_counter[i] = 'a';
 | 
			
		||||
            break;
 | 
			
		||||
        } else if (gensym_counter[i] == 'z') {
 | 
			
		||||
            gensym_counter[i] = 'A';
 | 
			
		||||
        } else if (janet_vm.gensym_counter[i] == 'z') {
 | 
			
		||||
            janet_vm.gensym_counter[i] = 'A';
 | 
			
		||||
            break;
 | 
			
		||||
        } else if (gensym_counter[i] == 'Z') {
 | 
			
		||||
            gensym_counter[i] = '0';
 | 
			
		||||
        } else if (janet_vm.gensym_counter[i] == 'Z') {
 | 
			
		||||
            janet_vm.gensym_counter[i] = '0';
 | 
			
		||||
        } else {
 | 
			
		||||
            gensym_counter[i]++;
 | 
			
		||||
            janet_vm.gensym_counter[i]++;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -227,19 +221,19 @@ const uint8_t *janet_symbol_gen(void) {
 | 
			
		||||
     * is enough for resolving collisions. */
 | 
			
		||||
    do {
 | 
			
		||||
        hash = janet_string_calchash(
 | 
			
		||||
                   gensym_counter,
 | 
			
		||||
                   sizeof(gensym_counter) - 1);
 | 
			
		||||
                   janet_vm.gensym_counter,
 | 
			
		||||
                   sizeof(janet_vm.gensym_counter) - 1);
 | 
			
		||||
        bucket = janet_symcache_findmem(
 | 
			
		||||
                     gensym_counter,
 | 
			
		||||
                     sizeof(gensym_counter) - 1,
 | 
			
		||||
                     janet_vm.gensym_counter,
 | 
			
		||||
                     sizeof(janet_vm.gensym_counter) - 1,
 | 
			
		||||
                     hash,
 | 
			
		||||
                     &status);
 | 
			
		||||
    } while (status && (inc_gensym(), 1));
 | 
			
		||||
    JanetStringHead *head = janet_gcalloc(JANET_MEMORY_SYMBOL, sizeof(JanetStringHead) + sizeof(gensym_counter));
 | 
			
		||||
    head->length = sizeof(gensym_counter) - 1;
 | 
			
		||||
    JanetStringHead *head = janet_gcalloc(JANET_MEMORY_SYMBOL, sizeof(JanetStringHead) + sizeof(janet_vm.gensym_counter));
 | 
			
		||||
    head->length = sizeof(janet_vm.gensym_counter) - 1;
 | 
			
		||||
    head->hash = hash;
 | 
			
		||||
    sym = (uint8_t *)(head->data);
 | 
			
		||||
    memcpy(sym, gensym_counter, sizeof(gensym_counter));
 | 
			
		||||
    memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter));
 | 
			
		||||
    janet_symcache_put((const uint8_t *)sym, bucket);
 | 
			
		||||
    return (const uint8_t *)sym;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user