mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 01:23:04 +00:00 
			
		
		
		
	Compare commits
	
		
			1289 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | ||
| 
						 | 
					03ba1f7021 | ||
| 
						 | 
					1f7f20788c | ||
| 
						 | 
					c59dd29190 | ||
| 
						 | 
					99f63a41a3 | ||
| 
						 | 
					a575f5df36 | ||
| 
						 | 
					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 | ||
| 
						 | 
					cd197e8be3 | ||
| 
						 | 
					51cf6465ff | ||
| 
						 | 
					bd95f742c0 | ||
| 
						 | 
					9ba94d2c6b | ||
| 
						 | 
					a4de83b3a3 | ||
| 
						 | 
					37a430c97c | ||
| 
						 | 
					f264cb0b18 | ||
| 
						 | 
					a0abf307b4 | ||
| 
						 | 
					328ee94412 | ||
| 
						 | 
					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,14 +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
 | 
			
		||||
    mkdir modpath
 | 
			
		||||
	jpm --verbose --modpath=./modpath install https://github.com/bakpakin/x43bot.git
 | 
			
		||||
@@ -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_array=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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							@@ -1 +1,9 @@
 | 
			
		||||
*.janet linguist-language=Clojure
 | 
			
		||||
*.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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										37
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										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"
 | 
			
		||||
							
								
								
									
										324
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										324
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,6 +1,330 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## 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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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) 2021 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										147
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2020 Calvin Rose
 | 
			
		||||
# Copyright (c) 2022 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,13 +21,14 @@
 | 
			
		||||
################################
 | 
			
		||||
##### 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_LIBRARY=build/libjanet.so
 | 
			
		||||
@@ -35,16 +36,18 @@ JANET_STATIC_LIBRARY=build/libjanet.a
 | 
			
		||||
JANET_PATH?=$(LIBDIR)/janet
 | 
			
		||||
JANET_MANPATH?=$(PREFIX)/share/man/man1/
 | 
			
		||||
JANET_PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig
 | 
			
		||||
JANET_DIST_DIR?=janet-dist
 | 
			
		||||
JPM_TAG?=master
 | 
			
		||||
DEBUGGER=gdb
 | 
			
		||||
SONAME_SETTER=-Wl,-soname,
 | 
			
		||||
 | 
			
		||||
# For cross compilation
 | 
			
		||||
HOSTCC?=$(CC)
 | 
			
		||||
HOSTAR?=$(AR)
 | 
			
		||||
CFLAGS?=-fPIC -O2
 | 
			
		||||
CFLAGS?=-O2
 | 
			
		||||
LDFLAGS?=-rdynamic
 | 
			
		||||
 | 
			
		||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden
 | 
			
		||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
 | 
			
		||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 -g $(COMMON_CFLAGS)
 | 
			
		||||
BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS)
 | 
			
		||||
 | 
			
		||||
@@ -56,24 +59,32 @@ 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)
 | 
			
		||||
$(shell mkdir -p build/core build/c build/boot)
 | 
			
		||||
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 \
 | 
			
		||||
@@ -97,6 +108,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 \
 | 
			
		||||
@@ -111,14 +124,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 \
 | 
			
		||||
@@ -141,36 +153,41 @@ JANET_BOOT_OBJECTS=$(patsubst src/%.c,build/%.boot.o,$(JANET_CORE_SOURCES) $(JAN
 | 
			
		||||
 | 
			
		||||
$(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)
 | 
			
		||||
	$(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: build/janet_boot src/boot/boot.janet
 | 
			
		||||
	build/janet_boot . JANET_PATH '$(JANET_PATH)' > $@
 | 
			
		||||
	cksum $@
 | 
			
		||||
 | 
			
		||||
########################
 | 
			
		||||
##### Amalgamation #####
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
SONAME=libjanet.so.1.10
 | 
			
		||||
ifeq ($(UNAME), Darwin)
 | 
			
		||||
SONAME=libjanet.1.25.dylib
 | 
			
		||||
else
 | 
			
		||||
SONAME=libjanet.so.1.25
 | 
			
		||||
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)
 | 
			
		||||
	./$(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
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@ -I build
 | 
			
		||||
 | 
			
		||||
build/shell.o: build/shell.c build/janet.h build/janetconf.h
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@ -I build
 | 
			
		||||
build/shell.o: build/c/shell.c $(JANETCONF_HEADER) src/include/janet.h
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
$(JANET_TARGET): build/janet.o build/shell.o
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS)
 | 
			
		||||
@@ -203,12 +220,10 @@ valgrind: $(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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@@ -220,13 +235,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 #####
 | 
			
		||||
@@ -241,10 +264,6 @@ build/doc.html: $(JANET_TARGET) tools/gendoc.janet
 | 
			
		||||
##### 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)' > $@
 | 
			
		||||
@@ -260,33 +279,49 @@ build/janet.pc: $(JANET_TARGET)
 | 
			
		||||
	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'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	cp -rf $(JANET_HEADERS) '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd
 | 
			
		||||
	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)$(JANET_MANPATH)'
 | 
			
		||||
	cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	cp jpm.1 '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
 | 
			
		||||
	cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || true
 | 
			
		||||
	[ -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)' \
 | 
			
		||||
		../../$(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)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_MANPATH)/janet.1'
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_MANPATH)/jpm.1'
 | 
			
		||||
	# -rm -rf '$(DESTDIR)$(JANET_PATH)'/* - err on the side of correctness here
 | 
			
		||||
 | 
			
		||||
#################
 | 
			
		||||
@@ -300,24 +335,16 @@ grammar: build/janet.tmLanguage
 | 
			
		||||
build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
 | 
			
		||||
	$(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
 | 
			
		||||
	cd test/install && jpm --verbose --modpath=./modpath install https://github.com/bakpakin/x43bot.git
 | 
			
		||||
	echo "JPM has been removed from default install."
 | 
			
		||||
 | 
			
		||||
help:
 | 
			
		||||
	@echo
 | 
			
		||||
@@ -342,4 +369,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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								README.md
									
									
									
									
									
								
							@@ -1,11 +1,8 @@
 | 
			
		||||
[](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">
 | 
			
		||||
 | 
			
		||||
@@ -14,11 +11,14 @@ 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.
 | 
			
		||||
 | 
			
		||||
There is a repl for trying out the language, as well as the ability
 | 
			
		||||
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
 | 
			
		||||
Janet can be embedded in other programs. Try Janet in your browser at
 | 
			
		||||
[https://janet-lang.org](https://janet-lang.org).
 | 
			
		||||
 | 
			
		||||
If you'd like to financially support the ongoing development of Janet, consider
 | 
			
		||||
[sponsoring its primary author](https://github.com/sponsors/bakpakin) through GitHub.
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
## Use Cases
 | 
			
		||||
@@ -29,24 +29,27 @@ Lua, but smaller than GNU Guile or Python.
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
* Configurable at build time - turn features on or off for a smaller or more featureful build
 | 
			
		||||
* Minimal setup - one binary and you are good to go!
 | 
			
		||||
* First class closures
 | 
			
		||||
* First-class closures
 | 
			
		||||
* Garbage collection
 | 
			
		||||
* First class green threads (continuations)
 | 
			
		||||
* Python style generators (implemented as a plain macro)
 | 
			
		||||
* First-class green threads (continuations)
 | 
			
		||||
* Python-style generators (implemented as a plain macro)
 | 
			
		||||
* Mutable and immutable arrays (array/tuple)
 | 
			
		||||
* Mutable and immutable hashtables (table/struct)
 | 
			
		||||
* Mutable and immutable strings (buffer/string)
 | 
			
		||||
* Macros
 | 
			
		||||
* Multithreading
 | 
			
		||||
* Per-thread event loop for efficient evented IO
 | 
			
		||||
* Byte code interpreter with an assembly interface, as well as bytecode verification
 | 
			
		||||
* Tailcall Optimization
 | 
			
		||||
* Tail call Optimization
 | 
			
		||||
* Direct interop with C via abstract types and C functions
 | 
			
		||||
* Dynamically load C libraries
 | 
			
		||||
* Functional and imperative standard library
 | 
			
		||||
* Lexical scoping
 | 
			
		||||
* Imperative programming as well as functional
 | 
			
		||||
* REPL
 | 
			
		||||
* Parsing Expression Grammars built in to the core library
 | 
			
		||||
* Parsing Expression Grammars built into the core library
 | 
			
		||||
* 400+ functions and macros in the core library
 | 
			
		||||
* Embedding Janet in other programs
 | 
			
		||||
* Interactive environment with detailed stack traces
 | 
			
		||||
@@ -56,17 +59,17 @@ Lua, but smaller than GNU Guile or Python.
 | 
			
		||||
* 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)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
@@ -86,13 +89,15 @@ 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`.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
@@ -100,11 +105,13 @@ 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,
 | 
			
		||||
FreeBSD build instructions are the same as the UNIX-like build instructions,
 | 
			
		||||
but you need `gmake` to compile. Alternatively, install directly from
 | 
			
		||||
packages, using `pkg install lang/janet`.
 | 
			
		||||
 | 
			
		||||
@@ -113,8 +120,15 @@ cd somewhere/my/projects/janet
 | 
			
		||||
gmake
 | 
			
		||||
gmake test
 | 
			
		||||
gmake repl
 | 
			
		||||
gmake install
 | 
			
		||||
gmake install-jpm-git
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### NetBSD
 | 
			
		||||
 | 
			
		||||
NetBSD build instructions are the same as the FreeBSD build instructions.
 | 
			
		||||
Alternatively, install directly from packages, using `pkgin install janet`.
 | 
			
		||||
 | 
			
		||||
### 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#)
 | 
			
		||||
@@ -131,11 +145,11 @@ Now you should have an `.msi`. You can run `build_win install` to install the `.
 | 
			
		||||
 | 
			
		||||
### 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.
 | 
			
		||||
@@ -167,16 +181,16 @@ Emacs, and Atom will 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
 | 
			
		||||
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
 | 
			
		||||
@@ -194,18 +208,18 @@ Options are:
 | 
			
		||||
  -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)
 | 
			
		||||
  -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)
 | 
			
		||||
  -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
 | 
			
		||||
  -n : Disable ANSI color output in the REPL
 | 
			
		||||
  -l path : Execute code in a file before running the main script
 | 
			
		||||
  -- : 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
 | 
			
		||||
 | 
			
		||||
@@ -227,21 +241,65 @@ 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?
 | 
			
		||||
### Where is (favorite feature from other language)?
 | 
			
		||||
 | 
			
		||||
It may exist, it may not. If you want to propose major language features, go ahead and open an issue, but
 | 
			
		||||
they will likely by closed as "will not implement". Often, such features make one usecase simpler at the expense
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
### 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 implemeted with keywords.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
(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, usually a feature can be added as a library
 | 
			
		||||
  without feeling "bolted on", especially when compared to ALGOL like languages. Adding features
 | 
			
		||||
  to the core also makes it a bit more difficult to keep Janet maximally portable.
 | 
			
		||||
 | 
			
		||||
### 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
 | 
			
		||||
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
 | 
			
		||||
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,36 +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
 | 
			
		||||
call jpm --verbose --modpath=. install https://github.com/bakpakin/x43bot.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))
 | 
			
		||||
							
								
								
									
										208
									
								
								examples/ffi/so.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								examples/ffi/so.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,208 @@
 | 
			
		||||
#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))
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								janet.1
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								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
 | 
			
		||||
@@ -167,6 +188,10 @@ Disable ANSI colors in the repl. Has no effect if no repl is run.
 | 
			
		||||
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,
 | 
			
		||||
@@ -193,12 +218,28 @@ Precompiles Janet source code into an image, a binary dump that can be efficient
 | 
			
		||||
Source should be a path to the Janet module to compile, and output should be the file path of
 | 
			
		||||
resulting image. Output should usually end with the .jimage extension.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.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 +254,19 @@ 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
 | 
			
		||||
 | 
			
		||||
.SH AUTHOR
 | 
			
		||||
Written by Calvin Rose <calsrose@gmail.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										263
									
								
								jpm.1
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								jpm.1
									
									
									
									
									
								
							@@ -1,263 +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 list-installed
 | 
			
		||||
List all installed packages in the current syspath.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR list-pkgs [\fBsearch\fR]
 | 
			
		||||
List all package aliases in the current package listing that contain the given search string.
 | 
			
		||||
If no search string is given, prints the entire listing.
 | 
			
		||||
 | 
			
		||||
.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>
 | 
			
		||||
							
								
								
									
										109
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								meson.build
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2020 Calvin Rose and contributors
 | 
			
		||||
# Copyright (c) 2021 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.11.0')
 | 
			
		||||
  default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
 | 
			
		||||
  version : '1.25.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,11 @@ 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'))
 | 
			
		||||
if get_option('os_name') != ''
 | 
			
		||||
  conf.set('JANET_OS_NAME', get_option('os_name'))
 | 
			
		||||
endif
 | 
			
		||||
@@ -110,6 +116,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 +132,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 +163,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 +173,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 +226,17 @@ 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/suite0000.janet',
 | 
			
		||||
  'test/suite0001.janet',
 | 
			
		||||
  'test/suite0002.janet',
 | 
			
		||||
  'test/suite0003.janet',
 | 
			
		||||
  'test/suite0004.janet',
 | 
			
		||||
  'test/suite0005.janet',
 | 
			
		||||
  'test/suite0006.janet',
 | 
			
		||||
  'test/suite0007.janet',
 | 
			
		||||
  'test/suite0008.janet',
 | 
			
		||||
  'test/suite0009.janet',
 | 
			
		||||
  'test/suite0010.janet'
 | 
			
		||||
]
 | 
			
		||||
foreach t : test_files
 | 
			
		||||
  test(t, janet_nativeclient, args : files([t]), workdir : meson.current_source_dir())
 | 
			
		||||
@@ -241,22 +252,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')),
 | 
			
		||||
      '--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,18 @@ 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('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) 2022 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) 2022 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();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3247
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
							
						
						
									
										3247
									
								
								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) 2022 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) 2022 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) 2022 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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,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 11
 | 
			
		||||
#define JANET_VERSION_PATCH 0
 | 
			
		||||
#define JANET_VERSION_MINOR 25
 | 
			
		||||
#define JANET_VERSION_PATCH 1
 | 
			
		||||
#define JANET_VERSION_EXTRA ""
 | 
			
		||||
#define JANET_VERSION "1.11.0"
 | 
			
		||||
#define JANET_VERSION "1.25.1"
 | 
			
		||||
 | 
			
		||||
/* #define JANET_BUILD "local" */
 | 
			
		||||
 | 
			
		||||
@@ -50,14 +27,16 @@
 | 
			
		||||
/* #define JANET_NO_ASSEMBLER */
 | 
			
		||||
/* #define JANET_NO_PEG */
 | 
			
		||||
/* #define JANET_NO_NET */
 | 
			
		||||
/* #define JANET_NO_TYPED_ARRAY */
 | 
			
		||||
/* #define JANET_NO_INT_TYPES */
 | 
			
		||||
/* #define JANET_NO_EV */
 | 
			
		||||
/* #define JANET_NO_REALPATH */
 | 
			
		||||
/* #define JANET_NO_SYMLINKS */
 | 
			
		||||
/* #define JANET_NO_UMASK */
 | 
			
		||||
/* #define JANET_NO_THREADS */
 | 
			
		||||
 | 
			
		||||
/* Other settings */
 | 
			
		||||
/* #define JANET_NO_PRF */
 | 
			
		||||
/* #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) */
 | 
			
		||||
@@ -68,6 +47,16 @@
 | 
			
		||||
/* #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 */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,15 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Create new userdata */
 | 
			
		||||
@@ -43,3 +51,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(&ab->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_decref(JanetAbstractHead *ab) {
 | 
			
		||||
    return InterlockedDecrement(&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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										199
									
								
								src/core/array.c
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								src/core/array.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,12 +316,14 @@ static Janet cfun_array_remove(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_trim(int32_t argc, Janet *argv) {
 | 
			
		||||
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 = realloc(array->data, array->count * sizeof(Janet));
 | 
			
		||||
            Janet *newData = janet_realloc(array->data, array->count * sizeof(Janet));
 | 
			
		||||
            if (NULL == newData) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -284,96 +332,39 @@ static Janet cfun_array_trim(int32_t argc, Janet *argv) {
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        array->capacity = 0;
 | 
			
		||||
        free(array->data);
 | 
			
		||||
        janet_free(array->data);
 | 
			
		||||
        array->data = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/trim", cfun_array_trim,
 | 
			
		||||
        JDOC("(array/trim arr)\n\n"
 | 
			
		||||
             "Set the backing capacity of an array to its current length. Returns the modified array.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										247
									
								
								src/core/asm.c
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								src/core/asm.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,6 +73,7 @@ static const JanetInstructionDef janet_ops[] = {
 | 
			
		||||
    {"call", JOP_CALL},
 | 
			
		||||
    {"clo", JOP_CLOSURE},
 | 
			
		||||
    {"cmp", JOP_COMPARE},
 | 
			
		||||
    {"cncl", JOP_CANCEL},
 | 
			
		||||
    {"div", JOP_DIVIDE},
 | 
			
		||||
    {"divim", JOP_DIVIDE_IMMEDIATE},
 | 
			
		||||
    {"eq", JOP_EQUALS},
 | 
			
		||||
@@ -223,7 +224,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -552,6 +553,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);
 | 
			
		||||
@@ -581,7 +586,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -613,7 +618,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;
 | 
			
		||||
                }
 | 
			
		||||
@@ -642,7 +647,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -684,7 +689,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -710,7 +715,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
 | 
			
		||||
    /* Set environments */
 | 
			
		||||
    def->environments =
 | 
			
		||||
        realloc(def->environments, def->environments_length * sizeof(int32_t));
 | 
			
		||||
        janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
 | 
			
		||||
    if (NULL == def->environments) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -840,85 +845,119 @@ Janet janet_asm_decode_instruction(uint32_t instr) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_disasm(JanetFuncDef *def) {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
/*
 | 
			
		||||
 * Disasm sections
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
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_bytecode(JanetFuncDef *def) {
 | 
			
		||||
    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));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 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];
 | 
			
		||||
        }
 | 
			
		||||
        constants->count = def->constants_length;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Add bytecode */
 | 
			
		||||
    for (i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
    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("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) {
 | 
			
		||||
@@ -927,33 +966,55 @@ static Janet cfun_asm(int32_t argc, Janet *argv) {
 | 
			
		||||
    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"
 | 
			
		||||
              "* :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) 2022 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,12 +31,11 @@
 | 
			
		||||
/* Initialize a buffer */
 | 
			
		||||
JanetBuffer *janet_buffer_init(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;
 | 
			
		||||
@@ -46,7 +45,7 @@ JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
 | 
			
		||||
 | 
			
		||||
/* Deinitialize a buffer (free data memory) */
 | 
			
		||||
void janet_buffer_deinit(JanetBuffer *buffer) {
 | 
			
		||||
    free(buffer->data);
 | 
			
		||||
    janet_free(buffer->data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize a buffer */
 | 
			
		||||
@@ -63,7 +62,7 @@ void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth)
 | 
			
		||||
    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 +91,8 @@ 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));
 | 
			
		||||
        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 +162,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,27 +206,28 @@ static Janet cfun_buffer_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_trim(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);
 | 
			
		||||
    if (buffer->count) {
 | 
			
		||||
        if (buffer->count < buffer->capacity) {
 | 
			
		||||
            uint8_t *newData = realloc(buffer->data, buffer->count);
 | 
			
		||||
            if (NULL == newData) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
            buffer->data = newData;
 | 
			
		||||
            buffer->capacity = buffer->count;
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        buffer->capacity = 0;
 | 
			
		||||
        free(buffer->data);
 | 
			
		||||
        buffer->data = NULL;
 | 
			
		||||
        buffer->data = newData;
 | 
			
		||||
        buffer->capacity = newcap;
 | 
			
		||||
    }
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
 | 
			
		||||
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);
 | 
			
		||||
@@ -227,7 +237,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);
 | 
			
		||||
@@ -241,7 +255,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);
 | 
			
		||||
@@ -256,14 +275,45 @@ static Janet cfun_buffer_chars(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_clear(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push,
 | 
			
		||||
              "(buffer/push buffer & xs)",
 | 
			
		||||
              "Push both individual bytes and byte sequences to a buffer. For each x in xs, "
 | 
			
		||||
              "push the byte if x is an integer, otherwise push the bytesequence to the buffer. "
 | 
			
		||||
              "Thus, this function behaves like both `buffer/push-string` and `buffer/push-byte`. "
 | 
			
		||||
              "Returns the modified buffer. "
 | 
			
		||||
              "Will throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    for (i = 1; 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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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);
 | 
			
		||||
@@ -276,7 +326,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);
 | 
			
		||||
@@ -300,7 +355,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;
 | 
			
		||||
@@ -309,7 +366,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;
 | 
			
		||||
@@ -318,7 +377,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;
 | 
			
		||||
@@ -326,7 +387,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;
 | 
			
		||||
@@ -335,7 +398,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);
 | 
			
		||||
@@ -372,7 +439,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);
 | 
			
		||||
@@ -380,106 +450,26 @@ 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/trim", cfun_buffer_trim,
 | 
			
		||||
        JDOC("(buffer/trim buffer)\n\n"
 | 
			
		||||
             "Set the backing capacity of the buffer to the current length of the buffer. 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/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) 2022 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
 | 
			
		||||
@@ -103,6 +103,7 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
 | 
			
		||||
    JINT_SSS, /* JOP_NEXT */
 | 
			
		||||
    JINT_SSS, /* JOP_NOT_EQUALS, */
 | 
			
		||||
    JINT_SSI, /* JOP_NOT_EQUALS_IMMEDIATE, */
 | 
			
		||||
    JINT_SSS /* JOP_CANCEL, */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Verify some bytecode */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,13 +51,15 @@ JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
@@ -147,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 *)
 | 
			
		||||
@@ -193,7 +212,7 @@ 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");
 | 
			
		||||
        janet_panic("string contains embedded 0s");
 | 
			
		||||
    }
 | 
			
		||||
    return cstr;
 | 
			
		||||
}
 | 
			
		||||
@@ -241,11 +260,27 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_checkint64(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) {
 | 
			
		||||
@@ -339,26 +374,26 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_dyn(const char *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) {
 | 
			
		||||
        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));
 | 
			
		||||
    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) {
 | 
			
		||||
        if (!janet_vm_top_dyns) janet_vm_top_dyns = janet_table(10);
 | 
			
		||||
        janet_table_put(janet_vm_top_dyns, janet_ckeywordv(name), value);
 | 
			
		||||
    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);
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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
 | 
			
		||||
@@ -231,6 +231,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;
 | 
			
		||||
@@ -383,6 +386,7 @@ static const JanetFunOptimizer optimizers[] = {
 | 
			
		||||
    {fixarity2, do_modulo},
 | 
			
		||||
    {fixarity2, do_remainder},
 | 
			
		||||
    {fixarity2, do_cmp},
 | 
			
		||||
    {fixarity2, do_cancel},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const JanetFunOptimizer *janetc_funopt(uint32_t flags) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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;
 | 
			
		||||
@@ -167,6 +197,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 +262,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 */
 | 
			
		||||
@@ -399,6 +500,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 +541,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 +606,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 +705,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;
 | 
			
		||||
 | 
			
		||||
@@ -601,6 +733,9 @@ static int macroexpand1(
 | 
			
		||||
    Janet tempOut;
 | 
			
		||||
    JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &tempOut);
 | 
			
		||||
    janet_table_put(c->env, mf_kw, janet_wrap_nil());
 | 
			
		||||
    if (c->lints) {
 | 
			
		||||
        janet_table_put(c->env, janet_ckeywordv("macro-lints"), janet_wrap_array(c->lints));
 | 
			
		||||
    }
 | 
			
		||||
    janet_gcunlock(lock);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK) {
 | 
			
		||||
        const uint8_t *es = janet_formatc("(macro) %V", tempOut);
 | 
			
		||||
@@ -745,7 +880,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -753,7 +888,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -778,7 +913,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -795,7 +930,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
@@ -804,6 +939,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;
 | 
			
		||||
@@ -821,12 +957,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");
 | 
			
		||||
@@ -854,26 +991,49 @@ 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_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 {
 | 
			
		||||
            janet_panic_type(x, 2, JANET_TFLAG_STRING | JANET_TFLAG_KEYWORD);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetCompileResult res = janet_compile(argv[0], env, source);
 | 
			
		||||
    JanetArray *lints = (argc >= 4) ? 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));
 | 
			
		||||
        }
 | 
			
		||||
@@ -881,18 +1041,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) 2022 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
 | 
			
		||||
@@ -61,6 +68,7 @@
 | 
			
		||||
#define JANET_FUN_MODULO 29
 | 
			
		||||
#define JANET_FUN_REMAINDER 30
 | 
			
		||||
#define JANET_FUN_CMP 31
 | 
			
		||||
#define JANET_FUN_CANCEL 32
 | 
			
		||||
 | 
			
		||||
/* Compiler typedefs */
 | 
			
		||||
typedef struct JanetCompiler JanetCompiler;
 | 
			
		||||
@@ -77,10 +85,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
 | 
			
		||||
 | 
			
		||||
@@ -163,6 +171,9 @@ struct JanetCompiler {
 | 
			
		||||
 | 
			
		||||
    /* Prevent unbounded recursion */
 | 
			
		||||
    int recursion_guard;
 | 
			
		||||
 | 
			
		||||
    /* Collect linting results */
 | 
			
		||||
    JanetArray *lints;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define JANET_FOPTS_TAIL 0x10000
 | 
			
		||||
@@ -229,6 +240,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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,38 +35,19 @@ extern const unsigned char *janet_core_image;
 | 
			
		||||
extern size_t janet_core_image_size;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
 | 
			
		||||
 * with native code. */
 | 
			
		||||
#if defined(JANET_NO_DYNAMIC_MODULES)
 | 
			
		||||
typedef int Clib;
 | 
			
		||||
#define load_clib(name) ((void) name, 0)
 | 
			
		||||
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
 | 
			
		||||
#define error_clib() "dynamic libraries not supported"
 | 
			
		||||
#elif defined(JANET_WINDOWS)
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
typedef HINSTANCE Clib;
 | 
			
		||||
#define load_clib(name) LoadLibrary((name))
 | 
			
		||||
#define symbol_clib(lib, sym) GetProcAddress((lib), (sym))
 | 
			
		||||
static char error_clib_buf[256];
 | 
			
		||||
static char *error_clib(void) {
 | 
			
		||||
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 | 
			
		||||
                   NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | 
			
		||||
                   error_clib_buf, sizeof(error_clib_buf), NULL);
 | 
			
		||||
    error_clib_buf[strlen(error_clib_buf) - 1] = '\0';
 | 
			
		||||
    return error_clib_buf;
 | 
			
		||||
}
 | 
			
		||||
/* Docstrings should only exist during bootstrap */
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
#define JDOC(x) (x)
 | 
			
		||||
#else
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
typedef void *Clib;
 | 
			
		||||
#define load_clib(name) dlopen((name), RTLD_NOW)
 | 
			
		||||
#define symbol_clib(lib, sym) dlsym((lib), (sym))
 | 
			
		||||
#define error_clib() dlerror()
 | 
			
		||||
#define JDOC(x) NULL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JanetModule janet_native(const char *name, const uint8_t **error) {
 | 
			
		||||
    Clib lib = load_clib(name);
 | 
			
		||||
    char *processed_name = get_processed_name(name);
 | 
			
		||||
    Clib lib = load_clib(processed_name);
 | 
			
		||||
    JanetModule init;
 | 
			
		||||
    JanetModconf getter;
 | 
			
		||||
    if (name != processed_name) janet_free(processed_name);
 | 
			
		||||
    if (!lib) {
 | 
			
		||||
        *error = janet_cstring(error_clib());
 | 
			
		||||
        return NULL;
 | 
			
		||||
@@ -111,7 +92,7 @@ static const char *janet_dyncstring(const char *name, const char *dflt) {
 | 
			
		||||
    const uint8_t *jstr = janet_unwrap_string(x);
 | 
			
		||||
    const char *cstr = (const char *)jstr;
 | 
			
		||||
    if (strlen(cstr) != (size_t) janet_string_length(jstr)) {
 | 
			
		||||
        janet_panicf("string %v contains embedded 0s");
 | 
			
		||||
        janet_panicf("string %v contains embedded 0s", x);
 | 
			
		||||
    }
 | 
			
		||||
    return cstr;
 | 
			
		||||
}
 | 
			
		||||
@@ -124,7 +105,18 @@ static int is_path_sep(char c) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Used for module system. */
 | 
			
		||||
static Janet janet_core_expand_path(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_expand_path,
 | 
			
		||||
              "(module/expand-path path template)",
 | 
			
		||||
              "Expands a path template as found in `module/paths` for `module/find`. "
 | 
			
		||||
              "This takes in a path (the argument to require) and a template string, "
 | 
			
		||||
              "to expand the path to a path that can be "
 | 
			
		||||
              "used for importing files. The replacements are as follows:\n\n"
 | 
			
		||||
              "* :all: -- the value of path verbatim\n\n"
 | 
			
		||||
              "* :cur: -- the current file, or (dyn :current-file)\n\n"
 | 
			
		||||
              "* :dir: -- the directory containing the current file\n\n"
 | 
			
		||||
              "* :name: -- the name component of path, with extension if given\n\n"
 | 
			
		||||
              "* :native: -- the extension used to load natives, .so or .dll\n\n"
 | 
			
		||||
              "* :sys: -- the system path, or (dyn :syspath)") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    const char *input = janet_getcstring(argv, 0);
 | 
			
		||||
    const char *template = janet_getcstring(argv, 1);
 | 
			
		||||
@@ -247,11 +239,13 @@ static Janet janet_core_expand_path(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_buffer(out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_dyn(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_dyn,
 | 
			
		||||
              "(dyn key &opt default)",
 | 
			
		||||
              "Get a dynamic binding. Returns the default value (or nil) if no binding found.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    Janet value;
 | 
			
		||||
    if (janet_vm_fiber->env) {
 | 
			
		||||
        value = janet_table_get(janet_vm_fiber->env, argv[0]);
 | 
			
		||||
    if (janet_vm.fiber->env) {
 | 
			
		||||
        value = janet_table_get(janet_vm.fiber->env, argv[0]);
 | 
			
		||||
    } else {
 | 
			
		||||
        value = janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
@@ -261,16 +255,24 @@ static Janet janet_core_dyn(int32_t argc, Janet *argv) {
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_setdyn(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_setdyn,
 | 
			
		||||
              "(setdyn key value)",
 | 
			
		||||
              "Set a dynamic binding. Returns value.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    if (!janet_vm_fiber->env) {
 | 
			
		||||
        janet_vm_fiber->env = janet_table(2);
 | 
			
		||||
    if (!janet_vm.fiber->env) {
 | 
			
		||||
        janet_vm.fiber->env = janet_table(2);
 | 
			
		||||
    }
 | 
			
		||||
    janet_table_put(janet_vm_fiber->env, argv[0], argv[1]);
 | 
			
		||||
    janet_table_put(janet_vm.fiber->env, argv[0], argv[1]);
 | 
			
		||||
    return argv[1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_native(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_native,
 | 
			
		||||
              "(native path &opt env)",
 | 
			
		||||
              "Load a native module from the given path. The path "
 | 
			
		||||
              "must be an absolute or relative path on the file system, and is "
 | 
			
		||||
              "usually a .so file on Unix systems, and a .dll file on Windows. "
 | 
			
		||||
              "Returns an environment table that contains functions and other values "
 | 
			
		||||
              "from the native module.") {
 | 
			
		||||
    JanetModule init;
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    const uint8_t *path = janet_getstring(argv, 0);
 | 
			
		||||
@@ -290,67 +292,107 @@ static Janet janet_core_native(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_table(env);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_describe(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_describe,
 | 
			
		||||
              "(describe x)",
 | 
			
		||||
              "Returns a string that is a human-readable description of `x`. "
 | 
			
		||||
              "For recursive data structures, the string returned contains a "
 | 
			
		||||
              "pointer value from which the identity of `x` "
 | 
			
		||||
              "can be determined.") {
 | 
			
		||||
    JanetBuffer *b = janet_buffer(0);
 | 
			
		||||
    for (int32_t i = 0; i < argc; ++i)
 | 
			
		||||
        janet_description_b(b, argv[i]);
 | 
			
		||||
    return janet_stringv(b->data, b->count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_string(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_string,
 | 
			
		||||
              "(string & xs)",
 | 
			
		||||
              "Creates a string by concatenating the elements of `xs` together. If an "
 | 
			
		||||
              "element is not a byte sequence, it is converted to bytes via `describe`. "
 | 
			
		||||
              "Returns the new string.") {
 | 
			
		||||
    JanetBuffer *b = janet_buffer(0);
 | 
			
		||||
    for (int32_t i = 0; i < argc; ++i)
 | 
			
		||||
        janet_to_string_b(b, argv[i]);
 | 
			
		||||
    return janet_stringv(b->data, b->count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_symbol(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_symbol,
 | 
			
		||||
              "(symbol & xs)",
 | 
			
		||||
              "Creates a symbol by concatenating the elements of `xs` together. If an "
 | 
			
		||||
              "element is not a byte sequence, it is converted to bytes via `describe`. "
 | 
			
		||||
              "Returns the new symbol.") {
 | 
			
		||||
    JanetBuffer *b = janet_buffer(0);
 | 
			
		||||
    for (int32_t i = 0; i < argc; ++i)
 | 
			
		||||
        janet_to_string_b(b, argv[i]);
 | 
			
		||||
    return janet_symbolv(b->data, b->count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_keyword(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_keyword,
 | 
			
		||||
              "(keyword & xs)",
 | 
			
		||||
              "Creates a keyword by concatenating the elements of `xs` together. If an "
 | 
			
		||||
              "element is not a byte sequence, it is converted to bytes via `describe`. "
 | 
			
		||||
              "Returns the new keyword.") {
 | 
			
		||||
    JanetBuffer *b = janet_buffer(0);
 | 
			
		||||
    for (int32_t i = 0; i < argc; ++i)
 | 
			
		||||
        janet_to_string_b(b, argv[i]);
 | 
			
		||||
    return janet_keywordv(b->data, b->count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_buffer(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_buffer,
 | 
			
		||||
              "(buffer & xs)",
 | 
			
		||||
              "Creates a buffer by concatenating the elements of `xs` together. If an "
 | 
			
		||||
              "element is not a byte sequence, it is converted to bytes via `describe`. "
 | 
			
		||||
              "Returns the new buffer.") {
 | 
			
		||||
    JanetBuffer *b = janet_buffer(0);
 | 
			
		||||
    for (int32_t i = 0; i < argc; ++i)
 | 
			
		||||
        janet_to_string_b(b, argv[i]);
 | 
			
		||||
    return janet_wrap_buffer(b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_is_abstract(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_is_abstract,
 | 
			
		||||
              "(abstract? x)",
 | 
			
		||||
              "Check if x is an abstract type.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_boolean(janet_checktype(argv[0], JANET_ABSTRACT));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_scannumber(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_scannumber,
 | 
			
		||||
              "(scan-number str &opt base)",
 | 
			
		||||
              "Parse a number from a byte sequence and return that number, either an integer "
 | 
			
		||||
              "or a real. The number "
 | 
			
		||||
              "must be in the same format as numbers in janet source code. Will return nil "
 | 
			
		||||
              "on an invalid number. Optionally provide a base - if a base is provided, no "
 | 
			
		||||
              "radix specifier is expected at the beginning of the number.") {
 | 
			
		||||
    double number;
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    if (janet_scan_number(view.bytes, view.len, &number))
 | 
			
		||||
    int32_t base = janet_optinteger(argv, argc, 1, 0);
 | 
			
		||||
    int valid = base == 0 || (base >= 2 && base <= 36);
 | 
			
		||||
    if (!valid) {
 | 
			
		||||
        janet_panicf("expected base between 2 and 36, got %d", base);
 | 
			
		||||
    }
 | 
			
		||||
    if (janet_scan_number_base(view.bytes, view.len, base, &number))
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    return janet_wrap_number(number);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_tuple(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_tuple,
 | 
			
		||||
              "(tuple & items)",
 | 
			
		||||
              "Creates a new tuple that contains items. Returns the new tuple.") {
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_n(argv, argc));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_array(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_array,
 | 
			
		||||
              "(array & items)",
 | 
			
		||||
              "Create a new array that contains items. Returns the new array.") {
 | 
			
		||||
    JanetArray *array = janet_array(argc);
 | 
			
		||||
    array->count = argc;
 | 
			
		||||
    safe_memcpy(array->data, argv, argc * sizeof(Janet));
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_slice,
 | 
			
		||||
              "(slice x &opt start end)",
 | 
			
		||||
              "Extract a sub-range of an indexed data structure or byte sequence.") {
 | 
			
		||||
    JanetRange range;
 | 
			
		||||
    JanetByteView bview;
 | 
			
		||||
    JanetView iview;
 | 
			
		||||
@@ -365,7 +407,12 @@ static Janet janet_core_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_table(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_table,
 | 
			
		||||
              "(table & kvs)",
 | 
			
		||||
              "Creates a new table from a variadic number of keys and values. "
 | 
			
		||||
              "kvs is a sequence k1, v1, k2, v2, k3, v3, ... If kvs has "
 | 
			
		||||
              "an odd number of elements, an error will be thrown. Returns the "
 | 
			
		||||
              "new table.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    if (argc & 1)
 | 
			
		||||
        janet_panic("expected even number of arguments");
 | 
			
		||||
@@ -376,10 +423,35 @@ static Janet janet_core_table(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_table(table);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_struct(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_getproto,
 | 
			
		||||
              "(getproto x)",
 | 
			
		||||
              "Get the prototype of a table or struct. Will return nil if `x` has no prototype.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (janet_checktype(argv[0], JANET_TABLE)) {
 | 
			
		||||
        JanetTable *t = janet_unwrap_table(argv[0]);
 | 
			
		||||
        return t->proto
 | 
			
		||||
               ? janet_wrap_table(t->proto)
 | 
			
		||||
               : janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    if (janet_checktype(argv[0], JANET_STRUCT)) {
 | 
			
		||||
        JanetStruct st = janet_unwrap_struct(argv[0]);
 | 
			
		||||
        return janet_struct_proto(st)
 | 
			
		||||
               ? janet_wrap_struct(janet_struct_proto(st))
 | 
			
		||||
               : janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    janet_panicf("expected struct|table, got %v", argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_struct,
 | 
			
		||||
              "(struct & kvs)",
 | 
			
		||||
              "Create a new struct from a sequence of key value pairs. "
 | 
			
		||||
              "kvs is a sequence k1, v1, k2, v2, k3, v3, ... If kvs has "
 | 
			
		||||
              "an odd number of elements, an error will be thrown. Returns the "
 | 
			
		||||
              "new struct.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    if (argc & 1)
 | 
			
		||||
    if (argc & 1) {
 | 
			
		||||
        janet_panic("expected even number of arguments");
 | 
			
		||||
    }
 | 
			
		||||
    JanetKV *st = janet_struct_begin(argc >> 1);
 | 
			
		||||
    for (i = 0; i < argc; i += 2) {
 | 
			
		||||
        janet_struct_put(st, argv[i], argv[i + 1]);
 | 
			
		||||
@@ -387,20 +459,30 @@ static Janet janet_core_struct(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_struct(janet_struct_end(st));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_gensym(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_gensym,
 | 
			
		||||
              "(gensym)",
 | 
			
		||||
              "Returns a new symbol that is unique across the runtime. This means it "
 | 
			
		||||
              "will not collide with any already created symbols during compilation, so "
 | 
			
		||||
              "it can be used in macros to generate automatic bindings.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    return janet_wrap_symbol(janet_symbol_gen());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_gccollect(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_gccollect,
 | 
			
		||||
              "(gccollect)",
 | 
			
		||||
              "Run garbage collection. You should probably not call this manually.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    (void) argc;
 | 
			
		||||
    janet_collect();
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_gcsetinterval(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_gcsetinterval,
 | 
			
		||||
              "(gcsetinterval interval)",
 | 
			
		||||
              "Set an integer number of bytes to allocate before running garbage collection. "
 | 
			
		||||
              "Low values for interval will be slower but use less memory. "
 | 
			
		||||
              "High values will be faster but use more memory.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    size_t s = janet_getsize(argv, 0);
 | 
			
		||||
    /* limit interval to 48 bits */
 | 
			
		||||
@@ -409,17 +491,37 @@ static Janet janet_core_gcsetinterval(int32_t argc, Janet *argv) {
 | 
			
		||||
        janet_panic("interval too large");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    janet_vm_gc_interval = s;
 | 
			
		||||
    janet_vm.gc_interval = s;
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_gcinterval(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_gcinterval,
 | 
			
		||||
              "(gcinterval)",
 | 
			
		||||
              "Returns the integer number of bytes to allocate before running an iteration "
 | 
			
		||||
              "of garbage collection.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    return janet_wrap_number((double) janet_vm_gc_interval);
 | 
			
		||||
    return janet_wrap_number((double) janet_vm.gc_interval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_type(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_type,
 | 
			
		||||
              "(type x)",
 | 
			
		||||
              "Returns the type of `x` as a keyword. `x` is one of:\n\n"
 | 
			
		||||
              "* :nil\n\n"
 | 
			
		||||
              "* :boolean\n\n"
 | 
			
		||||
              "* :number\n\n"
 | 
			
		||||
              "* :array\n\n"
 | 
			
		||||
              "* :tuple\n\n"
 | 
			
		||||
              "* :table\n\n"
 | 
			
		||||
              "* :struct\n\n"
 | 
			
		||||
              "* :string\n\n"
 | 
			
		||||
              "* :buffer\n\n"
 | 
			
		||||
              "* :symbol\n\n"
 | 
			
		||||
              "* :keyword\n\n"
 | 
			
		||||
              "* :function\n\n"
 | 
			
		||||
              "* :cfunction\n\n"
 | 
			
		||||
              "* :fiber\n\n"
 | 
			
		||||
              "or another keyword for an abstract type.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetType t = janet_type(argv[0]);
 | 
			
		||||
    if (t == JANET_ABSTRACT) {
 | 
			
		||||
@@ -429,12 +531,21 @@ static Janet janet_core_type(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_hash(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_hash,
 | 
			
		||||
              "(hash value)",
 | 
			
		||||
              "Gets a hash for any value. The hash is an integer can be used "
 | 
			
		||||
              "as a cheap hash function for all values. If two values are strictly equal, "
 | 
			
		||||
              "then they will have the same hash value.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_number(janet_hash(argv[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_getline(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_getline,
 | 
			
		||||
              "(getline &opt prompt buf env)",
 | 
			
		||||
              "Reads a line of input into a buffer, including the newline character, using a prompt. "
 | 
			
		||||
              "An optional environment table can be provided for auto-complete. "
 | 
			
		||||
              "Returns the modified buffer. "
 | 
			
		||||
              "Use this function to implement a simple interface for a terminal program.") {
 | 
			
		||||
    FILE *in = janet_dynfile("in", stdin);
 | 
			
		||||
    FILE *out = janet_dynfile("out", stdout);
 | 
			
		||||
    janet_arity(argc, 0, 3);
 | 
			
		||||
@@ -459,21 +570,27 @@ static Janet janet_core_getline(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_buffer(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_trace(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_trace,
 | 
			
		||||
              "(trace func)",
 | 
			
		||||
              "Enable tracing on a function. Returns the function.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFunction *func = janet_getfunction(argv, 0);
 | 
			
		||||
    func->gc.flags |= JANET_FUNCFLAG_TRACE;
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_untrace(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_untrace,
 | 
			
		||||
              "(untrace func)",
 | 
			
		||||
              "Disables tracing on a function. Returns the function.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFunction *func = janet_getfunction(argv, 0);
 | 
			
		||||
    func->gc.flags &= ~JANET_FUNCFLAG_TRACE;
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_check_int(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_check_int,
 | 
			
		||||
              "(int? x)",
 | 
			
		||||
              "Check if x can be exactly represented as a 32 bit signed two's complement integer.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
 | 
			
		||||
    double num = janet_unwrap_number(argv[0]);
 | 
			
		||||
@@ -482,7 +599,9 @@ ret_false:
 | 
			
		||||
    return janet_wrap_false();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_check_nat(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_check_nat,
 | 
			
		||||
              "(nat? x)",
 | 
			
		||||
              "Check if x can be exactly represented as a non-negative 32 bit signed two's complement integer.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
 | 
			
		||||
    double num = janet_unwrap_number(argv[0]);
 | 
			
		||||
@@ -491,228 +610,44 @@ ret_false:
 | 
			
		||||
    return janet_wrap_false();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_core_signal(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_core_signal,
 | 
			
		||||
              "(signal what x)",
 | 
			
		||||
              "Raise a signal with payload x. ") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    int sig;
 | 
			
		||||
    Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
 | 
			
		||||
    if (janet_checkint(argv[0])) {
 | 
			
		||||
        int32_t s = janet_unwrap_integer(argv[0]);
 | 
			
		||||
        if (s < 0 || s > 9) {
 | 
			
		||||
            janet_panicf("expected user signal between 0 and 9, got %d", s);
 | 
			
		||||
        }
 | 
			
		||||
        sig = JANET_SIGNAL_USER0 + s;
 | 
			
		||||
        janet_signalv(JANET_SIGNAL_USER0 + s, payload);
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetKeyword kw = janet_getkeyword(argv, 0);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "yield")) {
 | 
			
		||||
            sig = JANET_SIGNAL_YIELD;
 | 
			
		||||
        } else if (!janet_cstrcmp(kw, "error")) {
 | 
			
		||||
            sig = JANET_SIGNAL_ERROR;
 | 
			
		||||
        } else if (!janet_cstrcmp(kw, "debug")) {
 | 
			
		||||
            sig = JANET_SIGNAL_DEBUG;
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_panicf("unknown signal, expected :yield, :error, or :debug, got %v", argv[0]);
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(janet_signal_names) / sizeof(char *); i++) {
 | 
			
		||||
            if (!janet_cstrcmp(kw, janet_signal_names[i])) {
 | 
			
		||||
                janet_signalv((JanetSignal) i, payload);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
 | 
			
		||||
    janet_signalv(sig, payload);
 | 
			
		||||
    janet_panicf("unknown signal %v", argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg corelib_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "native", janet_core_native,
 | 
			
		||||
        JDOC("(native path &opt env)\n\n"
 | 
			
		||||
             "Load a native module from the given path. The path "
 | 
			
		||||
             "must be an absolute or relative path on the file system, and is "
 | 
			
		||||
             "usually a .so file on Unix systems, and a .dll file on Windows. "
 | 
			
		||||
             "Returns an environment table that contains functions and other values "
 | 
			
		||||
             "from the native module.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "describe", janet_core_describe,
 | 
			
		||||
        JDOC("(describe x)\n\n"
 | 
			
		||||
             "Returns a string that is a human readable description of a value x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "string", janet_core_string,
 | 
			
		||||
        JDOC("(string & parts)\n\n"
 | 
			
		||||
             "Creates a string by concatenating values together. Values are "
 | 
			
		||||
             "converted to bytes via describe if they are not byte sequences. "
 | 
			
		||||
             "Returns the new string.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "symbol", janet_core_symbol,
 | 
			
		||||
        JDOC("(symbol & xs)\n\n"
 | 
			
		||||
             "Creates a symbol by concatenating values together. Values are "
 | 
			
		||||
             "converted to bytes via describe if they are not byte sequences. Returns "
 | 
			
		||||
             "the new symbol.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "keyword", janet_core_keyword,
 | 
			
		||||
        JDOC("(keyword & xs)\n\n"
 | 
			
		||||
             "Creates a keyword by concatenating values together. Values are "
 | 
			
		||||
             "converted to bytes via describe if they are not byte sequences. Returns "
 | 
			
		||||
             "the new keyword.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer", janet_core_buffer,
 | 
			
		||||
        JDOC("(buffer & xs)\n\n"
 | 
			
		||||
             "Creates a new buffer by concatenating values together. Values are "
 | 
			
		||||
             "converted to bytes via describe if they are not byte sequences. Returns "
 | 
			
		||||
             "the new buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "abstract?", janet_core_is_abstract,
 | 
			
		||||
        JDOC("(abstract? x)\n\n"
 | 
			
		||||
             "Check if x is an abstract type.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "table", janet_core_table,
 | 
			
		||||
        JDOC("(table & kvs)\n\n"
 | 
			
		||||
             "Creates a new table from a variadic number of keys and values. "
 | 
			
		||||
             "kvs is a sequence k1, v1, k2, v2, k3, v3, ... If kvs has "
 | 
			
		||||
             "an odd number of elements, an error will be thrown. Returns the "
 | 
			
		||||
             "new table.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array", janet_core_array,
 | 
			
		||||
        JDOC("(array & items)\n\n"
 | 
			
		||||
             "Create a new array that contains items. Returns the new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "scan-number", janet_core_scannumber,
 | 
			
		||||
        JDOC("(scan-number str)\n\n"
 | 
			
		||||
             "Parse a number from a byte sequence an return that number, either and integer "
 | 
			
		||||
             "or a real. The number "
 | 
			
		||||
             "must be in the same format as numbers in janet source code. Will return nil "
 | 
			
		||||
             "on an invalid number.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tuple", janet_core_tuple,
 | 
			
		||||
        JDOC("(tuple & items)\n\n"
 | 
			
		||||
             "Creates a new tuple that contains items. Returns the new tuple.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "struct", janet_core_struct,
 | 
			
		||||
        JDOC("(struct & kvs)\n\n"
 | 
			
		||||
             "Create a new struct from a sequence of key value pairs. "
 | 
			
		||||
             "kvs is a sequence k1, v1, k2, v2, k3, v3, ... If kvs has "
 | 
			
		||||
             "an odd number of elements, an error will be thrown. Returns the "
 | 
			
		||||
             "new struct.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "gensym", janet_core_gensym,
 | 
			
		||||
        JDOC("(gensym)\n\n"
 | 
			
		||||
             "Returns a new symbol that is unique across the runtime. This means it "
 | 
			
		||||
             "will not collide with any already created symbols during compilation, so "
 | 
			
		||||
             "it can be used in macros to generate automatic bindings.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "gccollect", janet_core_gccollect,
 | 
			
		||||
        JDOC("(gccollect)\n\n"
 | 
			
		||||
             "Run garbage collection. You should probably not call this manually.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "gcsetinterval", janet_core_gcsetinterval,
 | 
			
		||||
        JDOC("(gcsetinterval interval)\n\n"
 | 
			
		||||
             "Set an integer number of bytes to allocate before running garbage collection. "
 | 
			
		||||
             "Low values for interval will be slower but use less memory. "
 | 
			
		||||
             "High values will be faster but use more memory.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "gcinterval", janet_core_gcinterval,
 | 
			
		||||
        JDOC("(gcinterval)\n\n"
 | 
			
		||||
             "Returns the integer number of bytes to allocate before running an iteration "
 | 
			
		||||
             "of garbage collection.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "type", janet_core_type,
 | 
			
		||||
        JDOC("(type x)\n\n"
 | 
			
		||||
             "Returns the type of x as a keyword. x is one of\n"
 | 
			
		||||
             "\t:nil\n"
 | 
			
		||||
             "\t:boolean\n"
 | 
			
		||||
             "\t:number\n"
 | 
			
		||||
             "\t:array\n"
 | 
			
		||||
             "\t:tuple\n"
 | 
			
		||||
             "\t:table\n"
 | 
			
		||||
             "\t:struct\n"
 | 
			
		||||
             "\t:string\n"
 | 
			
		||||
             "\t:buffer\n"
 | 
			
		||||
             "\t:symbol\n"
 | 
			
		||||
             "\t:keyword\n"
 | 
			
		||||
             "\t:function\n"
 | 
			
		||||
             "\t:cfunction\n\n"
 | 
			
		||||
             "or another keyword for an abstract type.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "hash", janet_core_hash,
 | 
			
		||||
        JDOC("(hash value)\n\n"
 | 
			
		||||
             "Gets a hash for any value. The hash is an integer can be used "
 | 
			
		||||
             "as a cheap hash function for all values. If two values are strictly equal, "
 | 
			
		||||
             "then they will have the same hash value.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "getline", janet_core_getline,
 | 
			
		||||
        JDOC("(getline &opt prompt buf env)\n\n"
 | 
			
		||||
             "Reads a line of input into a buffer, including the newline character, using a prompt. "
 | 
			
		||||
             "An optional environment table can be provided for auto-complete. "
 | 
			
		||||
             "Returns the modified buffer. "
 | 
			
		||||
             "Use this function to implement a simple interface for a terminal program.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "dyn", janet_core_dyn,
 | 
			
		||||
        JDOC("(dyn key &opt default)\n\n"
 | 
			
		||||
             "Get a dynamic binding. Returns the default value (or nil) if no binding found.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "setdyn", janet_core_setdyn,
 | 
			
		||||
        JDOC("(setdyn key value)\n\n"
 | 
			
		||||
             "Set a dynamic binding. Returns value.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "trace", janet_core_trace,
 | 
			
		||||
        JDOC("(trace func)\n\n"
 | 
			
		||||
             "Enable tracing on a function. Returns the function.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "untrace", janet_core_untrace,
 | 
			
		||||
        JDOC("(untrace func)\n\n"
 | 
			
		||||
             "Disables tracing on a function. Returns the function.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "module/expand-path", janet_core_expand_path,
 | 
			
		||||
        JDOC("(module/expand-path path template)\n\n"
 | 
			
		||||
             "Expands a path template as found in module/paths for module/find. "
 | 
			
		||||
             "This takes in a path (the argument to require) and a template string, template, "
 | 
			
		||||
             "to expand the path to a path that can be "
 | 
			
		||||
             "used for importing files. The replacements are as follows:\n\n"
 | 
			
		||||
             "\t:all:\tthe value of path verbatim\n"
 | 
			
		||||
             "\t:cur:\tthe current file, or (dyn :current-file)\n"
 | 
			
		||||
             "\t:dir:\tthe directory containing the current file\n"
 | 
			
		||||
             "\t:name:\tthe name component of path, with extension if given\n"
 | 
			
		||||
             "\t:native:\tthe extension used to load natives, .so or .dll\n"
 | 
			
		||||
             "\t:sys:\tthe system path, or (dyn :syspath)")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "int?", janet_core_check_int,
 | 
			
		||||
        JDOC("(int? x)\n\n"
 | 
			
		||||
             "Check if x can be exactly represented as a 32 bit signed two's complement integer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "nat?", janet_core_check_nat,
 | 
			
		||||
        JDOC("(nat? x)\n\n"
 | 
			
		||||
             "Check if x can be exactly represented as a non-negative 32 bit signed two's complement integer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "slice", janet_core_slice,
 | 
			
		||||
        JDOC("(slice x &opt start end)\n\n"
 | 
			
		||||
             "Extract a sub-range of an indexed data structure or byte sequence.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "signal", janet_core_signal,
 | 
			
		||||
        JDOC("(signal what x)\n\n"
 | 
			
		||||
             "Raise a signal with payload x. ")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
JANET_CORE_FN(janet_core_memcmp,
 | 
			
		||||
              "(memcmp a b &opt len offset-a offset-b)",
 | 
			
		||||
              "Compare memory. Takes to byte sequences `a` and `b`, and "
 | 
			
		||||
              "return 0 if they have identical contents, a negative integer if a is less than b, "
 | 
			
		||||
              "and a positive integer if a is greather than b. Optionally take a length and offsets "
 | 
			
		||||
              "to compare slices of the bytes sequences.") {
 | 
			
		||||
    janet_arity(argc, 2, 5);
 | 
			
		||||
    JanetByteView a = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView b = janet_getbytes(argv, 1);
 | 
			
		||||
    int32_t len = janet_optnat(argv, argc, 2, a.len < b.len ? a.len : b.len);
 | 
			
		||||
    int32_t offset_a = janet_optnat(argv, argc, 3, 0);
 | 
			
		||||
    int32_t offset_b = janet_optnat(argv, argc, 4, 0);
 | 
			
		||||
    if (offset_a + len > a.len) janet_panicf("invalid offset-a: %d", offset_a);
 | 
			
		||||
    if (offset_b + len > b.len) janet_panicf("invalid offset-b: %d", offset_b);
 | 
			
		||||
    return janet_wrap_integer(memcmp(a.bytes + offset_a, b.bytes + offset_b, (size_t) len));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
 | 
			
		||||
@@ -734,7 +669,7 @@ static void janet_quick_asm(
 | 
			
		||||
    def->max_arity = max_arity;
 | 
			
		||||
    def->flags = flags;
 | 
			
		||||
    def->slotcount = slots;
 | 
			
		||||
    def->bytecode = malloc(bytecode_size);
 | 
			
		||||
    def->bytecode = janet_malloc(bytecode_size);
 | 
			
		||||
    def->bytecode_length = (int32_t)(bytecode_size / sizeof(uint32_t));
 | 
			
		||||
    def->name = janet_cstring(name);
 | 
			
		||||
    if (!def->bytecode) {
 | 
			
		||||
@@ -927,6 +862,10 @@ static const uint32_t resume_asm[] = {
 | 
			
		||||
    JOP_RESUME | (1 << 24),
 | 
			
		||||
    JOP_RETURN
 | 
			
		||||
};
 | 
			
		||||
static const uint32_t cancel_asm[] = {
 | 
			
		||||
    JOP_CANCEL | (1 << 24),
 | 
			
		||||
    JOP_RETURN
 | 
			
		||||
};
 | 
			
		||||
static const uint32_t in_asm[] = {
 | 
			
		||||
    JOP_IN | (1 << 24),
 | 
			
		||||
    JOP_LOAD_NIL | (3 << 8),
 | 
			
		||||
@@ -982,13 +921,47 @@ static const uint32_t cmp_asm[] = {
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void janet_load_libs(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, corelib_cfuns);
 | 
			
		||||
    JanetRegExt corelib_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("native", janet_core_native),
 | 
			
		||||
        JANET_CORE_REG("describe", janet_core_describe),
 | 
			
		||||
        JANET_CORE_REG("string", janet_core_string),
 | 
			
		||||
        JANET_CORE_REG("symbol", janet_core_symbol),
 | 
			
		||||
        JANET_CORE_REG("keyword", janet_core_keyword),
 | 
			
		||||
        JANET_CORE_REG("buffer", janet_core_buffer),
 | 
			
		||||
        JANET_CORE_REG("abstract?", janet_core_is_abstract),
 | 
			
		||||
        JANET_CORE_REG("table", janet_core_table),
 | 
			
		||||
        JANET_CORE_REG("array", janet_core_array),
 | 
			
		||||
        JANET_CORE_REG("scan-number", janet_core_scannumber),
 | 
			
		||||
        JANET_CORE_REG("tuple", janet_core_tuple),
 | 
			
		||||
        JANET_CORE_REG("struct", janet_core_struct),
 | 
			
		||||
        JANET_CORE_REG("gensym", janet_core_gensym),
 | 
			
		||||
        JANET_CORE_REG("gccollect", janet_core_gccollect),
 | 
			
		||||
        JANET_CORE_REG("gcsetinterval", janet_core_gcsetinterval),
 | 
			
		||||
        JANET_CORE_REG("gcinterval", janet_core_gcinterval),
 | 
			
		||||
        JANET_CORE_REG("type", janet_core_type),
 | 
			
		||||
        JANET_CORE_REG("hash", janet_core_hash),
 | 
			
		||||
        JANET_CORE_REG("getline", janet_core_getline),
 | 
			
		||||
        JANET_CORE_REG("dyn", janet_core_dyn),
 | 
			
		||||
        JANET_CORE_REG("setdyn", janet_core_setdyn),
 | 
			
		||||
        JANET_CORE_REG("trace", janet_core_trace),
 | 
			
		||||
        JANET_CORE_REG("untrace", janet_core_untrace),
 | 
			
		||||
        JANET_CORE_REG("module/expand-path", janet_core_expand_path),
 | 
			
		||||
        JANET_CORE_REG("int?", janet_core_check_int),
 | 
			
		||||
        JANET_CORE_REG("nat?", janet_core_check_nat),
 | 
			
		||||
        JANET_CORE_REG("slice", janet_core_slice),
 | 
			
		||||
        JANET_CORE_REG("signal", janet_core_signal),
 | 
			
		||||
        JANET_CORE_REG("memcmp", janet_core_memcmp),
 | 
			
		||||
        JANET_CORE_REG("getproto", janet_core_getproto),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, corelib_cfuns);
 | 
			
		||||
    janet_lib_io(env);
 | 
			
		||||
    janet_lib_math(env);
 | 
			
		||||
    janet_lib_array(env);
 | 
			
		||||
    janet_lib_tuple(env);
 | 
			
		||||
    janet_lib_buffer(env);
 | 
			
		||||
    janet_lib_table(env);
 | 
			
		||||
    janet_lib_struct(env);
 | 
			
		||||
    janet_lib_fiber(env);
 | 
			
		||||
    janet_lib_os(env);
 | 
			
		||||
    janet_lib_parse(env);
 | 
			
		||||
@@ -1002,18 +975,18 @@ static void janet_load_libs(JanetTable *env) {
 | 
			
		||||
#ifdef JANET_ASSEMBLER
 | 
			
		||||
    janet_lib_asm(env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_TYPED_ARRAY
 | 
			
		||||
    janet_lib_typed_array(env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
    janet_lib_inttypes(env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
    janet_lib_thread(env);
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_lib_ev(env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    janet_lib_net(env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI
 | 
			
		||||
    janet_lib_ffi(env);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
@@ -1064,6 +1037,11 @@ JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
                         "Yield a value to a parent fiber. When a fiber yields, its execution is paused until "
 | 
			
		||||
                         "another thread resumes it. The fiber will then resume, and the last yield call will "
 | 
			
		||||
                         "return the value that was passed to resume."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_CANCEL,
 | 
			
		||||
                    "cancel", 2, 2, 2, 2, cancel_asm, sizeof(cancel_asm),
 | 
			
		||||
                    JDOC("(cancel fiber err)\n\n"
 | 
			
		||||
                         "Resume a fiber but have it immediately raise an error. This lets a programmer unwind a pending fiber. "
 | 
			
		||||
                         "Returns the same result as resume."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_RESUME,
 | 
			
		||||
                    "resume", 2, 1, 2, 2, resume_asm, sizeof(resume_asm),
 | 
			
		||||
                    JDOC("(resume fiber &opt x)\n\n"
 | 
			
		||||
@@ -1174,7 +1152,8 @@ JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
                   "if native modules are compatible with the host program."));
 | 
			
		||||
 | 
			
		||||
    /* Allow references to the environment */
 | 
			
		||||
    janet_def(env, "_env", janet_wrap_table(env), JDOC("The environment table for the current scope."));
 | 
			
		||||
    janet_def(env, "root-env", janet_wrap_table(env),
 | 
			
		||||
              JDOC("The root environment used to create environments with (make-env)."));
 | 
			
		||||
 | 
			
		||||
    janet_load_libs(env);
 | 
			
		||||
    janet_gcroot(janet_wrap_table(env));
 | 
			
		||||
@@ -1185,26 +1164,11 @@ JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
    /* Memoize core env, ignoring replacements the second time around. */
 | 
			
		||||
    if (NULL != janet_vm_core_env) {
 | 
			
		||||
        return janet_vm_core_env;
 | 
			
		||||
    if (NULL != janet_vm.core_env) {
 | 
			
		||||
        return janet_vm.core_env;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Load core cfunctions (and some built in janet assembly functions) */
 | 
			
		||||
    JanetTable *dict = janet_table(300);
 | 
			
		||||
    janet_load_libs(dict);
 | 
			
		||||
 | 
			
		||||
    /* Add replacements */
 | 
			
		||||
    if (replacements != NULL) {
 | 
			
		||||
        for (int32_t i = 0; i < replacements->capacity; i++) {
 | 
			
		||||
            JanetKV kv = replacements->data[i];
 | 
			
		||||
            if (!janet_checktype(kv.key, JANET_NIL)) {
 | 
			
		||||
                janet_table_put(dict, kv.key, kv.value);
 | 
			
		||||
                if (janet_checktype(kv.value, JANET_CFUNCTION)) {
 | 
			
		||||
                    janet_table_put(janet_vm_registry, kv.value, kv.key);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetTable *dict = janet_core_lookup_table(replacements);
 | 
			
		||||
 | 
			
		||||
    /* Unmarshal bytecode */
 | 
			
		||||
    Janet marsh_out = janet_unmarshal(
 | 
			
		||||
@@ -1217,9 +1181,42 @@ JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
    /* Memoize */
 | 
			
		||||
    janet_gcroot(marsh_out);
 | 
			
		||||
    JanetTable *env = janet_unwrap_table(marsh_out);
 | 
			
		||||
    janet_vm_core_env = env;
 | 
			
		||||
    janet_vm.core_env = env;
 | 
			
		||||
 | 
			
		||||
    /* Invert image dict manually here. We can't do this in boot.janet as it
 | 
			
		||||
     * breaks deterministic builds */
 | 
			
		||||
    Janet lidv, midv;
 | 
			
		||||
    lidv = midv = janet_wrap_nil();
 | 
			
		||||
    janet_resolve(env, janet_csymbol("load-image-dict"), &lidv);
 | 
			
		||||
    janet_resolve(env, janet_csymbol("make-image-dict"), &midv);
 | 
			
		||||
    JanetTable *lid = janet_unwrap_table(lidv);
 | 
			
		||||
    JanetTable *mid = janet_unwrap_table(midv);
 | 
			
		||||
    for (int32_t i = 0; i < lid->capacity; i++) {
 | 
			
		||||
        const JanetKV *kv = lid->data + i;
 | 
			
		||||
        if (!janet_checktype(kv->key, JANET_NIL)) {
 | 
			
		||||
            janet_table_put(mid, kv->value, kv->key);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return env;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_core_lookup_table(JanetTable *replacements) {
 | 
			
		||||
    JanetTable *dict = janet_table(512);
 | 
			
		||||
    janet_load_libs(dict);
 | 
			
		||||
 | 
			
		||||
    /* Add replacements */
 | 
			
		||||
    if (replacements != NULL) {
 | 
			
		||||
        for (int32_t i = 0; i < replacements->capacity; i++) {
 | 
			
		||||
            JanetKV kv = replacements->data[i];
 | 
			
		||||
            if (!janet_checktype(kv.key, JANET_NIL)) {
 | 
			
		||||
                janet_table_put(dict, kv.key, kv.value);
 | 
			
		||||
                /* Add replacement functions to registry? */
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return dict;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										219
									
								
								src/core/debug.c
									
									
									
									
									
								
							
							
						
						
									
										219
									
								
								src/core/debug.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,7 +130,6 @@ 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,
 | 
			
		||||
                              janet_status_names[status],
 | 
			
		||||
@@ -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());
 | 
			
		||||
@@ -282,7 +333,21 @@ static Janet doframe(JanetStackFrame *frame) {
 | 
			
		||||
    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 +363,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 +389,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 +401,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) 2022 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) 2022 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3173
									
								
								src/core/ev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3173
									
								
								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) 2022 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,40 @@
 | 
			
		||||
#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__)
 | 
			
		||||
/* Use BSD source on any BSD systems, include OSX */
 | 
			
		||||
# define _BSD_SOURCE
 | 
			
		||||
#else
 | 
			
		||||
/* Use POSIX feature flags */
 | 
			
		||||
# ifndef _POSIX_C_SOURCE
 | 
			
		||||
# define _POSIX_C_SOURCE 200809L
 | 
			
		||||
# endif
 | 
			
		||||
#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 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
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1372
									
								
								src/core/ffi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1372
									
								
								src/core/ffi.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										247
									
								
								src/core/fiber.c
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								src/core/fiber.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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;
 | 
			
		||||
}
 | 
			
		||||
@@ -77,6 +83,10 @@ JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t
 | 
			
		||||
    }
 | 
			
		||||
    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 +95,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 +202,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 +254,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 +338,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 +404,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 +423,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 +442,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 +462,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,7 +476,30 @@ static Janet cfun_fiber_setenv(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_new,
 | 
			
		||||
              "(fiber/new func &opt sigmask)",
 | 
			
		||||
              "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. 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"
 | 
			
		||||
              "* :0-9 - block a specific user signal\n\n"
 | 
			
		||||
              "The sigmask argument also can take environment flags. If any mutually "
 | 
			
		||||
              "exclusive flags are present, the last flag takes precedence.\n\n"
 | 
			
		||||
              "* :i - inherit the environment from the current fiber\n"
 | 
			
		||||
              "* :p - the environment table's prototype is the current environment table") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetFunction *func = janet_getfunction(argv, 0);
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
@@ -480,17 +549,17 @@ static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_MASK_YIELD;
 | 
			
		||||
                        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 +568,53 @@ 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-9) - the fiber is suspended by a user signal\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,7 +625,9 @@ 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_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);
 | 
			
		||||
    JanetFiberStatus s = janet_fiber_status(fiber);
 | 
			
		||||
@@ -549,87 +641,28 @@ static Janet cfun_fiber_can_resume(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_boolean(!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_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) 2022 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										257
									
								
								src/core/gc.c
									
									
									
									
									
								
							
							
						
						
									
										257
									
								
								src/core/gc.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,30 +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 size_t janet_vm_block_count;
 | 
			
		||||
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);
 | 
			
		||||
@@ -71,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 */
 | 
			
		||||
@@ -126,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));
 | 
			
		||||
@@ -136,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);
 | 
			
		||||
@@ -173,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) {
 | 
			
		||||
@@ -225,11 +217,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) {
 | 
			
		||||
@@ -240,6 +235,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);
 | 
			
		||||
@@ -261,6 +258,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;
 | 
			
		||||
@@ -278,13 +281,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);
 | 
			
		||||
@@ -299,18 +302,18 @@ 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);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
@@ -320,25 +323,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_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 */
 | 
			
		||||
@@ -346,8 +385,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) {
 | 
			
		||||
@@ -358,10 +397,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_block_count++;
 | 
			
		||||
    janet_vm.next_collection += size;
 | 
			
		||||
    mem->data.next = janet_vm.blocks;
 | 
			
		||||
    janet_vm.blocks = mem;
 | 
			
		||||
    janet_vm.block_count++;
 | 
			
		||||
 | 
			
		||||
    return (void *)mem;
 | 
			
		||||
}
 | 
			
		||||
@@ -370,15 +409,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) {
 | 
			
		||||
@@ -389,28 +428,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;
 | 
			
		||||
    /* Try and prevent many major collections back to back.
 | 
			
		||||
     * A full collection will take O(janet_vm_block_count) time.
 | 
			
		||||
     * 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);
 | 
			
		||||
    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_NET
 | 
			
		||||
    janet_net_markloop();
 | 
			
		||||
    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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -418,17 +458,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 */
 | 
			
		||||
@@ -449,11 +489,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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -462,12 +502,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -477,44 +517,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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -531,14 +586,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;
 | 
			
		||||
@@ -555,10 +610,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) 2022 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) 2022 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
 | 
			
		||||
@@ -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("bad s64 initializer: %t", 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("bad u64 initializer: %t", 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,13 +407,26 @@ 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); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
@@ -328,7 +435,8 @@ static Janet cfun_it_##type##_##name(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); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
@@ -383,31 +491,14 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_mod(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_abstract(box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_modi(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
 | 
			
		||||
    int64_t 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;
 | 
			
		||||
    }
 | 
			
		||||
    *box = x;
 | 
			
		||||
    *box = (op1 > 0)
 | 
			
		||||
           ? ((op2 > 0) ? x : (0 == x ? x : x + op2))
 | 
			
		||||
           : ((op2 > 0) ? (0 == x ? x : x + op2) : x);
 | 
			
		||||
    return janet_wrap_abstract(box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -418,7 +509,6 @@ 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, %)
 | 
			
		||||
OPMETHOD(int64_t, s64, and, &)
 | 
			
		||||
OPMETHOD(int64_t, s64, or, |)
 | 
			
		||||
OPMETHOD(int64_t, s64, xor, ^)
 | 
			
		||||
@@ -431,7 +521,6 @@ OPMETHOD(uint64_t, u64, mul, *)
 | 
			
		||||
DIVMETHOD(uint64_t, u64, div, /)
 | 
			
		||||
DIVMETHOD(uint64_t, u64, mod, %)
 | 
			
		||||
DIVMETHODINVERT(uint64_t, u64, divi, /)
 | 
			
		||||
DIVMETHODINVERT(uint64_t, u64, modi, %)
 | 
			
		||||
OPMETHOD(uint64_t, u64, and, &)
 | 
			
		||||
OPMETHOD(uint64_t, u64, or, |)
 | 
			
		||||
OPMETHOD(uint64_t, u64, xor, ^)
 | 
			
		||||
@@ -454,9 +543,9 @@ static JanetMethod it_s64_methods[] = {
 | 
			
		||||
    {"/", cfun_it_s64_div},
 | 
			
		||||
    {"r/", cfun_it_s64_divi},
 | 
			
		||||
    {"mod", cfun_it_s64_mod},
 | 
			
		||||
    {"rmod", cfun_it_s64_modi},
 | 
			
		||||
    {"rmod", cfun_it_s64_mod},
 | 
			
		||||
    {"%", cfun_it_s64_rem},
 | 
			
		||||
    {"r%", cfun_it_s64_remi},
 | 
			
		||||
    {"r%", cfun_it_s64_rem},
 | 
			
		||||
    {"&", cfun_it_s64_and},
 | 
			
		||||
    {"r&", cfun_it_s64_and},
 | 
			
		||||
    {"|", cfun_it_s64_or},
 | 
			
		||||
@@ -480,9 +569,9 @@ static JanetMethod it_u64_methods[] = {
 | 
			
		||||
    {"/", cfun_it_u64_div},
 | 
			
		||||
    {"r/", cfun_it_u64_divi},
 | 
			
		||||
    {"mod", cfun_it_u64_mod},
 | 
			
		||||
    {"rmod", cfun_it_u64_modi},
 | 
			
		||||
    {"rmod", cfun_it_u64_mod},
 | 
			
		||||
    {"%", cfun_it_u64_mod},
 | 
			
		||||
    {"r%", cfun_it_u64_modi},
 | 
			
		||||
    {"r%", cfun_it_u64_mod},
 | 
			
		||||
    {"&", cfun_it_u64_and},
 | 
			
		||||
    {"r&", cfun_it_u64_and},
 | 
			
		||||
    {"|", cfun_it_u64_or},
 | 
			
		||||
@@ -496,6 +585,16 @@ static JanetMethod it_u64_methods[] = {
 | 
			
		||||
    {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 +609,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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										503
									
								
								src/core/io.c
									
									
									
									
									
								
							
							
						
						
									
										503
									
								
								src/core/io.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -39,6 +39,7 @@ 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",
 | 
			
		||||
@@ -48,7 +49,11 @@ const JanetAbstractType janet_file_type = {
 | 
			
		||||
    NULL,
 | 
			
		||||
    io_file_marshal,
 | 
			
		||||
    io_file_unmarshal,
 | 
			
		||||
    JANET_ATEND_UNMARSHAL
 | 
			
		||||
    NULL, /* tostring */
 | 
			
		||||
    NULL, /* compare */
 | 
			
		||||
    NULL, /* hash */
 | 
			
		||||
    io_file_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Check arguments to fopen */
 | 
			
		||||
@@ -56,8 +61,8 @@ 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);
 | 
			
		||||
@@ -75,7 +80,7 @@ static int32_t checkflags(const uint8_t *str) {
 | 
			
		||||
    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;
 | 
			
		||||
@@ -85,6 +90,10 @@ static int32_t checkflags(const uint8_t *str) {
 | 
			
		||||
                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;
 | 
			
		||||
@@ -103,36 +112,10 @@ static void *makef(FILE *f, int32_t flags) {
 | 
			
		||||
    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;
 | 
			
		||||
    int32_t 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 janet_makefile(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.") {
 | 
			
		||||
    (void)argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    // XXX use mkostemp when we can to avoid CLOEXEC race.
 | 
			
		||||
@@ -142,7 +125,20 @@ 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;
 | 
			
		||||
@@ -155,7 +151,9 @@ static Janet cfun_io_fopen(int32_t argc, Janet *argv) {
 | 
			
		||||
        flags = JANET_FILE_READ;
 | 
			
		||||
    }
 | 
			
		||||
    FILE *f = fopen((const char *)fname, (const char *)fmode);
 | 
			
		||||
    return f ? janet_makefile(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. */
 | 
			
		||||
@@ -171,7 +169,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");
 | 
			
		||||
@@ -211,7 +218,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)
 | 
			
		||||
@@ -234,7 +244,10 @@ static Janet cfun_io_fwrite(int32_t argc, Janet *argv) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Flush the bytes in the file */
 | 
			
		||||
static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
 | 
			
		||||
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);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
@@ -247,54 +260,58 @@ static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
        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)
 | 
			
		||||
@@ -336,9 +353,15 @@ 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
 | 
			
		||||
@@ -388,26 +411,38 @@ 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);
 | 
			
		||||
@@ -418,7 +453,7 @@ static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
 | 
			
		||||
            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)) {
 | 
			
		||||
@@ -431,7 +466,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);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -440,41 +479,85 @@ 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);
 | 
			
		||||
@@ -486,37 +569,67 @@ static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    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)) {
 | 
			
		||||
@@ -535,14 +648,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);
 | 
			
		||||
@@ -577,6 +694,16 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
 | 
			
		||||
            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;
 | 
			
		||||
@@ -585,145 +712,22 @@ 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 */
 | 
			
		||||
 | 
			
		||||
JanetFile *janet_getjfile(const Janet *argv, int32_t n) {
 | 
			
		||||
    return janet_getabstract(argv, n, &janet_file_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) {
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, n, &janet_file_type);
 | 
			
		||||
    if (NULL != flags) *flags = iof->flags;
 | 
			
		||||
    return iof->file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetFile *janet_makejfile(FILE *f, int flags) {
 | 
			
		||||
    return makef(f, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_makefile(FILE *f, int flags) {
 | 
			
		||||
    return janet_wrap_abstract(makef(f, flags));
 | 
			
		||||
}
 | 
			
		||||
@@ -740,19 +744,44 @@ 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_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",
 | 
			
		||||
                   janet_makefile(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",
 | 
			
		||||
                   janet_makefile(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",
 | 
			
		||||
                   janet_makefile(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.");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										234
									
								
								src/core/marsh.c
									
									
									
									
									
								
							
							
						
						
									
										234
									
								
								src/core/marsh.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,11 @@ 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 */
 | 
			
		||||
#endif
 | 
			
		||||
} LeadBytes;
 | 
			
		||||
 | 
			
		||||
/* Helper to look inside an entry in an environment */
 | 
			
		||||
@@ -285,8 +290,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) {
 | 
			
		||||
@@ -325,6 +330,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) {
 | 
			
		||||
@@ -359,16 +365,33 @@ void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) {
 | 
			
		||||
 | 
			
		||||
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++));
 | 
			
		||||
    if (st->maybe_cycles) {
 | 
			
		||||
        janet_table_put(&st->seen,
 | 
			
		||||
                        janet_wrap_abstract(abstract),
 | 
			
		||||
                        janet_wrap_integer(st->nextid++));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MARK_SEEN() \
 | 
			
		||||
    janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++))
 | 
			
		||||
    do { if (st->maybe_cycles) janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); } while (0)
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
@@ -376,7 +399,7 @@ static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
        JanetMarshalContext context = {st, NULL, flags, 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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -408,11 +431,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);
 | 
			
		||||
@@ -523,8 +549,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;
 | 
			
		||||
@@ -542,9 +570,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;
 | 
			
		||||
@@ -590,6 +619,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);
 | 
			
		||||
@@ -737,7 +767,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -833,7 +863,7 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
 | 
			
		||||
        /* 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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -845,7 +875,7 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
        def->constants_length = constants_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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -854,7 +884,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -868,7 +898,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -883,7 +913,7 @@ 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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -899,7 +929,7 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
        /* Unmarshal closure bitset if needed */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASCLOBITSET) {
 | 
			
		||||
            int32_t n = (def->slotcount + 31) >> 5;
 | 
			
		||||
            def->closure_bitset = malloc(sizeof(uint32_t) * (size_t) n);
 | 
			
		||||
            def->closure_bitset = janet_malloc(sizeof(uint32_t) * (size_t) n);
 | 
			
		||||
            if (NULL == def->closure_bitset) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -934,6 +964,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));
 | 
			
		||||
@@ -955,7 +991,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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1040,6 +1076,9 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
        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;
 | 
			
		||||
@@ -1048,6 +1087,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;
 | 
			
		||||
@@ -1092,14 +1136,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1107,17 +1155,16 @@ 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));
 | 
			
		||||
        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(
 | 
			
		||||
@@ -1219,13 +1266,19 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
        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;
 | 
			
		||||
            *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;
 | 
			
		||||
@@ -1238,6 +1291,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 */
 | 
			
		||||
@@ -1267,9 +1321,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);
 | 
			
		||||
                    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);
 | 
			
		||||
@@ -1338,6 +1398,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,
 | 
			
		||||
@@ -1345,7 +1441,6 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#undef EXTRA
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_unmarshal(
 | 
			
		||||
@@ -1372,16 +1467,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);
 | 
			
		||||
    }
 | 
			
		||||
@@ -1390,11 +1497,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;
 | 
			
		||||
@@ -1404,35 +1518,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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										466
									
								
								src/core/math.c
									
									
									
									
									
								
							
							
						
						
									
										466
									
								
								src/core/math.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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 random integer in the range [0, max] from the RNG. 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,315 +223,201 @@ 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_MATHOP(name, fop, doc)\
 | 
			
		||||
JANET_CORE_FN(janet_##name, "(math/" #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)
 | 
			
		||||
JANET_DEFINE_MATHOP(acos, acos, "Returns the arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asin, asin, "Returns the arcsin of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atan, atan, "Returns the arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cos, cos, "Returns the cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cosh, cosh, "Returns the hyperbolic cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(acosh, acosh, "Returns the hyperbolic arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sin, sin, "Returns the sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sinh, sinh, "Returns the hyperbolic sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asinh, asinh, "Returns the hypberbolic arcsine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tan, tan, "Returns the tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tanh, tanh, "Returns the hyperbolic tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atanh, atanh, "Returns the hyperbolic arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp, exp, "Returns e to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp2, exp2, "Returns 2 to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(expm1, expm1, "Returns e to the power of x minus 1.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log, log, "Returns the natural logarithm of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log10, log10, "Returns the log base 10 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log2, log2, "Returns the log base 2 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sqrt, sqrt, "Returns the square root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cbrt, cbrt, "Returns the cube root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(ceil, ceil, "Returns the smallest integer value number that is not less than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(fabs, fabs, "Return the absolute value of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(floor, floor, "Returns the largest integer value number that is not greater than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(trunc, trunc, "Returns the integer between x and 0 nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(round, round, "Returns the integer nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(gamma, tgamma, "Returns gamma(x).")
 | 
			
		||||
JANET_DEFINE_MATHOP(lgamma, lgamma, "Returns log-gamma(x).")
 | 
			
		||||
JANET_DEFINE_MATHOP(log1p, log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
 | 
			
		||||
JANET_DEFINE_MATHOP(erf, erf, "Returns the error function of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(erfc, erfc, "Returns the complementary error function of x.")
 | 
			
		||||
 | 
			
		||||
#define JANET_DEFINE_MATH2OP(name, fop)\
 | 
			
		||||
static Janet janet_##name(int32_t argc, Janet *argv) {\
 | 
			
		||||
#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 vaue 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_gamma),
 | 
			
		||||
        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_def(env, "math/int32-min", janet_wrap_number(INT32_MIN),
 | 
			
		||||
              JDOC("The maximum contiguous integer representable by a 32 bit signed integer"));
 | 
			
		||||
    janet_def(env, "math/int32-max", janet_wrap_number(INT32_MAX),
 | 
			
		||||
              JDOC("The minimum contiguous integer represtenable by a 32 bit signed integer"));
 | 
			
		||||
    janet_def(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
 | 
			
		||||
              JDOC("The maximum contiguous integer representable by a double (2^53)"));
 | 
			
		||||
    janet_def(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
 | 
			
		||||
              JDOC("The minimum contiguous integer represtenable by a double (-(2^53))"));
 | 
			
		||||
    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_def(env, "math/nan", janet_wrap_number(NAN),
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#else
 | 
			
		||||
    janet_def(env, "math/nan", janet_wrap_number(0.0 / 0.0),
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(0.0 / 0.0), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#endif
 | 
			
		||||
              JDOC("Not a number (IEEE-754 NaN)"));
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1260
									
								
								src/core/net.c
									
									
									
									
									
								
							
							
						
						
									
										1260
									
								
								src/core/net.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1611
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										1611
									
								
								src/core/os.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										406
									
								
								src/core/parse.c
									
									
									
									
									
								
							
							
						
						
									
										406
									
								
								src/core/parse.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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:
 | 
			
		||||
@@ -313,11 +351,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 +442,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 +453,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 +473,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 +613,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 +643,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 +664,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 +715,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 +749,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 +772,7 @@ Janet janet_parser_produce(JanetParser *parser) {
 | 
			
		||||
    }
 | 
			
		||||
    parser->pending--;
 | 
			
		||||
    parser->argcount--;
 | 
			
		||||
    parser->states[0].argn--;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -726,9 +797,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 +826,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 +874,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 +903,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 +932,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 +957,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 +984,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 +1002,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 +1029,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 +1046,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 +1107,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));
 | 
			
		||||
    }
 | 
			
		||||
@@ -1061,11 +1204,14 @@ 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->args + p->argcount;
 | 
			
		||||
    for (int32_t i = count - 1; i >= 0; --i) {
 | 
			
		||||
        JanetParseState *s = p->states + i;
 | 
			
		||||
        if (s->flags & PFLAG_CONTAINER) {
 | 
			
		||||
            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 +1222,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 +1259,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 +1293,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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										467
									
								
								src/core/peg.c
									
									
									
									
									
								
							
							
						
						
									
										467
									
								
								src/core/peg.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,24 +261,46 @@ 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:
 | 
			
		||||
@@ -212,17 +309,18 @@ tail:
 | 
			
		||||
            const uint8_t *next_text;
 | 
			
		||||
            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[2]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -876,6 +1107,36 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    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");
 | 
			
		||||
            }
 | 
			
		||||
@@ -1006,6 +1277,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 +1412,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 +1432,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 +1481,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 +1527,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 +1548,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 +1590,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 +1598,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,7 +1622,11 @@ 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);
 | 
			
		||||
@@ -1348,26 +1672,37 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_match(int32_t argc, Janet *argv) {
 | 
			
		||||
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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_find(int32_t argc, Janet *argv) {
 | 
			
		||||
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);
 | 
			
		||||
@@ -1377,7 +1712,9 @@ static Janet cfun_peg_find(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_find_all(int32_t argc, Janet *argv) {
 | 
			
		||||
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++) {
 | 
			
		||||
@@ -1416,11 +1753,16 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
 | 
			
		||||
    return janet_wrap_buffer(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_replace_all(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace_all,
 | 
			
		||||
              "(peg/replace-all peg repl text &opt start & args)",
 | 
			
		||||
              "Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.") {
 | 
			
		||||
    return cfun_peg_replace_generic(argc, argv, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_replace(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace,
 | 
			
		||||
              "(peg/replace peg repl text &opt start & args)",
 | 
			
		||||
              "Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. "
 | 
			
		||||
              "If no matches are found, returns the input string in a new buffer.") {
 | 
			
		||||
    return cfun_peg_replace_generic(argc, argv, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1440,46 +1782,23 @@ static int cfun_peg_getter(JanetAbstract a, Janet key, Janet *out) {
 | 
			
		||||
    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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/find", cfun_peg_find,
 | 
			
		||||
        JDOC("(peg/find peg text &opt start & args)\n\n"
 | 
			
		||||
             "Find first index where the peg matches in text. Returns an integer, or nil if not found.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/find-all", cfun_peg_find_all,
 | 
			
		||||
        JDOC("(peg/find-all peg text &opt start & args)\n\n"
 | 
			
		||||
             "Find all indexes where the peg matches in text. Returns an array of integers.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/replace", cfun_peg_replace,
 | 
			
		||||
        JDOC("(peg/replace peg repl text &opt start & args)\n\n"
 | 
			
		||||
             "Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. "
 | 
			
		||||
             "If no matches are found, returns the input string in a new buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/replace-all", cfun_peg_replace_all,
 | 
			
		||||
        JDOC("(peg/replace-all peg repl text &opt start & args)\n\n"
 | 
			
		||||
             "Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.")
 | 
			
		||||
    },
 | 
			
		||||
    {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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										148
									
								
								src/core/pp.c
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								src/core/pp.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,7 +42,14 @@ static void number_to_string_b(JanetBuffer *buffer, double x) {
 | 
			
		||||
    const char *fmt = (x == floor(x) &&
 | 
			
		||||
                       x <= JANET_INTMAX_DOUBLE &&
 | 
			
		||||
                       x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g";
 | 
			
		||||
    int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, fmt, x);
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -220,12 +227,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -252,21 +261,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;
 | 
			
		||||
}
 | 
			
		||||
@@ -344,6 +345,9 @@ struct pretty {
 | 
			
		||||
    int indent;
 | 
			
		||||
    int flags;
 | 
			
		||||
    int32_t bufstartlen;
 | 
			
		||||
    int32_t *keysort_buffer;
 | 
			
		||||
    int32_t keysort_capacity;
 | 
			
		||||
    int32_t keysort_start;
 | 
			
		||||
    JanetTable seen;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -454,7 +458,7 @@ static const char *janet_pretty_colors[] = {
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m"
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[35m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
@@ -558,14 +562,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)) {
 | 
			
		||||
@@ -578,8 +599,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;
 | 
			
		||||
@@ -587,31 +608,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 + 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++;
 | 
			
		||||
@@ -634,6 +679,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);
 | 
			
		||||
@@ -656,6 +704,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);
 | 
			
		||||
@@ -711,8 +762,7 @@ static const char *scanformat(
 | 
			
		||||
    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)))
 | 
			
		||||
@@ -815,7 +865,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');
 | 
			
		||||
@@ -841,7 +891,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);
 | 
			
		||||
        }
 | 
			
		||||
@@ -932,8 +982,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 {
 | 
			
		||||
@@ -955,6 +1006,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':
 | 
			
		||||
@@ -964,7 +1018,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');
 | 
			
		||||
@@ -990,7 +1044,7 @@ 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);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,7 +36,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 +70,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 +87,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;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Run a string */
 | 
			
		||||
@@ -50,7 +51,7 @@ 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;
 | 
			
		||||
                }
 | 
			
		||||
@@ -58,7 +59,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
                ret = janet_wrap_string(cres.error);
 | 
			
		||||
                if (cres.macrofiber) {
 | 
			
		||||
                    janet_eprintf("compile error in %s: ", sourcePath);
 | 
			
		||||
                    janet_stacktrace(cres.macrofiber, ret);
 | 
			
		||||
                    janet_stacktrace_ext(cres.macrofiber, ret, "");
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf("compile error in %s: %s\n", sourcePath,
 | 
			
		||||
                                  (const char *)cres.error);
 | 
			
		||||
@@ -79,7 +80,9 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
                const char *e = janet_parser_error(&parser);
 | 
			
		||||
                errflags |= 0x04;
 | 
			
		||||
                ret = janet_cstringv(e);
 | 
			
		||||
                janet_eprintf("parse error in %s: %s\n", sourcePath, 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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -98,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;
 | 
			
		||||
}
 | 
			
		||||
@@ -108,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) 2022 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]);
 | 
			
		||||
@@ -40,7 +40,7 @@ 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 (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 +62,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);
 | 
			
		||||
@@ -82,7 +84,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 +93,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 +102,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 +117,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 +143,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 +156,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);
 | 
			
		||||
 | 
			
		||||
                    c->buffer[label_loop_cond_jump] |= (label_loop_exit - label_loop_cond_jump) << 16;
 | 
			
		||||
                    c->buffer[label_loop_loop] |= (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 {
 | 
			
		||||
@@ -239,11 +302,17 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
static JanetTable *handleattr(JanetCompiler *c, 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>";
 | 
			
		||||
    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,6 +320,9 @@ 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;
 | 
			
		||||
@@ -295,8 +367,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)));
 | 
			
		||||
@@ -312,10 +396,11 @@ static int varleaf(
 | 
			
		||||
static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    Janet head;
 | 
			
		||||
    JanetTable *attr_table = handleattr(c, argn, argv);
 | 
			
		||||
    JanetSlot ret = dohead(c, opts, &head, 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));
 | 
			
		||||
    destructure(c, argv[0], ret, varleaf, attr_table);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -328,28 +413,44 @@ 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;
 | 
			
		||||
    JanetTable *attr_table = handleattr(c, argn, argv);
 | 
			
		||||
    JanetSlot ret = dohead(c, opts, &head, 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));
 | 
			
		||||
    destructure(c, argv[0], ret, defleaf, attr_table);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -409,7 +510,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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -470,6 +573,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;
 | 
			
		||||
        }
 | 
			
		||||
        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;
 | 
			
		||||
@@ -700,6 +825,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;
 | 
			
		||||
@@ -724,6 +850,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]);
 | 
			
		||||
@@ -731,49 +860,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));
 | 
			
		||||
@@ -792,6 +947,14 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -854,6 +1017,7 @@ static const JanetSpecial janetc_specials[] = {
 | 
			
		||||
    {"set", janetc_varset},
 | 
			
		||||
    {"splice", janetc_splice},
 | 
			
		||||
    {"unquote", janetc_unquote},
 | 
			
		||||
    {"upscope", janetc_upscope},
 | 
			
		||||
    {"var", janetc_var},
 | 
			
		||||
    {"while", janetc_while}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose and contributors
 | 
			
		||||
* Copyright (c) 2022 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										212
									
								
								src/core/state.h
									
									
									
									
									
								
							
							
						
						
									
										212
									
								
								src/core/state.h
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,80 +25,160 @@
 | 
			
		||||
 | 
			
		||||
#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. */
 | 
			
		||||
typedef int64_t JanetTimestamp;
 | 
			
		||||
 | 
			
		||||
typedef struct JanetScratch JanetScratch;
 | 
			
		||||
typedef struct JanetScratch {
 | 
			
		||||
    JanetScratchFinalizer finalize;
 | 
			
		||||
    long long mem[]; /* for proper alignment */
 | 
			
		||||
} JanetScratch;
 | 
			
		||||
 | 
			
		||||
/* Top level dynamic bindings */
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetTable *janet_vm_top_dyns;
 | 
			
		||||
 | 
			
		||||
/* Cache the core environment */
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetTable *janet_vm_core_env;
 | 
			
		||||
 | 
			
		||||
/* 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 size_t janet_vm_block_count;
 | 
			
		||||
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 - containts 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 janet_run. */
 | 
			
		||||
    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 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;
 | 
			
		||||
 | 
			
		||||
    /* 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) */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    void **iocp;
 | 
			
		||||
#elif defined(JANET_EV_EPOLL)
 | 
			
		||||
    JanetHandle selfpipe[2];
 | 
			
		||||
    int epoll;
 | 
			
		||||
    int timerfd;
 | 
			
		||||
    int timer_enabled;
 | 
			
		||||
#elif defined(JANET_EV_KQUEUE)
 | 
			
		||||
    JanetHandle selfpipe[2];
 | 
			
		||||
    int kq;
 | 
			
		||||
    int timer;
 | 
			
		||||
    int timer_enabled;
 | 
			
		||||
#else
 | 
			
		||||
    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) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -108,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,25 +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_symbol_slice(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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_keyword_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_repeat(int32_t argc, Janet *argv) {
 | 
			
		||||
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);
 | 
			
		||||
@@ -204,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);
 | 
			
		||||
@@ -215,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++) {
 | 
			
		||||
@@ -225,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);
 | 
			
		||||
@@ -240,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);
 | 
			
		||||
@@ -255,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);
 | 
			
		||||
@@ -279,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);
 | 
			
		||||
@@ -290,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);
 | 
			
		||||
@@ -299,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);
 | 
			
		||||
@@ -310,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);
 | 
			
		||||
@@ -344,7 +384,10 @@ static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
 | 
			
		||||
    s->substlen = subst.len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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`. "
 | 
			
		||||
              "Will return the new string if `patt` is found, otherwise returns `str`.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct replace_state s;
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
@@ -364,7 +407,11 @@ static Janet cfun_string_replace(int32_t argc, Janet *argv) {
 | 
			
		||||
    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. "
 | 
			
		||||
              "Will return the new string if `patt` is found, otherwise returns `str`.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct replace_state s;
 | 
			
		||||
    JanetBuffer b;
 | 
			
		||||
@@ -384,7 +431,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;
 | 
			
		||||
@@ -398,6 +451,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));
 | 
			
		||||
@@ -405,7 +459,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);
 | 
			
		||||
@@ -427,7 +485,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;
 | 
			
		||||
@@ -467,7 +528,10 @@ 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.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(0);
 | 
			
		||||
    const char *strfrmt = (const char *) janet_getstring(argv, 0);
 | 
			
		||||
@@ -507,7 +571,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);
 | 
			
		||||
@@ -517,163 +584,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. ")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "keyword/slice", cfun_keyword_slice,
 | 
			
		||||
        JDOC("(keyword/slice bytes &opt start end)\n\n"
 | 
			
		||||
             "Same a string/slice, but returns a keyword.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "symbol/slice", cfun_symbol_slice,
 | 
			
		||||
        JDOC("(symbol/slice bytes &opt start end)\n\n"
 | 
			
		||||
             "Same a string/slice, but returns a symbol.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "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) 2022 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) 2022 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) 2022 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,10 +100,10 @@ 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:
 | 
			
		||||
@@ -121,15 +117,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 +141,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 +161,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 +190,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 +220,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) 2022 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										217
									
								
								src/core/table.c
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								src/core/table.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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
 | 
			
		||||
@@ -67,14 +67,23 @@ static JanetTable *janet_table_init_impl(JanetTable *table, int32_t capacity, in
 | 
			
		||||
    return table;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize a table */
 | 
			
		||||
/* Initialize a table (for use withs scratch memory) */
 | 
			
		||||
JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
 | 
			
		||||
    return janet_table_init_impl(table, capacity, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize a table without using scratch memory */
 | 
			
		||||
JanetTable *janet_table_init_raw(JanetTable *table, int32_t capacity) {
 | 
			
		||||
    return janet_table_init_impl(table, capacity, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Deinitialize a table */
 | 
			
		||||
void janet_table_deinit(JanetTable *table) {
 | 
			
		||||
    janet_sfree(table->data);
 | 
			
		||||
    if (table->gc.flags & JANET_TABLE_FLAG_STACK) {
 | 
			
		||||
        janet_sfree(table->data);
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_free(table->data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Create a new table */
 | 
			
		||||
@@ -117,43 +126,27 @@ static void janet_table_rehash(JanetTable *t, int32_t size) {
 | 
			
		||||
    if (islocal) {
 | 
			
		||||
        janet_sfree(olddata);
 | 
			
		||||
    } else {
 | 
			
		||||
        free(olddata);
 | 
			
		||||
        janet_free(olddata);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a value out of the table */
 | 
			
		||||
Janet janet_table_get(JanetTable *t, Janet key) {
 | 
			
		||||
    JanetKV *bucket = janet_table_find(t, key);
 | 
			
		||||
    if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
 | 
			
		||||
        return bucket->value;
 | 
			
		||||
    /* Check prototypes */
 | 
			
		||||
    {
 | 
			
		||||
        int i;
 | 
			
		||||
        for (i = JANET_MAX_PROTO_DEPTH, t = t->proto; t && i; t = t->proto, --i) {
 | 
			
		||||
            bucket = janet_table_find(t, key);
 | 
			
		||||
            if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
 | 
			
		||||
                return bucket->value;
 | 
			
		||||
        }
 | 
			
		||||
    for (int i = JANET_MAX_PROTO_DEPTH; t && i; t = t->proto, --i) {
 | 
			
		||||
        JanetKV *bucket = janet_table_find(t, key);
 | 
			
		||||
        if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
 | 
			
		||||
            return bucket->value;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a value out of the table, and record which prototype it was from. */
 | 
			
		||||
Janet janet_table_get_ex(JanetTable *t, Janet key, JanetTable **which) {
 | 
			
		||||
    JanetKV *bucket = janet_table_find(t, key);
 | 
			
		||||
    if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
 | 
			
		||||
        *which = t;
 | 
			
		||||
        return bucket->value;
 | 
			
		||||
    }
 | 
			
		||||
    /* Check prototypes */
 | 
			
		||||
    {
 | 
			
		||||
        int i;
 | 
			
		||||
        for (i = JANET_MAX_PROTO_DEPTH, t = t->proto; t && i; t = t->proto, --i) {
 | 
			
		||||
            bucket = janet_table_find(t, key);
 | 
			
		||||
            if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
 | 
			
		||||
                *which = t;
 | 
			
		||||
                return bucket->value;
 | 
			
		||||
            }
 | 
			
		||||
    for (int i = JANET_MAX_PROTO_DEPTH; t && i; t = t->proto, --i) {
 | 
			
		||||
        JanetKV *bucket = janet_table_find(t, key);
 | 
			
		||||
        if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
 | 
			
		||||
            *which = t;
 | 
			
		||||
            return bucket->value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
@@ -208,6 +201,23 @@ void janet_table_put(JanetTable *t, Janet key, Janet value) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Used internally so don't check arguments
 | 
			
		||||
 * Put into a table, but if the key already exists do nothing. */
 | 
			
		||||
static void janet_table_put_no_overwrite(JanetTable *t, Janet key, Janet value) {
 | 
			
		||||
    JanetKV *bucket = janet_table_find(t, key);
 | 
			
		||||
    if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL))
 | 
			
		||||
        return;
 | 
			
		||||
    if (NULL == bucket || 2 * (t->count + t->deleted + 1) > t->capacity) {
 | 
			
		||||
        janet_table_rehash(t, janet_tablen(2 * t->count + 2));
 | 
			
		||||
    }
 | 
			
		||||
    bucket = janet_table_find(t, key);
 | 
			
		||||
    if (janet_checktype(bucket->value, JANET_BOOLEAN))
 | 
			
		||||
        --t->deleted;
 | 
			
		||||
    bucket->key = key;
 | 
			
		||||
    bucket->value = value;
 | 
			
		||||
    ++t->count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clear a table */
 | 
			
		||||
void janet_table_clear(JanetTable *t) {
 | 
			
		||||
    int32_t capacity = t->capacity;
 | 
			
		||||
@@ -217,19 +227,6 @@ void janet_table_clear(JanetTable *t) {
 | 
			
		||||
    t->deleted = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert table to struct */
 | 
			
		||||
const JanetKV *janet_table_to_struct(JanetTable *t) {
 | 
			
		||||
    JanetKV *st = janet_struct_begin(t->count);
 | 
			
		||||
    JanetKV *kv = t->data;
 | 
			
		||||
    JanetKV *end = t->data + t->capacity;
 | 
			
		||||
    while (kv < end) {
 | 
			
		||||
        if (!janet_checktype(kv->key, JANET_NIL))
 | 
			
		||||
            janet_struct_put(st, kv->key, kv->value);
 | 
			
		||||
        kv++;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_struct_end(st);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clone a table. */
 | 
			
		||||
JanetTable *janet_table_clone(JanetTable *table) {
 | 
			
		||||
    JanetTable *newTable = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
 | 
			
		||||
@@ -237,7 +234,7 @@ JanetTable *janet_table_clone(JanetTable *table) {
 | 
			
		||||
    newTable->capacity = table->capacity;
 | 
			
		||||
    newTable->deleted = table->deleted;
 | 
			
		||||
    newTable->proto = table->proto;
 | 
			
		||||
    newTable->data = malloc(newTable->capacity * sizeof(JanetKV));
 | 
			
		||||
    newTable->data = janet_malloc(newTable->capacity * sizeof(JanetKV));
 | 
			
		||||
    if (NULL == newTable->data) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -256,7 +253,7 @@ static void janet_table_mergekv(JanetTable *table, const JanetKV *kvs, int32_t c
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Merge a table other into another table */
 | 
			
		||||
/* Merge a table into another table */
 | 
			
		||||
void janet_table_merge_table(JanetTable *table, JanetTable *other) {
 | 
			
		||||
    janet_table_mergekv(table, other->data, other->capacity);
 | 
			
		||||
}
 | 
			
		||||
@@ -266,15 +263,51 @@ void janet_table_merge_struct(JanetTable *table, const JanetKV *other) {
 | 
			
		||||
    janet_table_mergekv(table, other, janet_struct_capacity(other));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Convert table to struct */
 | 
			
		||||
const JanetKV *janet_table_to_struct(JanetTable *t) {
 | 
			
		||||
    JanetKV *st = janet_struct_begin(t->count);
 | 
			
		||||
    JanetKV *kv = t->data;
 | 
			
		||||
    JanetKV *end = t->data + t->capacity;
 | 
			
		||||
    while (kv < end) {
 | 
			
		||||
        if (!janet_checktype(kv->key, JANET_NIL))
 | 
			
		||||
            janet_struct_put(st, kv->key, kv->value);
 | 
			
		||||
        kv++;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_struct_end(st);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_table_proto_flatten(JanetTable *t) {
 | 
			
		||||
    JanetTable *newTable = janet_table(0);
 | 
			
		||||
    while (t) {
 | 
			
		||||
        JanetKV *kv = t->data;
 | 
			
		||||
        JanetKV *end = t->data + t->capacity;
 | 
			
		||||
        while (kv < end) {
 | 
			
		||||
            if (!janet_checktype(kv->key, JANET_NIL))
 | 
			
		||||
                janet_table_put_no_overwrite(newTable, kv->key, kv->value);
 | 
			
		||||
            kv++;
 | 
			
		||||
        }
 | 
			
		||||
        t = t->proto;
 | 
			
		||||
    }
 | 
			
		||||
    return newTable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* C Functions */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_table_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_table_new,
 | 
			
		||||
              "(table/new capacity)",
 | 
			
		||||
              "Creates a new empty table with pre-allocated memory "
 | 
			
		||||
              "for `capacity` entries. This means that if one knows the number of "
 | 
			
		||||
              "entries going into a table on creation, extra memory allocation "
 | 
			
		||||
              "can be avoided. Returns the new table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getinteger(argv, 0);
 | 
			
		||||
    int32_t cap = janet_getnat(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_table(cap));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_table_getproto(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_table_getproto,
 | 
			
		||||
              "(table/getproto tab)",
 | 
			
		||||
              "Get the prototype table of a table. Returns nil if the table "
 | 
			
		||||
              "has no prototype, otherwise returns the prototype.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTable *t = janet_gettable(argv, 0);
 | 
			
		||||
    return t->proto
 | 
			
		||||
@@ -282,7 +315,9 @@ static Janet cfun_table_getproto(int32_t argc, Janet *argv) {
 | 
			
		||||
           : janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_table_setproto(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_table_setproto,
 | 
			
		||||
              "(table/setproto tab proto)",
 | 
			
		||||
              "Set the prototype of a table. Returns the original table `tab`.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetTable *table = janet_gettable(argv, 0);
 | 
			
		||||
    JanetTable *proto = NULL;
 | 
			
		||||
@@ -293,67 +328,63 @@ static Janet cfun_table_setproto(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_table_tostruct(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_table_tostruct,
 | 
			
		||||
              "(table/to-struct tab)",
 | 
			
		||||
              "Convert a table to a struct. Returns a new struct. This function "
 | 
			
		||||
              "does not take into account prototype tables.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTable *t = janet_gettable(argv, 0);
 | 
			
		||||
    return janet_wrap_struct(janet_table_to_struct(t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_table_rawget(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_table_rawget,
 | 
			
		||||
              "(table/rawget tab key)",
 | 
			
		||||
              "Gets a value from a table `tab` without looking at the prototype table. "
 | 
			
		||||
              "If `tab` does not contain the key directly, the function will return "
 | 
			
		||||
              "nil without checking the prototype. Returns the value in the table.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetTable *table = janet_gettable(argv, 0);
 | 
			
		||||
    return janet_table_rawget(table, argv[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_table_clone(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_table_clone,
 | 
			
		||||
              "(table/clone tab)",
 | 
			
		||||
              "Create a copy of a table. Updates to the new table will not change the old table, "
 | 
			
		||||
              "and vice versa.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTable *table = janet_gettable(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_table_clone(table));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg table_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "table/new", cfun_table_new,
 | 
			
		||||
        JDOC("(table/new capacity)\n\n"
 | 
			
		||||
             "Creates a new empty table with pre-allocated memory "
 | 
			
		||||
             "for capacity entries. This means that if one knows the number of "
 | 
			
		||||
             "entries going to go in a table on creation, extra memory allocation "
 | 
			
		||||
             "can be avoided. Returns the new table.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "table/to-struct", cfun_table_tostruct,
 | 
			
		||||
        JDOC("(table/to-struct tab)\n\n"
 | 
			
		||||
             "Convert a table to a struct. Returns a new struct. This function "
 | 
			
		||||
             "does not take into account prototype tables.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "table/getproto", cfun_table_getproto,
 | 
			
		||||
        JDOC("(table/getproto tab)\n\n"
 | 
			
		||||
             "Get the prototype table of a table. Returns nil if a table "
 | 
			
		||||
             "has no prototype, otherwise returns the prototype.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "table/setproto", cfun_table_setproto,
 | 
			
		||||
        JDOC("(table/setproto tab proto)\n\n"
 | 
			
		||||
             "Set the prototype of a table. Returns the original table tab.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "table/rawget", cfun_table_rawget,
 | 
			
		||||
        JDOC("(table/rawget tab key)\n\n"
 | 
			
		||||
             "Gets a value from a table without looking at the prototype table. "
 | 
			
		||||
             "If a table tab does not contain t directly, the function will return "
 | 
			
		||||
             "nil without checking the prototype. Returns the value in the table.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "table/clone", cfun_table_clone,
 | 
			
		||||
        JDOC("(table/clone tab)\n\n"
 | 
			
		||||
             "Create a copy of a table. Updates to the new table will not change the old table, "
 | 
			
		||||
             "and vice versa.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
JANET_CORE_FN(cfun_table_clear,
 | 
			
		||||
              "(table/clear tab)",
 | 
			
		||||
              "Remove all key-value pairs in a table and return the modified table `tab`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTable *table = janet_gettable(argv, 0);
 | 
			
		||||
    janet_table_clear(table);
 | 
			
		||||
    return janet_wrap_table(table);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_proto_flatten,
 | 
			
		||||
              "(table/proto-flatten tab)",
 | 
			
		||||
              "Create a new table that is the result of merging all prototypes into a new table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTable *table = janet_gettable(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_table_proto_flatten(table));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load the table module */
 | 
			
		||||
void janet_lib_table(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, table_cfuns);
 | 
			
		||||
    JanetRegExt table_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("table/new", cfun_table_new),
 | 
			
		||||
        JANET_CORE_REG("table/to-struct", cfun_table_tostruct),
 | 
			
		||||
        JANET_CORE_REG("table/getproto", cfun_table_getproto),
 | 
			
		||||
        JANET_CORE_REG("table/setproto", cfun_table_setproto),
 | 
			
		||||
        JANET_CORE_REG("table/rawget", cfun_table_rawget),
 | 
			
		||||
        JANET_CORE_REG("table/clone", cfun_table_clone),
 | 
			
		||||
        JANET_CORE_REG("table/clear", cfun_table_clear),
 | 
			
		||||
        JANET_CORE_REG("table/proto-flatten", cfun_table_proto_flatten),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, table_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,758 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
* 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.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <setjmp.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* typedefed in janet.h */
 | 
			
		||||
struct JanetMailbox {
 | 
			
		||||
 | 
			
		||||
    /* Synchronization */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    CRITICAL_SECTION lock;
 | 
			
		||||
    CONDITION_VARIABLE cond;
 | 
			
		||||
#else
 | 
			
		||||
    pthread_mutex_t lock;
 | 
			
		||||
    pthread_cond_t cond;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Memory management - reference counting */
 | 
			
		||||
    int refCount;
 | 
			
		||||
    int closed;
 | 
			
		||||
 | 
			
		||||
    /* Store messages */
 | 
			
		||||
    uint16_t messageCapacity;
 | 
			
		||||
    uint16_t messageCount;
 | 
			
		||||
    uint16_t messageFirst;
 | 
			
		||||
    uint16_t messageNext;
 | 
			
		||||
 | 
			
		||||
    /* Buffers to store messages. These buffers are manually allocated, so
 | 
			
		||||
     * are not owned by any thread's GC. */
 | 
			
		||||
    JanetBuffer messages[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define JANET_THREAD_HEAVYWEIGHT 0x1
 | 
			
		||||
#define JANET_THREAD_ABSTRACTS 0x2
 | 
			
		||||
#define JANET_THREAD_CFUNCTIONS 0x4
 | 
			
		||||
static const char janet_thread_flags[] = "hac";
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    JanetMailbox *original;
 | 
			
		||||
    JanetMailbox *newbox;
 | 
			
		||||
    uint64_t flags;
 | 
			
		||||
} JanetMailboxPair;
 | 
			
		||||
 | 
			
		||||
static JANET_THREAD_LOCAL JanetMailbox *janet_vm_mailbox = NULL;
 | 
			
		||||
static JANET_THREAD_LOCAL JanetThread *janet_vm_thread_current = NULL;
 | 
			
		||||
static JANET_THREAD_LOCAL JanetTable *janet_vm_thread_decode = NULL;
 | 
			
		||||
 | 
			
		||||
static JanetTable *janet_thread_get_decode(void) {
 | 
			
		||||
    if (janet_vm_thread_decode == NULL) {
 | 
			
		||||
        janet_vm_thread_decode = janet_get_core_table("load-image-dict");
 | 
			
		||||
        janet_gcroot(janet_wrap_table(janet_vm_thread_decode));
 | 
			
		||||
    }
 | 
			
		||||
    return janet_vm_thread_decode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetMailbox *janet_mailbox_create(int refCount, uint16_t capacity) {
 | 
			
		||||
    JanetMailbox *mailbox = malloc(sizeof(JanetMailbox) + sizeof(JanetBuffer) * (size_t) capacity);
 | 
			
		||||
    if (NULL == mailbox) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    InitializeCriticalSection(&mailbox->lock);
 | 
			
		||||
    InitializeConditionVariable(&mailbox->cond);
 | 
			
		||||
#else
 | 
			
		||||
    pthread_mutex_init(&mailbox->lock, NULL);
 | 
			
		||||
    pthread_cond_init(&mailbox->cond, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
    mailbox->refCount = refCount;
 | 
			
		||||
    mailbox->closed = 0;
 | 
			
		||||
    mailbox->messageCount = 0;
 | 
			
		||||
    mailbox->messageCapacity = capacity;
 | 
			
		||||
    mailbox->messageFirst = 0;
 | 
			
		||||
    mailbox->messageNext = 0;
 | 
			
		||||
    for (uint16_t i = 0; i < capacity; i++) {
 | 
			
		||||
        janet_buffer_init(mailbox->messages + i, 0);
 | 
			
		||||
    }
 | 
			
		||||
    return mailbox;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mailbox_destroy(JanetMailbox *mailbox) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    DeleteCriticalSection(&mailbox->lock);
 | 
			
		||||
#else
 | 
			
		||||
    pthread_mutex_destroy(&mailbox->lock);
 | 
			
		||||
    pthread_cond_destroy(&mailbox->cond);
 | 
			
		||||
#endif
 | 
			
		||||
    for (uint16_t i = 0; i < mailbox->messageCapacity; i++) {
 | 
			
		||||
        janet_buffer_deinit(mailbox->messages + i);
 | 
			
		||||
    }
 | 
			
		||||
    free(mailbox);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mailbox_lock(JanetMailbox *mailbox) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    EnterCriticalSection(&mailbox->lock);
 | 
			
		||||
#else
 | 
			
		||||
    pthread_mutex_lock(&mailbox->lock);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mailbox_unlock(JanetMailbox *mailbox) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    LeaveCriticalSection(&mailbox->lock);
 | 
			
		||||
#else
 | 
			
		||||
    pthread_mutex_unlock(&mailbox->lock);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Assumes you have the mailbox lock already */
 | 
			
		||||
static void janet_mailbox_ref_with_lock(JanetMailbox *mailbox, int delta) {
 | 
			
		||||
    mailbox->refCount += delta;
 | 
			
		||||
    if (mailbox->refCount <= 0) {
 | 
			
		||||
        janet_mailbox_unlock(mailbox);
 | 
			
		||||
        janet_mailbox_destroy(mailbox);
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_mailbox_unlock(mailbox);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mailbox_ref(JanetMailbox *mailbox, int delta) {
 | 
			
		||||
    janet_mailbox_lock(mailbox);
 | 
			
		||||
    janet_mailbox_ref_with_lock(mailbox, delta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_close_thread(JanetThread *thread) {
 | 
			
		||||
    if (thread->mailbox) {
 | 
			
		||||
        janet_mailbox_ref(thread->mailbox, -1);
 | 
			
		||||
        thread->mailbox = NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int thread_gc(void *p, size_t size) {
 | 
			
		||||
    (void) size;
 | 
			
		||||
    JanetThread *thread = (JanetThread *)p;
 | 
			
		||||
    janet_close_thread(thread);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int thread_mark(void *p, size_t size) {
 | 
			
		||||
    (void) size;
 | 
			
		||||
    JanetThread *thread = (JanetThread *)p;
 | 
			
		||||
    if (thread->encode) {
 | 
			
		||||
        janet_mark(janet_wrap_table(thread->encode));
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetMailboxPair *make_mailbox_pair(JanetMailbox *original, uint64_t flags) {
 | 
			
		||||
    JanetMailboxPair *pair = malloc(sizeof(JanetMailboxPair));
 | 
			
		||||
    if (NULL == pair) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    pair->original = original;
 | 
			
		||||
    janet_mailbox_ref(original, 1);
 | 
			
		||||
    pair->newbox = janet_mailbox_create(1, 16);
 | 
			
		||||
    pair->flags = flags;
 | 
			
		||||
    return pair;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void destroy_mailbox_pair(JanetMailboxPair *pair) {
 | 
			
		||||
    janet_mailbox_ref(pair->original, -1);
 | 
			
		||||
    janet_mailbox_ref(pair->newbox, -1);
 | 
			
		||||
    free(pair);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Abstract waiting for timeout across windows/posix */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int timedwait;
 | 
			
		||||
    int nowait;
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    DWORD interval;
 | 
			
		||||
    DWORD ticksLeft;
 | 
			
		||||
#else
 | 
			
		||||
    struct timespec ts;
 | 
			
		||||
#endif
 | 
			
		||||
} JanetWaiter;
 | 
			
		||||
 | 
			
		||||
static void janet_waiter_init(JanetWaiter *waiter, double sec) {
 | 
			
		||||
    waiter->timedwait = 0;
 | 
			
		||||
    waiter->nowait = 0;
 | 
			
		||||
 | 
			
		||||
    if (sec <= 0.0 || isnan(sec)) {
 | 
			
		||||
        waiter->nowait = 1;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    waiter->timedwait = sec > 0.0 && !isinf(sec);
 | 
			
		||||
 | 
			
		||||
    /* Set maximum wait time to 30 days */
 | 
			
		||||
    if (sec > (60.0 * 60.0 * 24.0 * 30.0)) {
 | 
			
		||||
        sec = 60.0 * 60.0 * 24.0 * 30.0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    if (waiter->timedwait) {
 | 
			
		||||
        waiter->ticksLeft = waiter->interval = (DWORD) floor(1000.0 * sec);
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    if (waiter->timedwait) {
 | 
			
		||||
        /* N seconds -> timespec of (now + sec) */
 | 
			
		||||
        struct timespec now;
 | 
			
		||||
        janet_gettime(&now);
 | 
			
		||||
        time_t tvsec = (time_t) floor(sec);
 | 
			
		||||
        long tvnsec = (long) floor(1000000000.0 * (sec - ((double) tvsec)));
 | 
			
		||||
        tvsec += now.tv_sec;
 | 
			
		||||
        tvnsec += now.tv_nsec;
 | 
			
		||||
        if (tvnsec >= 1000000000L) {
 | 
			
		||||
            tvnsec -= 1000000000L;
 | 
			
		||||
            tvsec += 1;
 | 
			
		||||
        }
 | 
			
		||||
        waiter->ts.tv_sec = tvsec;
 | 
			
		||||
        waiter->ts.tv_nsec = tvnsec;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int janet_waiter_wait(JanetWaiter *wait, JanetMailbox *mailbox) {
 | 
			
		||||
    if (wait->nowait) return 1;
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    if (wait->timedwait) {
 | 
			
		||||
        if (wait->ticksLeft == 0) return 1;
 | 
			
		||||
        DWORD startTime = GetTickCount();
 | 
			
		||||
        int status = !SleepConditionVariableCS(&mailbox->cond, &mailbox->lock, wait->ticksLeft);
 | 
			
		||||
        DWORD dTick = GetTickCount() - startTime;
 | 
			
		||||
        /* Be careful about underflow */
 | 
			
		||||
        wait->ticksLeft = dTick > wait->ticksLeft ? 0 : dTick;
 | 
			
		||||
        return status;
 | 
			
		||||
    } else {
 | 
			
		||||
        SleepConditionVariableCS(&mailbox->cond, &mailbox->lock, INFINITE);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    if (wait->timedwait) {
 | 
			
		||||
        return pthread_cond_timedwait(&mailbox->cond, &mailbox->lock, &wait->ts);
 | 
			
		||||
    } else {
 | 
			
		||||
        pthread_cond_wait(&mailbox->cond, &mailbox->lock);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mailbox_wakeup(JanetMailbox *mailbox) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    WakeConditionVariable(&mailbox->cond);
 | 
			
		||||
#else
 | 
			
		||||
    pthread_cond_signal(&mailbox->cond);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mailbox_at_capacity(JanetMailbox *mailbox) {
 | 
			
		||||
    return mailbox->messageCount >= mailbox->messageCapacity;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns 1 if could not send (encode error or timeout), 2 for mailbox closed, and
 | 
			
		||||
 * 0 otherwise. Will not panic.  */
 | 
			
		||||
int janet_thread_send(JanetThread *thread, Janet msg, double timeout) {
 | 
			
		||||
 | 
			
		||||
    /* Ensure mailbox is not closed. */
 | 
			
		||||
    JanetMailbox *mailbox = thread->mailbox;
 | 
			
		||||
    if (NULL == mailbox) return 2;
 | 
			
		||||
    janet_mailbox_lock(mailbox);
 | 
			
		||||
    if (mailbox->closed) {
 | 
			
		||||
        janet_mailbox_ref_with_lock(mailbox, -1);
 | 
			
		||||
        thread->mailbox = NULL;
 | 
			
		||||
        return 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Back pressure */
 | 
			
		||||
    if (mailbox_at_capacity(mailbox)) {
 | 
			
		||||
        JanetWaiter wait;
 | 
			
		||||
        janet_waiter_init(&wait, timeout);
 | 
			
		||||
 | 
			
		||||
        if (wait.nowait) {
 | 
			
		||||
            janet_mailbox_unlock(mailbox);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Retry loop, as there can be multiple writers */
 | 
			
		||||
        while (mailbox_at_capacity(mailbox)) {
 | 
			
		||||
            if (janet_waiter_wait(&wait, mailbox)) {
 | 
			
		||||
                janet_mailbox_unlock(mailbox);
 | 
			
		||||
                janet_mailbox_wakeup(mailbox);
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Hack to capture all panics from marshalling. This works because
 | 
			
		||||
     * we know janet_marshal won't mess with other essential global state. */
 | 
			
		||||
    jmp_buf buf;
 | 
			
		||||
    jmp_buf *old_buf = janet_vm_jmp_buf;
 | 
			
		||||
    janet_vm_jmp_buf = &buf;
 | 
			
		||||
    int32_t oldmcount = mailbox->messageCount;
 | 
			
		||||
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    if (setjmp(buf)) {
 | 
			
		||||
        ret = 1;
 | 
			
		||||
        mailbox->messageCount = oldmcount;
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetBuffer *msgbuf = mailbox->messages + mailbox->messageNext;
 | 
			
		||||
        msgbuf->count = 0;
 | 
			
		||||
 | 
			
		||||
        /* Start panic zone */
 | 
			
		||||
        janet_marshal(msgbuf, msg, thread->encode, JANET_MARSHAL_UNSAFE);
 | 
			
		||||
        /* End panic zone */
 | 
			
		||||
 | 
			
		||||
        mailbox->messageNext = (mailbox->messageNext + 1) % mailbox->messageCapacity;
 | 
			
		||||
        mailbox->messageCount++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Cleanup */
 | 
			
		||||
    janet_vm_jmp_buf = old_buf;
 | 
			
		||||
    janet_mailbox_unlock(mailbox);
 | 
			
		||||
 | 
			
		||||
    /* Potentially wake up a blocked thread */
 | 
			
		||||
    janet_mailbox_wakeup(mailbox);
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Returns 0 on successful message. Returns 1 if timedout */
 | 
			
		||||
int janet_thread_receive(Janet *msg_out, double timeout) {
 | 
			
		||||
    JanetMailbox *mailbox = janet_vm_mailbox;
 | 
			
		||||
    janet_mailbox_lock(mailbox);
 | 
			
		||||
 | 
			
		||||
    /* For timeouts */
 | 
			
		||||
    JanetWaiter wait;
 | 
			
		||||
    janet_waiter_init(&wait, timeout);
 | 
			
		||||
 | 
			
		||||
    for (;;) {
 | 
			
		||||
 | 
			
		||||
        /* Check for messages waiting for us */
 | 
			
		||||
        if (mailbox->messageCount > 0) {
 | 
			
		||||
 | 
			
		||||
            /* Hack to capture all panics from marshalling. This works because
 | 
			
		||||
             * we know janet_marshal won't mess with other essential global state. */
 | 
			
		||||
            jmp_buf buf;
 | 
			
		||||
            jmp_buf *old_buf = janet_vm_jmp_buf;
 | 
			
		||||
            janet_vm_jmp_buf = &buf;
 | 
			
		||||
 | 
			
		||||
            /* Handle errors */
 | 
			
		||||
            if (setjmp(buf)) {
 | 
			
		||||
                /* Cleanup jmp_buf, return error.
 | 
			
		||||
                 * Do not ignore bad messages as before. */
 | 
			
		||||
                janet_vm_jmp_buf = old_buf;
 | 
			
		||||
                *msg_out = *janet_vm_return_reg;
 | 
			
		||||
                janet_mailbox_unlock(mailbox);
 | 
			
		||||
                return 2;
 | 
			
		||||
            } else {
 | 
			
		||||
                JanetBuffer *msgbuf = mailbox->messages + mailbox->messageFirst;
 | 
			
		||||
                mailbox->messageCount--;
 | 
			
		||||
                mailbox->messageFirst = (mailbox->messageFirst + 1) % mailbox->messageCapacity;
 | 
			
		||||
 | 
			
		||||
                /* Read from beginning of channel */
 | 
			
		||||
                const uint8_t *nextItem = NULL;
 | 
			
		||||
                Janet item = janet_unmarshal(
 | 
			
		||||
                                 msgbuf->data, msgbuf->count,
 | 
			
		||||
                                 JANET_MARSHAL_UNSAFE, janet_thread_get_decode(), &nextItem);
 | 
			
		||||
                *msg_out = item;
 | 
			
		||||
 | 
			
		||||
                /* Cleanup */
 | 
			
		||||
                janet_vm_jmp_buf = old_buf;
 | 
			
		||||
                janet_mailbox_unlock(mailbox);
 | 
			
		||||
 | 
			
		||||
                /* Potentially wake up pending threads */
 | 
			
		||||
                janet_mailbox_wakeup(mailbox);
 | 
			
		||||
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (wait.nowait) {
 | 
			
		||||
            janet_mailbox_unlock(mailbox);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Wait for next message */
 | 
			
		||||
        if (janet_waiter_wait(&wait, mailbox)) {
 | 
			
		||||
            janet_mailbox_unlock(mailbox);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int janet_thread_getter(void *p, Janet key, Janet *out);
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_thread_type = {
 | 
			
		||||
    "core/thread",
 | 
			
		||||
    thread_gc,
 | 
			
		||||
    thread_mark,
 | 
			
		||||
    janet_thread_getter,
 | 
			
		||||
    JANET_ATEND_GET
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static JanetThread *janet_make_thread(JanetMailbox *mailbox, JanetTable *encode) {
 | 
			
		||||
    JanetThread *thread = janet_abstract(&janet_thread_type, sizeof(JanetThread));
 | 
			
		||||
    janet_mailbox_ref(mailbox, 1);
 | 
			
		||||
    thread->mailbox = mailbox;
 | 
			
		||||
    thread->encode = encode;
 | 
			
		||||
    return thread;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetThread *janet_getthread(const Janet *argv, int32_t n) {
 | 
			
		||||
    return (JanetThread *) janet_getabstract(argv, n, &janet_thread_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Runs in new thread */
 | 
			
		||||
static int thread_worker(JanetMailboxPair *pair) {
 | 
			
		||||
    JanetFiber *fiber = NULL;
 | 
			
		||||
    Janet out;
 | 
			
		||||
 | 
			
		||||
    /* Use the mailbox we were given */
 | 
			
		||||
    janet_vm_mailbox = pair->newbox;
 | 
			
		||||
    janet_mailbox_ref(pair->newbox, 1);
 | 
			
		||||
 | 
			
		||||
    /* Init VM */
 | 
			
		||||
    janet_init();
 | 
			
		||||
 | 
			
		||||
    /* Get dictionaries for default encode/decode */
 | 
			
		||||
    JanetTable *encode;
 | 
			
		||||
    if (pair->flags & JANET_THREAD_HEAVYWEIGHT) {
 | 
			
		||||
        encode = janet_get_core_table("make-image-dict");
 | 
			
		||||
    } else {
 | 
			
		||||
        encode = NULL;
 | 
			
		||||
        janet_vm_thread_decode = janet_table(0);
 | 
			
		||||
        janet_gcroot(janet_wrap_table(janet_vm_thread_decode));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create parent thread */
 | 
			
		||||
    JanetThread *parent = janet_make_thread(pair->original, encode);
 | 
			
		||||
    Janet parentv = janet_wrap_abstract(parent);
 | 
			
		||||
 | 
			
		||||
    /* Unmarshal the abstract registry */
 | 
			
		||||
    if (pair->flags & JANET_THREAD_ABSTRACTS) {
 | 
			
		||||
        Janet reg;
 | 
			
		||||
        int status = janet_thread_receive(®, INFINITY);
 | 
			
		||||
        if (status) goto error;
 | 
			
		||||
        if (!janet_checktype(reg, JANET_TABLE)) goto error;
 | 
			
		||||
        janet_gcunroot(janet_wrap_table(janet_vm_abstract_registry));
 | 
			
		||||
        janet_vm_abstract_registry = janet_unwrap_table(reg);
 | 
			
		||||
        janet_gcroot(janet_wrap_table(janet_vm_abstract_registry));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Unmarshal the normal registry */
 | 
			
		||||
    if (pair->flags & JANET_THREAD_CFUNCTIONS) {
 | 
			
		||||
        Janet reg;
 | 
			
		||||
        int status = janet_thread_receive(®, INFINITY);
 | 
			
		||||
        if (status) goto error;
 | 
			
		||||
        if (!janet_checktype(reg, JANET_TABLE)) goto error;
 | 
			
		||||
        janet_gcunroot(janet_wrap_table(janet_vm_registry));
 | 
			
		||||
        janet_vm_registry = janet_unwrap_table(reg);
 | 
			
		||||
        janet_gcroot(janet_wrap_table(janet_vm_registry));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Unmarshal the function */
 | 
			
		||||
    Janet funcv;
 | 
			
		||||
    int status = janet_thread_receive(&funcv, INFINITY);
 | 
			
		||||
    if (status) goto error;
 | 
			
		||||
    if (!janet_checktype(funcv, JANET_FUNCTION)) goto error;
 | 
			
		||||
    JanetFunction *func = janet_unwrap_function(funcv);
 | 
			
		||||
 | 
			
		||||
    /* Arity check */
 | 
			
		||||
    if (func->def->min_arity > 1 || func->def->max_arity < 1) {
 | 
			
		||||
        goto error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Call function */
 | 
			
		||||
    Janet argv[1] = { parentv };
 | 
			
		||||
    fiber = janet_fiber(func, 64, 1, argv);
 | 
			
		||||
    if (pair->flags & JANET_THREAD_HEAVYWEIGHT) {
 | 
			
		||||
        fiber->env = janet_table(0);
 | 
			
		||||
        fiber->env->proto = janet_core_env(NULL);
 | 
			
		||||
    }
 | 
			
		||||
    JanetSignal sig = janet_continue(fiber, janet_wrap_nil(), &out);
 | 
			
		||||
    if (sig != JANET_SIGNAL_OK && sig < JANET_SIGNAL_USER0) {
 | 
			
		||||
        janet_eprintf("in thread %v: ", janet_wrap_abstract(janet_make_thread(pair->newbox, encode)));
 | 
			
		||||
        janet_stacktrace(fiber, out);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    janet_loop();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Normal exit */
 | 
			
		||||
    destroy_mailbox_pair(pair);
 | 
			
		||||
    janet_deinit();
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
    /* Fail to set something up */
 | 
			
		||||
error:
 | 
			
		||||
    destroy_mailbox_pair(pair);
 | 
			
		||||
    janet_eprintf("\nthread failed to start\n");
 | 
			
		||||
    janet_deinit();
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
 | 
			
		||||
static DWORD WINAPI janet_create_thread_wrapper(LPVOID param) {
 | 
			
		||||
    thread_worker((JanetMailboxPair *)param);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int janet_thread_start_child(JanetMailboxPair *pair) {
 | 
			
		||||
    HANDLE handle = CreateThread(NULL, 0, janet_create_thread_wrapper, pair, 0, NULL);
 | 
			
		||||
    int ret = NULL == handle;
 | 
			
		||||
    /* Does not kill thread, simply detatches */
 | 
			
		||||
    if (!ret) CloseHandle(handle);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
static void *janet_pthread_wrapper(void *param) {
 | 
			
		||||
    thread_worker((JanetMailboxPair *)param);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int janet_thread_start_child(JanetMailboxPair *pair) {
 | 
			
		||||
    pthread_t handle;
 | 
			
		||||
    int error = pthread_create(&handle, NULL, janet_pthread_wrapper, pair);
 | 
			
		||||
    if (error) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    } else {
 | 
			
		||||
        pthread_detach(handle);
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Setup/Teardown
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void janet_threads_init(void) {
 | 
			
		||||
    if (NULL == janet_vm_mailbox) {
 | 
			
		||||
        janet_vm_mailbox = janet_mailbox_create(1, 10);
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_thread_decode = NULL;
 | 
			
		||||
    janet_vm_thread_current = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_threads_deinit(void) {
 | 
			
		||||
    janet_mailbox_lock(janet_vm_mailbox);
 | 
			
		||||
    janet_vm_mailbox->closed = 1;
 | 
			
		||||
    janet_mailbox_ref_with_lock(janet_vm_mailbox, -1);
 | 
			
		||||
    janet_vm_mailbox = NULL;
 | 
			
		||||
    janet_vm_thread_current = NULL;
 | 
			
		||||
    janet_vm_thread_decode = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Cfuns
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_thread_current(int32_t argc, Janet *argv) {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    if (NULL == janet_vm_thread_current) {
 | 
			
		||||
        janet_vm_thread_current = janet_make_thread(janet_vm_mailbox, janet_get_core_table("make-image-dict"));
 | 
			
		||||
        janet_gcroot(janet_wrap_abstract(janet_vm_thread_current));
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_abstract(janet_vm_thread_current);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_thread_new(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    /* Just type checking */
 | 
			
		||||
    janet_getfunction(argv, 0);
 | 
			
		||||
    int32_t cap = janet_optinteger(argv, argc, 1, 10);
 | 
			
		||||
    if (cap < 1 || cap > UINT16_MAX) {
 | 
			
		||||
        janet_panicf("bad slot #1, expected integer in range [1, 65535], got %d", cap);
 | 
			
		||||
    }
 | 
			
		||||
    uint64_t flags = argc >= 3 ? janet_getflags(argv, 2, janet_thread_flags) : JANET_THREAD_ABSTRACTS;
 | 
			
		||||
    JanetTable *encode;
 | 
			
		||||
    if (flags & JANET_THREAD_HEAVYWEIGHT) {
 | 
			
		||||
        encode = janet_get_core_table("make-image-dict");
 | 
			
		||||
    } else {
 | 
			
		||||
        encode = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JanetMailboxPair *pair = make_mailbox_pair(janet_vm_mailbox, flags);
 | 
			
		||||
    JanetThread *thread = janet_make_thread(pair->newbox, encode);
 | 
			
		||||
    if (janet_thread_start_child(pair)) {
 | 
			
		||||
        destroy_mailbox_pair(pair);
 | 
			
		||||
        janet_panic("could not start thread");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (flags & JANET_THREAD_ABSTRACTS) {
 | 
			
		||||
        if (janet_thread_send(thread, janet_wrap_table(janet_vm_abstract_registry), INFINITY)) {
 | 
			
		||||
            janet_panic("could not send abstract registry to thread");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (flags & JANET_THREAD_CFUNCTIONS) {
 | 
			
		||||
        if (janet_thread_send(thread, janet_wrap_table(janet_vm_registry), INFINITY)) {
 | 
			
		||||
            janet_panic("could not send registry to thread");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If thread started, send the worker function. */
 | 
			
		||||
    if (janet_thread_send(thread, argv[0], INFINITY)) {
 | 
			
		||||
        janet_panicf("could not send worker function %v to thread", argv[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return janet_wrap_abstract(thread);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_thread_send(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetThread *thread = janet_getthread(argv, 0);
 | 
			
		||||
    int status = janet_thread_send(thread, argv[1], janet_optnumber(argv, argc, 2, 1.0));
 | 
			
		||||
    switch (status) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case 1:
 | 
			
		||||
            janet_panicf("failed to send message %v", argv[1]);
 | 
			
		||||
        case 2:
 | 
			
		||||
            janet_panic("thread mailbox is closed");
 | 
			
		||||
    }
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_thread_receive(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 0, 1);
 | 
			
		||||
    double wait = janet_optnumber(argv, argc, 0, 1.0);
 | 
			
		||||
    Janet out;
 | 
			
		||||
    int status = janet_thread_receive(&out, wait);
 | 
			
		||||
    switch (status) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case 1:
 | 
			
		||||
            janet_panicf("timeout after %f seconds", wait);
 | 
			
		||||
        case 2:
 | 
			
		||||
            janet_panicf("failed to receive message: %v", out);
 | 
			
		||||
    }
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_thread_close(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetThread *thread = janet_getthread(argv, 0);
 | 
			
		||||
    janet_close_thread(thread);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_thread_exit(int32_t argc, Janet *argv) {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_arity(argc, 0, 1);
 | 
			
		||||
#if defined(JANET_WINDOWS)
 | 
			
		||||
    int32_t flag = janet_optinteger(argv, argc, 0, 0);
 | 
			
		||||
    ExitThread(flag);
 | 
			
		||||
#else
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
#endif
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetMethod janet_thread_methods[] = {
 | 
			
		||||
    {"send", cfun_thread_send},
 | 
			
		||||
    {"close", cfun_thread_close},
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int janet_thread_getter(void *p, Janet key, Janet *out) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    if (!janet_checktype(key, JANET_KEYWORD)) return 0;
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), janet_thread_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg threadlib_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "thread/current", cfun_thread_current,
 | 
			
		||||
        JDOC("(thread/current)\n\n"
 | 
			
		||||
             "Get the current running thread.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "thread/new", cfun_thread_new,
 | 
			
		||||
        JDOC("(thread/new func &opt capacity flags)\n\n"
 | 
			
		||||
             "Start a new thread that will start immediately. "
 | 
			
		||||
             "If capacity is provided, that is how many messages can be stored in the thread's mailbox before blocking senders. "
 | 
			
		||||
             "The capacity must be between 1 and 65535 inclusive, and defaults to 10. "
 | 
			
		||||
             "Can optionally provide flags to the new thread - supported flags are:\n"
 | 
			
		||||
             "\t:h - Start a heavyweight thread. This loads the core environment by default, so may use more memory initially. Messages may compress better, though.\n"
 | 
			
		||||
             "\t:a - Allow sending over registered abstract types to the new thread\n"
 | 
			
		||||
             "\t:c - Send over cfunction information to the new thread.\n"
 | 
			
		||||
             "Returns a handle to the new thread.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "thread/send", cfun_thread_send,
 | 
			
		||||
        JDOC("(thread/send thread msg)\n\n"
 | 
			
		||||
             "Send a message to the thread. This will never block and returns thread immediately. "
 | 
			
		||||
             "Will throw an error if there is a problem sending the message.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "thread/receive", cfun_thread_receive,
 | 
			
		||||
        JDOC("(thread/receive &opt timeout)\n\n"
 | 
			
		||||
             "Get a message sent to this thread. If timeout is provided, an error will be thrown after the timeout has elapsed but "
 | 
			
		||||
             "no messages are received.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "thread/close", cfun_thread_close,
 | 
			
		||||
        JDOC("(thread/close thread)\n\n"
 | 
			
		||||
             "Close a thread, unblocking it and ending communication with it. Note that closing "
 | 
			
		||||
             "a thread is idempotent and does not cancel the thread's operation. Returns nil.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "thread/exit", cfun_thread_exit,
 | 
			
		||||
        JDOC("(thread/exit &opt code)\n\n"
 | 
			
		||||
             "Exit from the current thread. If no more threads are running, ends the process, but otherwise does "
 | 
			
		||||
             "not end the current process.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_thread(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, threadlib_cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_thread_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 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,19 +55,35 @@ const Janet *janet_tuple_n(const Janet *values, int32_t n) {
 | 
			
		||||
 | 
			
		||||
/* C Functions */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_tuple_brackets(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_tuple_brackets,
 | 
			
		||||
              "(tuple/brackets & xs)",
 | 
			
		||||
              "Creates a new bracketed tuple containing the elements xs.") {
 | 
			
		||||
    const Janet *tup = janet_tuple_n(argv, argc);
 | 
			
		||||
    janet_tuple_flag(tup) |= JANET_TUPLE_FLAG_BRACKETCTOR;
 | 
			
		||||
    return janet_wrap_tuple(tup);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_tuple_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_tuple_slice,
 | 
			
		||||
              "(tuple/slice arrtup [,start=0 [,end=(length arrtup)]])",
 | 
			
		||||
              "Take a sub-sequence of an array or tuple from index `start` "
 | 
			
		||||
              "inclusive to index `end` exclusive. If `start` or `end` are not provided, "
 | 
			
		||||
              "they default to 0 and the length of `arrtup`, respectively. "
 | 
			
		||||
              "`start` and `end` can also be negative to indicate indexing "
 | 
			
		||||
              "from the end of the input. Note that index -1 is synonymous with "
 | 
			
		||||
              "index `(length arrtup)` to allow a full negative slice range. "
 | 
			
		||||
              "Returns the new tuple.") {
 | 
			
		||||
    JanetView view = janet_getindexed(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_n(view.items + range.start, range.end - range.start));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_tuple_type(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_tuple_type,
 | 
			
		||||
              "(tuple/type tup)",
 | 
			
		||||
              "Checks how the tuple was constructed. Will return the keyword "
 | 
			
		||||
              ":brackets if the tuple was parsed with brackets, and :parens "
 | 
			
		||||
              "otherwise. The two types of tuples will behave the same most of "
 | 
			
		||||
              "the time, but will print differently and be treated differently by "
 | 
			
		||||
              "the compiler.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    const Janet *tup = janet_gettuple(argv, 0);
 | 
			
		||||
    if (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR) {
 | 
			
		||||
@@ -77,7 +93,10 @@ static Janet cfun_tuple_type(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_tuple_sourcemap(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_tuple_sourcemap,
 | 
			
		||||
              "(tuple/sourcemap tup)",
 | 
			
		||||
              "Returns the sourcemap metadata attached to a tuple, "
 | 
			
		||||
              "which is another tuple (line, column).") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    const Janet *tup = janet_gettuple(argv, 0);
 | 
			
		||||
    Janet contents[2];
 | 
			
		||||
@@ -86,7 +105,10 @@ static Janet cfun_tuple_sourcemap(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_n(contents, 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_tuple_setmap(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_tuple_setmap,
 | 
			
		||||
              "(tuple/setmap tup line column)",
 | 
			
		||||
              "Set the sourcemap metadata on a tuple. line and column indicate "
 | 
			
		||||
              "should be integers.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    const Janet *tup = janet_gettuple(argv, 0);
 | 
			
		||||
    janet_tuple_head(tup)->sm_line = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -94,48 +116,15 @@ static Janet cfun_tuple_setmap(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg tuple_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "tuple/brackets", cfun_tuple_brackets,
 | 
			
		||||
        JDOC("(tuple/brackets & xs)\n\n"
 | 
			
		||||
             "Creates a new bracketed tuple containing the elements xs.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tuple/slice", cfun_tuple_slice,
 | 
			
		||||
        JDOC("(tuple/slice arrtup [,start=0 [,end=(length arrtup)]])\n\n"
 | 
			
		||||
             "Take a sub sequence of an array or tuple from index start "
 | 
			
		||||
             "inclusive to index end exclusive. If start or end are not provided, "
 | 
			
		||||
             "they default to 0 and the length of arrtup respectively. "
 | 
			
		||||
             "'start' and 'end' can also be negative to indicate indexing "
 | 
			
		||||
             "from the end of the input. Note that index -1 is synonymous with "
 | 
			
		||||
             "index '(length arrtup)' to allow a full negative slice range. "
 | 
			
		||||
             "Returns the new tuple.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tuple/type", cfun_tuple_type,
 | 
			
		||||
        JDOC("(tuple/type tup)\n\n"
 | 
			
		||||
             "Checks how the tuple was constructed. Will return the keyword "
 | 
			
		||||
             ":brackets if the tuple was parsed with brackets, and :parens "
 | 
			
		||||
             "otherwise. The two types of tuples will behave the same most of "
 | 
			
		||||
             "the time, but will print differently and be treated differently by "
 | 
			
		||||
             "the compiler.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tuple/sourcemap", cfun_tuple_sourcemap,
 | 
			
		||||
        JDOC("(tuple/sourcemap tup)\n\n"
 | 
			
		||||
             "Returns the sourcemap metadata attached to a tuple, "
 | 
			
		||||
             " which is another tuple (line, column).")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tuple/setmap", cfun_tuple_setmap,
 | 
			
		||||
        JDOC("(tuple/setmap tup line column)\n\n"
 | 
			
		||||
             "Set the sourcemap metadata on a tuple. line and column indicate "
 | 
			
		||||
             "should be integers.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Load the tuple module */
 | 
			
		||||
void janet_lib_tuple(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, tuple_cfuns);
 | 
			
		||||
    JanetRegExt tuple_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("tuple/brackets", cfun_tuple_brackets),
 | 
			
		||||
        JANET_CORE_REG("tuple/slice", cfun_tuple_slice),
 | 
			
		||||
        JANET_CORE_REG("tuple/type", cfun_tuple_type),
 | 
			
		||||
        JANET_CORE_REG("tuple/sourcemap", cfun_tuple_sourcemap),
 | 
			
		||||
        JANET_CORE_REG("tuple/setmap", cfun_tuple_setmap),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, tuple_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,606 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 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
 | 
			
		||||
* 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.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_TYPED_ARRAY
 | 
			
		||||
 | 
			
		||||
static char *ta_type_names[] = {
 | 
			
		||||
    "uint8",
 | 
			
		||||
    "int8",
 | 
			
		||||
    "uint16",
 | 
			
		||||
    "int16",
 | 
			
		||||
    "uint32",
 | 
			
		||||
    "int32",
 | 
			
		||||
    "uint64",
 | 
			
		||||
    "int64",
 | 
			
		||||
    "float32",
 | 
			
		||||
    "float64",
 | 
			
		||||
    "?"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static size_t ta_type_sizes[] = {
 | 
			
		||||
    sizeof(uint8_t),
 | 
			
		||||
    sizeof(int8_t),
 | 
			
		||||
    sizeof(uint16_t),
 | 
			
		||||
    sizeof(int16_t),
 | 
			
		||||
    sizeof(uint32_t),
 | 
			
		||||
    sizeof(int32_t),
 | 
			
		||||
    sizeof(uint64_t),
 | 
			
		||||
    sizeof(int64_t),
 | 
			
		||||
    sizeof(float),
 | 
			
		||||
    sizeof(double),
 | 
			
		||||
    0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define TA_COUNT_TYPES (JANET_TARRAY_TYPE_F64 + 1)
 | 
			
		||||
#define TA_ATOM_MAXSIZE 8
 | 
			
		||||
#define TA_FLAG_BIG_ENDIAN 1
 | 
			
		||||
 | 
			
		||||
static JanetTArrayType get_ta_type_by_name(const uint8_t *name) {
 | 
			
		||||
    for (int i = 0; i < TA_COUNT_TYPES; i++) {
 | 
			
		||||
        if (!janet_cstrcmp(name, ta_type_names[i]))
 | 
			
		||||
            return i;
 | 
			
		||||
    }
 | 
			
		||||
    janet_panicf("invalid typed array type %S", name);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetTArrayBuffer *ta_buffer_init(JanetTArrayBuffer *buf, size_t size) {
 | 
			
		||||
    buf->data = NULL;
 | 
			
		||||
    if (size > 0) {
 | 
			
		||||
        buf->data = (uint8_t *)calloc(size, sizeof(uint8_t));
 | 
			
		||||
        if (buf->data == NULL) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    buf->size = size;
 | 
			
		||||
#ifdef JANET_BIG_ENDIAN
 | 
			
		||||
    buf->flags = TA_FLAG_BIG_ENDIAN;
 | 
			
		||||
#else
 | 
			
		||||
    buf->flags = 0;
 | 
			
		||||
#endif
 | 
			
		||||
    return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int ta_buffer_gc(void *p, size_t s) {
 | 
			
		||||
    (void) s;
 | 
			
		||||
    JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p;
 | 
			
		||||
    free(buf->data);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ta_buffer_marshal(void *p, JanetMarshalContext *ctx) {
 | 
			
		||||
    JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p;
 | 
			
		||||
    janet_marshal_abstract(ctx, p);
 | 
			
		||||
    janet_marshal_size(ctx, buf->size);
 | 
			
		||||
    janet_marshal_int(ctx, buf->flags);
 | 
			
		||||
    janet_marshal_bytes(ctx, buf->data, buf->size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *ta_buffer_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
    JanetTArrayBuffer *buf = janet_unmarshal_abstract(ctx, sizeof(JanetTArrayBuffer));
 | 
			
		||||
    size_t size = janet_unmarshal_size(ctx);
 | 
			
		||||
    int32_t flags = janet_unmarshal_int(ctx);
 | 
			
		||||
    ta_buffer_init(buf, size);
 | 
			
		||||
    buf->flags = flags;
 | 
			
		||||
    janet_unmarshal_bytes(ctx, buf->data, size);
 | 
			
		||||
    return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_ta_buffer_type = {
 | 
			
		||||
    "ta/buffer",
 | 
			
		||||
    ta_buffer_gc,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    ta_buffer_marshal,
 | 
			
		||||
    ta_buffer_unmarshal,
 | 
			
		||||
    JANET_ATEND_UNMARSHAL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int ta_mark(void *p, size_t s) {
 | 
			
		||||
    (void) s;
 | 
			
		||||
    JanetTArrayView *view = (JanetTArrayView *)p;
 | 
			
		||||
    janet_mark(janet_wrap_abstract(view->buffer));
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ta_view_marshal(void *p, JanetMarshalContext *ctx) {
 | 
			
		||||
    JanetTArrayView *view = (JanetTArrayView *)p;
 | 
			
		||||
    size_t offset = (view->buffer->data - view->as.u8);
 | 
			
		||||
    janet_marshal_abstract(ctx, p);
 | 
			
		||||
    janet_marshal_size(ctx, view->size);
 | 
			
		||||
    janet_marshal_size(ctx, view->stride);
 | 
			
		||||
    janet_marshal_int(ctx, view->type);
 | 
			
		||||
    janet_marshal_size(ctx, offset);
 | 
			
		||||
    janet_marshal_janet(ctx, janet_wrap_abstract(view->buffer));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *ta_view_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
    size_t offset;
 | 
			
		||||
    int32_t atype;
 | 
			
		||||
    Janet buffer;
 | 
			
		||||
    JanetTArrayView *view = janet_unmarshal_abstract(ctx, sizeof(JanetTArrayView));
 | 
			
		||||
    view->size = janet_unmarshal_size(ctx);
 | 
			
		||||
    view->stride = janet_unmarshal_size(ctx);
 | 
			
		||||
    atype = janet_unmarshal_int(ctx);
 | 
			
		||||
    if (atype < 0 || atype >= TA_COUNT_TYPES)
 | 
			
		||||
        janet_panic("bad typed array type");
 | 
			
		||||
    view->type = atype;
 | 
			
		||||
    offset = janet_unmarshal_size(ctx);
 | 
			
		||||
    buffer = janet_unmarshal_janet(ctx);
 | 
			
		||||
    if (!janet_checktype(buffer, JANET_ABSTRACT) ||
 | 
			
		||||
            (janet_abstract_type(janet_unwrap_abstract(buffer)) != &janet_ta_buffer_type)) {
 | 
			
		||||
        janet_panicf("expected typed array buffer");
 | 
			
		||||
    }
 | 
			
		||||
    view->buffer = (JanetTArrayBuffer *)janet_unwrap_abstract(buffer);
 | 
			
		||||
    size_t buf_need_size = offset + (ta_type_sizes[view->type]) * ((view->size - 1) * view->stride + 1);
 | 
			
		||||
    if (view->buffer->size < buf_need_size)
 | 
			
		||||
        janet_panic("bad typed array offset in marshalled data");
 | 
			
		||||
    view->as.u8 = view->buffer->data + offset;
 | 
			
		||||
    return view;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetMethod tarray_view_methods[6];
 | 
			
		||||
 | 
			
		||||
static int ta_getter(void *p, Janet key, Janet *out) {
 | 
			
		||||
    size_t index, i;
 | 
			
		||||
    JanetTArrayView *array = p;
 | 
			
		||||
    if (janet_checktype(key, JANET_KEYWORD)) {
 | 
			
		||||
        return janet_getmethod(janet_unwrap_keyword(key), tarray_view_methods, out);
 | 
			
		||||
    }
 | 
			
		||||
    if (!janet_checksize(key)) janet_panic("expected size as key");
 | 
			
		||||
    index = (size_t) janet_unwrap_number(key);
 | 
			
		||||
    i = index * array->stride;
 | 
			
		||||
    if (index >= array->size) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        switch (array->type) {
 | 
			
		||||
            case JANET_TARRAY_TYPE_U8:
 | 
			
		||||
                *out = janet_wrap_number(array->as.u8[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_TARRAY_TYPE_S8:
 | 
			
		||||
                *out = janet_wrap_number(array->as.s8[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_TARRAY_TYPE_U16:
 | 
			
		||||
                *out = janet_wrap_number(array->as.u16[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_TARRAY_TYPE_S16:
 | 
			
		||||
                *out = janet_wrap_number(array->as.s16[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_TARRAY_TYPE_U32:
 | 
			
		||||
                *out = janet_wrap_number(array->as.u32[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_TARRAY_TYPE_S32:
 | 
			
		||||
                *out = janet_wrap_number(array->as.s32[i]);
 | 
			
		||||
                break;
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
            case JANET_TARRAY_TYPE_U64:
 | 
			
		||||
                *out = janet_wrap_u64(array->as.u64[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_TARRAY_TYPE_S64:
 | 
			
		||||
                *out = janet_wrap_s64(array->as.s64[i]);
 | 
			
		||||
                break;
 | 
			
		||||
#endif
 | 
			
		||||
            case JANET_TARRAY_TYPE_F32:
 | 
			
		||||
                *out = janet_wrap_number_safe(array->as.f32[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_TARRAY_TYPE_F64:
 | 
			
		||||
                *out = janet_wrap_number_safe(array->as.f64[i]);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                janet_panicf("cannot get from typed array of type %s",
 | 
			
		||||
                             ta_type_names[array->type]);
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ta_setter(void *p, Janet key, Janet value) {
 | 
			
		||||
    size_t index, i;
 | 
			
		||||
    if (!janet_checksize(key)) janet_panic("expected size as key");
 | 
			
		||||
    index = (size_t) janet_unwrap_number(key);
 | 
			
		||||
    JanetTArrayView *array = p;
 | 
			
		||||
    i = index * array->stride;
 | 
			
		||||
    if (index >= array->size) {
 | 
			
		||||
        janet_panic("index out of bounds");
 | 
			
		||||
    }
 | 
			
		||||
    if (!janet_checktype(value, JANET_NUMBER) &&
 | 
			
		||||
            array->type != JANET_TARRAY_TYPE_U64 &&
 | 
			
		||||
            array->type != JANET_TARRAY_TYPE_S64) {
 | 
			
		||||
        janet_panic("expected number value");
 | 
			
		||||
    }
 | 
			
		||||
    switch (array->type) {
 | 
			
		||||
        case JANET_TARRAY_TYPE_U8:
 | 
			
		||||
            array->as.u8[i] = (uint8_t) janet_unwrap_number(value);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_TARRAY_TYPE_S8:
 | 
			
		||||
            array->as.s8[i] = (int8_t) janet_unwrap_number(value);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_TARRAY_TYPE_U16:
 | 
			
		||||
            array->as.u16[i] = (uint16_t) janet_unwrap_number(value);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_TARRAY_TYPE_S16:
 | 
			
		||||
            array->as.s16[i] = (int16_t) janet_unwrap_number(value);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_TARRAY_TYPE_U32:
 | 
			
		||||
            array->as.u32[i] = (uint32_t) janet_unwrap_number(value);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_TARRAY_TYPE_S32:
 | 
			
		||||
            array->as.s32[i] = (int32_t) janet_unwrap_number(value);
 | 
			
		||||
            break;
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
        case JANET_TARRAY_TYPE_U64:
 | 
			
		||||
            array->as.u64[i] = janet_unwrap_u64(value);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_TARRAY_TYPE_S64:
 | 
			
		||||
            array->as.s64[i] = janet_unwrap_s64(value);
 | 
			
		||||
            break;
 | 
			
		||||
#endif
 | 
			
		||||
        case JANET_TARRAY_TYPE_F32:
 | 
			
		||||
            array->as.f32[i] = (float) janet_unwrap_number(value);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_TARRAY_TYPE_F64:
 | 
			
		||||
            array->as.f64[i] = janet_unwrap_number(value);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panicf("cannot set typed array of type %s",
 | 
			
		||||
                         ta_type_names[array->type]);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet ta_view_next(void *p, Janet key) {
 | 
			
		||||
    JanetTArrayView *view = p;
 | 
			
		||||
    if (janet_checktype(key, JANET_NIL)) {
 | 
			
		||||
        if (view->size > 0) {
 | 
			
		||||
            return janet_wrap_number(0);
 | 
			
		||||
        } else {
 | 
			
		||||
            return janet_wrap_nil();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (!janet_checksize(key)) janet_panic("expected size as key");
 | 
			
		||||
    size_t index = (size_t) janet_unwrap_number(key);
 | 
			
		||||
    index++;
 | 
			
		||||
    if (index < view->size) {
 | 
			
		||||
        return janet_wrap_number((double) index);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_ta_view_type = {
 | 
			
		||||
    "ta/view",
 | 
			
		||||
    NULL,
 | 
			
		||||
    ta_mark,
 | 
			
		||||
    ta_getter,
 | 
			
		||||
    ta_setter,
 | 
			
		||||
    ta_view_marshal,
 | 
			
		||||
    ta_view_unmarshal,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    NULL,
 | 
			
		||||
    ta_view_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JanetTArrayBuffer *janet_tarray_buffer(size_t size) {
 | 
			
		||||
    JanetTArrayBuffer *buf = janet_abstract(&janet_ta_buffer_type, sizeof(JanetTArrayBuffer));
 | 
			
		||||
    ta_buffer_init(buf, size);
 | 
			
		||||
    return buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTArrayView *janet_tarray_view(
 | 
			
		||||
    JanetTArrayType type,
 | 
			
		||||
    size_t size,
 | 
			
		||||
    size_t stride,
 | 
			
		||||
    size_t offset,
 | 
			
		||||
    JanetTArrayBuffer *buffer) {
 | 
			
		||||
 | 
			
		||||
    JanetTArrayView *view = janet_abstract(&janet_ta_view_type, sizeof(JanetTArrayView));
 | 
			
		||||
 | 
			
		||||
    if ((stride < 1) || (size < 1)) janet_panic("stride and size should be > 0");
 | 
			
		||||
    size_t buf_size = offset + ta_type_sizes[type] * ((size - 1) * stride + 1);
 | 
			
		||||
 | 
			
		||||
    if (NULL == buffer) {
 | 
			
		||||
        buffer = janet_abstract(&janet_ta_buffer_type, sizeof(JanetTArrayBuffer));
 | 
			
		||||
        ta_buffer_init(buffer, buf_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (buffer->size < buf_size) {
 | 
			
		||||
        janet_panicf("bad buffer size, %i bytes allocated < %i required",
 | 
			
		||||
                     buffer->size,
 | 
			
		||||
                     buf_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    view->buffer = buffer;
 | 
			
		||||
    view->stride = stride;
 | 
			
		||||
    view->size = size;
 | 
			
		||||
    view->as.u8 = buffer->data + offset;
 | 
			
		||||
    view->type = type;
 | 
			
		||||
 | 
			
		||||
    return view;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTArrayBuffer *janet_gettarray_buffer(const Janet *argv, int32_t n) {
 | 
			
		||||
    return janet_getabstract(argv, n, &janet_ta_buffer_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTArrayView *janet_gettarray_any(const Janet *argv, int32_t n) {
 | 
			
		||||
    return janet_getabstract(argv, n, &janet_ta_view_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTArrayView *janet_gettarray_view(const Janet *argv, int32_t n, JanetTArrayType type) {
 | 
			
		||||
    JanetTArrayView *view = janet_getabstract(argv, n, &janet_ta_view_type);
 | 
			
		||||
    if (view->type != type) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected typed array of type %s, got %v",
 | 
			
		||||
                     n, ta_type_names[type], argv[n]);
 | 
			
		||||
    }
 | 
			
		||||
    return view;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_typed_array_new(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 2, 5);
 | 
			
		||||
    size_t offset = 0;
 | 
			
		||||
    size_t stride = 1;
 | 
			
		||||
    JanetTArrayBuffer *buffer = NULL;
 | 
			
		||||
    const uint8_t *keyw = janet_getkeyword(argv, 0);
 | 
			
		||||
    JanetTArrayType type = get_ta_type_by_name(keyw);
 | 
			
		||||
    size_t size = janet_getsize(argv, 1);
 | 
			
		||||
    if (argc > 2)
 | 
			
		||||
        stride = janet_getsize(argv, 2);
 | 
			
		||||
    if (argc > 3)
 | 
			
		||||
        offset = janet_getsize(argv, 3);
 | 
			
		||||
    if (argc > 4) {
 | 
			
		||||
        if (!janet_checktype(argv[4], JANET_ABSTRACT)) {
 | 
			
		||||
            janet_panicf("bad slot #%d, expected ta/view|ta/buffer, got %v",
 | 
			
		||||
                         4, argv[4]);
 | 
			
		||||
        }
 | 
			
		||||
        void *p = janet_unwrap_abstract(argv[4]);
 | 
			
		||||
        if (janet_abstract_type(p) == &janet_ta_view_type) {
 | 
			
		||||
            JanetTArrayView *view = (JanetTArrayView *)p;
 | 
			
		||||
            offset = (view->buffer->data - view->as.u8) + offset * ta_type_sizes[view->type];
 | 
			
		||||
            stride *= view->stride;
 | 
			
		||||
            buffer = view->buffer;
 | 
			
		||||
        } else if (janet_abstract_type(p) == &janet_ta_buffer_type) {
 | 
			
		||||
            buffer = p;
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_panicf("bad slot #%d, expected ta/view|ta/buffer, got %v",
 | 
			
		||||
                         4, argv[4]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetTArrayView *view = janet_tarray_view(type, size, stride, offset, buffer);
 | 
			
		||||
    return janet_wrap_abstract(view);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetTArrayView *ta_is_view(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_ABSTRACT)) return NULL;
 | 
			
		||||
    void *abst = janet_unwrap_abstract(x);
 | 
			
		||||
    if (janet_abstract_type(abst) != &janet_ta_view_type) return NULL;
 | 
			
		||||
    return (JanetTArrayView *)abst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_typed_array_buffer(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTArrayView *view;
 | 
			
		||||
    if ((view = ta_is_view(argv[0]))) {
 | 
			
		||||
        return janet_wrap_abstract(view->buffer);
 | 
			
		||||
    }
 | 
			
		||||
    size_t size = janet_getsize(argv, 0);
 | 
			
		||||
    JanetTArrayBuffer *buf = janet_tarray_buffer(size);
 | 
			
		||||
    return janet_wrap_abstract(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_typed_array_size(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTArrayView *view;
 | 
			
		||||
    if ((view = ta_is_view(argv[0]))) {
 | 
			
		||||
        return janet_wrap_number((double) view->size);
 | 
			
		||||
    }
 | 
			
		||||
    JanetTArrayBuffer *buf = (JanetTArrayBuffer *)janet_getabstract(argv, 0, &janet_ta_buffer_type);
 | 
			
		||||
    return janet_wrap_number((double) buf->size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_typed_array_properties(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTArrayView *view;
 | 
			
		||||
    if ((view = ta_is_view(argv[0]))) {
 | 
			
		||||
        JanetTArrayView *view = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
        JanetKV *props = janet_struct_begin(6);
 | 
			
		||||
        ptrdiff_t boffset = view->as.u8 - view->buffer->data;
 | 
			
		||||
        janet_struct_put(props, janet_ckeywordv("size"),
 | 
			
		||||
                         janet_wrap_number((double) view->size));
 | 
			
		||||
        janet_struct_put(props, janet_ckeywordv("byte-offset"),
 | 
			
		||||
                         janet_wrap_number((double) boffset));
 | 
			
		||||
        janet_struct_put(props, janet_ckeywordv("stride"),
 | 
			
		||||
                         janet_wrap_number((double) view->stride));
 | 
			
		||||
        janet_struct_put(props, janet_ckeywordv("type"),
 | 
			
		||||
                         janet_ckeywordv(ta_type_names[view->type]));
 | 
			
		||||
        janet_struct_put(props, janet_ckeywordv("type-size"),
 | 
			
		||||
                         janet_wrap_number((double) ta_type_sizes[view->type]));
 | 
			
		||||
        janet_struct_put(props, janet_ckeywordv("buffer"),
 | 
			
		||||
                         janet_wrap_abstract(view->buffer));
 | 
			
		||||
        return janet_wrap_struct(janet_struct_end(props));
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetTArrayBuffer *buffer = janet_gettarray_buffer(argv, 0);
 | 
			
		||||
        JanetKV *props = janet_struct_begin(2);
 | 
			
		||||
        janet_struct_put(props, janet_ckeywordv("size"),
 | 
			
		||||
                         janet_wrap_number((double) buffer->size));
 | 
			
		||||
        janet_struct_put(props, janet_ckeywordv("big-endian"),
 | 
			
		||||
                         janet_wrap_boolean(buffer->flags & TA_FLAG_BIG_ENDIAN));
 | 
			
		||||
        return janet_wrap_struct(janet_struct_end(props));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_typed_array_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetTArrayView *src = janet_getabstract(argv, 0, &janet_ta_view_type);
 | 
			
		||||
    JanetRange range;
 | 
			
		||||
    int32_t length = (int32_t)src->size;
 | 
			
		||||
    if (argc == 1) {
 | 
			
		||||
        range.start = 0;
 | 
			
		||||
        range.end = length;
 | 
			
		||||
    } else if (argc == 2) {
 | 
			
		||||
        range.start = janet_gethalfrange(argv, 1, length, "start");
 | 
			
		||||
        range.end = length;
 | 
			
		||||
    } else {
 | 
			
		||||
        range.start = janet_gethalfrange(argv, 1, length, "start");
 | 
			
		||||
        range.end = janet_gethalfrange(argv, 2, length, "end");
 | 
			
		||||
        if (range.end < range.start)
 | 
			
		||||
            range.end = range.start;
 | 
			
		||||
    }
 | 
			
		||||
    JanetArray *array = janet_array(range.end - range.start);
 | 
			
		||||
    if (array->data) {
 | 
			
		||||
        for (int32_t i = range.start; i < range.end; i++) {
 | 
			
		||||
            if (!ta_getter(src, janet_wrap_number(i), &array->data[i - range.start]))
 | 
			
		||||
                array->data[i - range.start] = janet_wrap_nil();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    array->count = range.end - range.start;
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_typed_array_copy_bytes(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 4, 5);
 | 
			
		||||
    JanetTArrayView *src = janet_getabstract(argv, 0, &janet_ta_view_type);
 | 
			
		||||
    size_t index_src = janet_getsize(argv, 1);
 | 
			
		||||
    JanetTArrayView *dst = janet_getabstract(argv, 2, &janet_ta_view_type);
 | 
			
		||||
    size_t index_dst = janet_getsize(argv, 3);
 | 
			
		||||
    size_t count = (argc == 5) ? janet_getsize(argv, 4) : 1;
 | 
			
		||||
    size_t src_atom_size = ta_type_sizes[src->type];
 | 
			
		||||
    size_t dst_atom_size = ta_type_sizes[dst->type];
 | 
			
		||||
    size_t step_src = src->stride * src_atom_size;
 | 
			
		||||
    size_t step_dst = dst->stride * dst_atom_size;
 | 
			
		||||
    size_t pos_src = (src->as.u8 - src->buffer->data) + (index_src * step_src);
 | 
			
		||||
    size_t pos_dst = (dst->as.u8 - dst->buffer->data) + (index_dst * step_dst);
 | 
			
		||||
    uint8_t *ps = src->buffer->data + pos_src, * pd = dst->buffer->data + pos_dst;
 | 
			
		||||
    if ((pos_dst + (count - 1)*step_dst + src_atom_size <= dst->buffer->size) &&
 | 
			
		||||
            (pos_src + (count - 1)*step_src + src_atom_size <= src->buffer->size)) {
 | 
			
		||||
        for (size_t i = 0; i < count; i++) {
 | 
			
		||||
            memmove(pd, ps, src_atom_size);
 | 
			
		||||
            pd += step_dst;
 | 
			
		||||
            ps += step_src;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panic("typed array copy out of bounds");
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_typed_array_swap_bytes(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 4, 5);
 | 
			
		||||
    JanetTArrayView *src = janet_getabstract(argv, 0, &janet_ta_view_type);
 | 
			
		||||
    size_t index_src = janet_getsize(argv, 1);
 | 
			
		||||
    JanetTArrayView *dst = janet_getabstract(argv, 2, &janet_ta_view_type);
 | 
			
		||||
    size_t index_dst = janet_getsize(argv, 3);
 | 
			
		||||
    size_t count = (argc == 5) ? janet_getsize(argv, 4) : 1;
 | 
			
		||||
    size_t src_atom_size = ta_type_sizes[src->type];
 | 
			
		||||
    size_t dst_atom_size = ta_type_sizes[dst->type];
 | 
			
		||||
    size_t step_src = src->stride * src_atom_size;
 | 
			
		||||
    size_t step_dst = dst->stride * dst_atom_size;
 | 
			
		||||
    size_t pos_src = (src->as.u8 - src->buffer->data) + (index_src * step_src);
 | 
			
		||||
    size_t pos_dst = (dst->as.u8 - dst->buffer->data) + (index_dst * step_dst);
 | 
			
		||||
    uint8_t *ps = src->buffer->data + pos_src, * pd = dst->buffer->data + pos_dst;
 | 
			
		||||
    uint8_t temp[TA_ATOM_MAXSIZE];
 | 
			
		||||
    if ((pos_dst + (count - 1)*step_dst + src_atom_size <= dst->buffer->size) &&
 | 
			
		||||
            (pos_src + (count - 1)*step_src + src_atom_size <= src->buffer->size)) {
 | 
			
		||||
        for (size_t i = 0; i < count; i++) {
 | 
			
		||||
            memcpy(temp, ps, src_atom_size);
 | 
			
		||||
            memcpy(ps, pd, src_atom_size);
 | 
			
		||||
            memcpy(pd, temp, src_atom_size);
 | 
			
		||||
            pd += step_dst;
 | 
			
		||||
            ps += step_src;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panic("typed array swap out of bounds");
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg ta_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "tarray/new", cfun_typed_array_new,
 | 
			
		||||
        JDOC("(tarray/new type size &opt stride offset tarray|buffer)\n\n"
 | 
			
		||||
             "Create new typed array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tarray/buffer", cfun_typed_array_buffer,
 | 
			
		||||
        JDOC("(tarray/buffer array|size)\n\n"
 | 
			
		||||
             "Return typed array buffer or create a new buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tarray/length", cfun_typed_array_size,
 | 
			
		||||
        JDOC("(tarray/length array|buffer)\n\n"
 | 
			
		||||
             "Return typed array or buffer size.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tarray/properties", cfun_typed_array_properties,
 | 
			
		||||
        JDOC("(tarray/properties array)\n\n"
 | 
			
		||||
             "Return typed array properties as a struct.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tarray/copy-bytes", cfun_typed_array_copy_bytes,
 | 
			
		||||
        JDOC("(tarray/copy-bytes src sindex dst dindex &opt count)\n\n"
 | 
			
		||||
             "Copy count elements (default 1) of src array from index sindex "
 | 
			
		||||
             "to dst array at position dindex "
 | 
			
		||||
             "memory can overlap.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tarray/swap-bytes", cfun_typed_array_swap_bytes,
 | 
			
		||||
        JDOC("(tarray/swap-bytes src sindex dst dindex &opt count)\n\n"
 | 
			
		||||
             "Swap count elements (default 1) between src array from index sindex "
 | 
			
		||||
             "and dst array at position dindex "
 | 
			
		||||
             "memory can overlap.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "tarray/slice", cfun_typed_array_slice,
 | 
			
		||||
        JDOC("(tarray/slice tarr &opt start end)\n\n"
 | 
			
		||||
             "Takes a slice of a typed array 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 typed array. By default, start is 0 and end is "
 | 
			
		||||
             "the size of the typed array. Returns a new janet array.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static JanetMethod tarray_view_methods[] = {
 | 
			
		||||
    {"length", cfun_typed_array_size},
 | 
			
		||||
    {"properties", cfun_typed_array_properties},
 | 
			
		||||
    {"copy-bytes", cfun_typed_array_copy_bytes},
 | 
			
		||||
    {"swap-bytes", cfun_typed_array_swap_bytes},
 | 
			
		||||
    {"slice", cfun_typed_array_slice},
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_typed_array(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, ta_cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_ta_buffer_type);
 | 
			
		||||
    janet_register_abstract_type(&janet_ta_view_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user