mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 01:23:04 +00:00 
			
		
		
		
	Compare commits
	
		
			1038 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					4dc281a05f | ||
| 
						 | 
					3a0af8caad | ||
| 
						 | 
					8ff2fecb26 | ||
| 
						 | 
					1855c6aed5 | ||
| 
						 | 
					d4c6643311 | ||
| 
						 | 
					e8c738002b | ||
| 
						 | 
					309c3aaeb8 | ||
| 
						 | 
					1f8bcadb3b | ||
| 
						 | 
					6f4af5fef8 | ||
| 
						 | 
					868cdb9f8b | ||
| 
						 | 
					2f76a429ef | ||
| 
						 | 
					a69799aa42 | ||
| 
						 | 
					139bef2142 | ||
| 
						 | 
					8ba142bcf4 | ||
| 
						 | 
					c49e4966f6 | ||
| 
						 | 
					516fa4e49d | ||
| 
						 | 
					6bf9f89429 | ||
| 
						 | 
					a0ddfcb109 | ||
| 
						 | 
					3df7921fdc | ||
| 
						 | 
					6172a9ca2d | ||
| 
						 | 
					4a40e57cf0 | ||
| 
						 | 
					cdedda4ca1 | ||
| 
						 | 
					e6babd84f7 | ||
| 
						 | 
					868ec1a7e3 | ||
| 
						 | 
					e08394c870 | ||
| 
						 | 
					a99500aebf | ||
| 
						 | 
					aa5095c23b | ||
| 
						 | 
					9e0f36e5a7 | ||
| 
						 | 
					d481d079ba | ||
| 
						 | 
					bc9ec7ac4a | ||
| 
						 | 
					6f7e81067c | ||
| 
						 | 
					af946f398e | ||
| 
						 | 
					c7ca26e9c7 | ||
| 
						 | 
					ef7129f45d | ||
| 
						 | 
					a20bdd334a | ||
| 
						 | 
					2ef49a92cc | ||
| 
						 | 
					75f56b68c6 | ||
| 
						 | 
					d34d319d89 | ||
| 
						 | 
					6660c1da38 | ||
| 
						 | 
					4e263b8c39 | ||
| 
						 | 
					3cb604df02 | ||
| 
						 | 
					af9dc7a69e | ||
| 
						 | 
					1247e69c78 | ||
| 
						 | 
					aab0e4315d | ||
| 
						 | 
					14f6517733 | ||
| 
						 | 
					5d75effb37 | ||
| 
						 | 
					ab4f18954b | ||
| 
						 | 
					e1460c65e8 | ||
| 
						 | 
					425a0fcf07 | ||
| 
						 | 
					7205ee5e0a | ||
| 
						 | 
					72c5db8910 | ||
| 
						 | 
					3067f4be3a | ||
| 
						 | 
					2aa1ccdd76 | ||
| 
						 | 
					0284df503f | ||
| 
						 | 
					2833a983d8 | ||
| 
						 | 
					39c6be7cb7 | ||
| 
						 | 
					fdc94c1353 | ||
| 
						 | 
					9cc4e48124 | ||
| 
						 | 
					34c7f15d6d | ||
| 
						 | 
					899a9b025e | ||
| 
						 | 
					deb4315383 | ||
| 
						 | 
					9a06660fdb | ||
| 
						 | 
					5c35d24e13 | ||
| 
						 | 
					03f99752a7 | ||
| 
						 | 
					fd37567c18 | ||
| 
						 | 
					6e38bf1578 | ||
| 
						 | 
					8b2d278840 | ||
| 
						 | 
					06aa0a124d | ||
| 
						 | 
					eb4595158d | ||
| 
						 | 
					32103441f1 | ||
| 
						 | 
					7ed0aa6630 | ||
| 
						 | 
					f690229f31 | ||
| 
						 | 
					f3bab72a86 | ||
| 
						 | 
					2bd63c2d27 | ||
| 
						 | 
					545d9e85e9 | ||
| 
						 | 
					21a4ab4ec7 | ||
| 
						 | 
					66fbbeb5ec | ||
| 
						 | 
					55879c7b6d | ||
| 
						 | 
					66c4e5a5e2 | ||
| 
						 | 
					884139e246 | ||
| 
						 | 
					c3d7b1541e | ||
| 
						 | 
					51ada4d70b | ||
| 
						 | 
					e3a5d52c5e | ||
| 
						 | 
					559fd70737 | ||
| 
						 | 
					e0dba85cbb | ||
| 
						 | 
					74c9cf03d0 | ||
| 
						 | 
					0774e79e4f | ||
| 
						 | 
					a3ec37741a | ||
| 
						 | 
					9bf5cd83c3 | ||
| 
						 | 
					f0da793f99 | ||
| 
						 | 
					684f3ac172 | ||
| 
						 | 
					3e5bd460a5 | ||
| 
						 | 
					3b1d787fbe | ||
| 
						 | 
					980f55ff69 | ||
| 
						 | 
					52ed68bfeb | ||
| 
						 | 
					be0d4c28e4 | ||
| 
						 | 
					79807bf2ab | ||
| 
						 | 
					e48ca1a03f | ||
| 
						 | 
					eae18ce973 | ||
| 
						 | 
					591344ca9d | ||
| 
						 | 
					fbe067823e | ||
| 
						 | 
					ffece911e6 | ||
| 
						 | 
					186afa9651 | ||
| 
						 | 
					6b3037106a | ||
| 
						 | 
					1bf22288ee | ||
| 
						 | 
					3cec470f25 | ||
| 
						 | 
					e1ec0d13ae | ||
| 
						 | 
					924fe97fc3 | ||
| 
						 | 
					504411eade | ||
| 
						 | 
					038ca1b9ca | ||
| 
						 | 
					544b192f8c | ||
| 
						 | 
					7748ccdb8e | ||
| 
						 | 
					64e29c6fce | ||
| 
						 | 
					acdf097998 | ||
| 
						 | 
					ba3107c1fa | ||
| 
						 | 
					9985f787eb | ||
| 
						 | 
					d6f41bcf98 | ||
| 
						 | 
					50bced49ad | ||
| 
						 | 
					4fd7470bbf | ||
| 
						 | 
					033c6f1fdb | ||
| 
						 | 
					6c58347916 | ||
| 
						 | 
					cccbdc164c | ||
| 
						 | 
					cea14a6869 | ||
| 
						 | 
					9b4b24edf7 | ||
| 
						 | 
					8b10a5fb7c | ||
| 
						 | 
					b0d0d9cad2 | ||
| 
						 | 
					d5c8eb048a | ||
| 
						 | 
					9abee3f29a | ||
| 
						 | 
					bf9b6b1301 | ||
| 
						 | 
					8cd57025a0 | ||
| 
						 | 
					faf60b6b1f | ||
| 
						 | 
					da2c1be49c | ||
| 
						 | 
					92c02449f4 | ||
| 
						 | 
					e381622a9a | ||
| 
						 | 
					b799223ebc | ||
| 
						 | 
					40ef224a95 | ||
| 
						 | 
					a4c20b6e1c | ||
| 
						 | 
					e6ee867f72 | ||
| 
						 | 
					468a31f515 | ||
| 
						 | 
					4d746794cc | ||
| 
						 | 
					02d2a66ef2 | ||
| 
						 | 
					4638baf545 | ||
| 
						 | 
					2be23d3768 | ||
| 
						 | 
					b39b1746ba | ||
| 
						 | 
					24f97510b0 | ||
| 
						 | 
					325d5399fa | ||
| 
						 | 
					d8f6fbf594 | ||
| 
						 | 
					21b3e4052c | ||
| 
						 | 
					bf2928805e | ||
| 
						 | 
					7d2bf334c8 | ||
| 
						 | 
					7446802a70 | ||
| 
						 | 
					077bf5ebae | ||
| 
						 | 
					c9bef39f96 | ||
| 
						 | 
					3740eadb7d | ||
| 
						 | 
					e29fa66a74 | ||
| 
						 | 
					ca5406c8e4 | ||
| 
						 | 
					7217caacd1 | ||
| 
						 | 
					8081082251 | ||
| 
						 | 
					1597ca0de5 | ||
| 
						 | 
					8c938ceff9 | ||
| 
						 | 
					65a6945ea5 | ||
| 
						 | 
					02640812af | ||
| 
						 | 
					ba761d5c35 | ||
| 
						 | 
					48a3b1f07f | ||
| 
						 | 
					4370cb77e7 | ||
| 
						 | 
					470e8f6fc7 | ||
| 
						 | 
					b270d88427 | ||
| 
						 | 
					66ce247129 | ||
| 
						 | 
					6ad016c587 | ||
| 
						 | 
					532dac1b95 | ||
| 
						 | 
					2a4bcc262f | ||
| 
						 | 
					1ce2361daf | ||
| 
						 | 
					6e8584e8e0 | ||
| 
						 | 
					121aa91139 | ||
| 
						 | 
					bbc07c72d3 | ||
| 
						 | 
					43b48fdbea | ||
| 
						 | 
					604f97aba1 | ||
| 
						 | 
					dc980081cd | ||
| 
						 | 
					981f03fef3 | ||
| 
						 | 
					d40133dc72 | ||
| 
						 | 
					c9fa586fce | ||
| 
						 | 
					b847a7d90b | ||
| 
						 | 
					8b67108dc8 | ||
| 
						 | 
					b559f9625a | ||
| 
						 | 
					1736c9b0f8 | ||
| 
						 | 
					4fb2d8d318 | ||
| 
						 | 
					95891eb0a5 | ||
| 
						 | 
					c133443eb7 | ||
| 
						 | 
					8f0641f36c | ||
| 
						 | 
					f48dbde736 | ||
| 
						 | 
					f2e4c1ae9a | ||
| 
						 | 
					a4aef38cc0 | ||
| 
						 | 
					b445ecde51 | ||
| 
						 | 
					a209a01284 | ||
| 
						 | 
					7037532943 | ||
| 
						 | 
					bb405ee1aa | ||
| 
						 | 
					ef23356309 | ||
| 
						 | 
					1613e2593c | ||
| 
						 | 
					5464a7a379 | ||
| 
						 | 
					bb1331e449 | ||
| 
						 | 
					acbebc5631 | ||
| 
						 | 
					e1c4fc29de | ||
| 
						 | 
					b903433284 | ||
| 
						 | 
					31a7fdc7b6 | ||
| 
						 | 
					9909adb665 | ||
| 
						 | 
					26f8ba48ee | ||
| 
						 | 
					29ea408980 | ||
| 
						 | 
					0bb7ca7441 | ||
| 
						 | 
					a992644c62 | ||
| 
						 | 
					1c15926e6f | ||
| 
						 | 
					c921315b3e | ||
| 
						 | 
					ab740f92db | ||
| 
						 | 
					1d7390fa7c | ||
| 
						 | 
					0ab96b8e47 | ||
| 
						 | 
					6f6edd37ef | ||
| 
						 | 
					f4282de068 | ||
| 
						 | 
					85c85c07b7 | ||
| 
						 | 
					7abcb1579a | ||
| 
						 | 
					7ce733cc16 | ||
| 
						 | 
					41a3c5f846 | ||
| 
						 | 
					7734e77dfc | ||
| 
						 | 
					257c8b65c2 | ||
| 
						 | 
					846c9e5e12 | ||
| 
						 | 
					685d2b460c | ||
| 
						 | 
					bd71e1cd02 | ||
| 
						 | 
					43a5e12449 | ||
| 
						 | 
					ca97510a52 | ||
| 
						 | 
					50b753cb44 | ||
| 
						 | 
					5ca6704c4d | ||
| 
						 | 
					49142fa385 | ||
| 
						 | 
					d631d29cb4 | ||
| 
						 | 
					01b7891347 | ||
| 
						 | 
					c786a4cbeb | ||
| 
						 | 
					1920ecd668 | ||
| 
						 | 
					c8827424e7 | ||
| 
						 | 
					cc066dd6a1 | ||
| 
						 | 
					eb0b37f729 | ||
| 
						 | 
					e552757edc | ||
| 
						 | 
					87b8dffe23 | ||
| 
						 | 
					81b5904188 | ||
| 
						 | 
					894a3b2fe2 | ||
| 
						 | 
					b75b3e3984 | ||
| 
						 | 
					dea4906144 | ||
| 
						 | 
					97e5117a3f | ||
| 
						 | 
					037215f7c4 | ||
| 
						 | 
					0277187fde | ||
| 
						 | 
					c80a3c1401 | ||
| 
						 | 
					5614f85ea1 | ||
| 
						 | 
					1a3c8692e6 | ||
| 
						 | 
					f2e8691ad5 | ||
| 
						 | 
					c94d7574bc | ||
| 
						 | 
					a38cb5df18 | ||
| 
						 | 
					5407868620 | ||
| 
						 | 
					7edf77561b | ||
| 
						 | 
					a78cbd91da | ||
| 
						 | 
					bb5c3773f1 | ||
| 
						 | 
					2e641a266d | ||
| 
						 | 
					3a787afec6 | ||
| 
						 | 
					34019222c2 | ||
| 
						 | 
					5f3378213b | ||
| 
						 | 
					547fda6a40 | ||
| 
						 | 
					2080ac3bda | ||
| 
						 | 
					61769c8f16 | ||
| 
						 | 
					934e091410 | ||
| 
						 | 
					7f7ee75954 | ||
| 
						 | 
					e76b8da269 | ||
| 
						 | 
					7e5f226480 | ||
| 
						 | 
					2f634184f0 | ||
| 
						 | 
					e3e01466ee | ||
| 
						 | 
					025918cfcc | ||
| 
						 | 
					28fb76e602 | ||
| 
						 | 
					b0f97393a3 | ||
| 
						 | 
					2a7041e751 | ||
| 
						 | 
					58c78d0d78 | ||
| 
						 | 
					eed158afdd | ||
| 
						 | 
					1c7505e04a | ||
| 
						 | 
					617da24942 | ||
| 
						 | 
					98bdbfd3d5 | ||
| 
						 | 
					b289f253c7 | ||
| 
						 | 
					aabae03305 | ||
| 
						 | 
					194d645551 | ||
| 
						 | 
					889d6f9e43 | ||
| 
						 | 
					151de093d0 | ||
| 
						 | 
					cc13e45f21 | ||
| 
						 | 
					7492a4c871 | ||
| 
						 | 
					d20543b92c | ||
| 
						 | 
					59aab2ebbd | ||
| 
						 | 
					08f7b1b9e5 | ||
| 
						 | 
					f2ac1c15e6 | ||
| 
						 | 
					eaf8f198c1 | ||
| 
						 | 
					2955286606 | ||
| 
						 | 
					40561340a8 | ||
| 
						 | 
					4f00a7db88 | ||
| 
						 | 
					acc21d0b76 | ||
| 
						 | 
					db5df70d0c | ||
| 
						 | 
					a6073dc237 | ||
| 
						 | 
					92c132381e | ||
| 
						 | 
					d0575e4087 | ||
| 
						 | 
					5ca48b96af | ||
| 
						 | 
					2a9f30fc8a | ||
| 
						 | 
					ba89a81a3e | ||
| 
						 | 
					5f32300592 | ||
| 
						 | 
					15b4d9363b | ||
| 
						 | 
					ceca0e7f0e | ||
| 
						 | 
					700770b883 | ||
| 
						 | 
					8365037be5 | ||
| 
						 | 
					dfaba7daa6 | ||
| 
						 | 
					5756934144 | ||
| 
						 | 
					7b3ab2727f | ||
| 
						 | 
					714ba808dd | ||
| 
						 | 
					6e94e03baa | ||
| 
						 | 
					ac98dbccb8 | ||
| 
						 | 
					6e3355d7f2 | ||
| 
						 | 
					97907906c5 | ||
| 
						 | 
					eb84200f28 | ||
| 
						 | 
					caaa26e153 | ||
| 
						 | 
					030dd747e9 | ||
| 
						 | 
					dccb98bb92 | ||
| 
						 | 
					e356b7141c | ||
| 
						 | 
					4cae7e6d5d | ||
| 
						 | 
					cc07b4a89a | ||
| 
						 | 
					7e8154e648 | ||
| 
						 | 
					dfee997e45 | ||
| 
						 | 
					f6b7cb9c49 | ||
| 
						 | 
					4452d0e0f5 | ||
| 
						 | 
					7fba44ccce | ||
| 
						 | 
					6f1695ecd4 | ||
| 
						 | 
					76acbf9bb6 | ||
| 
						 | 
					2769a62bb3 | ||
| 
						 | 
					160dd830a0 | ||
| 
						 | 
					aafc595e3a | ||
| 
						 | 
					202783c67a | ||
| 
						 | 
					f11b2c5a0d | ||
| 
						 | 
					e8a86013da | ||
| 
						 | 
					a89c377c92 | ||
| 
						 | 
					54d73f6722 | ||
| 
						 | 
					2e58f5f0d4 | ||
| 
						 | 
					e7ea39f410 | ||
| 
						 | 
					a125218d03 | ||
| 
						 | 
					55b8563c08 | ||
| 
						 | 
					aea1f59f6e | ||
| 
						 | 
					ab27b789e4 | ||
| 
						 | 
					3a1a59f1eb | ||
| 
						 | 
					c20a76cddb | ||
| 
						 | 
					1ef6db16ed | ||
| 
						 | 
					230b734663 | ||
| 
						 | 
					dc414f1239 | ||
| 
						 | 
					dafd2329c5 | ||
| 
						 | 
					12cfda1f58 | ||
| 
						 | 
					96b4e71704 | ||
| 
						 | 
					edb415d1a8 | ||
| 
						 | 
					72c1d1c484 | ||
| 
						 | 
					41a7154aa5 | ||
| 
						 | 
					346d024e48 | ||
| 
						 | 
					04a248dc37 | ||
| 
						 | 
					5defc3b914 | ||
| 
						 | 
					04ca945ecf | ||
| 
						 | 
					d687db71e7 | ||
| 
						 | 
					87f8fe14dd | ||
| 
						 | 
					af08124229 | ||
| 
						 | 
					2eadb21eb7 | ||
| 
						 | 
					8b97a0dbbf | ||
| 
						 | 
					69afa2a7a3 | ||
| 
						 | 
					da5328bae5 | ||
| 
						 | 
					a4325372e2 | ||
| 
						 | 
					4b96b73858 | ||
| 
						 | 
					bbae43f259 | ||
| 
						 | 
					14fedbf063 | ||
| 
						 | 
					ab974c409d | ||
| 
						 | 
					2040709585 | ||
| 
						 | 
					60214dc659 | ||
| 
						 | 
					b990d77f16 | ||
| 
						 | 
					d204e06e11 | ||
| 
						 | 
					f6b37dbc77 | ||
| 
						 | 
					fab65d6c40 | ||
| 
						 | 
					ff4d49f556 | ||
| 
						 | 
					dfa5fa1187 | ||
| 
						 | 
					1f4f69a5b6 | ||
| 
						 | 
					84f82f5465 | ||
| 
						 | 
					c911f7c47e | ||
| 
						 | 
					4d983e54b5 | ||
| 
						 | 
					33c000daea | ||
| 
						 | 
					7ff204ec44 | ||
| 
						 | 
					7c757ef3bf | ||
| 
						 | 
					2db7945d6f | ||
| 
						 | 
					81186bf262 | ||
| 
						 | 
					eeef5b0896 | ||
| 
						 | 
					8189b6fc11 | ||
| 
						 | 
					e5a2df93ab | ||
| 
						 | 
					c3f770da27 | ||
| 
						 | 
					49f66a936c | ||
| 
						 | 
					83dda98240 | ||
| 
						 | 
					b4ddbd0097 | ||
| 
						 | 
					cbe92bb985 | ||
| 
						 | 
					60c6a0d334 | ||
| 
						 | 
					1baab5eb61 | ||
| 
						 | 
					8fc8974b60 | ||
| 
						 | 
					ecb49c2e5e | ||
| 
						 | 
					29797b9eb0 | ||
| 
						 | 
					e181ee586b | ||
| 
						 | 
					7b7d742bec | ||
| 
						 | 
					612eaff9ff | ||
| 
						 | 
					d76ef187e8 | ||
| 
						 | 
					e01ab86a89 | ||
| 
						 | 
					89b59b4ffc | ||
| 
						 | 
					e367ecf806 | ||
| 
						 | 
					effc9e0f33 | ||
| 
						 | 
					da06e6c6e3 | ||
| 
						 | 
					c258bee54f | ||
| 
						 | 
					cde4a505cf | ||
| 
						 | 
					2802e66259 | ||
| 
						 | 
					3a3003029a | ||
| 
						 | 
					08bca8fb63 | ||
| 
						 | 
					7c7ff802fa | ||
| 
						 | 
					0945acc780 | ||
| 
						 | 
					64ec9f9cb6 | ||
| 
						 | 
					83f7de33c0 | ||
| 
						 | 
					ec2d7bf349 | ||
| 
						 | 
					f4c9064b79 | ||
| 
						 | 
					8ede16dc26 | ||
| 
						 | 
					27e400fba3 | ||
| 
						 | 
					37d6cb469b | ||
| 
						 | 
					100a82feb2 | ||
| 
						 | 
					90e5828d5d | ||
| 
						 | 
					b3e80308d4 | ||
| 
						 | 
					a7abe11105 | ||
| 
						 | 
					3c63a48df4 | ||
| 
						 | 
					fcb88e5a98 | ||
| 
						 | 
					a467b34de4 | ||
| 
						 | 
					a24cc77ff8 | ||
| 
						 | 
					d6675d9909 | ||
| 
						 | 
					fa163093d2 | ||
| 
						 | 
					e70f64e23d | ||
| 
						 | 
					6f605f8141 | ||
| 
						 | 
					d9419ef994 | ||
| 
						 | 
					7e8639a682 | ||
| 
						 | 
					452b303b4c | ||
| 
						 | 
					b0f1a4967d | ||
| 
						 | 
					9eb4c59c04 | ||
| 
						 | 
					0d42506cde | ||
| 
						 | 
					c8a13ce475 | ||
| 
						 | 
					05e3467d09 | ||
| 
						 | 
					90639e5068 | ||
| 
						 | 
					73c7711c78 | ||
| 
						 | 
					78f6b6a507 | ||
| 
						 | 
					84f0ab5356 | ||
| 
						 | 
					546437d799 | ||
| 
						 | 
					0f05aec563 | ||
| 
						 | 
					c9097623d6 | ||
| 
						 | 
					6392b37c47 | ||
| 
						 | 
					4fcc8075d4 | ||
| 
						 | 
					b2d6a55335 | ||
| 
						 | 
					1fea5f8fe7 | ||
| 
						 | 
					d3e52a2afb | ||
| 
						 | 
					d6ea1989cc | ||
| 
						 | 
					96513665d6 | ||
| 
						 | 
					b795d13f61 | ||
| 
						 | 
					970f9b3981 | ||
| 
						 | 
					be7dab4d17 | ||
| 
						 | 
					0e44ce5cba | ||
| 
						 | 
					1f8c2781dd | ||
| 
						 | 
					f381a9c773 | ||
| 
						 | 
					855a9a01fc | ||
| 
						 | 
					a5f237993d | ||
| 
						 | 
					c68264802a | ||
| 
						 | 
					742469a8bc | ||
| 
						 | 
					92928d5c4f | ||
| 
						 | 
					8320e25d64 | ||
| 
						 | 
					c16a9d8463 | ||
| 
						 | 
					f1819c916a | ||
| 
						 | 
					dd14b24d2a | ||
| 
						 | 
					050d7c12a3 | ||
| 
						 | 
					7e2c433abc | ||
| 
						 | 
					6713b23a65 | ||
| 
						 | 
					60078e7950 | ||
| 
						 | 
					69095fbb48 | ||
| 
						 | 
					c88a3c64e3 | ||
| 
						 | 
					771b0d0ab1 | ||
| 
						 | 
					c85310578b | ||
| 
						 | 
					60e2992158 | ||
| 
						 | 
					2795e8a8b7 | ||
| 
						 | 
					bdf14170a4 | ||
| 
						 | 
					10dcbc639a | ||
| 
						 | 
					6a9bb0f4e4 | ||
| 
						 | 
					c941e5a8f4 | ||
| 
						 | 
					be91414c7a | ||
| 
						 | 
					6839b603c8 | ||
| 
						 | 
					926b68d62e | ||
| 
						 | 
					d374e90033 | ||
| 
						 | 
					b168b0758a | ||
| 
						 | 
					54c66ecfc0 | ||
| 
						 | 
					1c158bd4ff | ||
| 
						 | 
					ff24143f54 | ||
| 
						 | 
					dd117e81c2 | ||
| 
						 | 
					f4744a18c6 | ||
| 
						 | 
					259d5fabd9 | ||
| 
						 | 
					d122a75efd | ||
| 
						 | 
					c9ea3ac304 | ||
| 
						 | 
					c63fe6ef8a | ||
| 
						 | 
					72ec89dfe9 | ||
| 
						 | 
					04805d106e | ||
| 
						 | 
					9aed578466 | ||
| 
						 | 
					77c5279296 | ||
| 
						 | 
					af75bf3b64 | ||
| 
						 | 
					a5157e868b | ||
| 
						 | 
					01a3d8f932 | ||
| 
						 | 
					f22472a644 | ||
| 
						 | 
					5cac8bcf9f | ||
| 
						 | 
					a2801fbef9 | ||
| 
						 | 
					0b14e913da | ||
| 
						 | 
					85155bb2b4 | ||
| 
						 | 
					dd8de1e9ac | ||
| 
						 | 
					c909835b0a | ||
| 
						 | 
					a18aafedfd | ||
| 
						 | 
					317ab6df6b | ||
| 
						 | 
					1fcaffe6b0 | ||
| 
						 | 
					3ae5c410dc | ||
| 
						 | 
					381128364e | ||
| 
						 | 
					0acf167e84 | ||
| 
						 | 
					f7ca6deeb0 | ||
| 
						 | 
					251486e4aa | ||
| 
						 | 
					c6467be60d | ||
| 
						 | 
					4dd512ad28 | ||
| 
						 | 
					28076b9385 | ||
| 
						 | 
					49dcc816ae | ||
| 
						 | 
					fa61c70103 | ||
| 
						 | 
					5ee6dbcdf4 | ||
| 
						 | 
					634219da2c | ||
| 
						 | 
					fbe3849b4b | ||
| 
						 | 
					bd2e335063 | ||
| 
						 | 
					96262e7d87 | ||
| 
						 | 
					c5da87b860 | ||
| 
						 | 
					848d4a1498 | ||
| 
						 | 
					70e23df6f8 | ||
| 
						 | 
					95af205681 | ||
| 
						 | 
					6dfb689d1f | ||
| 
						 | 
					462e74ef87 | ||
| 
						 | 
					c6aa536590 | ||
| 
						 | 
					c79480342b | ||
| 
						 | 
					a1cc5ca045 | ||
| 
						 | 
					7f74ff3dd7 | ||
| 
						 | 
					c4a95e9a1e | ||
| 
						 | 
					71f9e2b1d7 | ||
| 
						 | 
					16fe32215b | ||
| 
						 | 
					dd7342a6cf | ||
| 
						 | 
					35c88d10cd | ||
| 
						 | 
					42532de0eb | ||
| 
						 | 
					122e2a9378 | ||
| 
						 | 
					33c9395d79 | ||
| 
						 | 
					fc49aa359c | ||
| 
						 | 
					fcf37942a7 | ||
| 
						 | 
					9b42d5a5e9 | ||
| 
						 | 
					ba92dfcbe9 | ||
| 
						 | 
					fd03603adb | ||
| 
						 | 
					2008ddf8a8 | ||
| 
						 | 
					c56b876bfe | ||
| 
						 | 
					c4957d5dfb | ||
| 
						 | 
					068bd33afb | ||
| 
						 | 
					e9bd108be9 | ||
| 
						 | 
					4f2d1cdc00 | ||
| 
						 | 
					61cca10cf6 | ||
| 
						 | 
					dfbdd17dce | ||
| 
						 | 
					9078d3bd37 | ||
| 
						 | 
					5e1a8c86f9 | ||
| 
						 | 
					bf01bf631d | ||
| 
						 | 
					80c5ba32b5 | ||
| 
						 | 
					874cc79443 | ||
| 
						 | 
					3883460202 | ||
| 
						 | 
					f0dbc2e404 | ||
| 
						 | 
					4df1ac5b23 | ||
| 
						 | 
					1f6d0d342b | ||
| 
						 | 
					4625c28e6a | ||
| 
						 | 
					5536ba20a8 | ||
| 
						 | 
					ef398e9036 | ||
| 
						 | 
					0c73c3f1cd | ||
| 
						 | 
					7ae7984f3c | ||
| 
						 | 
					8286b33c52 | ||
| 
						 | 
					475775cc9d | ||
| 
						 | 
					11067d7a56 | ||
| 
						 | 
					5b05da65f0 | ||
| 
						 | 
					444e630783 | ||
| 
						 | 
					8951b8de7a | ||
| 
						 | 
					2abb87eb63 | ||
| 
						 | 
					32e8ac912d | ||
| 
						 | 
					e403fb4652 | ||
| 
						 | 
					daa37c22f5 | ||
| 
						 | 
					5a2a134c95 | ||
| 
						 | 
					b9acb6dfa5 | ||
| 
						 | 
					4e7ad3c7ce | ||
| 
						 | 
					ee0e1a2342 | ||
| 
						 | 
					f206b476d1 | ||
| 
						 | 
					dd2595c53f | ||
| 
						 | 
					545df28d71 | ||
| 
						 | 
					16f80b78cf | ||
| 
						 | 
					147bcce01b | ||
| 
						 | 
					f5877ac6d1 | ||
| 
						 | 
					adc41e31f4 | ||
| 
						 | 
					2e555a930f | ||
| 
						 | 
					bcba0c0279 | ||
| 
						 | 
					c7f382add6 | ||
| 
						 | 
					665b1e68d5 | ||
| 
						 | 
					2ca9300bf7 | ||
| 
						 | 
					81f62b246c | ||
| 
						 | 
					87badc71d2 | ||
| 
						 | 
					e5242c67ff | ||
| 
						 | 
					4355420994 | ||
| 
						 | 
					c357af02c2 | ||
| 
						 | 
					19576effbe | ||
| 
						 | 
					ecc6eb7497 | ||
| 
						 | 
					d0ac318980 | ||
| 
						 | 
					7b030fe70d | ||
| 
						 | 
					115556fcf2 | ||
| 
						 | 
					9760cf1f4e | ||
| 
						 | 
					47bb7fd21b | ||
| 
						 | 
					1c7ed8ca48 | ||
| 
						 | 
					6b268c5df4 | ||
| 
						 | 
					62f783f1dc | ||
| 
						 | 
					61c65f3df1 | ||
| 
						 | 
					05166b3673 | ||
| 
						 | 
					0a1c93b869 | ||
| 
						 | 
					788f91a36f | ||
| 
						 | 
					c831ecf5d2 | ||
| 
						 | 
					9e42ee153c | ||
| 
						 | 
					d457aa5951 | ||
| 
						 | 
					ab37ee6ebb | ||
| 
						 | 
					8655530b19 | ||
| 
						 | 
					27b1f59aa9 | ||
| 
						 | 
					cc2cc4db43 | ||
| 
						 | 
					20bcd95279 | ||
| 
						 | 
					d7954be5e5 | ||
| 
						 | 
					0ea77cabfb | ||
| 
						 | 
					0d46352ff4 | ||
| 
						 | 
					ffa0d5fe45 | ||
| 
						 | 
					a2c837a99c | ||
| 
						 | 
					13d8d11011 | ||
| 
						 | 
					2357b6162f | ||
| 
						 | 
					b4f242193d | ||
| 
						 | 
					7242ee0186 | ||
| 
						 | 
					3e742ffc4c | ||
| 
						 | 
					2ec12fe06f | ||
| 
						 | 
					c76e0ae685 | ||
| 
						 | 
					25ded775ad | ||
| 
						 | 
					cae4f19629 | ||
| 
						 | 
					04f6c7b156 | ||
| 
						 | 
					77b79e9899 | ||
| 
						 | 
					a55354357c | ||
| 
						 | 
					392d5d51df | ||
| 
						 | 
					9bc996a630 | ||
| 
						 | 
					7b709d4c68 | ||
| 
						 | 
					eab5f67c5c | ||
| 
						 | 
					6020106000 | ||
| 
						 | 
					12f470ed10 | ||
| 
						 | 
					945cbcfad6 | ||
| 
						 | 
					d53007739e | ||
| 
						 | 
					6eaf8272e1 | ||
| 
						 | 
					6fb83dce06 | ||
| 
						 | 
					52addc877d | ||
| 
						 | 
					53a5f3d2dc | ||
| 
						 | 
					711ee5a36d | ||
| 
						 | 
					cd09b696b5 | ||
| 
						 | 
					df1ca255a9 | ||
| 
						 | 
					811a5d93f4 | ||
| 
						 | 
					adbe361b9b | ||
| 
						 | 
					0f16f21677 | ||
| 
						 | 
					aa0de01e5f | ||
| 
						 | 
					785757f2f6 | ||
| 
						 | 
					01120dfc46 | ||
| 
						 | 
					a119eb4ef0 | ||
| 
						 | 
					0aa4c3d217 | ||
| 
						 | 
					3c0cc59d77 | ||
| 
						 | 
					7e1d095996 | ||
| 
						 | 
					cfa9fb6ee4 | ||
| 
						 | 
					9d23192614 | ||
| 
						 | 
					7c1a52ae65 | ||
| 
						 | 
					9aa1b9c740 | ||
| 
						 | 
					c4a4916055 | ||
| 
						 | 
					b402e0671a | ||
| 
						 | 
					8144f83b66 | ||
| 
						 | 
					cd2a55e268 | ||
| 
						 | 
					f92b5d69c8 | ||
| 
						 | 
					a8c21459c3 | ||
| 
						 | 
					4789b4c9f3 | ||
| 
						 | 
					ee1cd6f151 | ||
| 
						 | 
					dfcda296a3 | ||
| 
						 | 
					4d38fcb289 | ||
| 
						 | 
					cbdea8f331 | ||
| 
						 | 
					51d6a13510 | ||
| 
						 | 
					7b4eeecd9f | ||
| 
						 | 
					82eff7e082 | ||
| 
						 | 
					b922e36071 | ||
| 
						 | 
					7c75aeaad2 | ||
| 
						 | 
					2db9323671 | ||
| 
						 | 
					31ae93de19 | ||
| 
						 | 
					a81e9f23f0 | ||
| 
						 | 
					59f09a4386 | ||
| 
						 | 
					53400ecac1 | ||
| 
						 | 
					1b8928a8ec | ||
| 
						 | 
					e706494893 | ||
| 
						 | 
					894aea7ce7 | ||
| 
						 | 
					87167a21c9 | ||
| 
						 | 
					7c8f5ef811 | ||
| 
						 | 
					7aa4241662 | ||
| 
						 | 
					56a915b5b1 | ||
| 
						 | 
					90a0dfa35f | ||
| 
						 | 
					128d72785f | ||
| 
						 | 
					21a6017547 | ||
| 
						 | 
					a0964d44d5 | ||
| 
						 | 
					fb0859dfe6 | ||
| 
						 | 
					dadd6037bb | ||
| 
						 | 
					6f3eff3258 | ||
| 
						 | 
					02224d5aa9 | ||
| 
						 | 
					bfd2845077 | ||
| 
						 | 
					ba2e0489e6 | ||
| 
						 | 
					ca7c5b8b10 | ||
| 
						 | 
					6c43489fb2 | ||
| 
						 | 
					d76f671d37 | ||
| 
						 | 
					776ce586bc | ||
| 
						 | 
					adc3066dc8 | ||
| 
						 | 
					7fd2da1096 | ||
| 
						 | 
					451340e4c0 | ||
| 
						 | 
					a3e812b86d | ||
| 
						 | 
					a3f98091c4 | ||
| 
						 | 
					6720b34868 | ||
| 
						 | 
					781ed0dc67 | ||
| 
						 | 
					8f00848c7b | ||
| 
						 | 
					53aa19a916 | ||
| 
						 | 
					2dc04d2957 | ||
| 
						 | 
					306bdee673 | ||
| 
						 | 
					cff52ded58 | ||
| 
						 | 
					fbe658a724 | ||
| 
						 | 
					f9d0eb47b7 | ||
| 
						 | 
					078f50d45a | ||
| 
						 | 
					974a45c804 | ||
| 
						 | 
					760e4e3d68 | ||
| 
						 | 
					9ec5689d6b | ||
| 
						 | 
					c8b72431a3 | ||
| 
						 | 
					0eb913fb9a | ||
| 
						 | 
					fce27cb2e8 | ||
| 
						 | 
					1b6272db2e | ||
| 
						 | 
					b1c0ad5e42 | ||
| 
						 | 
					3f7cdcb6a7 | ||
| 
						 | 
					a25b030e36 | ||
| 
						 | 
					717fac02d1 | ||
| 
						 | 
					dcf8ba0edb | ||
| 
						 | 
					3ab2ae130b | ||
| 
						 | 
					6e6900fa3a | ||
| 
						 | 
					d7af4596e1 | ||
| 
						 | 
					1759151875 | ||
| 
						 | 
					a7ed3dea4b | ||
| 
						 | 
					cdcb774dc8 | ||
| 
						 | 
					d199c817dc | ||
| 
						 | 
					dc51bd09f7 | ||
| 
						 | 
					139e3fab25 | ||
| 
						 | 
					7a98f9aa02 | ||
| 
						 | 
					b53dd67e74 | ||
| 
						 | 
					e546731093 | ||
| 
						 | 
					d50c4ef6da | ||
| 
						 | 
					7d0b1955a2 | ||
| 
						 | 
					16cf7681f0 | ||
| 
						 | 
					12f09ad2d7 | ||
| 
						 | 
					b3e88a8d80 | ||
| 
						 | 
					761273bcc4 | ||
| 
						 | 
					1a75f68cb2 | ||
| 
						 | 
					1b0edf54f1 | ||
| 
						 | 
					caa6576719 | ||
| 
						 | 
					93bd2c11fa | ||
| 
						 | 
					2be09790a9 | ||
| 
						 | 
					bf6eae711a | ||
| 
						 | 
					69b68c0091 | ||
| 
						 | 
					6f1d5d3b73 | ||
| 
						 | 
					099a912992 | ||
| 
						 | 
					56b1ea3726 | ||
| 
						 | 
					d6391f2d70 | ||
| 
						 | 
					07910272e2 | ||
| 
						 | 
					1092013c2b | ||
| 
						 | 
					0db83bd787 | ||
| 
						 | 
					f55316eabc | ||
| 
						 | 
					840f59934e | ||
| 
						 | 
					75a9c59ad8 | ||
| 
						 | 
					adfccd33ae | ||
| 
						 | 
					9d41243c15 | ||
| 
						 | 
					e33e182eb0 | ||
| 
						 | 
					4dffd662f0 | ||
| 
						 | 
					5064d579d4 | ||
| 
						 | 
					540425a41b | ||
| 
						 | 
					4d21b582c7 | ||
| 
						 | 
					f288bc1790 | ||
| 
						 | 
					8942e348bd | ||
| 
						 | 
					9f27336827 | ||
| 
						 | 
					f517cccf7b | ||
| 
						 | 
					3a937ace51 | ||
| 
						 | 
					b8661f8bff | ||
| 
						 | 
					51828ab5f8 | ||
| 
						 | 
					84fe5d7f34 | ||
| 
						 | 
					2891d2b260 | ||
| 
						 | 
					edfb861a5f | ||
| 
						 | 
					88c1cf3ee7 | ||
| 
						 | 
					813e3fdcfd | ||
| 
						 | 
					bbe10e4938 | ||
| 
						 | 
					cb4903fa86 | ||
| 
						 | 
					ea45165db8 | ||
| 
						 | 
					1fba699ed4 | ||
| 
						 | 
					ce3d574c41 | ||
| 
						 | 
					7a601a7eb2 | ||
| 
						 | 
					9ec66ab826 | ||
| 
						 | 
					ebfa07f8ce | ||
| 
						 | 
					964a800d51 | ||
| 
						 | 
					5c05dec65a | ||
| 
						 | 
					bf6ebc4a68 | ||
| 
						 | 
					2e944931b3 | ||
| 
						 | 
					db67538311 | ||
| 
						 | 
					307c7e00e2 | ||
| 
						 | 
					45feb55483 | ||
| 
						 | 
					0a1d902f46 | ||
| 
						 | 
					959a577b5f | ||
| 
						 | 
					b91fe8be5a | ||
| 
						 | 
					d1f0a13ddc | ||
| 
						 | 
					c455bdad11 | ||
| 
						 | 
					bc1ef813c2 | ||
| 
						 | 
					603791c0ba | ||
| 
						 | 
					8091b1289f | ||
| 
						 | 
					cc0035b1d7 | ||
| 
						 | 
					ceba1ba4ee | ||
| 
						 | 
					468e13501c | ||
| 
						 | 
					32bf70571a | ||
| 
						 | 
					95f4bd8e23 | ||
| 
						 | 
					4c9624db64 | ||
| 
						 | 
					2cbf4d8ad1 | ||
| 
						 | 
					524c9b50d4 | ||
| 
						 | 
					d3147b661b | ||
| 
						 | 
					d3182dce51 | ||
| 
						 | 
					8763df1cd0 | ||
| 
						 | 
					15e05b692c | ||
| 
						 | 
					2bf5e341d3 | ||
| 
						 | 
					b53890ddae | ||
| 
						 | 
					93602ad9ea | ||
| 
						 | 
					191d0001f4 | ||
| 
						 | 
					1a04ce33f1 | ||
| 
						 | 
					babfe50550 | ||
| 
						 | 
					ff57b3eb72 | ||
| 
						 | 
					1837e89fe4 | ||
| 
						 | 
					24b8b0e382 | ||
| 
						 | 
					321a758ab9 | ||
| 
						 | 
					1a9c14acde | ||
| 
						 | 
					e8734c77b4 | ||
| 
						 | 
					1eb00a9f74 | ||
| 
						 | 
					922a21d359 | ||
| 
						 | 
					4a4f314768 | ||
| 
						 | 
					3c64596ea1 | ||
| 
						 | 
					33283b1b6e | ||
| 
						 | 
					2f89bdc672 | ||
| 
						 | 
					2d275c4782 | ||
| 
						 | 
					25156eb83e | ||
| 
						 | 
					39032b45c9 | ||
| 
						 | 
					821a8dca3b | ||
| 
						 | 
					0145b133a1 | ||
| 
						 | 
					b0b137d7f0 | ||
| 
						 | 
					b0c09153c2 | ||
| 
						 | 
					0485078c6c | ||
| 
						 | 
					7079cc43c9 | ||
| 
						 | 
					e7fca0051e | ||
| 
						 | 
					6273e56886 | ||
| 
						 | 
					8b9ad2dce8 | ||
| 
						 | 
					301cbb0e68 | ||
| 
						 | 
					5313963baf | ||
| 
						 | 
					f60348eee4 | ||
| 
						 | 
					a31e079f93 | ||
| 
						 | 
					556edc9f0d | ||
| 
						 | 
					17d0b7a985 | ||
| 
						 | 
					86e00e865e | ||
| 
						 | 
					5dda83dc73 | ||
| 
						 | 
					28439d822a | ||
| 
						 | 
					b1d8ee19ca | ||
| 
						 | 
					f7c556ed8d | ||
| 
						 | 
					5377e10532 | ||
| 
						 | 
					30522bbf7d | ||
| 
						 | 
					58374623b7 | ||
| 
						 | 
					7e7498350f | ||
| 
						 | 
					06c268c274 | ||
| 
						 | 
					9b36e2b145 | ||
| 
						 | 
					ca75f8dc20 | ||
| 
						 | 
					6f2f3fdb68 | ||
| 
						 | 
					c903e49a4f | ||
| 
						 | 
					9121feb44f | ||
| 
						 | 
					7b42ed66f2 | ||
| 
						 | 
					fb26c9b2c4 | ||
| 
						 | 
					78ffb63429 | ||
| 
						 | 
					1213990b7d | ||
| 
						 | 
					c3af30d520 | ||
| 
						 | 
					2598123140 | ||
| 
						 | 
					40627191f3 | ||
| 
						 | 
					38dc844e85 | ||
| 
						 | 
					abc4405a76 | ||
| 
						 | 
					243c66442d | ||
| 
						 | 
					9afcec77f6 | ||
| 
						 | 
					70ad98cc6f | ||
| 
						 | 
					76cfbde933 | ||
| 
						 | 
					f200bd9594 | ||
| 
						 | 
					4d4ca7bb36 | ||
| 
						 | 
					78c3c6dafa | ||
| 
						 | 
					6d859dec67 | ||
| 
						 | 
					3563e7e1aa | ||
| 
						 | 
					cb898fabf4 | ||
| 
						 | 
					5899671d96 | ||
| 
						 | 
					8c1eb23aa1 | ||
| 
						 | 
					b564087db0 | ||
| 
						 | 
					1748e8510e | ||
| 
						 | 
					742c5bb639 | ||
| 
						 | 
					297de01d95 | ||
| 
						 | 
					fb31c3b46d | ||
| 
						 | 
					ba2beffcd8 | ||
| 
						 | 
					2eb2dddb59 | ||
| 
						 | 
					0601d851d0 | ||
| 
						 | 
					b731f6ab03 | ||
| 
						 | 
					0403e306ed | ||
| 
						 | 
					d393fbf360 | ||
| 
						 | 
					4cc680965c | ||
| 
						 | 
					ba08e487cb | ||
| 
						 | 
					d37eda4e9b | ||
| 
						 | 
					3960d0f6de | ||
| 
						 | 
					5be5e5b58f | ||
| 
						 | 
					04ac9b8e32 | ||
| 
						 | 
					409a8a3a43 | ||
| 
						 | 
					1ba3f72e4c | ||
| 
						 | 
					3e5e9e57e9 | ||
| 
						 | 
					02e5e49de2 | ||
| 
						 | 
					43438d3824 | ||
| 
						 | 
					8f82d19fd1 | ||
| 
						 | 
					ee450bcd77 | ||
| 
						 | 
					553b4d9428 | ||
| 
						 | 
					df145f4bc9 | ||
| 
						 | 
					fa55283f62 | ||
| 
						 | 
					9e163db491 | ||
| 
						 | 
					286230f477 | ||
| 
						 | 
					3ba2c7e7e8 | ||
| 
						 | 
					b4f5e5bc00 | ||
| 
						 | 
					f580d2e41a | ||
| 
						 | 
					cd197e8be3 | ||
| 
						 | 
					51cf6465ff | ||
| 
						 | 
					a1feb32a2f | ||
| 
						 | 
					7478ad115f | ||
| 
						 | 
					9d8e338a11 | ||
| 
						 | 
					ed4163cfde | ||
| 
						 | 
					bd95f742c0 | ||
| 
						 | 
					463e6d9316 | ||
| 
						 | 
					3358811788 | ||
| 
						 | 
					a45509d28e | ||
| 
						 | 
					9ba94d2c6b | ||
| 
						 | 
					a4de83b3a3 | ||
| 
						 | 
					68a12d1d17 | ||
| 
						 | 
					c97d3cf359 | ||
| 
						 | 
					4721337c7c | ||
| 
						 | 
					2b36ed967c | ||
| 
						 | 
					3bb8f1ac8d | ||
| 
						 | 
					617ec7f565 | ||
| 
						 | 
					dc259b9f8e | ||
| 
						 | 
					7b31a87b3c | ||
| 
						 | 
					37a430c97c | ||
| 
						 | 
					f264cb0b18 | ||
| 
						 | 
					6ea530cc48 | ||
| 
						 | 
					a0abf307b4 | ||
| 
						 | 
					55cf9f5e1c | ||
| 
						 | 
					b89f0fac7b | ||
| 
						 | 
					8b3b3182bd | ||
| 
						 | 
					97c64f27ff | ||
| 
						 | 
					e548e1f6e0 | ||
| 
						 | 
					7ea1c7d85a | ||
| 
						 | 
					e08235b575 | ||
| 
						 | 
					783c672130 | ||
| 
						 | 
					5351a6b2ed | ||
| 
						 | 
					a110b103e8 | ||
| 
						 | 
					c26f573620 | ||
| 
						 | 
					f06e9ae30c | ||
| 
						 | 
					f5d208d5d6 | ||
| 
						 | 
					7fb8c4a68d | ||
| 
						 | 
					647fc56d47 | ||
| 
						 | 
					597d84e263 | ||
| 
						 | 
					977b0c3c0c | ||
| 
						 | 
					1b0d6de735 | ||
| 
						 | 
					2f5bb7774e | ||
| 
						 | 
					5565f02dbd | ||
| 
						 | 
					17a131ac21 | ||
| 
						 | 
					9a5cfe9f75 | ||
| 
						 | 
					cc936d9977 | ||
| 
						 | 
					e9911fee4d | ||
| 
						 | 
					aefde67aa2 | ||
| 
						 | 
					a1ea62a923 | ||
| 
						 | 
					7209ced446 | ||
| 
						 | 
					db63d352a2 | ||
| 
						 | 
					289de840fd | ||
| 
						 | 
					cb34a8b620 | ||
| 
						 | 
					95c633914f | ||
| 
						 | 
					d033412b1f | ||
| 
						 | 
					9c5e97144d | ||
| 
						 | 
					8b96289e2f | ||
| 
						 | 
					51ff43e2f2 | ||
| 
						 | 
					1e30f4f973 | ||
| 
						 | 
					36f66661f7 | ||
| 
						 | 
					de27fc15b6 | ||
| 
						 | 
					f9f90ba1d6 | ||
| 
						 | 
					51bf8a3538 | ||
| 
						 | 
					7b033a48a3 | ||
| 
						 | 
					1b420f69aa | ||
| 
						 | 
					6a187a384b | ||
| 
						 | 
					ac5de1f96e | ||
| 
						 | 
					6c917f686a | ||
| 
						 | 
					328ee94412 | ||
| 
						 | 
					de9951594e | ||
| 
						 | 
					561fc15ae9 | ||
| 
						 | 
					d65814c53f | ||
| 
						 | 
					803f17aa90 | ||
| 
						 | 
					08a3687eb5 | ||
| 
						 | 
					c4035b2273 | ||
| 
						 | 
					5c364e0f7c | ||
| 
						 | 
					9cfc3d9d37 | ||
| 
						 | 
					b5fdd30b77 | ||
| 
						 | 
					280292d3f5 | ||
| 
						 | 
					c593d864be | ||
| 
						 | 
					6d17348c72 | ||
| 
						 | 
					536648ec19 | ||
| 
						 | 
					b5e32a9ce5 | ||
| 
						 | 
					4077822e37 | ||
| 
						 | 
					e2d8750625 | ||
| 
						 | 
					79f5751375 | ||
| 
						 | 
					106437bd45 | ||
| 
						 | 
					b7cd13bb0b | ||
| 
						 | 
					b1a4f05b5a | ||
| 
						 | 
					ce2079104a | ||
| 
						 | 
					d64e9b6263 | ||
| 
						 | 
					123710078d | ||
| 
						 | 
					ec0d0ba368 | ||
| 
						 | 
					3f434f2a44 | ||
| 
						 | 
					71d8e6b4cd | ||
| 
						 | 
					a78af0a7fb | ||
| 
						 | 
					117ae196fd | ||
| 
						 | 
					4c211c8dce | ||
| 
						 | 
					c10d9b9d9d | ||
| 
						 | 
					b68b0a256e | 
@@ -9,4 +9,3 @@ tasks:
 | 
			
		||||
    gmake
 | 
			
		||||
    gmake test
 | 
			
		||||
    sudo gmake install
 | 
			
		||||
    gmake test-install
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								.builds/linux.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.builds/linux.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
image: archlinux
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
- meson
 | 
			
		||||
tasks:
 | 
			
		||||
- with-epoll: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup with-epoll --buildtype=release
 | 
			
		||||
    cd with-epoll
 | 
			
		||||
    meson configure -Depoll=true
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
- no-epoll: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup no-epoll --buildtype=release
 | 
			
		||||
    cd no-epoll
 | 
			
		||||
    meson configure -Depoll=false
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
    sudo ninja install
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
image: openbsd/latest
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
- meson
 | 
			
		||||
tasks:
 | 
			
		||||
- build: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build --buildtype=release
 | 
			
		||||
    cd build
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
@@ -1,23 +0,0 @@
 | 
			
		||||
image: openbsd/latest
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
- meson
 | 
			
		||||
tasks:
 | 
			
		||||
- build: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build --buildtype=release
 | 
			
		||||
    cd build
 | 
			
		||||
    meson configure -Dsingle_threaded=true
 | 
			
		||||
    meson configure -Dnanbox=false
 | 
			
		||||
    meson configure -Ddynamic_modules=false
 | 
			
		||||
    meson configure -Ddocstrings=false
 | 
			
		||||
    meson configure -Dnet=false
 | 
			
		||||
    meson configure -Dsourcemaps=false
 | 
			
		||||
    meson configure -Dpeg=false
 | 
			
		||||
    meson configure -Dassembler=false
 | 
			
		||||
    meson configure -Dint_types=false
 | 
			
		||||
    meson configure -Dtyped_arrays=false
 | 
			
		||||
    meson configure -Dreduced_os=true
 | 
			
		||||
    meson configure -Dprf=false
 | 
			
		||||
    ninja # will not pass tests but should build
 | 
			
		||||
@@ -3,10 +3,30 @@ sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
- gmake
 | 
			
		||||
- meson
 | 
			
		||||
tasks:
 | 
			
		||||
- build: |
 | 
			
		||||
- gmake: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    gmake
 | 
			
		||||
    gmake test
 | 
			
		||||
    doas gmake install
 | 
			
		||||
    gmake test-install
 | 
			
		||||
- meson_min: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dtyped_array=false -Dreduced_os=true
 | 
			
		||||
    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
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
*.janet linguist-language=Clojure
 | 
			
		||||
 | 
			
		||||
*.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
 | 
			
		||||
							
								
								
									
										54
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
name: Release
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    tags:
 | 
			
		||||
      - "v*.*.*"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
 | 
			
		||||
  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:
 | 
			
		||||
    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: |
 | 
			
		||||
            ./*.zip
 | 
			
		||||
            ./*.msi
 | 
			
		||||
							
								
								
									
										34
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
name: Test
 | 
			
		||||
 | 
			
		||||
on: [push, pull_request]
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										13
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -32,6 +32,10 @@ lockfile.janet
 | 
			
		||||
# Local directory for testing
 | 
			
		||||
local
 | 
			
		||||
 | 
			
		||||
# Common test files I use.
 | 
			
		||||
temp.janet
 | 
			
		||||
scratch.janet
 | 
			
		||||
 | 
			
		||||
# Emscripten
 | 
			
		||||
*.bc
 | 
			
		||||
janet.js
 | 
			
		||||
@@ -43,6 +47,7 @@ janet.wasm
 | 
			
		||||
 | 
			
		||||
# Generate test files
 | 
			
		||||
*.out
 | 
			
		||||
.orig
 | 
			
		||||
 | 
			
		||||
# Tools
 | 
			
		||||
xxd
 | 
			
		||||
@@ -50,6 +55,7 @@ xxd.exe
 | 
			
		||||
 | 
			
		||||
# VSCode
 | 
			
		||||
.vs
 | 
			
		||||
.clangd
 | 
			
		||||
 | 
			
		||||
# Swap files
 | 
			
		||||
*.swp
 | 
			
		||||
@@ -61,6 +67,10 @@ tags
 | 
			
		||||
vgcore.*
 | 
			
		||||
*.out.*
 | 
			
		||||
 | 
			
		||||
# Wix artifacts
 | 
			
		||||
*.msi
 | 
			
		||||
*.wixpdb
 | 
			
		||||
 | 
			
		||||
# Created by https://www.gitignore.io/api/c
 | 
			
		||||
 | 
			
		||||
### C ###
 | 
			
		||||
@@ -131,3 +141,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"
 | 
			
		||||
							
								
								
									
										280
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										280
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,7 +1,285 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## 1.10.1 - 2020-06-14
 | 
			
		||||
## 1.19.1 - 2021-12-04
 | 
			
		||||
- Add an optional `prefix` paramater to `debug/stacktrace` to allow printing prettier error messages.
 | 
			
		||||
- Remove appveyor for CI pipeline
 | 
			
		||||
- Fixed a bug that prevented sending threaded abstracts over threaded channels.
 | 
			
		||||
- Fix bug in the `map` function with arity at least 3.
 | 
			
		||||
 | 
			
		||||
## 1.19.0 - 2021-11-27
 | 
			
		||||
- Add `math/log-gamma` to replace `math/gamma`, and change `math/gamma` to be the expected gamma function.
 | 
			
		||||
- Fix leaking file-descriptors in os/spawn and os/execute.
 | 
			
		||||
- Ctrl-C will now raise SIGINT.
 | 
			
		||||
- Allow quoted literals in the `match` macro to behave as expected in patterns.
 | 
			
		||||
- Fix windows net related bug for TCP servers.
 | 
			
		||||
- Allow evaluating ev streams with dofile.
 | 
			
		||||
- Fix `ev` related bug with operations on already closed file descriptors.
 | 
			
		||||
- Add struct and table agnostic `getproto` function.
 | 
			
		||||
- Add a number of functions related to structs.
 | 
			
		||||
- Add prototypes to structs. Structs can now inherit from other structs, just like tables.
 | 
			
		||||
- Create a struct with a prototype with `struct/with-proto`.
 | 
			
		||||
- Deadlocked channels will no longer exit early - instead they will hang, which is more intuitive.
 | 
			
		||||
 | 
			
		||||
## 1.18.1 - 2021-10-16
 | 
			
		||||
- Fix some documentation typos
 | 
			
		||||
- Fix - Set pipes passed to subprocess to blocking mode.
 | 
			
		||||
- Fix `-r` switch in repl.
 | 
			
		||||
 | 
			
		||||
## 1.18.0 - 2021-10-10
 | 
			
		||||
- Allow `ev/cancel` to work on already scheduled fibers.
 | 
			
		||||
- Fix bugs with ev/ module.
 | 
			
		||||
- Add optional `base` argument to scan-number
 | 
			
		||||
- Add `-i` flag to janet binary to make it easier to run image files from the command line
 | 
			
		||||
- Remove `thread/` module.
 | 
			
		||||
- Add `(number ...)` pattern to peg for more efficient number parsing using Janet's
 | 
			
		||||
  scan-number function without immediate string creation.
 | 
			
		||||
 | 
			
		||||
## 1.17.2 - 2021-09-18
 | 
			
		||||
- Remove include of windows.h from janet.h. This caused issues on certain projects.
 | 
			
		||||
- Fix formatting in doc-format to better handle special characters in signatures.
 | 
			
		||||
- Fix some marshalling bugs.
 | 
			
		||||
- Add optional Makefile target to install jpm as well.
 | 
			
		||||
- Supervisor channels in threads will no longer include a wasteful copy of the fiber in every
 | 
			
		||||
  message across a thread.
 | 
			
		||||
- Allow passing a closure to `ev/thread` as well as a whole fiber.
 | 
			
		||||
- Allow passing a closure directly to `ev/go` to spawn fibers on the event loop.
 | 
			
		||||
 | 
			
		||||
## 1.17.1 - 2021-08-29
 | 
			
		||||
- Fix docstring typos
 | 
			
		||||
- Add `make install-jpm-git` to make jpm co-install simpler if using the Makefile.
 | 
			
		||||
- Fix bugs with starting ev/threads and fiber marshaling.
 | 
			
		||||
 | 
			
		||||
## 1.17.0 - 2021-08-21
 | 
			
		||||
- Add the `-E` flag for one-liners with the `short-fn` syntax for argument passing.
 | 
			
		||||
- Add support for threaded abstract types. Threaded abstract types can easily be shared between threads.
 | 
			
		||||
- Deprecate the `thread` library. Use threaded channels and ev instead.
 | 
			
		||||
- Channels can now be marshalled.
 | 
			
		||||
- Add the ability to close channels with `ev/chan-close` (or `:close`).
 | 
			
		||||
- Add threaded channels with `ev/thread-chan`.
 | 
			
		||||
- Add `JANET_FN` and `JANET_REG` macros to more easily define C functions that export their source mapping information.
 | 
			
		||||
- Add `janet_interpreter_interupt` and `janet_loop1_interrupt` to interrupt the interpreter while running.
 | 
			
		||||
- Add `table/clear`
 | 
			
		||||
- Add build option to disable the threading library without disabling all threads.
 | 
			
		||||
- Remove JPM from the main Janet distribution. Instead, JPM must be installed
 | 
			
		||||
  separately like any other package.
 | 
			
		||||
- Fix issue with `ev/go` when called with an initial value and supervisor.
 | 
			
		||||
- Add the C API functions `janet_vm_save` and `janet_vm_load` to allow
 | 
			
		||||
saving and restoring the entire VM state.
 | 
			
		||||
 | 
			
		||||
## 1.16.1 - 2021-06-09
 | 
			
		||||
- Add `maclintf` - a utility for adding linting messages when inside macros.
 | 
			
		||||
- Print source code of offending line on compiler warnings and errors.
 | 
			
		||||
- Fix some issues with linting and re-add missing `make docs`.
 | 
			
		||||
- Allow controlling linting with dynamic bindings `:lint-warn`, `:lint-error`, and `:lint-levels`.
 | 
			
		||||
- Add `-w` and `-x` command line flags to the `janet` binary to set linting thresholds.
 | 
			
		||||
  linting thresholds are as follows:
 | 
			
		||||
    - :none - will never be trigger.
 | 
			
		||||
    - :relaxed - will only trigger on `:relaxed` lints.
 | 
			
		||||
    - :normal - will trigger on `:relaxed` and `:normal` lints.
 | 
			
		||||
    - :strict - will trigger on `:strict`, `:normal`, and `:relaxed` lints. This will catch the most issues
 | 
			
		||||
      but can be distracting.
 | 
			
		||||
 | 
			
		||||
## 1.16.0 - 2021-05-30
 | 
			
		||||
- Add color documentation to the `doc` macro - enable/disable with `(dyn :doc-color)`.
 | 
			
		||||
- Remove simpler HTML docs from distribution - use website or built-in documentation instead.
 | 
			
		||||
- Add compiler warnings and deprecation levels.
 | 
			
		||||
- Add `as-macro` to make using macros within quasiquote easier to do hygienically.
 | 
			
		||||
- Expose `JANET_OUT_OF_MEMORY` as part of the Janet API.
 | 
			
		||||
- Add `native-deps` option to `declare-native` in `jpm`. This lets native libraries link to other
 | 
			
		||||
  native libraries when building with jpm.
 | 
			
		||||
- Remove the `tarray` module. The functionality of typed arrays will be moved to an external module
 | 
			
		||||
  that can be installed via `jpm`.
 | 
			
		||||
- Add `from-pairs` to core.
 | 
			
		||||
- Add `JPM_OS_WHICH` environment variable to jpm to allow changing auto-detection behavior.
 | 
			
		||||
- The flychecker will consider any top-level calls of functions that start with `define-` to
 | 
			
		||||
  be safe to execute and execute them. This allows certain patterns (like spork/path) to be
 | 
			
		||||
  better processed by the flychecker.
 | 
			
		||||
 | 
			
		||||
## 1.15.5 - 2021-04-25
 | 
			
		||||
- Add `declare-headers` to jpm.
 | 
			
		||||
- Fix error using unix pipes on BSDs.
 | 
			
		||||
- Support .cc and .cxx extensions in `jpm` for C++ code.
 | 
			
		||||
- Change networking code to not create as many HUP errors.
 | 
			
		||||
- Add `net/shutdown` to close sockets in one direction without hang ups.
 | 
			
		||||
- Update code for printing the debug repl
 | 
			
		||||
 | 
			
		||||
## 1.15.4 - 2021-03-16
 | 
			
		||||
- Increase default nesting depth of pretty printing to `JANET_RECURSION_GUARD`
 | 
			
		||||
- Update meson.build
 | 
			
		||||
- Add option to automatically add shebang line in installed scripts with `jpm`.
 | 
			
		||||
- Add `partition-by` and `group-by` to the core.
 | 
			
		||||
- Sort keys in pretty printing output.
 | 
			
		||||
 | 
			
		||||
## 1.15.3 - 2021-02-28
 | 
			
		||||
- Fix a fiber bug that occured in deeply nested fibers
 | 
			
		||||
- Add `unref` combinator to pegs.
 | 
			
		||||
- Small docstring changes.
 | 
			
		||||
 | 
			
		||||
## 1.15.2 - 2021-02-15
 | 
			
		||||
- Fix bug in windows version of `os/spawn` and `os/execute` with setting environment variables.
 | 
			
		||||
- Fix documentation typos.
 | 
			
		||||
- Fix peg integer reading combinators when used with capture tags.
 | 
			
		||||
 | 
			
		||||
## 1.15.0 - 2021-02-08
 | 
			
		||||
- Fix `gtim` and `ltim` bytecode instructions on non-integer values.
 | 
			
		||||
- Clean up output of flychecking to be the same as the repl.
 | 
			
		||||
- Change behavior of `debug/stacktrace` with a nil error value.
 | 
			
		||||
- Add optional argument to `parser/produce`.
 | 
			
		||||
- Add `no-core` option to creating standalone binaries to make execution faster.
 | 
			
		||||
- Fix bug where a buffer overflow could be confused with an out of memory error.
 | 
			
		||||
- Change error output to `file:line:column: message`. Column is in bytes - tabs
 | 
			
		||||
  are considered to have width 1 (instead of 8).
 | 
			
		||||
 | 
			
		||||
## 1.14.2 - 2021-01-23
 | 
			
		||||
- Allow `JANET_PROFILE` env variable to load a profile before loading the repl.
 | 
			
		||||
- Update `tracev` macro to allow `def` and `var` inside to work as expected.
 | 
			
		||||
- Use `(dyn :peg-grammar)` for passing a default grammar to `peg/compile` instead of loading
 | 
			
		||||
  `default-peg-grammar` directly from the root environment.
 | 
			
		||||
- Add `ev/thread` for combining threading with the event loop.
 | 
			
		||||
- Add `ev/do-thread` to make `ev/thread` easier to use.
 | 
			
		||||
- Automatically set supervisor channel in `net/accept-loop` and `net/server` correctly.
 | 
			
		||||
 | 
			
		||||
## 1.14.1 - 2021-01-18
 | 
			
		||||
- Add `doc-of` for reverse documentation lookup.
 | 
			
		||||
- Add `ev/give-supervsior` to send a message to the supervising channel.
 | 
			
		||||
- Add `ev/gather` and `chan` argument to `ev/go`. This new argument allows "supervisor channels"
 | 
			
		||||
  for fibers to enable structured concurrency.
 | 
			
		||||
- Make `-k` flag work on stdin if no files are given.
 | 
			
		||||
- Add `flycheck` function to core.
 | 
			
		||||
- Make `backmatch` and `backref` more expressive in pegs.
 | 
			
		||||
- Fix buggy `string/split`.
 | 
			
		||||
- Add `fiber/last-value` to get the value that was last yielded, errored, or signaled
 | 
			
		||||
  by a fiber.
 | 
			
		||||
- Remove `:generate` verb from `loop` macros. Instead, use the `:in` verb
 | 
			
		||||
  which will now work on fibers as well as other data structures.
 | 
			
		||||
- Define `next`, `get`, and `in` for fibers. This lets
 | 
			
		||||
  `each`, `map`, and similar iteration macros can now iterate over fibers.
 | 
			
		||||
- Remove macro `eachy`, which can be replaced by `each`.
 | 
			
		||||
- Add `dflt` argument to find-index.
 | 
			
		||||
- Deprecate `file/popen` in favor of `os/spawn`.
 | 
			
		||||
- Add `:all` keyword to `ev/read` and `net/read` to make them more like `file/read`. However, we
 | 
			
		||||
  do not provide any `:line` option as that requires buffering.
 | 
			
		||||
- Change repl behavior to make Ctrl-C raise SIGINT on posix. The old behavior for Ctrl-C,
 | 
			
		||||
  to clear the current line buffer, has been moved to Ctrl-Q.
 | 
			
		||||
- Importing modules that start with `/` is now the only way to import from project root.
 | 
			
		||||
  Before, this would import from / on disk. Previous imports that did not start with `.` or `/`
 | 
			
		||||
  are now unambiguously importing from the syspath, instead of checking both the syspath and
 | 
			
		||||
  the project root. This is backwards incompatible and dependencies should be updated for this.
 | 
			
		||||
- Change hash function for numbers.
 | 
			
		||||
- Improve error handling of `dofile`.
 | 
			
		||||
- Bug fixes in networking and subprocess code.
 | 
			
		||||
- Use markdown formatting in more places for docstrings.
 | 
			
		||||
 | 
			
		||||
## 1.13.1 - 2020-12-13
 | 
			
		||||
- Pretty printing a table with a prototype will look for `:_name` instead of `:name`
 | 
			
		||||
  in the prototype table to tag the output.
 | 
			
		||||
- `match` macro implementation changed to be tail recursive.
 | 
			
		||||
- Adds a :preload loader which allows one to manually put things into `module/cache`.
 | 
			
		||||
- Add `buffer/push` function.
 | 
			
		||||
- Backtick delimited strings and buffers are now reindented based on the column of the
 | 
			
		||||
  opening delimiter. Whitespace in columns to the left of the starting column is ignored unless
 | 
			
		||||
  there are non-space/non-newline characters in that region, in which case the old behavior is preserved.
 | 
			
		||||
- Argument to `(error)` combinator in PEGs is now optional.
 | 
			
		||||
- Add `(line)` and `(column)` combinators to PEGs to capture source line and column.
 | 
			
		||||
  This should make error reporting a bit easier.
 | 
			
		||||
- Add `merge-module` to core.
 | 
			
		||||
- During installation and release, merge janetconf.h into janet.h for easier install.
 | 
			
		||||
- Add `upscope` special form.
 | 
			
		||||
- `os/execute` and `os/spawn` can take streams for redirecting IO.
 | 
			
		||||
- Add `:parser` and `:read` parameters to `run-context`.
 | 
			
		||||
- Add `os/open` if ev is enabled.
 | 
			
		||||
- Add `os/pipe` if ev is enabled.
 | 
			
		||||
- Add `janet_thread_current(void)` to C API
 | 
			
		||||
- Add integer parsing forms to pegs. This makes parsing many binary protocols easier.
 | 
			
		||||
- Lots of updates to networking code - now can use epoll (or poll) on linux and IOCP on windows.
 | 
			
		||||
- Add `ev/` module. This exposes a fiber scheduler, queues, timeouts, and other functionality to users
 | 
			
		||||
  for single threaded cooperative scheduling and asynchronous IO.
 | 
			
		||||
- Add `net/accept-loop` and `net/listen`. These functions break down `net/server` into it's essential parts
 | 
			
		||||
  and are more flexible. They also allow further improvements to these utility functions.
 | 
			
		||||
- Various small bug fixes.
 | 
			
		||||
 | 
			
		||||
## 1.12.2 - 2020-09-20
 | 
			
		||||
- Add janet\_try and janet\_restore to C API.
 | 
			
		||||
- Fix `os/execute` regression on windows.
 | 
			
		||||
- Add :pipe option to `os/spawn`.
 | 
			
		||||
- Fix docstring typos.
 | 
			
		||||
 | 
			
		||||
## 1.12.1 - 2020-09-07
 | 
			
		||||
- Make `zero?`, `one?`, `pos?`, and `neg?` polymorphic.
 | 
			
		||||
- Add C++ support to jpm and improve C++ interop in janet.h.
 | 
			
		||||
- Add `%t` formatter to `printf`, `string/format`, and other formatter functions.
 | 
			
		||||
- Expose `janet_cfuns_prefix` in C API.
 | 
			
		||||
- Add `os/proc-wait` and `os/proc-kill` for interacting with processes.
 | 
			
		||||
- Add `janet_getjfile` to C API.
 | 
			
		||||
- Allow redirection of stdin, stdout, and stderr by passing keywords in the env table in `os/spawn` and `os/execute`.
 | 
			
		||||
- Add `os/spawn` to get a core/process back instead of an exit code as in `os/execute`.
 | 
			
		||||
  When called like this, `os/execute` returns immediately.
 | 
			
		||||
- Add `:x` flag to os/execute to raise error when exit code is non-zero.
 | 
			
		||||
- Don't run `main` when flychecking.
 | 
			
		||||
- Add `:n` flag to `file/open` to raise an error if file cannot be opened.
 | 
			
		||||
- Fix import macro to not try and coerce everything to a string.
 | 
			
		||||
- Allow passing a second argument to `disasm`.
 | 
			
		||||
- Add `cancel`. Resumes a fiber but makes it immediately error at the yield point.
 | 
			
		||||
- Allow multi-line paste into built in repl.
 | 
			
		||||
- Add `(curenv)`.
 | 
			
		||||
- Change `net/read`, `net/chunk`, and `net/write` to raise errors in the case of failures.
 | 
			
		||||
- Add `janet_continue_signal` to C API. This indirectly enables C functions that yield to the event loop
 | 
			
		||||
  to raise errors or other signals.
 | 
			
		||||
- Update meson build script to fix bug on Debian's version of meson
 | 
			
		||||
- Add `xprint`, `xprin`, `xprintf`, and `xprinf`.
 | 
			
		||||
- `net/write` now raises an error message if write fails.
 | 
			
		||||
- Fix issue with SIGPIPE on macOS and BSDs.
 | 
			
		||||
 | 
			
		||||
## 1.11.3 - 2020-08-03
 | 
			
		||||
- Add `JANET_HASHSEED` environment variable when `JANET_PRF` is enabled.
 | 
			
		||||
- Expose `janet_cryptorand` in C API.
 | 
			
		||||
- Properly initialize PRF in default janet program
 | 
			
		||||
- Add `index-of` to core library.
 | 
			
		||||
- Add `-fPIC` back to core CFLAGS (non-optional when compiling default client with Makefile)
 | 
			
		||||
- Fix defaults on Windows for ARM
 | 
			
		||||
- Fix defaults on NetBSD.
 | 
			
		||||
 | 
			
		||||
## 1.11.1 - 2020-07-25
 | 
			
		||||
- Fix jpm and git with multiple git installs on Windows
 | 
			
		||||
- Fix importing a .so file in the current directory
 | 
			
		||||
- Allow passing byte sequence types directly to typed-array constructors.
 | 
			
		||||
- Fix bug sending files between threads.
 | 
			
		||||
- Disable PRF by default.
 | 
			
		||||
- Update the soname.
 | 
			
		||||
 | 
			
		||||
## 1.11.0 - 2020-07-18
 | 
			
		||||
- Add `forever` macro.
 | 
			
		||||
- Add `any?` predicate to core.
 | 
			
		||||
- Add `jpm list-pkgs` subcommand to see which package aliases are in the listing.
 | 
			
		||||
- Add `jpm list-installed` subcommand to see which packages are installed.
 | 
			
		||||
- Add `math/int-min`, `math/int-max`, `math/int32-min`, and `math/int32-max` for getting integer limits.
 | 
			
		||||
- The gc interval is now autotuned, to prevent very bad gc behavior.
 | 
			
		||||
- Improvements to the bytecode compiler, Janet will now generate more efficient bytecode.
 | 
			
		||||
- Add `peg/find`, `peg/find-all`, `peg/replace`, and `peg/replace-all`
 | 
			
		||||
- Add `math/nan`
 | 
			
		||||
- Add `forv` macro
 | 
			
		||||
- Add `symbol/slice`
 | 
			
		||||
- Add `keyword/slice`
 | 
			
		||||
- Allow cross compilation with Makefile.
 | 
			
		||||
- Change `compare-primitve` to `cmp` and make it more efficient.
 | 
			
		||||
- Add `reverse!` for reversing an array or buffer in place.
 | 
			
		||||
- `janet_dobytes` and `janet_dostring` return parse errors in \*out
 | 
			
		||||
- Add `repeat` macro for iterating something n times.
 | 
			
		||||
- Add `eachy` (each yield) macro for iterating a fiber.
 | 
			
		||||
- Fix `:generate` verb in loop macro to accept non symbols as bindings.
 | 
			
		||||
- Add `:h`, `:h+`, and `:h*` in `default-peg-grammar` for hexidecimal digits.
 | 
			
		||||
- Fix `%j` formatter to print numbers precisely (using the `%.17g` format string to printf).
 | 
			
		||||
 | 
			
		||||
## 1.10.1 - 2020-06-18
 | 
			
		||||
- Expose `janet_table_clear` in API.
 | 
			
		||||
- Respect `JANET_NO_PROCESSES` define when building
 | 
			
		||||
- Fix `jpm` rules having multiple copies of the same dependency.
 | 
			
		||||
- Fix `jpm` install in some cases.
 | 
			
		||||
- Add `array/trim` and `buffer/trim` to shrink the backing capacity of these types
 | 
			
		||||
  to their current length.
 | 
			
		||||
 | 
			
		||||
## 1.10.0 - 2020-06-14
 | 
			
		||||
- Hardcode default jpm paths on install so env variables are needed in fewer cases.
 | 
			
		||||
- Add `:no-compile` to `create-executable` option for jpm.
 | 
			
		||||
- Fix bug with the `trace` function.
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,6 @@ Please read this document before making contributions.
 | 
			
		||||
  on how to reproduce it. If it is a compiler or language bug, please try to include a minimal
 | 
			
		||||
  example. This means don't post all 200 lines of code from your project, but spend some time
 | 
			
		||||
  distilling the problem to just the relevant code.
 | 
			
		||||
* Add the `bug` tag to the issue.
 | 
			
		||||
 | 
			
		||||
## Contributing Changes
 | 
			
		||||
 | 
			
		||||
@@ -30,8 +29,7 @@ may require changes before being merged.
 | 
			
		||||
  the test folder and make sure it is run when`make test` is invoked.
 | 
			
		||||
* Be consistent with the style. For C this means follow the indentation and style in
 | 
			
		||||
  other files (files have MIT license at top, 4 spaces indentation, no trailing
 | 
			
		||||
          whitespace, cuddled brackets, etc.) Use `make format` to
 | 
			
		||||
  automatically format your C code with
 | 
			
		||||
  whitespace, cuddled brackets, etc.) Use `make format` to automatically format your C code with
 | 
			
		||||
  [astyle](http://astyle.sourceforge.net/astyle.html). You will probably need
 | 
			
		||||
  to install this, but it can be installed with most package managers.
 | 
			
		||||
 | 
			
		||||
@@ -75,4 +73,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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										146
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2020 Calvin Rose
 | 
			
		||||
# Copyright (c) 2021 Calvin Rose
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -27,19 +27,28 @@ PREFIX?=/usr/local
 | 
			
		||||
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
 | 
			
		||||
JANET_STATIC_LIBRARY=build/libjanet.a
 | 
			
		||||
JANET_PATH?=$(LIBDIR)/janet
 | 
			
		||||
MANPATH?=$(PREFIX)/share/man/man1/
 | 
			
		||||
PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig
 | 
			
		||||
JANET_MANPATH?=$(PREFIX)/share/man/man1/
 | 
			
		||||
JANET_PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig
 | 
			
		||||
JANET_DIST_DIR?=janet-dist
 | 
			
		||||
JPM_TAG?=master
 | 
			
		||||
DEBUGGER=gdb
 | 
			
		||||
SONAME_SETTER=-Wl,-soname,
 | 
			
		||||
 | 
			
		||||
CFLAGS:=$(CFLAGS) -std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fPIC -O2 -fvisibility=hidden
 | 
			
		||||
LDFLAGS:=$(LDFLAGS) -rdynamic
 | 
			
		||||
# For cross compilation
 | 
			
		||||
HOSTCC?=$(CC)
 | 
			
		||||
HOSTAR?=$(AR)
 | 
			
		||||
CFLAGS?=-O2
 | 
			
		||||
LDFLAGS?=-rdynamic
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
# For installation
 | 
			
		||||
LDCONFIG:=ldconfig "$(LIBDIR)"
 | 
			
		||||
@@ -53,14 +62,21 @@ ifeq ($(UNAME), Darwin)
 | 
			
		||||
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 #####
 | 
			
		||||
@@ -90,6 +106,7 @@ 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/fiber.c \
 | 
			
		||||
				   src/core/gc.c \
 | 
			
		||||
				   src/core/inttypes.c \
 | 
			
		||||
@@ -104,14 +121,13 @@ JANET_CORE_SOURCES=src/core/abstract.c \
 | 
			
		||||
				   src/core/regalloc.c \
 | 
			
		||||
				   src/core/run.c \
 | 
			
		||||
				   src/core/specials.c \
 | 
			
		||||
				   src/core/state.c \
 | 
			
		||||
				   src/core/string.c \
 | 
			
		||||
				   src/core/strtod.c \
 | 
			
		||||
				   src/core/struct.c \
 | 
			
		||||
				   src/core/symcache.c \
 | 
			
		||||
				   src/core/table.c \
 | 
			
		||||
				   src/core/thread.c \
 | 
			
		||||
				   src/core/tuple.c \
 | 
			
		||||
				   src/core/typedarray.c \
 | 
			
		||||
				   src/core/util.c \
 | 
			
		||||
				   src/core/value.c \
 | 
			
		||||
				   src/core/vector.c \
 | 
			
		||||
@@ -131,54 +147,56 @@ JANET_BOOT_HEADERS=src/boot/tests.h
 | 
			
		||||
##########################################################
 | 
			
		||||
 | 
			
		||||
JANET_BOOT_OBJECTS=$(patsubst src/%.c,build/%.boot.o,$(JANET_CORE_SOURCES) $(JANET_BOOT_SOURCES))
 | 
			
		||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) $(CFLAGS)
 | 
			
		||||
 | 
			
		||||
$(JANET_BOOT_OBJECTS): $(JANET_BOOT_HEADERS)
 | 
			
		||||
 | 
			
		||||
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
 | 
			
		||||
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
 | 
			
		||||
	$(CC) $(BOOT_CFLAGS) -o $@ -c $<
 | 
			
		||||
 | 
			
		||||
build/janet_boot: $(JANET_BOOT_OBJECTS)
 | 
			
		||||
	$(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
 | 
			
		||||
SONAME=libjanet.so.1.19
 | 
			
		||||
 | 
			
		||||
build/shell.c: src/mainclient/shell.c
 | 
			
		||||
build/c/shell.c: src/mainclient/shell.c
 | 
			
		||||
	cp $< $@
 | 
			
		||||
 | 
			
		||||
build/janet.h: src/include/janet.h
 | 
			
		||||
	cp $< $@
 | 
			
		||||
build/janet.h: $(JANET_TARGET) src/include/janet.h src/conf/janetconf.h
 | 
			
		||||
	./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h src/conf/janetconf.h $@
 | 
			
		||||
 | 
			
		||||
build/janetconf.h: src/conf/janetconf.h
 | 
			
		||||
	cp $< $@
 | 
			
		||||
 | 
			
		||||
build/janet.o: build/janet.c build/janet.h build/janetconf.h
 | 
			
		||||
	$(CC) $(CFLAGS) -c $< -o $@ -I build
 | 
			
		||||
build/janet.o: build/c/janet.c src/conf/janetconf.h src/include/janet.h
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
build/shell.o: build/shell.c build/janet.h build/janetconf.h
 | 
			
		||||
	$(CC) $(CFLAGS) -c $< -o $@ -I build
 | 
			
		||||
build/shell.o: build/c/shell.c src/conf/janetconf.h src/include/janet.h
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
$(JANET_TARGET): build/janet.o build/shell.o
 | 
			
		||||
	$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ $(CLIBS)
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_LIBRARY): build/janet.o build/shell.o
 | 
			
		||||
	$(CC) $(LDFLAGS) $(CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_STATIC_LIBRARY): build/janet.o build/shell.o
 | 
			
		||||
	$(AR) rcs $@ $^
 | 
			
		||||
	$(HOSTAR) rcs $@ $^
 | 
			
		||||
 | 
			
		||||
###################
 | 
			
		||||
##### Testing #####
 | 
			
		||||
###################
 | 
			
		||||
 | 
			
		||||
# Testing assumes HOSTCC=CC
 | 
			
		||||
 | 
			
		||||
TEST_SCRIPTS=$(wildcard test/suite*.janet)
 | 
			
		||||
 | 
			
		||||
repl: $(JANET_TARGET)
 | 
			
		||||
@@ -195,12 +213,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
 | 
			
		||||
@@ -212,13 +228,20 @@ 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/
 | 
			
		||||
	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 #####
 | 
			
		||||
@@ -233,10 +256,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)' > $@
 | 
			
		||||
@@ -252,33 +271,41 @@ 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'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(LIBDIR)'
 | 
			
		||||
	cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')'
 | 
			
		||||
	cp $(JANET_STATIC_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.a'
 | 
			
		||||
	ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.so'
 | 
			
		||||
	ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(DESTDIR)$(LIBDIR)/$(SONAME)
 | 
			
		||||
	cp -rf build/jpm '$(DESTDIR)$(BINDIR)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(MANPATH)'
 | 
			
		||||
	cp janet.1 '$(DESTDIR)$(MANPATH)'
 | 
			
		||||
	cp jpm.1 '$(DESTDIR)$(MANPATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(PKG_CONFIG_PATH)'
 | 
			
		||||
	cp build/janet.pc '$(DESTDIR)$(PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
 | 
			
		||||
	cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || true
 | 
			
		||||
 | 
			
		||||
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)$(LIBDIR)'/libjanet.*
 | 
			
		||||
	-rm '$(DESTDIR)$(PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	-rm '$(DESTDIR)$(MANPATH)/janet.1'
 | 
			
		||||
	-rm '$(DESTDIR)$(MANPATH)/jpm.1'
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_MANPATH)/janet.1'
 | 
			
		||||
	# -rm -rf '$(DESTDIR)$(JANET_PATH)'/* - err on the side of correctness here
 | 
			
		||||
 | 
			
		||||
#################
 | 
			
		||||
@@ -292,23 +319,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
 | 
			
		||||
	echo "JPM has been removed from default install."
 | 
			
		||||
 | 
			
		||||
help:
 | 
			
		||||
	@echo
 | 
			
		||||
@@ -333,4 +353,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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										126
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										126
									
								
								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/freebsd.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/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
 | 
			
		||||
@@ -92,7 +95,7 @@ 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`.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
@@ -104,7 +107,7 @@ make repl
 | 
			
		||||
 | 
			
		||||
### 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`.
 | 
			
		||||
 | 
			
		||||
@@ -115,6 +118,11 @@ gmake test
 | 
			
		||||
gmake repl
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 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 +139,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 +175,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 +202,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 +235,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 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,34 +150,6 @@ FOR %%a in (janet-*-windows-*-installer.msi) DO (
 | 
			
		||||
)
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
@rem Test the installation.
 | 
			
		||||
:TESTINSTALL
 | 
			
		||||
pushd test\install
 | 
			
		||||
call jpm clean
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm test
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --modpath=. install https://github.com/janet-lang/json.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call build\testexec
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose quickbin testexec.janet build\testexec2.exe
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call build\testexec2.exe
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/jhydro.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/path.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/argparse.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
popd
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
:TESTINSTALLFAIL
 | 
			
		||||
popd
 | 
			
		||||
goto :TESTFAIL
 | 
			
		||||
 | 
			
		||||
@rem build, test, dist, install. Useful for local dev.
 | 
			
		||||
:ALL
 | 
			
		||||
call %0 build
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,22 @@
 | 
			
		||||
# Example of dst bytecode assembly
 | 
			
		||||
 | 
			
		||||
# Fibonacci sequence, implemented with naive recursion.
 | 
			
		||||
(def fibasm (asm '{
 | 
			
		||||
  arity 1
 | 
			
		||||
  bytecode [
 | 
			
		||||
    (ltim 1 0 0x2)      # $1 = $0 < 2
 | 
			
		||||
    (jmpif 1 :done)     # if ($1) goto :done
 | 
			
		||||
    (lds 1)             # $1 = self
 | 
			
		||||
    (addim 0 0 -0x1)    # $0 = $0 - 1
 | 
			
		||||
    (push 0)            # push($0), push argument for next function call
 | 
			
		||||
    (call 2 1)          # $2 = call($1)
 | 
			
		||||
    (addim 0 0 -0x1)    # $0 = $0 - 1
 | 
			
		||||
    (push 0)            # push($0)
 | 
			
		||||
    (call 0 1)          # $0 = call($1)
 | 
			
		||||
    (add 0 0 2)        # $0 = $0 + $2 (integers)
 | 
			
		||||
    :done
 | 
			
		||||
    (ret 0)             # return $0
 | 
			
		||||
  ]
 | 
			
		||||
}))
 | 
			
		||||
(def fibasm
 | 
			
		||||
  (asm
 | 
			
		||||
    '{:arity 1
 | 
			
		||||
      :bytecode @[(ltim 1 0 0x2)   # $1 = $0 < 2
 | 
			
		||||
                  (jmpif 1 :done)  # if ($1) goto :done
 | 
			
		||||
                  (lds 1)          # $1 = self
 | 
			
		||||
                  (addim 0 0 -0x1) # $0 = $0 - 1
 | 
			
		||||
                  (push 0)         # push($0), push argument for next function call
 | 
			
		||||
                  (call 2 1)       # $2 = call($1)
 | 
			
		||||
                  (addim 0 0 -0x1) # $0 = $0 - 1
 | 
			
		||||
                  (push 0)         # push($0)
 | 
			
		||||
                  (call 0 1)       # $0 = call($1)
 | 
			
		||||
                  (add 0 0 2)      # $0 = $0 + $2 (integers)
 | 
			
		||||
                  :done
 | 
			
		||||
                  (ret 0)          # return $0
 | 
			
		||||
]}))
 | 
			
		||||
 | 
			
		||||
# Test it
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								examples/async-execute.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								examples/async-execute.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
(defn dowork [name n]
 | 
			
		||||
  (print name " starting work...")
 | 
			
		||||
  (os/execute [(dyn :executable) "-e" (string "(os/sleep " n ")")])
 | 
			
		||||
  (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)
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
							
								
								
									
										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))
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								janet.1
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								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,11 @@ 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.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-d
 | 
			
		||||
Enable debug mode. On all terminating signals as well the debug signal, this will
 | 
			
		||||
@@ -167,6 +183,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,
 | 
			
		||||
@@ -194,11 +214,27 @@ Source should be a path to the Janet module to compile, and output should be the
 | 
			
		||||
resulting image. Output should usually end with the .jimage extension.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-l\ path
 | 
			
		||||
Load a Janet file before running a script or repl. Multiple files can be loaded
 | 
			
		||||
.BR \-i
 | 
			
		||||
When this flag is passed, a script passed to the interpreter will be treated as a janet image file
 | 
			
		||||
rather than a janet source file.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-l\ lib
 | 
			
		||||
Import a Janet module before running a script or repl. Multiple files can be loaded
 | 
			
		||||
in this manner, and exports from each file will be made available to the script
 | 
			
		||||
or repl.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-w\ level
 | 
			
		||||
Set the warning linting level for Janet.
 | 
			
		||||
This linting level should be one of :relaxed, :none, :strict, :normal, or a
 | 
			
		||||
Janet number. Any linting message that is of a greater lint level than this setting will be displayed as
 | 
			
		||||
a warning, but not stop compilation or execution.
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-x\ level
 | 
			
		||||
Set the error linting level for Janet.
 | 
			
		||||
This linting level should be one of :relaxed, :none, :strict, :normal, or a
 | 
			
		||||
Janet number. Any linting message that is of a greater lint level will cause a compilation error
 | 
			
		||||
and stop compilation.
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-
 | 
			
		||||
Stop parsing command line arguments. All arguments after this one will be considered file names
 | 
			
		||||
@@ -213,5 +249,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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										254
									
								
								jpm.1
									
									
									
									
									
								
							
							
						
						
									
										254
									
								
								jpm.1
									
									
									
									
									
								
							@@ -1,254 +0,0 @@
 | 
			
		||||
.TH JPM 1
 | 
			
		||||
.SH NAME
 | 
			
		||||
jpm \- the Janet Project Manager, a build tool for Janet 
 | 
			
		||||
.SH SYNOPSIS
 | 
			
		||||
.B jpm
 | 
			
		||||
[\fB\-\-flag ...\fR]
 | 
			
		||||
[\fB\-\-option=value ...\fR]
 | 
			
		||||
.IR command
 | 
			
		||||
.IR args ...
 | 
			
		||||
.SH DESCRIPTION
 | 
			
		||||
jpm is the build tool that ships with a standard Janet install. It is
 | 
			
		||||
used for building Janet projects, installing dependencies, installing
 | 
			
		||||
projects, building native modules, and exporting your Janet project to a
 | 
			
		||||
standalone executable. Although not required for working with Janet, it
 | 
			
		||||
removes much of the boilerplate with installing dependencies and
 | 
			
		||||
building native modules. jpm requires only Janet to run, and uses git
 | 
			
		||||
to install dependencies (jpm will work without git installed).
 | 
			
		||||
.SH DOCUMENTATION
 | 
			
		||||
 | 
			
		||||
jpm has several subcommands, each used for managing either a single Janet project or
 | 
			
		||||
all Janet modules installed on the system. Global commands, those that manage modules
 | 
			
		||||
at the system level, do things like install and uninstall packages, as well as clear the cache.
 | 
			
		||||
More interesting are the local commands. For more information on jpm usage, see https://janet-lang.org/docs/index.html
 | 
			
		||||
 | 
			
		||||
.SH FLAGS
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-nocolor
 | 
			
		||||
Disable color in the jpm debug repl.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-verbose
 | 
			
		||||
Print detailed messages of what jpm is doing, including compilation commands and other shell commands.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-test
 | 
			
		||||
If passed to jpm install, runs tests before installing. Will run tests recursively on dependencies.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-offline
 | 
			
		||||
Prevents jpm from going to network to get dependencies - all dependencies should be in the cache or this command will fail.
 | 
			
		||||
Use this flag with the deps and update-pkgs subcommands. This is not a surefire way to prevent a build script from accessing
 | 
			
		||||
the network, for example, a build script that invokes curl will still have network access.
 | 
			
		||||
 | 
			
		||||
.SH OPTIONS
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-modpath=/some/path
 | 
			
		||||
Set the path to install modules to. Defaults to $JANET_MODPATH, $JANET_PATH, or (dyn :syspath) in that order. You most likely don't need this.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-headerpath=/some/path
 | 
			
		||||
Set the path the jpm will include when building C source code. This lets
 | 
			
		||||
you specify the location of janet.h and janetconf.h on your system. On a
 | 
			
		||||
normal install, this option is not needed.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-binpath=/some/path
 | 
			
		||||
Set the path that jpm will install scripts and standalone executables to. Executables
 | 
			
		||||
defined via declare-execuatble or scripts declared via declare-binscript will be installed
 | 
			
		||||
here when jpm install is run. Defaults to $JANET_BINPATH, or a reasonable default for the system.
 | 
			
		||||
See JANET_BINPATH for more.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-libpath=/some/path
 | 
			
		||||
Sets the path jpm will use to look for libjanet.a for building standalone executables. libjanet.so
 | 
			
		||||
is \fBnot\fR used for building native modules or standalone executables, only
 | 
			
		||||
for linking into applications that want to embed janet as a dynamic module.
 | 
			
		||||
Linking statically might be a better idea, even in that case. Defaults to
 | 
			
		||||
$JANET_LIBPATH, or a reasonable default. See JANET_LIBPATH for more.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-compiler=$CC
 | 
			
		||||
Sets the compiler used for compiling native modules and standalone executables. Defaults
 | 
			
		||||
to cc.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-linker
 | 
			
		||||
Sets the linker used to create native modules and executables. Only used on windows, where
 | 
			
		||||
it defaults to link.exe.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-pkglist=https://github.com/janet-lang/pkgs.git
 | 
			
		||||
Sets the git repository for the package listing used to resolve shorthand package names.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-archiver=$AR
 | 
			
		||||
Sets the command used for creating static libraries, use for linking into the standalone executable.
 | 
			
		||||
Native modules are compiled twice, once a normal native module (shared object), and once as an
 | 
			
		||||
archive. Defaults to ar.
 | 
			
		||||
 | 
			
		||||
.SH COMMANDS
 | 
			
		||||
.TP
 | 
			
		||||
.BR help
 | 
			
		||||
Shows the usage text and exits immediately.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR build
 | 
			
		||||
Builds all artifacts specified in the project.janet file in the current directory. Artifacts will
 | 
			
		||||
be created in the ./build/ directory.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR install\ [\fBrepo...\fR]
 | 
			
		||||
 | 
			
		||||
When run with no arguments, installs all installable artifacts in the current project to
 | 
			
		||||
the current JANET_MODPATH for modules and JANET_BINPATH for executables and scripts. Can also
 | 
			
		||||
take an optional git repository URL and will install all artifacts in that repository instead.
 | 
			
		||||
When run with an argument, install does not need to be run from a jpm project directory. Will also
 | 
			
		||||
install multiple dependencies in one command.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR uninstall\ [\fBname...\fR]
 | 
			
		||||
Uninstall a project installed with install. uninstall expects the name of the project, not the
 | 
			
		||||
repository url, path to installed file or executable name. The name of the project must be specified
 | 
			
		||||
at the top of the project.janet file in the declare-project form. If no name is given, uninstalls
 | 
			
		||||
the current project if installed. Will also uninstall multiple packages in one command.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clean
 | 
			
		||||
Remove all artifacts created by jpm. This just deletes the build folder.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR test
 | 
			
		||||
Runs jpm tests. jpm will run all janet source files in the test directory as tests. A test
 | 
			
		||||
is considered failing if it exits with a non-zero exit code.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR deps
 | 
			
		||||
Install all dependencies that this project requires recursively. jpm does not
 | 
			
		||||
resolve dependency issues, like conflicting versions of the same module are required, or
 | 
			
		||||
different modules with the same name. Dependencies are installed with git, so deps requires
 | 
			
		||||
git to be on the PATH.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clear-cache
 | 
			
		||||
jpm caches git repositories that are needed to install modules from a remote
 | 
			
		||||
source in a global cache ($JANET_PATH/.cache). If these dependencies are out of
 | 
			
		||||
date or too large, clear-cache will remove the cache and jpm will rebuild it
 | 
			
		||||
when needed. clear-cache is a global command, so a project.janet is not
 | 
			
		||||
required.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clear-manifest
 | 
			
		||||
jpm creates a manifest directory that contains a list of all installed files.
 | 
			
		||||
By deleting this directory, jpm will think that nothing is installed and will
 | 
			
		||||
try reinstalling everything on the jpm deps or jpm load-lockfile commands. Be careful with
 | 
			
		||||
this command, as it may leave extra files on your system and shouldn't be needed
 | 
			
		||||
most of the time in a healthy install.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR run\ [\fBrule\fR]
 | 
			
		||||
Run a given rule defined in project.janet. Project definitions files (project.janet) usually
 | 
			
		||||
contain a few artifact declarations, which set up rules that jpm can then resolve, or execute.
 | 
			
		||||
A project.janet can also create custom rules to create arbitrary files or run arbitrary code, much
 | 
			
		||||
like make. run will run a single rule or build a single file.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR rules
 | 
			
		||||
List all rules that can be run via run. This is useful for exploring rules in the project.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR rule-tree\ [\fBroot\fR] [\fdepth\fR]
 | 
			
		||||
Show rule dependency tree in a pretty format. Optionally provide a rule to use as the tree
 | 
			
		||||
root, as well as a max depth to print. By default, prints the full tree for all rules. This
 | 
			
		||||
can be quite long, so it is recommended to give a root rule.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR show-paths
 | 
			
		||||
Show all of the paths used when installing and building artifacts.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR update-pkgs
 | 
			
		||||
Update the package listing by installing the 'pkgs' package. Same as jpm install pkgs
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR quickbin [\fBentry\fR] [\fBexecutable\fR]
 | 
			
		||||
Create a standalone, statically linked executable from a Janet source file that contains a main function.
 | 
			
		||||
The main function is the entry point of the program and will receive command line arguments
 | 
			
		||||
as function arguments. The entry file can import other modules, including native C modules, and
 | 
			
		||||
jpm will attempt to include the dependencies into the generated executable.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR debug-repl
 | 
			
		||||
Load the current project.janet file and start a repl in it's environment. This lets a user better
 | 
			
		||||
debug the project file, as well as run rules manually.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR make-lockfile\ [\fBfilename\fR]
 | 
			
		||||
Create a lockfile. A lockfile is a record that describes what dependencies were installed at the
 | 
			
		||||
time of the lockfile's creation, including exact versions. A lockfile can then be later used
 | 
			
		||||
to set up that environment on a different machine via load-lockfile. By default, the lockfile
 | 
			
		||||
is created at lockfile.jdn, although any path can be used.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR load-lockfile\ [\fBfilename\fR]
 | 
			
		||||
Install dependencies from a lockfile previously created with make-lockfile. By default, will look
 | 
			
		||||
for a lockfile at lockfile.jdn, although any path can be used.
 | 
			
		||||
 | 
			
		||||
.SH ENVIRONMENT
 | 
			
		||||
 | 
			
		||||
.B JANET_PATH
 | 
			
		||||
.RS
 | 
			
		||||
The location to look for Janet libraries. This is the only environment variable Janet needs to
 | 
			
		||||
find native and source code modules. If no JANET_PATH is set, Janet will look in
 | 
			
		||||
the default location set at compile time, which can be determined with (dyn :syspath)
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_MODPATH
 | 
			
		||||
.RS
 | 
			
		||||
The location that jpm will use to install libraries to. Defaults to JANET_PATH, but you could
 | 
			
		||||
set this to a different directory if you want to. Doing so would let you import Janet modules
 | 
			
		||||
on the normal system path (JANET_PATH or (dyn :syspath)), but install to a different directory. It is also a more reliable way to install
 | 
			
		||||
This variable is overwritten by the --modpath=/some/path if it is provided.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_HEADERPATH
 | 
			
		||||
.RS
 | 
			
		||||
The location that jpm will look for janet header files (janet.h and janetconf.h) that are used
 | 
			
		||||
to build native modules and standalone executables. If janet.h and janetconf.h are available as
 | 
			
		||||
default includes on your system, this value is not required. If not provided, will default to
 | 
			
		||||
<jpm script location>/../include/janet. The --headerpath=/some/path option will override this
 | 
			
		||||
variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_LIBPATH
 | 
			
		||||
.RS
 | 
			
		||||
Similar to JANET_HEADERPATH, this path is where jpm will look for
 | 
			
		||||
libjanet.a for creating standalong executables. This does not need to be
 | 
			
		||||
set on a normal install. 
 | 
			
		||||
If not provided, this will default to <jpm script location>/../lib.
 | 
			
		||||
The --libpath=/some/path option will override this variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_BINPATH
 | 
			
		||||
.RS
 | 
			
		||||
The directory where jpm will install binary scripts and executables to.
 | 
			
		||||
Defaults to
 | 
			
		||||
(dyn :syspath)/bin
 | 
			
		||||
The --binpath=/some/path will override this variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_PKGLIST
 | 
			
		||||
.RS
 | 
			
		||||
The git repository URL that contains a listing of packages. This allows installing packages with shortnames, which
 | 
			
		||||
is mostly a convenience. However, package dependencies can use short names, package listings
 | 
			
		||||
can be used to choose a particular set of dependency versions for a whole project.
 | 
			
		||||
 | 
			
		||||
.B JANET_GIT
 | 
			
		||||
.RS
 | 
			
		||||
An optional path to a git executable to use to clone git dependencies. By default, uses "git" on the current $PATH. You shouldn't need to set this
 | 
			
		||||
if you have a normal install of git.
 | 
			
		||||
 | 
			
		||||
.SH AUTHOR
 | 
			
		||||
Written by Calvin Rose <calsrose@gmail.com>
 | 
			
		||||
							
								
								
									
										103
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								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.10.0')
 | 
			
		||||
  default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
 | 
			
		||||
  version : '1.19.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,10 @@ 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'))
 | 
			
		||||
if get_option('os_name') != ''
 | 
			
		||||
  conf.set('JANET_OS_NAME', get_option('os_name'))
 | 
			
		||||
endif
 | 
			
		||||
@@ -110,6 +115,7 @@ core_src = [
 | 
			
		||||
  'src/core/corelib.c',
 | 
			
		||||
  'src/core/debug.c',
 | 
			
		||||
  'src/core/emit.c',
 | 
			
		||||
  'src/core/ev.c',
 | 
			
		||||
  'src/core/fiber.c',
 | 
			
		||||
  'src/core/gc.c',
 | 
			
		||||
  'src/core/inttypes.c',
 | 
			
		||||
@@ -124,14 +130,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 +161,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 +171,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 +224,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 +250,16 @@ janet_dep = declare_dependency(include_directories : incdir,
 | 
			
		||||
# pkgconfig
 | 
			
		||||
pkg = import('pkgconfig')
 | 
			
		||||
pkg.generate(libjanet,
 | 
			
		||||
  subdirs: 'janet',
 | 
			
		||||
  description: 'Library for the Janet programming language.')
 | 
			
		||||
 | 
			
		||||
# Installation
 | 
			
		||||
install_man('janet.1')
 | 
			
		||||
install_headers(['src/include/janet.h', jconf], subdir: 'janet')
 | 
			
		||||
install_data(sources : ['tools/.keep'], install_dir : join_paths(get_option('libdir'), 'janet'))
 | 
			
		||||
if get_option('peg') and not get_option('reduced_os') and get_option('processes')
 | 
			
		||||
  install_man('jpm.1')
 | 
			
		||||
  patched_jpm = custom_target('patched-jpm',
 | 
			
		||||
    input : ['tools/patch-jpm.janet', 'jpm'],
 | 
			
		||||
    install : true,
 | 
			
		||||
    install_dir : get_option('bindir'),
 | 
			
		||||
    build_by_default : true,
 | 
			
		||||
    output : ['jpm'],
 | 
			
		||||
    command : [janet_nativeclient, '@INPUT@', '@OUTPUT@',
 | 
			
		||||
      '--binpath=' + join_paths(get_option('prefix'), get_option('bindir')),
 | 
			
		||||
      '--libpath=' + join_paths(get_option('prefix'), get_option('libdir'), 'janet'),
 | 
			
		||||
      '--headerpath=' + join_paths(get_option('prefix'), get_option('includedir'))])
 | 
			
		||||
endif
 | 
			
		||||
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@'])
 | 
			
		||||
 
 | 
			
		||||
@@ -8,13 +8,17 @@ 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('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) 2021 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) 2021 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();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2872
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
							
						
						
									
										2872
									
								
								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) 2021 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) 2021 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) 2021 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) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -61,5 +61,11 @@ int table_test() {
 | 
			
		||||
    assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key1")), janet_wrap_integer(10)));
 | 
			
		||||
    assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key2")), janet_wrap_integer(100)));
 | 
			
		||||
 | 
			
		||||
    assert(t2->count == 4);
 | 
			
		||||
    assert(janet_equals(janet_table_remove(t2, janet_csymbolv("t2key1")), janet_wrap_integer(10)));
 | 
			
		||||
    assert(t2->count == 3);
 | 
			
		||||
    assert(janet_equals(janet_table_remove(t2, janet_csymbolv("t2key2")), janet_wrap_integer(100)));
 | 
			
		||||
    assert(t2->count == 2);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,36 +1,13 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
* deal in the Software without restriction, including without limitation the
 | 
			
		||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 | 
			
		||||
* sell copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
* furnished to do so, subject to the following conditions:
 | 
			
		||||
*
 | 
			
		||||
* The above copyright notice and this permission notice shall be included in
 | 
			
		||||
* all copies or substantial portions of the Software.
 | 
			
		||||
*
 | 
			
		||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | 
			
		||||
* IN THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/* This is an example janetconf.h file. This will be usually generated
 | 
			
		||||
 * by the build system. */
 | 
			
		||||
/* This will be generated by the build system if this file is not used */
 | 
			
		||||
 | 
			
		||||
#ifndef JANETCONF_H
 | 
			
		||||
#define JANETCONF_H
 | 
			
		||||
 | 
			
		||||
#define JANET_VERSION_MAJOR 1
 | 
			
		||||
#define JANET_VERSION_MINOR 10
 | 
			
		||||
#define JANET_VERSION_PATCH 0
 | 
			
		||||
#define JANET_VERSION_MINOR 19
 | 
			
		||||
#define JANET_VERSION_PATCH 1
 | 
			
		||||
#define JANET_VERSION_EXTRA ""
 | 
			
		||||
#define JANET_VERSION "1.10.0"
 | 
			
		||||
#define JANET_VERSION "1.19.1"
 | 
			
		||||
 | 
			
		||||
/* #define JANET_BUILD "local" */
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +18,8 @@
 | 
			
		||||
/* #define JANET_API __attribute__((visibility ("default"))) */
 | 
			
		||||
 | 
			
		||||
/* These settings should be specified before amalgamation is
 | 
			
		||||
 * built. */
 | 
			
		||||
 * built. Any build with these set should be considered non-standard, and
 | 
			
		||||
 * certain Janet libraries should be expected not to work. */
 | 
			
		||||
/* #define JANET_NO_DOCSTRINGS */
 | 
			
		||||
/* #define JANET_NO_SOURCEMAPS */
 | 
			
		||||
/* #define JANET_REDUCED_OS */
 | 
			
		||||
@@ -49,15 +27,17 @@
 | 
			
		||||
/* #define JANET_NO_ASSEMBLER */
 | 
			
		||||
/* #define JANET_NO_PEG */
 | 
			
		||||
/* #define JANET_NO_NET */
 | 
			
		||||
/* #define JANET_NO_TYPED_ARRAY */
 | 
			
		||||
/* #define JANET_NO_INT_TYPES */
 | 
			
		||||
 | 
			
		||||
/* Other settings */
 | 
			
		||||
/* #define JANET_NO_PRF */
 | 
			
		||||
/* #define JANET_NO_UTC_MKTIME */
 | 
			
		||||
/* #define JANET_NO_EV */
 | 
			
		||||
/* #define JANET_NO_REALPATH */
 | 
			
		||||
/* #define JANET_NO_SYMLINKS */
 | 
			
		||||
/* #define JANET_NO_UMASK */
 | 
			
		||||
/* #define JANET_NO_THREADS */
 | 
			
		||||
 | 
			
		||||
/* Other settings */
 | 
			
		||||
/* #define JANET_DEBUG */
 | 
			
		||||
/* #define JANET_PRF */
 | 
			
		||||
/* #define JANET_NO_UTC_MKTIME */
 | 
			
		||||
/* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */
 | 
			
		||||
/* #define JANET_EXIT(msg) do { printf("C assert failed executing janet: %s\n", msg); exit(1); } while (0) */
 | 
			
		||||
/* #define JANET_TOP_LEVEL_SIGNAL(msg) call_my_function((msg), stderr) */
 | 
			
		||||
@@ -67,5 +47,18 @@
 | 
			
		||||
/* #define JANET_STACK_MAX 16384 */
 | 
			
		||||
/* #define JANET_OS_NAME my-custom-os */
 | 
			
		||||
/* #define JANET_ARCH_NAME pdp-8 */
 | 
			
		||||
/* #define JANET_EV_NO_EPOLL */
 | 
			
		||||
/* #define JANET_EV_NO_KQUEUE */
 | 
			
		||||
/* #define JANET_NO_INTERPRETER_INTERRUPT */
 | 
			
		||||
 | 
			
		||||
/* Custom vm allocator support */
 | 
			
		||||
/* #include <mimalloc.h> */
 | 
			
		||||
/* #define janet_malloc(X) mi_malloc((X)) */
 | 
			
		||||
/* #define janet_realloc(X, Y) mi_realloc((X), (Y)) */
 | 
			
		||||
/* #define janet_calloc(X, Y) mi_calloc((X), (Y)) */
 | 
			
		||||
/* #define janet_free(X) mi_free((X)) */
 | 
			
		||||
 | 
			
		||||
/* Main client settings, does not affect library code */
 | 
			
		||||
/* #define JANET_SIMPLE_GETLINE */
 | 
			
		||||
 | 
			
		||||
#endif /* end of include guard: JANETCONF_H */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -24,6 +24,12 @@
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Create new userdata */
 | 
			
		||||
@@ -43,3 +49,100 @@ 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
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    LeaveCriticalSection((CRITICAL_SECTION *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
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_mutex_init(mutex, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_deinit(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_destroy(mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_lock(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_lock(mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_unlock(mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										206
									
								
								src/core/array.c
									
									
									
									
									
								
							
							
						
						
									
										206
									
								
								src/core/array.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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,14 +122,19 @@ 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);
 | 
			
		||||
    Janet x = (argc == 2) ? argv[1] : janet_wrap_nil();
 | 
			
		||||
@@ -141,7 +146,10 @@ static Janet cfun_array_new_filled(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_fill,
 | 
			
		||||
              "(array/fill arr &opt value)",
 | 
			
		||||
              "Replace all elements of an array with `value` (defaulting to nil) without changing the length of the array. "
 | 
			
		||||
              "Returns the modified array.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    Janet x = (argc == 2) ? argv[1] : janet_wrap_nil();
 | 
			
		||||
@@ -151,19 +159,26 @@ static Janet cfun_array_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_pop(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_pop,
 | 
			
		||||
              "(array/pop arr)",
 | 
			
		||||
              "Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
 | 
			
		||||
              "the input array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    return janet_array_pop(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_peek(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_peek,
 | 
			
		||||
              "(array/peek arr)",
 | 
			
		||||
              "Returns the last element of the array. Does not modify the array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    return janet_array_peek(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_push(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_push,
 | 
			
		||||
              "(array/push arr x)",
 | 
			
		||||
              "Insert an element in the end of an array. Modifies the input array and returns it.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    if (INT32_MAX - argc + 1 <= array->count) {
 | 
			
		||||
@@ -176,7 +191,12 @@ static Janet cfun_array_push(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_ensure,
 | 
			
		||||
              "(array/ensure arr capacity growth)",
 | 
			
		||||
              "Ensures that the memory backing the array is large enough for `capacity` "
 | 
			
		||||
              "items at the given rate of growth. Capacity and growth must be integers. "
 | 
			
		||||
              "If the backing capacity is already enough, then this function does nothing. "
 | 
			
		||||
              "Otherwise, the backing memory will be reallocated so that there is enough space.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    int32_t newcount = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -186,7 +206,13 @@ static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_slice,
 | 
			
		||||
              "(array/slice arrtup &opt start end)",
 | 
			
		||||
              "Takes a slice of array or tuple from `start` to `end`. The range is half open, "
 | 
			
		||||
              "[start, end). Indexes can also be negative, indicating indexing from the "
 | 
			
		||||
              "end of the array. By default, `start` is 0 and `end` is the length of the array. "
 | 
			
		||||
              "Note that index -1 is synonymous with index `(length arrtup)` to allow a full "
 | 
			
		||||
              "negative slice range. Returns a new array.") {
 | 
			
		||||
    JanetView view = janet_getindexed(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    JanetArray *array = janet_array(range.end - range.start);
 | 
			
		||||
@@ -196,7 +222,12 @@ static Janet cfun_array_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_concat,
 | 
			
		||||
              "(array/concat arr & parts)",
 | 
			
		||||
              "Concatenates a variable number of arrays (and tuples) into the first argument, "
 | 
			
		||||
              "which must be an array. If any of the parts are arrays or tuples, their elements will "
 | 
			
		||||
              "be inserted into the array. Otherwise, each part in `parts` will be appended to `arr` in order. "
 | 
			
		||||
              "Return the modified array `arr`.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
@@ -210,6 +241,11 @@ static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
                int32_t j, len = 0;
 | 
			
		||||
                const Janet *vals = NULL;
 | 
			
		||||
                janet_indexed_view(argv[i], &vals, &len);
 | 
			
		||||
                if (array->data == vals) {
 | 
			
		||||
                    int32_t newcount = array->count + len;
 | 
			
		||||
                    janet_array_ensure(array, newcount, 2);
 | 
			
		||||
                    janet_indexed_view(argv[i], &vals, &len);
 | 
			
		||||
                }
 | 
			
		||||
                for (j = 0; j < len; j++)
 | 
			
		||||
                    janet_array_push(array, vals[j]);
 | 
			
		||||
            }
 | 
			
		||||
@@ -219,7 +255,12 @@ static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_insert,
 | 
			
		||||
              "(array/insert arr at & xs)",
 | 
			
		||||
              "Insert all `xs` into array `arr` at index `at`. `at` should be an integer between "
 | 
			
		||||
              "0 and the length of the array. A negative value for `at` will index backwards from "
 | 
			
		||||
              "the end of the array, such that inserting at -1 appends to the array. "
 | 
			
		||||
              "Returns the array.") {
 | 
			
		||||
    size_t chunksize, restsize;
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
@@ -245,7 +286,12 @@ static Janet cfun_array_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_remove(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_remove,
 | 
			
		||||
              "(array/remove arr at &opt n)",
 | 
			
		||||
              "Remove up to `n` elements starting at index `at` in array `arr`. `at` can index from "
 | 
			
		||||
              "the end of the array with a negative index, and `n` must be a non-negative integer. "
 | 
			
		||||
              "By default, `n` is 1. "
 | 
			
		||||
              "Returns the array.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    int32_t at = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -270,85 +316,55 @@ static Janet cfun_array_remove(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg array_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "array/new", cfun_array_new,
 | 
			
		||||
        JDOC("(array/new capacity)\n\n"
 | 
			
		||||
             "Creates a new empty array with a pre-allocated capacity. The same as "
 | 
			
		||||
             "(array) but can be more efficient if the maximum size of an array is known.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/new-filled", cfun_array_new_filled,
 | 
			
		||||
        JDOC("(array/new-filled count &opt value)\n\n"
 | 
			
		||||
             "Creates a new array of count elements, all set to value, which defaults to nil. Returns the new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/fill", cfun_array_fill,
 | 
			
		||||
        JDOC("(array/fill arr &opt value)\n\n"
 | 
			
		||||
             "Replace all elements of an array with value (defaulting to nil) without changing the length of the array. "
 | 
			
		||||
             "Returns the modified array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/pop", cfun_array_pop,
 | 
			
		||||
        JDOC("(array/pop arr)\n\n"
 | 
			
		||||
             "Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
 | 
			
		||||
             "the input array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/peek", cfun_array_peek,
 | 
			
		||||
        JDOC("(array/peek arr)\n\n"
 | 
			
		||||
             "Returns the last element of the array. Does not modify the array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/push", cfun_array_push,
 | 
			
		||||
        JDOC("(array/push arr x)\n\n"
 | 
			
		||||
             "Insert an element in the end of an array. Modifies the input array and returns it.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/ensure", cfun_array_ensure,
 | 
			
		||||
        JDOC("(array/ensure arr capacity growth)\n\n"
 | 
			
		||||
             "Ensures that the memory backing the array is large enough for capacity "
 | 
			
		||||
             "items at the given rate of growth. Capacity and growth must be integers. "
 | 
			
		||||
             "If the backing capacity is already enough, then this function does nothing. "
 | 
			
		||||
             "Otherwise, the backing memory will be reallocated so that there is enough space.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/slice", cfun_array_slice,
 | 
			
		||||
        JDOC("(array/slice arrtup &opt start end)\n\n"
 | 
			
		||||
             "Takes a slice of array or tuple from start to end. The range is half open, "
 | 
			
		||||
             "[start, end). Indexes can also be negative, indicating indexing from the end of the "
 | 
			
		||||
             "end of the array. By default, start is 0 and end is the length of the array. "
 | 
			
		||||
             "Note that index -1 is synonymous with index (length arrtup) to allow a full "
 | 
			
		||||
             "negative slice range. Returns a new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/concat", cfun_array_concat,
 | 
			
		||||
        JDOC("(array/concat arr & parts)\n\n"
 | 
			
		||||
             "Concatenates a variadic number of arrays (and tuples) into the first argument "
 | 
			
		||||
             "which must an array. If any of the parts are arrays or tuples, their elements will "
 | 
			
		||||
             "be inserted into the array. Otherwise, each part in parts will be appended to arr in order. "
 | 
			
		||||
             "Return the modified array arr.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/insert", cfun_array_insert,
 | 
			
		||||
        JDOC("(array/insert arr at & xs)\n\n"
 | 
			
		||||
             "Insert all of xs into array arr at index at. at should be an integer "
 | 
			
		||||
             "0 and the length of the array. A negative value for at will index from "
 | 
			
		||||
             "the end of the array, such that inserting at -1 appends to the array. "
 | 
			
		||||
             "Returns the array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/remove", cfun_array_remove,
 | 
			
		||||
        JDOC("(array/remove arr at &opt n)\n\n"
 | 
			
		||||
             "Remove up to n elements starting at index at in array arr. at can index from "
 | 
			
		||||
             "the end of the array with a negative index, and n must be a non-negative integer. "
 | 
			
		||||
             "By default, n is 1. "
 | 
			
		||||
             "Returns the array.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
JANET_CORE_FN(cfun_array_trim,
 | 
			
		||||
              "(array/trim arr)",
 | 
			
		||||
              "Set the backing capacity of an array to its current length. Returns the modified array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    if (array->count) {
 | 
			
		||||
        if (array->count < array->capacity) {
 | 
			
		||||
            Janet *newData = janet_realloc(array->data, array->count * sizeof(Janet));
 | 
			
		||||
            if (NULL == newData) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
            array->data = newData;
 | 
			
		||||
            array->capacity = array->count;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        array->capacity = 0;
 | 
			
		||||
        janet_free(array->data);
 | 
			
		||||
        array->data = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_array_clear,
 | 
			
		||||
              "(array/clear arr)",
 | 
			
		||||
              "Empties an array, setting it's count to 0 but does not free the backing capacity. "
 | 
			
		||||
              "Returns the modified array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    array->count = 0;
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load the array module */
 | 
			
		||||
void janet_lib_array(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, array_cfuns);
 | 
			
		||||
    JanetRegExt array_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("array/new", cfun_array_new),
 | 
			
		||||
        JANET_CORE_REG("array/new-filled", cfun_array_new_filled),
 | 
			
		||||
        JANET_CORE_REG("array/fill", cfun_array_fill),
 | 
			
		||||
        JANET_CORE_REG("array/pop", cfun_array_pop),
 | 
			
		||||
        JANET_CORE_REG("array/peek", cfun_array_peek),
 | 
			
		||||
        JANET_CORE_REG("array/push", cfun_array_push),
 | 
			
		||||
        JANET_CORE_REG("array/ensure", cfun_array_ensure),
 | 
			
		||||
        JANET_CORE_REG("array/slice", cfun_array_slice),
 | 
			
		||||
        JANET_CORE_REG("array/concat", cfun_array_concat),
 | 
			
		||||
        JANET_CORE_REG("array/insert", cfun_array_insert),
 | 
			
		||||
        JANET_CORE_REG("array/remove", cfun_array_remove),
 | 
			
		||||
        JANET_CORE_REG("array/trim", cfun_array_trim),
 | 
			
		||||
        JANET_CORE_REG("array/clear", cfun_array_clear),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, array_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										242
									
								
								src/core/asm.c
									
									
									
									
									
								
							
							
						
						
									
										242
									
								
								src/core/asm.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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},
 | 
			
		||||
@@ -112,6 +113,8 @@ static const JanetInstructionDef janet_ops[] = {
 | 
			
		||||
    {"movn", JOP_MOVE_NEAR},
 | 
			
		||||
    {"mul", JOP_MULTIPLY},
 | 
			
		||||
    {"mulim", JOP_MULTIPLY_IMMEDIATE},
 | 
			
		||||
    {"neq", JOP_NOT_EQUALS},
 | 
			
		||||
    {"neqim", JOP_NOT_EQUALS_IMMEDIATE},
 | 
			
		||||
    {"next", JOP_NEXT},
 | 
			
		||||
    {"noop", JOP_NOOP},
 | 
			
		||||
    {"prop", JOP_PROPAGATE},
 | 
			
		||||
@@ -221,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -579,7 +582,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -611,7 +614,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
            newlen = def->defs_length + 1;
 | 
			
		||||
            if (a.defs_capacity < newlen) {
 | 
			
		||||
                int32_t newcap = newlen;
 | 
			
		||||
                def->defs = realloc(def->defs, newcap * sizeof(JanetFuncDef *));
 | 
			
		||||
                def->defs = janet_realloc(def->defs, newcap * sizeof(JanetFuncDef *));
 | 
			
		||||
                if (NULL == def->defs) {
 | 
			
		||||
                    JANET_OUT_OF_MEMORY;
 | 
			
		||||
                }
 | 
			
		||||
@@ -640,7 +643,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
        }
 | 
			
		||||
        /* Allocate bytecode array */
 | 
			
		||||
        def->bytecode_length = blength;
 | 
			
		||||
        def->bytecode = malloc(sizeof(uint32_t) * (size_t) blength);
 | 
			
		||||
        def->bytecode = janet_malloc(sizeof(uint32_t) * (size_t) blength);
 | 
			
		||||
        if (NULL == def->bytecode) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -682,7 +685,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -708,7 +711,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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -718,6 +721,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
        janet_asm_error(&a, "invalid assembly");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Add final flags */
 | 
			
		||||
    janet_def_addflags(def);
 | 
			
		||||
 | 
			
		||||
    /* Finish everything and return funcdef */
 | 
			
		||||
    janet_asm_deinit(&a);
 | 
			
		||||
    result.error = NULL;
 | 
			
		||||
@@ -835,85 +841,114 @@ 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_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("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) {
 | 
			
		||||
@@ -922,33 +957,54 @@ 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, "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) 2021 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,14 +162,20 @@ 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);
 | 
			
		||||
    int32_t byte = 0;
 | 
			
		||||
@@ -184,7 +189,10 @@ static Janet cfun_buffer_new_filled(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_fill,
 | 
			
		||||
              "(buffer/fill buffer &opt byte)",
 | 
			
		||||
              "Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int32_t byte = 0;
 | 
			
		||||
@@ -197,7 +205,28 @@ static Janet cfun_buffer_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_trim,
 | 
			
		||||
              "(buffer/trim buffer)",
 | 
			
		||||
              "Set the backing capacity of the buffer to the current length of the buffer. Returns the "
 | 
			
		||||
              "modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    if (buffer->count < buffer->capacity) {
 | 
			
		||||
        int32_t newcap = buffer->count > 4 ? buffer->count : 4;
 | 
			
		||||
        uint8_t *newData = janet_realloc(buffer->data, newcap);
 | 
			
		||||
        if (NULL == newData) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        buffer->data = newData;
 | 
			
		||||
        buffer->capacity = newcap;
 | 
			
		||||
    }
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_u8,
 | 
			
		||||
              "(buffer/push-byte buffer & xs)",
 | 
			
		||||
              "Append bytes to a buffer. Will expand the buffer as necessary. "
 | 
			
		||||
              "Returns the modified buffer. Will throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -207,7 +236,11 @@ static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_word(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_word,
 | 
			
		||||
              "(buffer/push-word buffer & xs)",
 | 
			
		||||
              "Append machine words to a buffer. The 4 bytes of the integer are appended "
 | 
			
		||||
              "in twos complement, little endian order, unsigned for all x. Returns the modified buffer. Will "
 | 
			
		||||
              "throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -221,7 +254,12 @@ static Janet cfun_buffer_word(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_chars(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_chars,
 | 
			
		||||
              "(buffer/push-string buffer & xs)",
 | 
			
		||||
              "Push byte sequences onto the end of a buffer. "
 | 
			
		||||
              "Will accept any of strings, keywords, symbols, and buffers. "
 | 
			
		||||
              "Returns the modified buffer. "
 | 
			
		||||
              "Will throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -236,14 +274,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);
 | 
			
		||||
@@ -256,7 +325,12 @@ static Janet cfun_buffer_popn(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_slice,
 | 
			
		||||
              "(buffer/slice bytes &opt start end)",
 | 
			
		||||
              "Takes a slice of a byte sequence from start to end. The range is half open, "
 | 
			
		||||
              "[start, end). Indexes can also be negative, indicating indexing from the end of the "
 | 
			
		||||
              "end of the array. By default, start is 0 and end is the length of the buffer. "
 | 
			
		||||
              "Returns a new buffer.") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(range.end - range.start);
 | 
			
		||||
@@ -280,7 +354,9 @@ static void bitloc(int32_t argc, Janet *argv, JanetBuffer **b, int32_t *index, i
 | 
			
		||||
    *bit = which_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitset(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitset,
 | 
			
		||||
              "(buffer/bit-set buffer index)",
 | 
			
		||||
              "Sets the bit at the given bit-index. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -289,7 +365,9 @@ static Janet cfun_buffer_bitset(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitclear(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitclear,
 | 
			
		||||
              "(buffer/bit-clear buffer index)",
 | 
			
		||||
              "Clears the bit at the given bit-index. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -298,7 +376,9 @@ static Janet cfun_buffer_bitclear(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitget(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitget,
 | 
			
		||||
              "(buffer/bit buffer index)",
 | 
			
		||||
              "Gets the bit at the given bit-index. Returns true if the bit is set, false if not.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -306,7 +386,9 @@ static Janet cfun_buffer_bitget(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_boolean(buffer->data[index] & (1 << bit));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bittoggle(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bittoggle,
 | 
			
		||||
              "(buffer/bit-toggle buffer index)",
 | 
			
		||||
              "Toggles the bit at the given bit index in buffer. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -315,7 +397,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 to index from the end of src or dest. Returns dest.") {
 | 
			
		||||
    janet_arity(argc, 2, 5);
 | 
			
		||||
    JanetBuffer *dest = janet_getbuffer(argv, 0);
 | 
			
		||||
    JanetByteView src = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -352,7 +438,10 @@ static Janet cfun_buffer_blit(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_format(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_format,
 | 
			
		||||
              "(buffer/format buffer format & args)",
 | 
			
		||||
              "Snprintf like functionality for printing values into a buffer. Returns "
 | 
			
		||||
              " the modified buffer.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    const char *strfrmt = (const char *) janet_getstring(argv, 1);
 | 
			
		||||
@@ -360,100 +449,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/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) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -101,10 +101,13 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
 | 
			
		||||
    JINT_SSS, /* JOP_GREATER_THAN_EQUAL */
 | 
			
		||||
    JINT_SSS, /* JOP_LESS_THAN_EQUAL */
 | 
			
		||||
    JINT_SSS, /* JOP_NEXT */
 | 
			
		||||
    JINT_SSS, /* JOP_NOT_EQUALS, */
 | 
			
		||||
    JINT_SSI, /* JOP_NOT_EQUALS_IMMEDIATE, */
 | 
			
		||||
    JINT_SSS /* JOP_CANCEL, */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Verify some bytecode */
 | 
			
		||||
int32_t janet_verify(JanetFuncDef *def) {
 | 
			
		||||
int janet_verify(JanetFuncDef *def) {
 | 
			
		||||
    int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG);
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    int32_t maxslot = def->arity + vargs;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -27,23 +27,39 @@
 | 
			
		||||
#include "fiber.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_SINGLE_THREADED
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
 | 
			
		||||
#ifdef JANET_TOP_LEVEL_SIGNAL
 | 
			
		||||
    JANET_TOP_LEVEL_SIGNAL(msg);
 | 
			
		||||
#else
 | 
			
		||||
    fputs(msg, stdout);
 | 
			
		||||
    exit(1);
 | 
			
		||||
# ifdef JANET_SINGLE_THREADED
 | 
			
		||||
    exit(-1);
 | 
			
		||||
# elif defined(JANET_WINDOWS)
 | 
			
		||||
    ExitThread(-1);
 | 
			
		||||
# else
 | 
			
		||||
    pthread_exit(NULL);
 | 
			
		||||
# endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_signalv(JanetSignal sig, Janet message) {
 | 
			
		||||
    if (janet_vm_return_reg != NULL) {
 | 
			
		||||
        *janet_vm_return_reg = message;
 | 
			
		||||
        janet_vm_fiber->flags |= JANET_FIBER_DID_LONGJUMP;
 | 
			
		||||
    if (janet_vm.return_reg != NULL) {
 | 
			
		||||
        *janet_vm.return_reg = message;
 | 
			
		||||
        if (NULL != janet_vm.fiber) {
 | 
			
		||||
            janet_vm.fiber->flags |= JANET_FIBER_DID_LONGJUMP;
 | 
			
		||||
        }
 | 
			
		||||
#if defined(JANET_BSD) || defined(JANET_APPLE)
 | 
			
		||||
        _longjmp(*janet_vm_jmp_buf, sig);
 | 
			
		||||
        _longjmp(*janet_vm.signal_buf, sig);
 | 
			
		||||
#else
 | 
			
		||||
        longjmp(*janet_vm_jmp_buf, sig);
 | 
			
		||||
        longjmp(*janet_vm.signal_buf, sig);
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        const char *str = (const char *)janet_formatc("janet top level signal - %v\n", message);
 | 
			
		||||
@@ -133,6 +149,23 @@ int janet_getmethod(const uint8_t *method, const JanetMethod *methods, Janet *ou
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_nextmethod(const JanetMethod *methods, Janet key) {
 | 
			
		||||
    if (!janet_checktype(key, JANET_NIL)) {
 | 
			
		||||
        while (methods->name) {
 | 
			
		||||
            if (janet_keyeq(key, methods->name)) {
 | 
			
		||||
                methods++;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            methods++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (methods->name) {
 | 
			
		||||
        return janet_ckeywordv(methods->name);
 | 
			
		||||
    } else {
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFINE_GETTER(number, NUMBER, double)
 | 
			
		||||
DEFINE_GETTER(array, ARRAY, JanetArray *)
 | 
			
		||||
DEFINE_GETTER(tuple, TUPLE, const Janet *)
 | 
			
		||||
@@ -179,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;
 | 
			
		||||
}
 | 
			
		||||
@@ -325,20 +358,27 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_dyn(const char *name) {
 | 
			
		||||
    if (!janet_vm_fiber) return janet_wrap_nil();
 | 
			
		||||
    if (janet_vm_fiber->env) {
 | 
			
		||||
        return janet_table_get(janet_vm_fiber->env, janet_ckeywordv(name));
 | 
			
		||||
    if (!janet_vm.fiber) {
 | 
			
		||||
        if (!janet_vm.top_dyns) return janet_wrap_nil();
 | 
			
		||||
        return janet_table_get(janet_vm.top_dyns, janet_ckeywordv(name));
 | 
			
		||||
    }
 | 
			
		||||
    if (janet_vm.fiber->env) {
 | 
			
		||||
        return janet_table_get(janet_vm.fiber->env, janet_ckeywordv(name));
 | 
			
		||||
    } else {
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_setdyn(const char *name, Janet value) {
 | 
			
		||||
    if (!janet_vm_fiber) return;
 | 
			
		||||
    if (!janet_vm_fiber->env) {
 | 
			
		||||
        janet_vm_fiber->env = janet_table(1);
 | 
			
		||||
    if (!janet_vm.fiber) {
 | 
			
		||||
        if (!janet_vm.top_dyns) janet_vm.top_dyns = janet_table(10);
 | 
			
		||||
        janet_table_put(janet_vm.top_dyns, janet_ckeywordv(name), value);
 | 
			
		||||
    } else {
 | 
			
		||||
        if (!janet_vm.fiber->env) {
 | 
			
		||||
            janet_vm.fiber->env = janet_table(1);
 | 
			
		||||
        }
 | 
			
		||||
        janet_table_put(janet_vm.fiber->env, janet_ckeywordv(name), value);
 | 
			
		||||
    }
 | 
			
		||||
    janet_table_put(janet_vm_fiber->env, janet_ckeywordv(name), value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										134
									
								
								src/core/cfuns.c
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								src/core/cfuns.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -33,6 +33,11 @@ static int arity1or2(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    int32_t arity = janet_v_count(args);
 | 
			
		||||
    return arity == 1 || arity == 2;
 | 
			
		||||
}
 | 
			
		||||
static int arity2or3(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    (void) opts;
 | 
			
		||||
    int32_t arity = janet_v_count(args);
 | 
			
		||||
    return arity == 2 || arity == 3;
 | 
			
		||||
}
 | 
			
		||||
static int fixarity1(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    (void) opts;
 | 
			
		||||
    return janet_v_count(args) == 1;
 | 
			
		||||
@@ -90,34 +95,67 @@ static JanetSlot opfunction(
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a value can be coerced to an immediate value */
 | 
			
		||||
static int can_be_imm(Janet x, int8_t *out) {
 | 
			
		||||
    if (!janet_checkint(x)) return 0;
 | 
			
		||||
    int32_t integer = janet_unwrap_integer(x);
 | 
			
		||||
    if (integer > 127 || integer < -127) return 0;
 | 
			
		||||
    *out = (int8_t) integer;
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a slot can be coerced to an immediate value */
 | 
			
		||||
static int can_slot_be_imm(JanetSlot s, int8_t *out) {
 | 
			
		||||
    if (!(s.flags & JANET_SLOT_CONSTANT)) return 0;
 | 
			
		||||
    return can_be_imm(s.constant, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Emit a series of instructions instead of a function call to a math op */
 | 
			
		||||
static JanetSlot opreduce(
 | 
			
		||||
    JanetFopts opts,
 | 
			
		||||
    JanetSlot *args,
 | 
			
		||||
    int op,
 | 
			
		||||
    int opim,
 | 
			
		||||
    Janet nullary) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    int32_t i, len;
 | 
			
		||||
    int8_t imm = 0;
 | 
			
		||||
    int neg = opim < 0;
 | 
			
		||||
    if (opim < 0) opim = -opim;
 | 
			
		||||
    len = janet_v_count(args);
 | 
			
		||||
    JanetSlot t;
 | 
			
		||||
    if (len == 0) {
 | 
			
		||||
        return janetc_cslot(nullary);
 | 
			
		||||
    } else if (len == 1) {
 | 
			
		||||
        t = janetc_gettarget(opts);
 | 
			
		||||
        janetc_emit_sss(c, op, t, janetc_cslot(nullary), args[0], 1);
 | 
			
		||||
        /* Special case subtract to be times -1 */
 | 
			
		||||
        if (op == JOP_SUBTRACT) {
 | 
			
		||||
            janetc_emit_ssi(c, JOP_MULTIPLY_IMMEDIATE, t, args[0], -1, 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            janetc_emit_sss(c, op, t, janetc_cslot(nullary), args[0], 1);
 | 
			
		||||
        }
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
    t = janetc_gettarget(opts);
 | 
			
		||||
    janetc_emit_sss(c, op, t, args[0], args[1], 1);
 | 
			
		||||
    for (i = 2; i < len; i++)
 | 
			
		||||
        janetc_emit_sss(c, op, t, t, args[i], 1);
 | 
			
		||||
    if (opim && can_slot_be_imm(args[1], &imm)) {
 | 
			
		||||
        janetc_emit_ssi(c, opim, t, args[0], neg ? -imm : imm, 1);
 | 
			
		||||
    } else {
 | 
			
		||||
        janetc_emit_sss(c, op, t, args[0], args[1], 1);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 2; i < len; i++) {
 | 
			
		||||
        if (opim && can_slot_be_imm(args[i], &imm)) {
 | 
			
		||||
            janetc_emit_ssi(c, opim, t, t, neg ? -imm : imm, 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            janetc_emit_sss(c, op, t, t, args[i], 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Function optimizers */
 | 
			
		||||
 | 
			
		||||
static JanetSlot do_propagate(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_PROPAGATE, janet_wrap_nil());
 | 
			
		||||
    return opreduce(opts, args, JOP_PROPAGATE, 0, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_error(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    janetc_emit_s(opts.compiler, JOP_ERROR, args[0], 0);
 | 
			
		||||
@@ -134,19 +172,40 @@ static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_in(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_IN, janet_wrap_nil());
 | 
			
		||||
    return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_GET, janet_wrap_nil());
 | 
			
		||||
    if (janet_v_count(args) == 3) {
 | 
			
		||||
        JanetCompiler *c = opts.compiler;
 | 
			
		||||
        JanetSlot t = janetc_gettarget(opts);
 | 
			
		||||
        int target_is_default = janetc_sequal(t, args[2]);
 | 
			
		||||
        JanetSlot dflt_slot = args[2];
 | 
			
		||||
        if (target_is_default) {
 | 
			
		||||
            dflt_slot = janetc_farslot(c);
 | 
			
		||||
            janetc_copy(c, dflt_slot, t);
 | 
			
		||||
        }
 | 
			
		||||
        janetc_emit_sss(c, JOP_GET, t, args[0], args[1], 1);
 | 
			
		||||
        int32_t label = janetc_emit_si(c, JOP_JUMP_IF_NOT_NIL, t, 0, 0);
 | 
			
		||||
        janetc_copy(c, t, dflt_slot);
 | 
			
		||||
        if (target_is_default) janetc_freeslot(c, dflt_slot);
 | 
			
		||||
        int32_t current = janet_v_count(c->buffer);
 | 
			
		||||
        c->buffer[label] |= (current - label) << 16;
 | 
			
		||||
        return t;
 | 
			
		||||
    } else {
 | 
			
		||||
        return opreduce(opts, args, JOP_GET, 0, janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_next(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opfunction(opts, args, JOP_NEXT, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_MODULO, janet_wrap_nil());
 | 
			
		||||
    return opreduce(opts, args, JOP_MODULO, 0, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_REMAINDER, janet_wrap_nil());
 | 
			
		||||
    return opreduce(opts, args, JOP_REMAINDER, 0, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    if (opts.flags & JANET_FOPTS_DROP) {
 | 
			
		||||
@@ -172,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;
 | 
			
		||||
@@ -200,34 +262,34 @@ static JanetSlot do_apply(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
/* Variadic operators specialization */
 | 
			
		||||
 | 
			
		||||
static JanetSlot do_add(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_ADD, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_ADD, JOP_ADD_IMMEDIATE, janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_sub(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_SUBTRACT, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_SUBTRACT, -JOP_ADD_IMMEDIATE, janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_mul(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_MULTIPLY, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_MULTIPLY, JOP_MULTIPLY_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_div(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_DIVIDE, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_DIVIDE, JOP_DIVIDE_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_band(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_BAND, janet_wrap_integer(-1));
 | 
			
		||||
    return opreduce(opts, args, JOP_BAND, 0, janet_wrap_integer(-1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_bor(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_BOR, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_BOR, 0, janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_bxor(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_BXOR, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_BXOR, 0, janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_lshift(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_LEFT, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_LEFT, JOP_SHIFT_LEFT_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_rshift(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_RIGHT, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_RIGHT, JOP_SHIFT_RIGHT_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_rshiftu(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_RIGHT, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_RIGHT_UNSIGNED, JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_bnot(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return genericSS(opts, JOP_BNOT, args[0]);
 | 
			
		||||
@@ -238,9 +300,11 @@ static JanetSlot compreduce(
 | 
			
		||||
    JanetFopts opts,
 | 
			
		||||
    JanetSlot *args,
 | 
			
		||||
    int op,
 | 
			
		||||
    int opim,
 | 
			
		||||
    int invert) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    int32_t i, len;
 | 
			
		||||
    int8_t imm = 0;
 | 
			
		||||
    len = janet_v_count(args);
 | 
			
		||||
    int32_t *labels = NULL;
 | 
			
		||||
    JanetSlot t;
 | 
			
		||||
@@ -251,19 +315,17 @@ static JanetSlot compreduce(
 | 
			
		||||
    }
 | 
			
		||||
    t = janetc_gettarget(opts);
 | 
			
		||||
    for (i = 1; i < len; i++) {
 | 
			
		||||
        janetc_emit_sss(c, op, t, args[i - 1], args[i], 1);
 | 
			
		||||
        if (opim && can_slot_be_imm(args[i], &imm)) {
 | 
			
		||||
            janetc_emit_ssi(c, opim, t, args[i - 1], imm, 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            janetc_emit_sss(c, op, t, args[i - 1], args[i], 1);
 | 
			
		||||
        }
 | 
			
		||||
        if (i != (len - 1)) {
 | 
			
		||||
            int32_t label = janetc_emit_si(c, JOP_JUMP_IF_NOT, t, 0, 1);
 | 
			
		||||
            int32_t label = janetc_emit_si(c, invert ? JOP_JUMP_IF : JOP_JUMP_IF_NOT, t, 0, 1);
 | 
			
		||||
            janet_v_push(labels, label);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    int32_t end = janet_v_count(c->buffer);
 | 
			
		||||
    if (invert) {
 | 
			
		||||
        janetc_emit_si(c, JOP_JUMP_IF, t, 3, 0);
 | 
			
		||||
        janetc_emit_s(c, JOP_LOAD_TRUE, t, 1);
 | 
			
		||||
        janetc_emit(c, JOP_JUMP | (2 << 8));
 | 
			
		||||
        janetc_emit_s(c, JOP_LOAD_FALSE, t, 1);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 0; i < janet_v_count(labels); i++) {
 | 
			
		||||
        int32_t label = labels[i];
 | 
			
		||||
        c->buffer[label] |= ((end - label) << 16);
 | 
			
		||||
@@ -273,22 +335,22 @@ static JanetSlot compreduce(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetSlot do_gt(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_GREATER_THAN, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_GREATER_THAN, JOP_GREATER_THAN_IMMEDIATE, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_lt(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_LESS_THAN, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_LESS_THAN, JOP_LESS_THAN_IMMEDIATE, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_gte(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_GREATER_THAN_EQUAL, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_GREATER_THAN_EQUAL, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_lte(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_LESS_THAN_EQUAL, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_LESS_THAN_EQUAL, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_eq(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_EQUALS, 0);
 | 
			
		||||
    return compreduce(opts, args, JOP_EQUALS, JOP_EQUALS_IMMEDIATE, 0);
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_neq(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return compreduce(opts, args, JOP_EQUALS, 1);
 | 
			
		||||
    return compreduce(opts, args, JOP_NOT_EQUALS, JOP_NOT_EQUALS_IMMEDIATE, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Arranged by tag */
 | 
			
		||||
@@ -319,10 +381,12 @@ static const JanetFunOptimizer optimizers[] = {
 | 
			
		||||
    {NULL, do_eq},
 | 
			
		||||
    {NULL, do_neq},
 | 
			
		||||
    {fixarity2, do_propagate},
 | 
			
		||||
    {fixarity2, do_get},
 | 
			
		||||
    {arity2or3, do_get},
 | 
			
		||||
    {arity1or2, do_next},
 | 
			
		||||
    {fixarity2, do_modulo},
 | 
			
		||||
    {fixarity2, do_remainder},
 | 
			
		||||
    {fixarity2, do_cmp},
 | 
			
		||||
    {fixarity2, do_cancel},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const JanetFunOptimizer *janetc_funopt(uint32_t flags) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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;
 | 
			
		||||
@@ -199,24 +229,41 @@ 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);
 | 
			
		||||
        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_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 +446,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 +487,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 +552,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -601,6 +679,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);
 | 
			
		||||
@@ -698,7 +779,32 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add function flags to janet functions */
 | 
			
		||||
void janet_def_addflags(JanetFuncDef *def) {
 | 
			
		||||
    int32_t set_flags = 0;
 | 
			
		||||
    int32_t unset_flags = 0;
 | 
			
		||||
    /* pos checks */
 | 
			
		||||
    if (def->name)            set_flags |= JANET_FUNCDEF_FLAG_HASNAME;
 | 
			
		||||
    if (def->source)          set_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
 | 
			
		||||
    if (def->defs)            set_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
 | 
			
		||||
    if (def->environments)    set_flags |= JANET_FUNCDEF_FLAG_HASENVS;
 | 
			
		||||
    if (def->sourcemap)       set_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
 | 
			
		||||
    if (def->closure_bitset)  set_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
 | 
			
		||||
    /* negative checks */
 | 
			
		||||
    if (!def->name)           unset_flags |= JANET_FUNCDEF_FLAG_HASNAME;
 | 
			
		||||
    if (!def->source)         unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
 | 
			
		||||
    if (!def->defs)           unset_flags |= JANET_FUNCDEF_FLAG_HASDEFS;
 | 
			
		||||
    if (!def->environments)   unset_flags |= JANET_FUNCDEF_FLAG_HASENVS;
 | 
			
		||||
    if (!def->sourcemap)      unset_flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
 | 
			
		||||
    if (!def->closure_bitset) unset_flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
 | 
			
		||||
    /* Update flags */
 | 
			
		||||
    def->flags |= set_flags;
 | 
			
		||||
    def->flags &= ~unset_flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compile a funcdef */
 | 
			
		||||
/* Once the various other settings of the FuncDef have been tweaked,
 | 
			
		||||
 * call janet_def_addflags to set the proper flags for the funcdef */
 | 
			
		||||
JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
    JanetScope *scope = c->scope;
 | 
			
		||||
    JanetFuncDef *def = janet_funcdef_alloc();
 | 
			
		||||
@@ -720,7 +826,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
    def->bytecode_length = janet_v_count(c->buffer) - scope->bytecode_start;
 | 
			
		||||
    if (def->bytecode_length) {
 | 
			
		||||
        size_t s = sizeof(int32_t) * (size_t) def->bytecode_length;
 | 
			
		||||
        def->bytecode = malloc(s);
 | 
			
		||||
        def->bytecode = janet_malloc(s);
 | 
			
		||||
        if (NULL == def->bytecode) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -728,7 +834,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        janet_v__cnt(c->buffer) = scope->bytecode_start;
 | 
			
		||||
        if (NULL != c->mapbuffer && c->source) {
 | 
			
		||||
            size_t s = sizeof(JanetSourceMapping) * (size_t) def->bytecode_length;
 | 
			
		||||
            def->sourcemap = malloc(s);
 | 
			
		||||
            def->sourcemap = janet_malloc(s);
 | 
			
		||||
            if (NULL == def->sourcemap) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -753,7 +859,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        int32_t slotchunks = (def->slotcount + 31) >> 5;
 | 
			
		||||
        /* numchunks is min of slotchunks and scope->ua.count */
 | 
			
		||||
        int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks;
 | 
			
		||||
        uint32_t *chunks = calloc(sizeof(uint32_t), slotchunks);
 | 
			
		||||
        uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks);
 | 
			
		||||
        if (NULL == chunks) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -761,7 +867,6 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        /* Register allocator preallocates some registers [240-255, high 16 bits of chunk index 7], we can ignore those. */
 | 
			
		||||
        if (scope->ua.count > 7) chunks[7] &= 0xFFFFU;
 | 
			
		||||
        def->closure_bitset = chunks;
 | 
			
		||||
        def->flags |= JANET_FUNCDEF_FLAG_HASCLOBITSET;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Pop the scope */
 | 
			
		||||
@@ -771,7 +876,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;
 | 
			
		||||
@@ -780,6 +885,7 @@ static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where)
 | 
			
		||||
    c->source = where;
 | 
			
		||||
    c->current_mapping.line = -1;
 | 
			
		||||
    c->current_mapping.column = -1;
 | 
			
		||||
    c->lints = lints;
 | 
			
		||||
    /* Init result */
 | 
			
		||||
    c->result.error = NULL;
 | 
			
		||||
    c->result.status = JANET_COMPILE_OK;
 | 
			
		||||
@@ -797,12 +903,13 @@ static void janetc_deinit(JanetCompiler *c) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Compile a form. */
 | 
			
		||||
JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *where) {
 | 
			
		||||
JanetCompileResult janet_compile_lint(Janet source,
 | 
			
		||||
                                      JanetTable *env, const uint8_t *where, JanetArray *lints) {
 | 
			
		||||
    JanetCompiler c;
 | 
			
		||||
    JanetScope rootscope;
 | 
			
		||||
    JanetFopts fopts;
 | 
			
		||||
 | 
			
		||||
    janetc_init(&c, env, where);
 | 
			
		||||
    janetc_init(&c, env, where, lints);
 | 
			
		||||
 | 
			
		||||
    /* Push a function scope */
 | 
			
		||||
    janetc_scope(&rootscope, &c, JANET_SCOPE_FUNCTION | JANET_SCOPE_TOP, "root");
 | 
			
		||||
@@ -818,6 +925,7 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
 | 
			
		||||
    if (c.result.status == JANET_COMPILE_OK) {
 | 
			
		||||
        JanetFuncDef *def = janetc_pop_funcdef(&c);
 | 
			
		||||
        def->name = janet_cstring("_thunk");
 | 
			
		||||
        janet_def_addflags(def);
 | 
			
		||||
        c.result.funcdef = def;
 | 
			
		||||
    } else {
 | 
			
		||||
        c.result.error_mapping = c.current_mapping;
 | 
			
		||||
@@ -829,26 +937,42 @@ 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 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) {
 | 
			
		||||
    if (argc >= 3) {
 | 
			
		||||
        source = janet_getstring(argv, 2);
 | 
			
		||||
    }
 | 
			
		||||
    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));
 | 
			
		||||
        }
 | 
			
		||||
@@ -856,18 +980,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),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -29,6 +29,13 @@
 | 
			
		||||
#include "regalloc.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Levels for compiler warnings */
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_C_LINT_RELAXED,
 | 
			
		||||
    JANET_C_LINT_NORMAL,
 | 
			
		||||
    JANET_C_LINT_STRICT
 | 
			
		||||
} JanetCompileLintLevel;
 | 
			
		||||
 | 
			
		||||
/* Tags for some functions for the prepared inliner */
 | 
			
		||||
#define JANET_FUN_DEBUG 1
 | 
			
		||||
#define JANET_FUN_ERROR 2
 | 
			
		||||
@@ -60,6 +67,8 @@
 | 
			
		||||
#define JANET_FUN_NEXT 28
 | 
			
		||||
#define JANET_FUN_MODULO 29
 | 
			
		||||
#define JANET_FUN_REMAINDER 30
 | 
			
		||||
#define JANET_FUN_CMP 31
 | 
			
		||||
#define JANET_FUN_CANCEL 32
 | 
			
		||||
 | 
			
		||||
/* Compiler typedefs */
 | 
			
		||||
typedef struct JanetCompiler JanetCompiler;
 | 
			
		||||
@@ -76,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
 | 
			
		||||
 | 
			
		||||
@@ -162,6 +171,9 @@ struct JanetCompiler {
 | 
			
		||||
 | 
			
		||||
    /* Prevent unbounded recursion */
 | 
			
		||||
    int recursion_guard;
 | 
			
		||||
 | 
			
		||||
    /* Collect linting results */
 | 
			
		||||
    JanetArray *lints;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define JANET_FOPTS_TAIL 0x10000
 | 
			
		||||
@@ -228,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) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -35,6 +35,13 @@ extern const unsigned char *janet_core_image;
 | 
			
		||||
extern size_t janet_core_image_size;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Docstrings should only exist during bootstrap */
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
#define JDOC(x) (x)
 | 
			
		||||
#else
 | 
			
		||||
#define JDOC(x) NULL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
 | 
			
		||||
 * with native code. */
 | 
			
		||||
#if defined(JANET_NO_DYNAMIC_MODULES)
 | 
			
		||||
@@ -63,10 +70,29 @@ typedef void *Clib;
 | 
			
		||||
#define error_clib() dlerror()
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static char *get_processed_name(const char *name) {
 | 
			
		||||
    if (name[0] == '.') return (char *) name;
 | 
			
		||||
    const char *c;
 | 
			
		||||
    for (c = name; *c; c++) {
 | 
			
		||||
        if (*c == '/') return (char *) name;
 | 
			
		||||
    }
 | 
			
		||||
    size_t l = (size_t)(c - name);
 | 
			
		||||
    char *ret = janet_malloc(l + 3);
 | 
			
		||||
    if (NULL == ret) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    ret[0] = '.';
 | 
			
		||||
    ret[1] = '/';
 | 
			
		||||
    memcpy(ret + 2, name, l + 1);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetModule janet_native(const char *name, const uint8_t **error) {
 | 
			
		||||
    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 +137,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 +150,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 +284,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 +300,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 +337,104 @@ 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 a value x.") {
 | 
			
		||||
    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 +449,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 +465,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,37 +501,69 @@ 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 */
 | 
			
		||||
    if (s > 0xFFFFFFFFFFFFUl) {
 | 
			
		||||
#ifdef JANET_64
 | 
			
		||||
    if (s >> 48) {
 | 
			
		||||
        janet_panic("interval too large");
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_gc_interval = s;
 | 
			
		||||
#endif
 | 
			
		||||
    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) {
 | 
			
		||||
@@ -427,12 +573,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);
 | 
			
		||||
@@ -457,21 +612,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]);
 | 
			
		||||
@@ -480,7 +641,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]);
 | 
			
		||||
@@ -489,7 +652,9 @@ 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;
 | 
			
		||||
    if (janet_checkint(argv[0])) {
 | 
			
		||||
@@ -514,204 +679,6 @@ static Janet janet_core_signal(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_signalv(sig, payload);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
 | 
			
		||||
/* Utility for inline assembly */
 | 
			
		||||
@@ -732,13 +699,14 @@ 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) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    memcpy(def->bytecode, bytecode, bytecode_size);
 | 
			
		||||
    janet_def_addflags(def);
 | 
			
		||||
    janet_def(env, name, janet_wrap_function(janet_thunk(def)), doc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -924,6 +892,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),
 | 
			
		||||
@@ -968,6 +940,10 @@ static const uint32_t remainder_asm[] = {
 | 
			
		||||
    JOP_REMAINDER | (1 << 24),
 | 
			
		||||
    JOP_RETURN
 | 
			
		||||
};
 | 
			
		||||
static const uint32_t cmp_asm[] = {
 | 
			
		||||
    JOP_COMPARE | (1 << 24),
 | 
			
		||||
    JOP_RETURN
 | 
			
		||||
};
 | 
			
		||||
#endif /* ifdef JANET_BOOTSTRAP */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -975,13 +951,46 @@ static const uint32_t remainder_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("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);
 | 
			
		||||
@@ -995,14 +1004,11 @@ 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);
 | 
			
		||||
@@ -1021,6 +1027,11 @@ JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
                    "%", 2, 2, 2, 2, remainder_asm, sizeof(remainder_asm),
 | 
			
		||||
                    JDOC("(% dividend divisor)\n\n"
 | 
			
		||||
                         "Returns the remainder of dividend / divisor."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_CMP,
 | 
			
		||||
                    "cmp", 2, 2, 2, 2, cmp_asm, sizeof(cmp_asm),
 | 
			
		||||
                    JDOC("(cmp x y)\n\n"
 | 
			
		||||
                         "Returns -1 if x is strictly less than y, 1 if y is strictly greater "
 | 
			
		||||
                         "than x, and 0 otherwise. To return 0, x and y must be the exact same type."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_NEXT,
 | 
			
		||||
                    "next", 2, 1, 2, 2, next_asm, sizeof(next_asm),
 | 
			
		||||
                    JDOC("(next ds &opt key)\n\n"
 | 
			
		||||
@@ -1052,6 +1063,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"
 | 
			
		||||
@@ -1162,7 +1178,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));
 | 
			
		||||
@@ -1173,26 +1190,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(
 | 
			
		||||
@@ -1205,9 +1207,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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										221
									
								
								src/core/debug.c
									
									
									
									
									
								
							
							
						
						
									
										221
									
								
								src/core/debug.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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,18 @@ void janet_debug_find(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
    janet_stacktrace_ext(fiber, err, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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 +121,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,9 +129,9 @@ 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 ";
 | 
			
		||||
                const char *override_prefix = prefix ? prefix : "";
 | 
			
		||||
                janet_eprintf("%s%s: %s\n",
 | 
			
		||||
                              prefix,
 | 
			
		||||
                              override_prefix,
 | 
			
		||||
                              janet_status_names[status],
 | 
			
		||||
                              errstr);
 | 
			
		||||
                wrote_error = 1;
 | 
			
		||||
@@ -142,11 +148,19 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
            } else {
 | 
			
		||||
                JanetCFunction cfun = (JanetCFunction)(frame->pc);
 | 
			
		||||
                if (cfun) {
 | 
			
		||||
                    Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
 | 
			
		||||
                    if (!janet_checktype(name, JANET_NIL))
 | 
			
		||||
                        janet_eprintf(" %s", (const char *)janet_to_string(name));
 | 
			
		||||
                    else
 | 
			
		||||
                    reg = janet_registry_get(cfun);
 | 
			
		||||
                    if (NULL != reg && NULL != reg->name) {
 | 
			
		||||
                        if (reg->name_prefix) {
 | 
			
		||||
                            janet_eprintf(" %s/%s", reg->name_prefix, reg->name);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            janet_eprintf(" %s", reg->name);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (NULL != reg->source_file) {
 | 
			
		||||
                            janet_eprintf(" [%s]", reg->source_file);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        janet_eprintf(" <cfunction>");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (frame->flags & JANET_STACKFRAME_TAILCALL)
 | 
			
		||||
@@ -159,6 +173,11 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf(" pc=%d", off);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (NULL != reg) {
 | 
			
		||||
                /* C Function */
 | 
			
		||||
                if (reg->source_line > 0) {
 | 
			
		||||
                    janet_eprintf(" on line %d", (long) reg->source_line);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            janet_eprintf("\n");
 | 
			
		||||
        }
 | 
			
		||||
@@ -193,7 +212,13 @@ static void helper_find_fun(int32_t argc, Janet *argv, JanetFuncDef **def, int32
 | 
			
		||||
    *bytecode_offset = offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_break(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_break,
 | 
			
		||||
              "(debug/break source line col)",
 | 
			
		||||
              "Sets a breakpoint in `source` at a given line and column. "
 | 
			
		||||
              "Will throw an error if the breakpoint location "
 | 
			
		||||
              "cannot be found. For example\n\n"
 | 
			
		||||
              "\t(debug/break \"core.janet\" 10 4)\n\n"
 | 
			
		||||
              "will set a breakpoint at line 10, 4th column of the file core.janet.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset;
 | 
			
		||||
    helper_find(argc, argv, &def, &offset);
 | 
			
		||||
@@ -201,7 +226,11 @@ static Janet cfun_debug_break(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_unbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_unbreak,
 | 
			
		||||
              "(debug/unbreak source line column)",
 | 
			
		||||
              "Remove a breakpoint with a source key at a given line and column. "
 | 
			
		||||
              "Will throw an error if the breakpoint "
 | 
			
		||||
              "cannot be found.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset = 0;
 | 
			
		||||
    helper_find(argc, argv, &def, &offset);
 | 
			
		||||
@@ -209,7 +238,11 @@ static Janet cfun_debug_unbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_fbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_fbreak,
 | 
			
		||||
              "(debug/fbreak fun &opt pc)",
 | 
			
		||||
              "Set a breakpoint in a given function. pc is an optional offset, which "
 | 
			
		||||
              "is in bytecode instructions. fun is a function value. Will throw an error "
 | 
			
		||||
              "if the offset is too large or negative.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset = 0;
 | 
			
		||||
    helper_find_fun(argc, argv, &def, &offset);
 | 
			
		||||
@@ -217,7 +250,9 @@ static Janet cfun_debug_fbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_unfbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_unfbreak,
 | 
			
		||||
              "(debug/unfbreak fun &opt pc)",
 | 
			
		||||
              "Unset a breakpoint set with debug/fbreak.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset;
 | 
			
		||||
    helper_find_fun(argc, argv, &def, &offset);
 | 
			
		||||
@@ -225,7 +260,12 @@ static Janet cfun_debug_unfbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_lineage(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_lineage,
 | 
			
		||||
              "(debug/lineage fib)",
 | 
			
		||||
              "Returns an array of all child fibers from a root fiber. This function "
 | 
			
		||||
              "is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
 | 
			
		||||
              "the fiber handling the error can see which fiber raised the signal. This function should "
 | 
			
		||||
              "be used mostly for debugging purposes.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(0);
 | 
			
		||||
@@ -250,9 +290,20 @@ static Janet doframe(JanetStackFrame *frame) {
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetCFunction cfun = (JanetCFunction)(frame->pc);
 | 
			
		||||
        if (cfun) {
 | 
			
		||||
            Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
 | 
			
		||||
            if (!janet_checktype(name, JANET_NIL)) {
 | 
			
		||||
                janet_table_put(t, janet_ckeywordv("name"), name);
 | 
			
		||||
            JanetCFunRegistry *reg = janet_registry_get(cfun);
 | 
			
		||||
            if (NULL != reg->name) {
 | 
			
		||||
                if (NULL != reg->name_prefix) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("name"), janet_wrap_string(janet_formatc("%s/%s", reg->name_prefix, reg->name)));
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("name"), janet_cstringv(reg->name));
 | 
			
		||||
                }
 | 
			
		||||
                if (NULL != reg->source_file) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source"), janet_cstringv(reg->source_file));
 | 
			
		||||
                }
 | 
			
		||||
                if (reg->source_line > 0) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source-line"), janet_wrap_integer(reg->source_line));
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source-column"), janet_wrap_integer(1));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("c"), janet_wrap_true());
 | 
			
		||||
@@ -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"
 | 
			
		||||
              "* :column - the current source column of the stack frame\n\n"
 | 
			
		||||
              "* :function - the function that the stack frame represents\n\n"
 | 
			
		||||
              "* :line - the current source line of the stack frame\n\n"
 | 
			
		||||
              "* :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) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -37,7 +37,7 @@ int32_t janetc_allocfar(JanetCompiler *c) {
 | 
			
		||||
    return reg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a register less than 256 */
 | 
			
		||||
/* Get a register less than 256 for temporary use. */
 | 
			
		||||
int32_t janetc_allocnear(JanetCompiler *c, JanetcRegisterTemp tag) {
 | 
			
		||||
    return janetc_regalloc_temp(&c->scope->ra, tag);
 | 
			
		||||
}
 | 
			
		||||
@@ -205,7 +205,7 @@ static int32_t janetc_regnear(JanetCompiler *c, JanetSlot s, JanetcRegisterTemp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if two slots are equal */
 | 
			
		||||
static int janetc_sequal(JanetSlot lhs, JanetSlot rhs) {
 | 
			
		||||
int janetc_sequal(JanetSlot lhs, JanetSlot rhs) {
 | 
			
		||||
    if ((lhs.flags & ~JANET_SLOTTYPE_ANY) == (rhs.flags & ~JANET_SLOTTYPE_ANY) &&
 | 
			
		||||
            lhs.index == rhs.index &&
 | 
			
		||||
            lhs.envindex == rhs.envindex) {
 | 
			
		||||
@@ -245,8 +245,8 @@ void janetc_copy(
 | 
			
		||||
    janetc_moveback(c, dest, nearreg);
 | 
			
		||||
    /* Cleanup */
 | 
			
		||||
    janetc_regalloc_freetemp(&c->scope->ra, nearreg, JANETC_REGTEMP_3);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Instruction templated emitters */
 | 
			
		||||
 | 
			
		||||
static int32_t emit1s(JanetCompiler *c, uint8_t op, JanetSlot s, int32_t rest, int wr) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -42,6 +42,9 @@ int32_t janetc_emit_ssi(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2
 | 
			
		||||
int32_t janetc_emit_ssu(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, uint8_t immediate, int wr);
 | 
			
		||||
int32_t janetc_emit_sss(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2, JanetSlot s3, int wr);
 | 
			
		||||
 | 
			
		||||
/* Check if two slots are equivalent */
 | 
			
		||||
int janetc_sequal(JanetSlot x, JanetSlot y);
 | 
			
		||||
 | 
			
		||||
/* Move value from one slot to another. Cannot copy to constant slots. */
 | 
			
		||||
void janetc_copy(JanetCompiler *c, JanetSlot dest, JanetSlot src);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3024
									
								
								src/core/ev.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3024
									
								
								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) 2021 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,8 +25,15 @@
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
#if defined(WIN32) || defined(_WIN32)
 | 
			
		||||
@@ -38,4 +45,11 @@
 | 
			
		||||
#define _XOPEN_SOURCE 500
 | 
			
		||||
#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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										247
									
								
								src/core/fiber.c
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								src/core/fiber.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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) 2021 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,9 @@
 | 
			
		||||
#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_FLAG_CANCELED 0x400000
 | 
			
		||||
#define JANET_FIBER_STATUS_OFFSET 16
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_BREAKPOINT       0x1000000
 | 
			
		||||
@@ -55,8 +57,6 @@
 | 
			
		||||
#define JANET_FIBER_DID_LONGJUMP     0x8000000
 | 
			
		||||
#define JANET_FIBER_FLAG_MASK        0xF000000
 | 
			
		||||
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
 | 
			
		||||
 | 
			
		||||
#define janet_fiber_set_status(f, s) do {\
 | 
			
		||||
    (f)->flags &= ~JANET_FIBER_STATUS_MASK;\
 | 
			
		||||
    (f)->flags |= (s) << JANET_FIBER_STATUS_OFFSET;\
 | 
			
		||||
@@ -76,4 +76,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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										256
									
								
								src/core/gc.c
									
									
									
									
									
								
							
							
						
						
									
										256
									
								
								src/core/gc.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -28,29 +28,9 @@
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "fiber.h"
 | 
			
		||||
#include "vector.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct JanetScratch {
 | 
			
		||||
    JanetScratchFinalizer finalize;
 | 
			
		||||
    long long mem[]; /* for proper alignment */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* GC State */
 | 
			
		||||
JANET_THREAD_LOCAL void *janet_vm_blocks;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_gc_interval;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_next_collection;
 | 
			
		||||
JANET_THREAD_LOCAL int janet_vm_gc_suspend = 0;
 | 
			
		||||
 | 
			
		||||
/* Roots */
 | 
			
		||||
JANET_THREAD_LOCAL Janet *janet_vm_roots;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_root_count;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_root_capacity;
 | 
			
		||||
 | 
			
		||||
/* Scratch Memory */
 | 
			
		||||
JANET_THREAD_LOCAL JanetScratch **janet_scratch_mem;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_scratch_cap;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_scratch_len;
 | 
			
		||||
 | 
			
		||||
/* Helpers for marking the various gc types */
 | 
			
		||||
static void janet_mark_funcenv(JanetFuncEnv *env);
 | 
			
		||||
static void janet_mark_funcdef(JanetFuncDef *def);
 | 
			
		||||
@@ -70,7 +50,7 @@ static JANET_THREAD_LOCAL size_t orig_rootcount;
 | 
			
		||||
 | 
			
		||||
/* Hint to the GC that we may need to collect */
 | 
			
		||||
void janet_gcpressure(size_t s) {
 | 
			
		||||
    janet_vm_next_collection += s;
 | 
			
		||||
    janet_vm.next_collection += s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark a value */
 | 
			
		||||
@@ -125,6 +105,14 @@ static void janet_mark_buffer(JanetBuffer *buffer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_abstract(void *adata) {
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Check if abstract type is a threaded abstract type. If it is, marking means
 | 
			
		||||
     * updating the threaded_abstract table. */
 | 
			
		||||
    if ((janet_abstract_head(adata)->gc.flags & JANET_MEM_TYPEBITS) == JANET_MEMORY_THREADED_ABSTRACT) {
 | 
			
		||||
        janet_table_put(&janet_vm.threaded_abstracts, janet_wrap_abstract(adata), janet_wrap_true());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (janet_gc_reachable(janet_abstract_head(adata)))
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(janet_abstract_head(adata));
 | 
			
		||||
@@ -135,6 +123,8 @@ static void janet_mark_abstract(void *adata) {
 | 
			
		||||
 | 
			
		||||
/* Mark a bunch of items in memory */
 | 
			
		||||
static void janet_mark_many(const Janet *values, int32_t n) {
 | 
			
		||||
    if (values == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
    const Janet *end = values + n;
 | 
			
		||||
    while (values < end) {
 | 
			
		||||
        janet_mark(*values);
 | 
			
		||||
@@ -172,10 +162,13 @@ recur: /* Manual tail recursion */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_struct(const JanetKV *st) {
 | 
			
		||||
recur:
 | 
			
		||||
    if (janet_gc_reachable(janet_struct_head(st)))
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(janet_struct_head(st));
 | 
			
		||||
    janet_mark_kvs(st, janet_struct_capacity(st));
 | 
			
		||||
    st = janet_struct_proto(st);
 | 
			
		||||
    if (st) goto recur;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_tuple(const Janet *tuple) {
 | 
			
		||||
@@ -224,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) {
 | 
			
		||||
@@ -239,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);
 | 
			
		||||
@@ -260,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;
 | 
			
		||||
@@ -277,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);
 | 
			
		||||
@@ -298,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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -319,24 +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_deinit_block(current);
 | 
			
		||||
            if (NULL != previous) {
 | 
			
		||||
                previous->next = next;
 | 
			
		||||
                previous->data.next = next;
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_vm_blocks = next;
 | 
			
		||||
                janet_vm.blocks = next;
 | 
			
		||||
            }
 | 
			
		||||
            free(current);
 | 
			
		||||
            janet_free(current);
 | 
			
		||||
        }
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Sweep threaded abstract types for references to decrement */
 | 
			
		||||
    JanetKV *items = janet_vm.threaded_abstracts.data;
 | 
			
		||||
    for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
 | 
			
		||||
        if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
 | 
			
		||||
 | 
			
		||||
            /* If item was not visited during the mark phase, then this
 | 
			
		||||
             * abstract type isn't present in the heap and needs its refcount
 | 
			
		||||
             * decremented, and shouuld be removed from table. If the refcount is
 | 
			
		||||
             * then 0, the item will be collected. This ensures that only one interpreter
 | 
			
		||||
             * will clean up the threaded abstract. */
 | 
			
		||||
 | 
			
		||||
            /* If not visited... */
 | 
			
		||||
            if (!janet_truthy(items[i].value)) {
 | 
			
		||||
                void *abst = janet_unwrap_abstract(items[i].key);
 | 
			
		||||
                if (0 == janet_abstract_decref(abst)) {
 | 
			
		||||
                    /* Run finalizer */
 | 
			
		||||
                    JanetAbstractHead *head = janet_abstract_head(abst);
 | 
			
		||||
                    if (head->type->gc) {
 | 
			
		||||
                        janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
 | 
			
		||||
                    }
 | 
			
		||||
                    /* Mark as tombstone in place */
 | 
			
		||||
                    items[i].key = janet_wrap_nil();
 | 
			
		||||
                    items[i].value = janet_wrap_false();
 | 
			
		||||
                    janet_vm.threaded_abstracts.deleted++;
 | 
			
		||||
                    janet_vm.threaded_abstracts.count--;
 | 
			
		||||
                    /* Free memory */
 | 
			
		||||
                    janet_free(janet_abstract_head(abst));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Reset for next sweep */
 | 
			
		||||
            items[i].value = janet_wrap_false();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Allocate some memory that is tracked for garbage collection */
 | 
			
		||||
@@ -344,8 +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) {
 | 
			
		||||
@@ -356,9 +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.next_collection += size;
 | 
			
		||||
    mem->data.next = janet_vm.blocks;
 | 
			
		||||
    janet_vm.blocks = mem;
 | 
			
		||||
    janet_vm.block_count++;
 | 
			
		||||
 | 
			
		||||
    return (void *)mem;
 | 
			
		||||
}
 | 
			
		||||
@@ -367,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) {
 | 
			
		||||
@@ -386,20 +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;
 | 
			
		||||
    orig_rootcount = janet_vm_root_count;
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    janet_net_markloop();
 | 
			
		||||
    /* Try and prevent many major collections back to back.
 | 
			
		||||
     * A full collection will take O(janet_vm.block_count) time.
 | 
			
		||||
     * If we have a large heap, make sure our interval is not too
 | 
			
		||||
     * small so we won't make many collections over it. This is just a
 | 
			
		||||
     * heuristic for automatically changing the gc interval */
 | 
			
		||||
    if (janet_vm.block_count * 8 > janet_vm.gc_interval) {
 | 
			
		||||
        janet_vm.gc_interval = janet_vm.block_count * sizeof(JanetGCObject);
 | 
			
		||||
    }
 | 
			
		||||
    orig_rootcount = janet_vm.root_count;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_ev_mark();
 | 
			
		||||
#endif
 | 
			
		||||
    janet_mark_fiber(janet_vm.root_fiber);
 | 
			
		||||
    for (i = 0; i < orig_rootcount; i++)
 | 
			
		||||
        janet_mark(janet_vm_roots[i]);
 | 
			
		||||
    while (orig_rootcount < janet_vm_root_count) {
 | 
			
		||||
        Janet x = janet_vm_roots[--janet_vm_root_count];
 | 
			
		||||
        janet_mark(janet_vm.roots[i]);
 | 
			
		||||
    while (orig_rootcount < janet_vm.root_count) {
 | 
			
		||||
        Janet x = janet_vm.roots[--janet_vm.root_count];
 | 
			
		||||
        janet_mark(x);
 | 
			
		||||
    }
 | 
			
		||||
    janet_sweep();
 | 
			
		||||
    janet_vm_next_collection = 0;
 | 
			
		||||
    janet_vm.next_collection = 0;
 | 
			
		||||
    janet_free_all_scratch();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -407,17 +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 */
 | 
			
		||||
@@ -438,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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -451,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -466,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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -520,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;
 | 
			
		||||
@@ -544,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) 2021 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) 2021 Calvin Rose & contributors
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -20,18 +20,18 @@
 | 
			
		||||
* IN THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
/* Conditional compilation */
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +39,8 @@
 | 
			
		||||
 | 
			
		||||
static int it_s64_get(void *p, Janet key, Janet *out);
 | 
			
		||||
static int it_u64_get(void *p, Janet key, Janet *out);
 | 
			
		||||
static Janet janet_int64_next(void *p, Janet key);
 | 
			
		||||
static Janet janet_uint64_next(void *p, Janet key);
 | 
			
		||||
 | 
			
		||||
static int32_t janet_int64_hash(void *p1, size_t size) {
 | 
			
		||||
    (void) size;
 | 
			
		||||
@@ -92,7 +94,8 @@ const JanetAbstractType janet_s64_type = {
 | 
			
		||||
    it_s64_tostring,
 | 
			
		||||
    janet_int64_compare,
 | 
			
		||||
    janet_int64_hash,
 | 
			
		||||
    JANET_ATEND_HASH
 | 
			
		||||
    janet_int64_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_u64_type = {
 | 
			
		||||
@@ -106,7 +109,8 @@ const JanetAbstractType janet_u64_type = {
 | 
			
		||||
    it_u64_tostring,
 | 
			
		||||
    janet_uint64_compare,
 | 
			
		||||
    janet_int64_hash,
 | 
			
		||||
    JANET_ATEND_HASH
 | 
			
		||||
    janet_uint64_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int64_t janet_unwrap_s64(Janet x) {
 | 
			
		||||
@@ -134,7 +138,7 @@ int64_t janet_unwrap_s64(Janet x) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_panic("bad s64 initializer");
 | 
			
		||||
    janet_panicf("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,28 @@ 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.
 | 
			
		||||
//
 | 
			
		||||
/*
 | 
			
		||||
 * 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 +251,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)
 | 
			
		||||
@@ -383,31 +391,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 +409,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 +421,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 +443,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 +469,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 +485,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 +509,14 @@ 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_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, it_cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_s64_type);
 | 
			
		||||
    janet_register_abstract_type(&janet_u64_type);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										532
									
								
								src/core/io.c
									
									
									
									
									
								
							
							
						
						
									
										532
									
								
								src/core/io.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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,22 +37,32 @@
 | 
			
		||||
 | 
			
		||||
static int cfun_io_gc(void *p, size_t len);
 | 
			
		||||
static int io_file_get(void *p, Janet key, Janet *out);
 | 
			
		||||
static void io_file_marshal(void *p, JanetMarshalContext *ctx);
 | 
			
		||||
static void *io_file_unmarshal(JanetMarshalContext *ctx);
 | 
			
		||||
static Janet io_file_next(void *p, Janet key);
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_file_type = {
 | 
			
		||||
    "core/file",
 | 
			
		||||
    cfun_io_gc,
 | 
			
		||||
    NULL,
 | 
			
		||||
    io_file_get,
 | 
			
		||||
    JANET_ATEND_GET
 | 
			
		||||
    NULL,
 | 
			
		||||
    io_file_marshal,
 | 
			
		||||
    io_file_unmarshal,
 | 
			
		||||
    NULL, /* tostring */
 | 
			
		||||
    NULL, /* compare */
 | 
			
		||||
    NULL, /* hash */
 | 
			
		||||
    io_file_next,
 | 
			
		||||
    JANET_ATEND_NEXT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Check arguments to fopen */
 | 
			
		||||
static int checkflags(const uint8_t *str) {
 | 
			
		||||
    int flags = 0;
 | 
			
		||||
static int32_t checkflags(const uint8_t *str) {
 | 
			
		||||
    int32_t flags = 0;
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    int32_t len = janet_string_length(str);
 | 
			
		||||
    if (!len || len > 3)
 | 
			
		||||
        janet_panic("file mode must have a length between 1 and 3");
 | 
			
		||||
    if (!len || len > 10)
 | 
			
		||||
        janet_panic("file mode must have a length between 1 and 10");
 | 
			
		||||
    switch (*str) {
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panicf("invalid flag %c, expected w, a, or r", *str);
 | 
			
		||||
@@ -70,7 +80,7 @@ static int 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;
 | 
			
		||||
@@ -80,12 +90,16 @@ static int 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet makef(FILE *f, int flags) {
 | 
			
		||||
static void *makef(FILE *f, int32_t flags) {
 | 
			
		||||
    JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
 | 
			
		||||
    iof->file = f;
 | 
			
		||||
    iof->flags = flags;
 | 
			
		||||
@@ -95,23 +109,28 @@ static Janet makef(FILE *f, int flags) {
 | 
			
		||||
    if (!(flags & JANET_FILE_NOT_CLOSEABLE))
 | 
			
		||||
        fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
 | 
			
		||||
#endif
 | 
			
		||||
    return janet_wrap_abstract(iof);
 | 
			
		||||
    return iof;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Open a process */
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_popen,
 | 
			
		||||
              "(file/popen command &opt mode) (DEPRECATED for os/spawn)",
 | 
			
		||||
              "Open a file that is backed by a process. The file must be opened in either "
 | 
			
		||||
              "the :r (read) or the :w (write) mode. In :r mode, the stdout of the "
 | 
			
		||||
              "process can be read from the file. In :w mode, the stdin of the process "
 | 
			
		||||
              "can be written to. Returns the new file.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    const uint8_t *fname = janet_getstring(argv, 0);
 | 
			
		||||
    const uint8_t *fmode = NULL;
 | 
			
		||||
    int flags;
 | 
			
		||||
    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 | checkflags(fmode);
 | 
			
		||||
        if (flags & (JANET_FILE_UPDATE | JANET_FILE_BINARY | JANET_FILE_APPEND)) {
 | 
			
		||||
            janet_panicf("invalid popen file mode :%S, expected :r or :w", fmode);
 | 
			
		||||
        }
 | 
			
		||||
        flags = JANET_FILE_PIPED | (fmode[0] == 'r' ? JANET_FILE_READ : JANET_FILE_WRITE);
 | 
			
		||||
        fmode = (const uint8_t *)((fmode[0] == 'r') ? "r" : "w");
 | 
			
		||||
    } else {
 | 
			
		||||
        fmode = (const uint8_t *)"r";
 | 
			
		||||
        flags = JANET_FILE_PIPED | JANET_FILE_READ;
 | 
			
		||||
@@ -121,13 +140,18 @@ static Janet cfun_io_popen(int32_t argc, Janet *argv) {
 | 
			
		||||
#endif
 | 
			
		||||
    FILE *f = popen((const char *)fname, (const char *)fmode);
 | 
			
		||||
    if (!f) {
 | 
			
		||||
        if (flags & JANET_FILE_NONIL)
 | 
			
		||||
            janet_panicf("failed to popen %s: %s", fname, strerror(errno));
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    return makef(f, flags);
 | 
			
		||||
    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.
 | 
			
		||||
@@ -137,11 +161,24 @@ static Janet cfun_io_temp(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_fopen(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fopen,
 | 
			
		||||
              "(file/open path &opt mode)",
 | 
			
		||||
              "Open a file. `path` is an absolute or relative path, and "
 | 
			
		||||
              "`mode` is a set of flags indicating the mode to open the file in. "
 | 
			
		||||
              "`mode` is a keyword where each character represents a flag. If the file "
 | 
			
		||||
              "cannot be opened, returns nil, otherwise returns the new file handle. "
 | 
			
		||||
              "Mode flags:\n\n"
 | 
			
		||||
              "* r - allow reading from the file\n\n"
 | 
			
		||||
              "* w - allow writing to the file\n\n"
 | 
			
		||||
              "* a - append to the file\n\n"
 | 
			
		||||
              "Following one of the initial flags, 0 or more of the following flags can be appended:\n\n"
 | 
			
		||||
              "* b - open the file in binary mode (rather than text mode)\n\n"
 | 
			
		||||
              "* + - append to the file instead of overwriting it\n\n"
 | 
			
		||||
              "* n - error if the file cannot be opened instead of returning nil") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    const uint8_t *fname = janet_getstring(argv, 0);
 | 
			
		||||
    const uint8_t *fmode;
 | 
			
		||||
    int flags;
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
        fmode = janet_getkeyword(argv, 1);
 | 
			
		||||
        flags = checkflags(fmode);
 | 
			
		||||
@@ -150,7 +187,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 ? makef(f, flags) : janet_wrap_nil();
 | 
			
		||||
    return f ? janet_makefile(f, flags)
 | 
			
		||||
           : (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil())
 | 
			
		||||
           : janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Read up to n bytes into buffer. */
 | 
			
		||||
@@ -166,7 +205,16 @@ static void read_chunk(JanetFile *iof, JanetBuffer *buffer, int32_t nBytesMax) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Read a certain number of bytes into memory */
 | 
			
		||||
static Janet cfun_io_fread(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fread,
 | 
			
		||||
              "(file/read f what &opt buf)",
 | 
			
		||||
              "Read a number of bytes from a file `f` into a buffer. A buffer `buf` can "
 | 
			
		||||
              "be provided as an optional third argument, otherwise a new buffer "
 | 
			
		||||
              "is created. `what` can either be an integer or a keyword. Returns the "
 | 
			
		||||
              "buffer with file contents. "
 | 
			
		||||
              "Values for `what`:\n\n"
 | 
			
		||||
              "* :all - read the whole file\n\n"
 | 
			
		||||
              "* :line - read up to and including the next newline character\n\n"
 | 
			
		||||
              "* n (integer) - read up to n bytes from the file") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED) janet_panic("file is closed");
 | 
			
		||||
@@ -206,7 +254,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)
 | 
			
		||||
@@ -229,7 +280,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)
 | 
			
		||||
@@ -246,25 +300,39 @@ static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
 | 
			
		||||
#define WEXITSTATUS(x) x
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* 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) {
 | 
			
		||||
/* 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))) {
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
            pclose(iof->file);
 | 
			
		||||
        if (file->flags & JANET_FILE_PIPED) {
 | 
			
		||||
            ret = pclose(file->file);
 | 
			
		||||
        } else
 | 
			
		||||
#endif
 | 
			
		||||
        } else {
 | 
			
		||||
            fclose(iof->file);
 | 
			
		||||
        {
 | 
			
		||||
            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;
 | 
			
		||||
    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. If the file is the result of a file/popen "
 | 
			
		||||
              "call, close waits for and returns the process exit status.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
@@ -277,6 +345,8 @@ static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
 | 
			
		||||
        iof->flags |= JANET_FILE_CLOSED;
 | 
			
		||||
        if (status == -1) janet_panic("could not close file");
 | 
			
		||||
        return janet_wrap_integer(WEXITSTATUS(status));
 | 
			
		||||
#else
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        if (fclose(iof->file)) {
 | 
			
		||||
@@ -284,12 +354,20 @@ static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
 | 
			
		||||
            janet_panic("could not close file");
 | 
			
		||||
        }
 | 
			
		||||
        iof->flags |= JANET_FILE_CLOSED;
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    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)
 | 
			
		||||
@@ -331,6 +409,55 @@ static int io_file_get(void *p, Janet key, Janet *out) {
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), io_file_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet io_file_next(void *p, Janet key) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    return janet_nextmethod(io_file_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void io_file_marshal(void *p, JanetMarshalContext *ctx) {
 | 
			
		||||
    JanetFile *iof = (JanetFile *)p;
 | 
			
		||||
    if (ctx->flags & JANET_MARSHAL_UNSAFE) {
 | 
			
		||||
        janet_marshal_abstract(ctx, p);
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
        janet_marshal_int(ctx, _fileno(iof->file));
 | 
			
		||||
#else
 | 
			
		||||
        janet_marshal_int(ctx, fileno(iof->file));
 | 
			
		||||
#endif
 | 
			
		||||
        janet_marshal_int(ctx, iof->flags);
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panic("cannot marshal file in safe mode");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *io_file_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
    if (ctx->flags & JANET_MARSHAL_UNSAFE) {
 | 
			
		||||
        JanetFile *iof = janet_unmarshal_abstract(ctx, sizeof(JanetFile));
 | 
			
		||||
        int32_t fd = janet_unmarshal_int(ctx);
 | 
			
		||||
        int32_t flags = janet_unmarshal_int(ctx);
 | 
			
		||||
        char fmt[4] = {0};
 | 
			
		||||
        int index = 0;
 | 
			
		||||
        if (flags & JANET_FILE_READ) fmt[index++] = 'r';
 | 
			
		||||
        if (flags & JANET_FILE_APPEND) {
 | 
			
		||||
            fmt[index++] = 'a';
 | 
			
		||||
        } else if (flags & JANET_FILE_WRITE) {
 | 
			
		||||
            fmt[index++] = 'w';
 | 
			
		||||
        }
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
        iof->file = _fdopen(fd, fmt);
 | 
			
		||||
#else
 | 
			
		||||
        iof->file = fdopen(fd, fmt);
 | 
			
		||||
#endif
 | 
			
		||||
        if (iof->file == NULL) {
 | 
			
		||||
            iof->flags = JANET_FILE_CLOSED;
 | 
			
		||||
        } else {
 | 
			
		||||
            iof->flags = flags;
 | 
			
		||||
        }
 | 
			
		||||
        return iof;
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panic("cannot unmarshal file in safe mode");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FILE *janet_dynfile(const char *name, FILE *def) {
 | 
			
		||||
    Janet x = janet_dyn(name);
 | 
			
		||||
    if (!janet_checktype(x, JANET_ABSTRACT)) return def;
 | 
			
		||||
@@ -340,26 +467,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);
 | 
			
		||||
@@ -370,7 +509,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)) {
 | 
			
		||||
@@ -383,7 +522,11 @@ static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
 | 
			
		||||
        }
 | 
			
		||||
        if (len) {
 | 
			
		||||
            if (1 != fwrite(vstr, len, 1, f)) {
 | 
			
		||||
                janet_panicf("could not print %d bytes to (dyn :%s)", len, name);
 | 
			
		||||
                if (f == dflt_file) {
 | 
			
		||||
                    janet_panicf("cannot print %d bytes", len);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_panicf("cannot print %d bytes to %v", len, x);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -392,41 +535,75 @@ 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_NIL:
 | 
			
		||||
            f = dflt_file;
 | 
			
		||||
            if (f == NULL) janet_panic("cannot print to nil");
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ABSTRACT: {
 | 
			
		||||
            void *abstract = janet_unwrap_abstract(x);
 | 
			
		||||
@@ -438,37 +615,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)) {
 | 
			
		||||
@@ -487,14 +694,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);
 | 
			
		||||
@@ -537,149 +748,26 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_makefile(FILE *f, int flags) {
 | 
			
		||||
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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetAbstract janet_checkfile(Janet j) {
 | 
			
		||||
    return janet_checkabstract(j, &janet_file_type);
 | 
			
		||||
}
 | 
			
		||||
@@ -692,19 +780,47 @@ 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),
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
        JANET_CORE_REG("file/popen", cfun_io_popen),
 | 
			
		||||
#endif
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, io_cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_file_type);
 | 
			
		||||
    int default_flags = JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE;
 | 
			
		||||
    /* stdout */
 | 
			
		||||
    janet_core_def(env, "stdout",
 | 
			
		||||
                   makef(stdout, JANET_FILE_APPEND | JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE),
 | 
			
		||||
                   JDOC("The standard output file."));
 | 
			
		||||
    JANET_CORE_DEF(env, "stdout",
 | 
			
		||||
                   janet_makefile(stdout, JANET_FILE_APPEND | default_flags),
 | 
			
		||||
                   "The standard output file.");
 | 
			
		||||
    /* stderr */
 | 
			
		||||
    janet_core_def(env, "stderr",
 | 
			
		||||
                   makef(stderr, JANET_FILE_APPEND | JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE),
 | 
			
		||||
                   JDOC("The standard error file."));
 | 
			
		||||
    JANET_CORE_DEF(env, "stderr",
 | 
			
		||||
                   janet_makefile(stderr, JANET_FILE_APPEND | default_flags),
 | 
			
		||||
                   "The standard error file.");
 | 
			
		||||
    /* stdin */
 | 
			
		||||
    janet_core_def(env, "stdin",
 | 
			
		||||
                   makef(stdin, JANET_FILE_READ | JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE),
 | 
			
		||||
                   JDOC("The standard input file."));
 | 
			
		||||
    JANET_CORE_DEF(env, "stdin",
 | 
			
		||||
                   janet_makefile(stdin, JANET_FILE_READ | default_flags),
 | 
			
		||||
                   "The standard input file.");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										216
									
								
								src/core/marsh.c
									
									
									
									
									
								
							
							
						
						
									
										216
									
								
								src/core/marsh.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -63,7 +63,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 */
 | 
			
		||||
@@ -214,15 +218,6 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add function flags to janet functions */
 | 
			
		||||
static void janet_func_addflags(JanetFuncDef *def) {
 | 
			
		||||
    if (def->name) def->flags |= JANET_FUNCDEF_FLAG_HASNAME;
 | 
			
		||||
    if (def->source) def->flags |= JANET_FUNCDEF_FLAG_HASSOURCE;
 | 
			
		||||
    if (def->defs) def->flags |= JANET_FUNCDEF_FLAG_HASDEFS;
 | 
			
		||||
    if (def->environments) def->flags |= JANET_FUNCDEF_FLAG_HASENVS;
 | 
			
		||||
    if (def->sourcemap) def->flags |= JANET_FUNCDEF_FLAG_HASSOURCEMAP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Marshal a sequence of u32s */
 | 
			
		||||
static void janet_marshal_u32s(MarshalState *st, const uint32_t *u32s, int32_t n) {
 | 
			
		||||
    for (int32_t i = 0; i < n; i++) {
 | 
			
		||||
@@ -243,7 +238,6 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_func_addflags(def);
 | 
			
		||||
    /* Add to lookup */
 | 
			
		||||
    janet_v_push(st->seen_defs, def);
 | 
			
		||||
    pushint(st, def->flags);
 | 
			
		||||
@@ -295,8 +289,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_FLAG_HASCHILD (1 << 29)
 | 
			
		||||
#define JANET_FIBER_FLAG_HASENV (1 << 28)
 | 
			
		||||
#define JANET_STACKFRAME_HASENV (1 << 30)
 | 
			
		||||
#define JANET_FIBER_FLAG_HASENV   (1 << 30)
 | 
			
		||||
#define JANET_STACKFRAME_HASENV   (INT32_MIN)
 | 
			
		||||
 | 
			
		||||
/* Marshal a fiber */
 | 
			
		||||
static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
 | 
			
		||||
@@ -335,6 +329,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) {
 | 
			
		||||
@@ -379,6 +374,21 @@ void janet_marshal_abstract(JanetMarshalContext *ctx, void *abstract) {
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
@@ -386,7 +396,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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -533,8 +543,10 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
            int32_t count;
 | 
			
		||||
            const JanetKV *struct_ = janet_unwrap_struct(x);
 | 
			
		||||
            count = janet_struct_length(struct_);
 | 
			
		||||
            pushbyte(st, LB_STRUCT);
 | 
			
		||||
            pushbyte(st, janet_struct_proto(struct_) ? LB_STRUCT_PROTO : LB_STRUCT);
 | 
			
		||||
            pushint(st, count);
 | 
			
		||||
            if (janet_struct_proto(struct_))
 | 
			
		||||
                marshal_one(st, janet_wrap_struct(janet_struct_proto(struct_)), flags + 1);
 | 
			
		||||
            for (int32_t i = 0; i < janet_struct_capacity(struct_); i++) {
 | 
			
		||||
                if (janet_checktype(struct_[i].key, JANET_NIL))
 | 
			
		||||
                    continue;
 | 
			
		||||
@@ -552,9 +564,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;
 | 
			
		||||
@@ -747,7 +760,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -843,7 +856,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -855,7 +868,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -864,7 +877,7 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal environments */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASENVS) {
 | 
			
		||||
            def->environments = calloc(1, sizeof(int32_t) * (size_t) environments_length);
 | 
			
		||||
            def->environments = janet_calloc(1, sizeof(int32_t) * (size_t) environments_length);
 | 
			
		||||
            if (!def->environments) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -878,7 +891,7 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal sub funcdefs */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS) {
 | 
			
		||||
            def->defs = calloc(1, sizeof(JanetFuncDef *) * (size_t) defs_length);
 | 
			
		||||
            def->defs = janet_calloc(1, sizeof(JanetFuncDef *) * (size_t) defs_length);
 | 
			
		||||
            if (!def->defs) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
@@ -893,14 +906,14 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
        /* Unmarshal source maps if needed */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
 | 
			
		||||
            int32_t current = 0;
 | 
			
		||||
            def->sourcemap = malloc(sizeof(JanetSourceMapping) * (size_t) bytecode_length);
 | 
			
		||||
            def->sourcemap = janet_malloc(sizeof(JanetSourceMapping) * (size_t) bytecode_length);
 | 
			
		||||
            if (!def->sourcemap) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
            for (int32_t i = 0; i < bytecode_length; i++) {
 | 
			
		||||
                current += readint(st, &data);
 | 
			
		||||
                def->sourcemap[i].line = current;
 | 
			
		||||
                def->sourcemap[i].column = readnat(st, &data);
 | 
			
		||||
                def->sourcemap[i].column = readint(st, &data);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            def->sourcemap = NULL;
 | 
			
		||||
@@ -908,11 +921,12 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal closure bitset if needed */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASCLOBITSET) {
 | 
			
		||||
            def->closure_bitset = malloc(sizeof(uint32_t) * def->slotcount);
 | 
			
		||||
            int32_t n = (def->slotcount + 31) >> 5;
 | 
			
		||||
            def->closure_bitset = janet_malloc(sizeof(uint32_t) * (size_t) n);
 | 
			
		||||
            if (NULL == def->closure_bitset) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
            data = janet_unmarshal_u32s(st, data, def->closure_bitset, (def->slotcount + 31) >> 5);
 | 
			
		||||
            data = janet_unmarshal_u32s(st, data, def->closure_bitset, n);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Validate */
 | 
			
		||||
@@ -943,6 +957,12 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
    fiber->data = NULL;
 | 
			
		||||
    fiber->child = NULL;
 | 
			
		||||
    fiber->env = NULL;
 | 
			
		||||
    fiber->last_value = janet_wrap_nil();
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    fiber->waiting = NULL;
 | 
			
		||||
    fiber->sched_id = 0;
 | 
			
		||||
    fiber->supervisor_channel = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Push fiber to seen stack */
 | 
			
		||||
    janet_v_push(st->lookup, janet_wrap_fiber(fiber));
 | 
			
		||||
@@ -964,7 +984,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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1049,6 +1069,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;
 | 
			
		||||
@@ -1057,6 +1080,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;
 | 
			
		||||
@@ -1101,14 +1129,18 @@ Janet janet_unmarshal_janet(JanetMarshalContext *ctx) {
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
 | 
			
		||||
void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p) {
 | 
			
		||||
    UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
 | 
			
		||||
    if (ctx->at == NULL) {
 | 
			
		||||
        janet_panicf("janet_unmarshal_abstract called more than once");
 | 
			
		||||
    }
 | 
			
		||||
    void *p = janet_abstract(ctx->at, size);
 | 
			
		||||
    janet_v_push(st->lookup, janet_wrap_abstract(p));
 | 
			
		||||
    ctx->at = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
 | 
			
		||||
    void *p = janet_abstract(ctx->at, size);
 | 
			
		||||
    janet_unmarshal_abstract_reuse(ctx, p);
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1116,17 +1148,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(
 | 
			
		||||
@@ -1228,13 +1259,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;
 | 
			
		||||
@@ -1247,6 +1284,7 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
        case LB_ARRAY:
 | 
			
		||||
        case LB_TUPLE:
 | 
			
		||||
        case LB_STRUCT:
 | 
			
		||||
        case LB_STRUCT_PROTO:
 | 
			
		||||
        case LB_TABLE:
 | 
			
		||||
        case LB_TABLE_PROTO:
 | 
			
		||||
            /* Things that open with integers */
 | 
			
		||||
@@ -1276,9 +1314,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);
 | 
			
		||||
@@ -1347,6 +1391,42 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
        case LB_THREADED_ABSTRACT: {
 | 
			
		||||
            MARSH_EOS(st, data + sizeof(void *));
 | 
			
		||||
            data++;
 | 
			
		||||
            if (!(flags & JANET_MARSHAL_UNSAFE)) {
 | 
			
		||||
                janet_panicf("unsafe flag not given, "
 | 
			
		||||
                             "will not unmarshal threaded abstract pointer at index %d",
 | 
			
		||||
                             (int)(data - st->start));
 | 
			
		||||
            }
 | 
			
		||||
            union {
 | 
			
		||||
                void *ptr;
 | 
			
		||||
                uint8_t bytes[sizeof(void *)];
 | 
			
		||||
            } u;
 | 
			
		||||
            memcpy(u.bytes, data, sizeof(void *));
 | 
			
		||||
            data += sizeof(void *);
 | 
			
		||||
 | 
			
		||||
            if (flags & JANET_MARSHAL_DECREF) {
 | 
			
		||||
                /* Decrement immediately and don't bother putting into heap */
 | 
			
		||||
                janet_abstract_decref(u.ptr);
 | 
			
		||||
                *out = janet_wrap_nil();
 | 
			
		||||
            } else {
 | 
			
		||||
                *out = janet_wrap_abstract(u.ptr);
 | 
			
		||||
                Janet check = janet_table_get(&janet_vm.threaded_abstracts, *out);
 | 
			
		||||
                if (janet_checktype(check, JANET_NIL)) {
 | 
			
		||||
                    /* Transfers reference from threaded channel buffer to current heap */
 | 
			
		||||
                    janet_table_put(&janet_vm.threaded_abstracts, *out, janet_wrap_false());
 | 
			
		||||
                } else {
 | 
			
		||||
                    /* Heap reference already accounted for, remove threaded channel reference. */
 | 
			
		||||
                    janet_abstract_decref(u.ptr);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        default: {
 | 
			
		||||
            janet_panicf("unknown byte %x at index %d",
 | 
			
		||||
                         *data,
 | 
			
		||||
@@ -1354,7 +1434,6 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#undef EXTRA
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_unmarshal(
 | 
			
		||||
@@ -1381,13 +1460,24 @@ 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_CORE_FN(cfun_marshal,
 | 
			
		||||
              "(marshal x &opt reverse-lookup buffer)",
 | 
			
		||||
              "Marshal a value into a buffer and return the buffer. The buffer "
 | 
			
		||||
              "can then later be unmarshalled to reconstruct the initial value. "
 | 
			
		||||
              "Optionally, one can pass in a reverse lookup table to not marshal "
 | 
			
		||||
              "aliased values that are found in the table. Then a forward "
 | 
			
		||||
              "lookup table can be used to recover the original value when "
 | 
			
		||||
              "unmarshalling.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
    JanetTable *rreg = NULL;
 | 
			
		||||
@@ -1403,7 +1493,11 @@ static Janet cfun_marshal(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_unmarshal(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_unmarshal,
 | 
			
		||||
              "(unmarshal buffer &opt lookup)",
 | 
			
		||||
              "Unmarshal a value from a buffer. An optional lookup table "
 | 
			
		||||
              "can be provided to allow for aliases to be resolved. Returns the value "
 | 
			
		||||
              "unmarshalled from the buffer.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetTable *reg = NULL;
 | 
			
		||||
@@ -1413,35 +1507,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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										458
									
								
								src/core/math.c
									
									
									
									
									
								
							
							
						
						
									
										458
									
								
								src/core/math.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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,301 +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 arccosize of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asin, asin, "Returns the arcsin of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atan, atan, "Returns the arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cos, cos, "Returns the cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cosh, cosh, "Returns the hyperbolic cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(acosh, acosh, "Returns the hyperbolic arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sin, sin, "Returns the sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sinh, sinh, "Returns the hyperbolic sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asinh, asinh, "Returns the hypberbolic arcsine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tan, tan, "Returns the tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tanh, tanh, "Returns the hyperbolic tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atanh, atanh, "Returns the hyperbolic arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp, exp, "Returns e to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp2, exp2, "Returns 2 to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(expm1, expm1, "Returns e to the power of x minus 1.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log, log, "Returns the natural logarithm of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log10, log10, "Returns the log base 10 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log2, log2, "Returns the log base 2 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sqrt, sqrt, "Returns the square root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cbrt, cbrt, "Returns the cube root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(ceil, ceil, "Returns the smallest integer value number that is not less than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(fabs, fabs, "Return the absolute value of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(floor, floor, "Returns the largest integer value number that is not greater than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(trunc, trunc, "Returns the integer between x and 0 nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(round, round, "Returns the integer nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(gamma, 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_CORE_DEF(env, "math/pi", janet_wrap_number(3.1415926535897931),
 | 
			
		||||
                   "The value pi.");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/e", janet_wrap_number(2.7182818284590451),
 | 
			
		||||
                   "The base of the natural log.");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/inf", janet_wrap_number(INFINITY),
 | 
			
		||||
                   "The number representing positive infinity");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/-inf", janet_wrap_number(-INFINITY),
 | 
			
		||||
                   "The number representing negative infinity");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int32-min", janet_wrap_number(INT32_MIN),
 | 
			
		||||
                   "The minimum contiguous integer representable by a 32 bit signed integer");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX),
 | 
			
		||||
                   "The maximum contiguous integer represtenable by a 32 bit signed integer");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
 | 
			
		||||
                   "The minimum contiguous integer representable by a double (2^53)");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
 | 
			
		||||
                   "The maximum contiguous integer represtenable by a double (-(2^53))");
 | 
			
		||||
#ifdef NAN
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#else
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(0.0 / 0.0), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1254
									
								
								src/core/net.c
									
									
									
									
									
								
							
							
						
						
									
										1254
									
								
								src/core/net.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1595
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										1595
									
								
								src/core/os.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										350
									
								
								src/core/parse.c
									
									
									
									
									
								
							
							
						
						
									
										350
									
								
								src/core/parse.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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) {
 | 
			
		||||
@@ -313,11 +320,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 +411,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 +422,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 +442,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 +582,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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -693,6 +737,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 +760,7 @@ Janet janet_parser_produce(JanetParser *parser) {
 | 
			
		||||
    }
 | 
			
		||||
    parser->pending--;
 | 
			
		||||
    parser->argcount--;
 | 
			
		||||
    parser->states[0].argn--;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -726,9 +785,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 +814,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 +862,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 +891,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 +920,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 that the end of file was reached to the parser. 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 +945,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 +972,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 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 +990,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 +1017,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 +1034,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 +1095,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 +1192,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 +1210,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 +1247,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 +1281,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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										547
									
								
								src/core/peg.c
									
									
									
									
									
								
							
							
						
						
									
										547
									
								
								src/core/peg.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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"); \
 | 
			
		||||
@@ -212,7 +286,7 @@ 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) {
 | 
			
		||||
@@ -222,7 +296,7 @@ tail:
 | 
			
		||||
                text++;
 | 
			
		||||
            }
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (text >= s->text_end) {
 | 
			
		||||
            if (text > s->text_end) {
 | 
			
		||||
                cap_load(s, cs);
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
@@ -262,7 +336,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 +348,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 +373,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 +422,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 +453,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 +498,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 +519,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 +529,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 +574,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 +655,7 @@ typedef struct {
 | 
			
		||||
    Janet form;
 | 
			
		||||
    int depth;
 | 
			
		||||
    uint32_t nexttag;
 | 
			
		||||
    int has_backref;
 | 
			
		||||
} Builder;
 | 
			
		||||
 | 
			
		||||
/* Forward declaration to allow recursion */
 | 
			
		||||
@@ -783,10 +954,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 +968,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 +990,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 +1033,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 +1083,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 +1143,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 +1168,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 +1187,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 +1253,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 +1388,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 +1408,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 +1457,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 +1503,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 +1524,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 +1566,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 +1574,20 @@ static JanetPeg *make_peg(Builder *b) {
 | 
			
		||||
static JanetPeg *compile_peg(Janet x) {
 | 
			
		||||
    Builder builder;
 | 
			
		||||
    builder.grammar = janet_table(0);
 | 
			
		||||
    builder.default_grammar = janet_get_core_table("default-peg-grammar");
 | 
			
		||||
    builder.default_grammar = NULL;
 | 
			
		||||
    {
 | 
			
		||||
        Janet default_grammarv = janet_dyn("peg-grammar");
 | 
			
		||||
        if (janet_checktype(default_grammarv, JANET_TABLE)) {
 | 
			
		||||
            builder.default_grammar = janet_unwrap_table(default_grammarv);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    builder.tags = janet_table(0);
 | 
			
		||||
    builder.constants = NULL;
 | 
			
		||||
    builder.bytecode = NULL;
 | 
			
		||||
    builder.nexttag = 1;
 | 
			
		||||
    builder.form = x;
 | 
			
		||||
    builder.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    builder.has_backref = 0;
 | 
			
		||||
    peg_compile1(&builder, x);
 | 
			
		||||
    JanetPeg *peg = make_peg(&builder);
 | 
			
		||||
    builder_cleanup(&builder);
 | 
			
		||||
@@ -1302,74 +1598,181 @@ static JanetPeg *compile_peg(Janet x) {
 | 
			
		||||
 * C Functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_compile(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_compile,
 | 
			
		||||
              "(peg/compile peg)",
 | 
			
		||||
              "Compiles a peg source data structure into a <core/peg>. This will speed up matching "
 | 
			
		||||
              "if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to suppliment "
 | 
			
		||||
              "the grammar of the peg for otherwise undefined peg keywords.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetPeg *peg = compile_peg(argv[0]);
 | 
			
		||||
    return janet_wrap_abstract(peg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_match(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
/* Common data for peg cfunctions */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    JanetPeg *peg;
 | 
			
		||||
    PegState s;
 | 
			
		||||
    JanetByteView bytes;
 | 
			
		||||
    JanetByteView repl;
 | 
			
		||||
    int32_t start;
 | 
			
		||||
} PegCall;
 | 
			
		||||
 | 
			
		||||
/* Initialize state for peg cfunctions */
 | 
			
		||||
static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
 | 
			
		||||
    PegCall ret;
 | 
			
		||||
    int32_t min = get_replace ? 3 : 2;
 | 
			
		||||
    janet_arity(argc, get_replace, -1);
 | 
			
		||||
    if (janet_checktype(argv[0], JANET_ABSTRACT) &&
 | 
			
		||||
            janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) {
 | 
			
		||||
        peg = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
        ret.peg = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
    } else {
 | 
			
		||||
        peg = compile_peg(argv[0]);
 | 
			
		||||
        ret.peg = compile_peg(argv[0]);
 | 
			
		||||
    }
 | 
			
		||||
    JanetByteView bytes = janet_getbytes(argv, 1);
 | 
			
		||||
    int32_t start;
 | 
			
		||||
    PegState s;
 | 
			
		||||
    if (argc > 2) {
 | 
			
		||||
        start = janet_gethalfrange(argv, 2, bytes.len, "offset");
 | 
			
		||||
        s.extrac = argc - 3;
 | 
			
		||||
        s.extrav = janet_tuple_n(argv + 3, argc - 3);
 | 
			
		||||
    if (get_replace) {
 | 
			
		||||
        ret.repl = janet_getbytes(argv, 1);
 | 
			
		||||
        ret.bytes = janet_getbytes(argv, 2);
 | 
			
		||||
    } else {
 | 
			
		||||
        start = 0;
 | 
			
		||||
        s.extrac = 0;
 | 
			
		||||
        s.extrav = NULL;
 | 
			
		||||
        ret.bytes = janet_getbytes(argv, 1);
 | 
			
		||||
    }
 | 
			
		||||
    s.mode = PEG_MODE_NORMAL;
 | 
			
		||||
    s.text_start = bytes.bytes;
 | 
			
		||||
    s.text_end = bytes.bytes + bytes.len;
 | 
			
		||||
    s.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    s.captures = janet_array(0);
 | 
			
		||||
    s.scratch = janet_buffer(10);
 | 
			
		||||
    s.tags = janet_buffer(10);
 | 
			
		||||
    s.constants = peg->constants;
 | 
			
		||||
    s.bytecode = peg->bytecode;
 | 
			
		||||
    const uint8_t *result = peg_rule(&s, s.bytecode, bytes.bytes + start);
 | 
			
		||||
    return result ? janet_wrap_array(s.captures) : janet_wrap_nil();
 | 
			
		||||
    if (argc > min) {
 | 
			
		||||
        ret.start = janet_gethalfrange(argv, min, ret.bytes.len, "offset");
 | 
			
		||||
        ret.s.extrac = argc - min - 1;
 | 
			
		||||
        ret.s.extrav = janet_tuple_n(argv + min + 1, argc - min - 1);
 | 
			
		||||
    } else {
 | 
			
		||||
        ret.start = 0;
 | 
			
		||||
        ret.s.extrac = 0;
 | 
			
		||||
        ret.s.extrav = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    ret.s.mode = PEG_MODE_NORMAL;
 | 
			
		||||
    ret.s.text_start = ret.bytes.bytes;
 | 
			
		||||
    ret.s.text_end = ret.bytes.bytes + ret.bytes.len;
 | 
			
		||||
    ret.s.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    ret.s.captures = janet_array(0);
 | 
			
		||||
    ret.s.tagged_captures = janet_array(0);
 | 
			
		||||
    ret.s.scratch = janet_buffer(10);
 | 
			
		||||
    ret.s.tags = janet_buffer(10);
 | 
			
		||||
    ret.s.constants = ret.peg->constants;
 | 
			
		||||
    ret.s.bytecode = ret.peg->bytecode;
 | 
			
		||||
    ret.s.linemap = NULL;
 | 
			
		||||
    ret.s.linemaplen = -1;
 | 
			
		||||
    ret.s.has_backref = ret.peg->has_backref;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void peg_call_reset(PegCall *c) {
 | 
			
		||||
    c->s.captures->count = 0;
 | 
			
		||||
    c->s.scratch->count = 0;
 | 
			
		||||
    c->s.tags->count = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_match,
 | 
			
		||||
              "(peg/match peg text &opt start & args)",
 | 
			
		||||
              "Match a Parsing Expression Grammar to a byte string and return an array of captured values. "
 | 
			
		||||
              "Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + c.start);
 | 
			
		||||
    return result ? janet_wrap_array(c.s.captures) : janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_find,
 | 
			
		||||
              "(peg/find peg text &opt start & args)",
 | 
			
		||||
              "Find first index where the peg matches in text. Returns an integer, or nil if not found.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    for (int32_t i = c.start; i < c.bytes.len; i++) {
 | 
			
		||||
        peg_call_reset(&c);
 | 
			
		||||
        if (peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i))
 | 
			
		||||
            return janet_wrap_integer(i);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_find_all,
 | 
			
		||||
              "(peg/find-all peg text &opt start & args)",
 | 
			
		||||
              "Find all indexes where the peg matches in text. Returns an array of integers.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    JanetArray *ret = janet_array(0);
 | 
			
		||||
    for (int32_t i = c.start; i < c.bytes.len; i++) {
 | 
			
		||||
        peg_call_reset(&c);
 | 
			
		||||
        if (peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i))
 | 
			
		||||
            janet_array_push(ret, janet_wrap_integer(i));
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_array(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 1);
 | 
			
		||||
    JanetBuffer *ret = janet_buffer(0);
 | 
			
		||||
    int32_t trail = 0;
 | 
			
		||||
    for (int32_t i = c.start; i < c.bytes.len;) {
 | 
			
		||||
        peg_call_reset(&c);
 | 
			
		||||
        const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + i);
 | 
			
		||||
        if (NULL != result) {
 | 
			
		||||
            if (trail < i) {
 | 
			
		||||
                janet_buffer_push_bytes(ret, c.bytes.bytes + trail, (i - trail));
 | 
			
		||||
                trail = i;
 | 
			
		||||
            }
 | 
			
		||||
            int32_t nexti = (int32_t)(result - c.bytes.bytes);
 | 
			
		||||
            janet_buffer_push_bytes(ret, c.repl.bytes, c.repl.len);
 | 
			
		||||
            trail = nexti;
 | 
			
		||||
            if (nexti == i) nexti++;
 | 
			
		||||
            i = nexti;
 | 
			
		||||
            if (only_one) break;
 | 
			
		||||
        } else {
 | 
			
		||||
            i++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (trail < c.bytes.len) {
 | 
			
		||||
        janet_buffer_push_bytes(ret, c.bytes.bytes + trail, (c.bytes.len - trail));
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_buffer(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace_all,
 | 
			
		||||
              "(peg/replace-all peg 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetMethod peg_methods[] = {
 | 
			
		||||
    {"match", cfun_peg_match},
 | 
			
		||||
    {"find", cfun_peg_find},
 | 
			
		||||
    {"find-all", cfun_peg_find_all},
 | 
			
		||||
    {"replace", cfun_peg_replace},
 | 
			
		||||
    {"replace-all", cfun_peg_replace_all},
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int cfun_peg_getter(JanetAbstract a, Janet key, Janet *out) {
 | 
			
		||||
    (void) a;
 | 
			
		||||
    if (janet_keyeq(key, "match")) {
 | 
			
		||||
        *out = janet_wrap_cfunction(cfun_peg_match);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
    if (!janet_checktype(key, JANET_KEYWORD))
 | 
			
		||||
        return 0;
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), peg_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg peg_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "peg/compile", cfun_peg_compile,
 | 
			
		||||
        JDOC("(peg/compile peg)\n\n"
 | 
			
		||||
             "Compiles a peg source data structure into a <core/peg>. This will speed up matching "
 | 
			
		||||
             "if the same peg will be used multiple times.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/match", cfun_peg_match,
 | 
			
		||||
        JDOC("(peg/match peg text &opt start & args)\n\n"
 | 
			
		||||
             "Match a Parsing Expression Grammar to a byte string and return an array of captured values. "
 | 
			
		||||
             "Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
static Janet peg_next(void *p, Janet key) {
 | 
			
		||||
    (void) p;
 | 
			
		||||
    return janet_nextmethod(peg_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load the peg module */
 | 
			
		||||
void janet_lib_peg(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, peg_cfuns);
 | 
			
		||||
    JanetRegExt cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("peg/compile", cfun_peg_compile),
 | 
			
		||||
        JANET_CORE_REG("peg/match", cfun_peg_match),
 | 
			
		||||
        JANET_CORE_REG("peg/find", cfun_peg_find),
 | 
			
		||||
        JANET_CORE_REG("peg/find-all", cfun_peg_find_all),
 | 
			
		||||
        JANET_CORE_REG("peg/replace", cfun_peg_replace),
 | 
			
		||||
        JANET_CORE_REG("peg/replace-all", cfun_peg_replace_all),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_peg_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										163
									
								
								src/core/pp.c
									
									
									
									
									
								
							
							
						
						
									
										163
									
								
								src/core/pp.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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,12 +39,17 @@
 | 
			
		||||
 | 
			
		||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
 | 
			
		||||
    janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
 | 
			
		||||
    /* Use int32_t range for valid integers because that is the
 | 
			
		||||
     * range most integer-expecting functions in the C api use. */
 | 
			
		||||
    const char *fmt = (x == floor(x) &&
 | 
			
		||||
                       x <= ((double) INT32_MAX) &&
 | 
			
		||||
                       x >= ((double) INT32_MIN)) ? "%.0f" : "%g";
 | 
			
		||||
    int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, fmt, x);
 | 
			
		||||
                       x <= JANET_INTMAX_DOUBLE &&
 | 
			
		||||
                       x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g";
 | 
			
		||||
    int count;
 | 
			
		||||
    if (x == 0.0) {
 | 
			
		||||
        /* Prevent printing of '-0' */
 | 
			
		||||
        count = 1;
 | 
			
		||||
        buffer->data[buffer->count] = '0';
 | 
			
		||||
    } else {
 | 
			
		||||
        count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, fmt, x);
 | 
			
		||||
    }
 | 
			
		||||
    buffer->count += count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -123,9 +128,6 @@ static void string_description_b(JanetBuffer *buffer, const char *title, void *p
 | 
			
		||||
#undef POINTSIZE
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef HEX
 | 
			
		||||
#undef BUFSIZE
 | 
			
		||||
 | 
			
		||||
static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
 | 
			
		||||
    janet_buffer_push_u8(buffer, '"');
 | 
			
		||||
    for (int32_t i = 0; i < len; ++i) {
 | 
			
		||||
@@ -191,7 +193,7 @@ static void janet_escape_buffer_b(JanetBuffer *buffer, JanetBuffer *bx) {
 | 
			
		||||
void janet_to_string_b(JanetBuffer *buffer, Janet x) {
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        case JANET_NIL:
 | 
			
		||||
            janet_buffer_push_cstring(buffer, "nil");
 | 
			
		||||
            janet_buffer_push_cstring(buffer, "");
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_BOOLEAN:
 | 
			
		||||
            janet_buffer_push_cstring(buffer,
 | 
			
		||||
@@ -225,12 +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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -257,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;
 | 
			
		||||
}
 | 
			
		||||
@@ -280,6 +276,9 @@ void janet_description_b(JanetBuffer *buffer, Janet x) {
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_NIL:
 | 
			
		||||
            janet_buffer_push_cstring(buffer, "nil");
 | 
			
		||||
            return;
 | 
			
		||||
        case JANET_KEYWORD:
 | 
			
		||||
            janet_buffer_push_u8(buffer, ':');
 | 
			
		||||
            break;
 | 
			
		||||
@@ -346,6 +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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -354,12 +356,16 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
 | 
			
		||||
    if (depth == 0) return 1;
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        case JANET_NIL:
 | 
			
		||||
        case JANET_NUMBER:
 | 
			
		||||
        case JANET_BOOLEAN:
 | 
			
		||||
        case JANET_BUFFER:
 | 
			
		||||
        case JANET_STRING:
 | 
			
		||||
            janet_description_b(S->buffer, x);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_NUMBER:
 | 
			
		||||
            janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2);
 | 
			
		||||
            int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", janet_unwrap_number(x));
 | 
			
		||||
            S->buffer->count += count;
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
        case JANET_KEYWORD:
 | 
			
		||||
            if (contains_bad_chars(janet_unwrap_keyword(x), janet_type(x) == JANET_SYMBOL)) return 1;
 | 
			
		||||
@@ -452,7 +458,7 @@ static const char *janet_pretty_colors[] = {
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m"
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[35m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
    "\x1B[36m",
 | 
			
		||||
@@ -556,14 +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)) {
 | 
			
		||||
@@ -576,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;
 | 
			
		||||
@@ -585,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++;
 | 
			
		||||
@@ -632,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);
 | 
			
		||||
@@ -654,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);
 | 
			
		||||
@@ -813,7 +866,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
                case 'P':
 | 
			
		||||
                case 'p': { /* janet pretty , precision = depth */
 | 
			
		||||
                    int depth = atoi(precision);
 | 
			
		||||
                    if (depth < 1) depth = 4;
 | 
			
		||||
                    if (depth < 1) depth = JANET_RECURSION_GUARD;
 | 
			
		||||
                    char d = c[-1];
 | 
			
		||||
                    int has_color = (d == 'P') || (d == 'Q') || (d == 'M') || (d == 'N');
 | 
			
		||||
                    int has_oneline = (d == 'Q') || (d == 'q') || (d == 'N') || (d == 'n');
 | 
			
		||||
@@ -839,7 +892,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);
 | 
			
		||||
        }
 | 
			
		||||
@@ -953,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':
 | 
			
		||||
@@ -962,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');
 | 
			
		||||
@@ -988,9 +1044,12 @@ void janet_buffer_format(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (nb >= MAX_ITEM)
 | 
			
		||||
                janet_panicf("format buffer overflow", form);
 | 
			
		||||
                janet_panic("format buffer overflow");
 | 
			
		||||
            if (nb > 0)
 | 
			
		||||
                janet_buffer_push_bytes(b, (uint8_t *) item, nb);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#undef HEX
 | 
			
		||||
#undef BUFSIZE
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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) 2021 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) 2021 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,6 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Run a string */
 | 
			
		||||
@@ -56,9 +55,10 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
                    done = 1;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                ret = janet_wrap_string(cres.error);
 | 
			
		||||
                if (cres.macrofiber) {
 | 
			
		||||
                    janet_eprintf("compile error in %s: ", sourcePath);
 | 
			
		||||
                    janet_stacktrace(cres.macrofiber, janet_wrap_string(cres.error));
 | 
			
		||||
                    janet_stacktrace(cres.macrofiber, ret);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf("compile error in %s: %s\n", sourcePath,
 | 
			
		||||
                                  (const char *)cres.error);
 | 
			
		||||
@@ -68,25 +68,25 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (done) break;
 | 
			
		||||
 | 
			
		||||
        /* Dispatch based on parse state */
 | 
			
		||||
        switch (janet_parser_status(&parser)) {
 | 
			
		||||
            case JANET_PARSE_DEAD:
 | 
			
		||||
                done = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_PARSE_ERROR:
 | 
			
		||||
            case JANET_PARSE_ERROR: {
 | 
			
		||||
                const char *e = janet_parser_error(&parser);
 | 
			
		||||
                errflags |= 0x04;
 | 
			
		||||
                janet_eprintf("parse error in %s: %s\n",
 | 
			
		||||
                              sourcePath, janet_parser_error(&parser));
 | 
			
		||||
                ret = janet_cstringv(e);
 | 
			
		||||
                size_t line = parser.line;
 | 
			
		||||
                size_t col = parser.column;
 | 
			
		||||
                janet_eprintf("%s:%lu:%lu: parse error: %s\n", sourcePath, line, col, e);
 | 
			
		||||
                done = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_PARSE_PENDING:
 | 
			
		||||
                if (index == len) {
 | 
			
		||||
                    janet_parser_eof(&parser);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_parser_consume(&parser, bytes[index++]);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case JANET_PARSE_ROOT:
 | 
			
		||||
            case JANET_PARSE_PENDING:
 | 
			
		||||
                if (index >= len) {
 | 
			
		||||
                    janet_parser_eof(&parser);
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -110,3 +110,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(fiber, out);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -251,6 +251,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;
 | 
			
		||||
@@ -336,10 +339,8 @@ static int defleaf(
 | 
			
		||||
 | 
			
		||||
        /* 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) {
 | 
			
		||||
@@ -409,7 +410,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 +473,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;
 | 
			
		||||
@@ -649,6 +674,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
        /* Compile function */
 | 
			
		||||
        JanetFuncDef *def = janetc_pop_funcdef(c);
 | 
			
		||||
        def->name = janet_cstring("_while");
 | 
			
		||||
        janet_def_addflags(def);
 | 
			
		||||
        int32_t defindex = janetc_addfuncdef(c, def);
 | 
			
		||||
        /* And then load the closure and call it. */
 | 
			
		||||
        int32_t cloreg = janetc_regalloc_temp(&c->scope->ra, JANETC_REGTEMP_0);
 | 
			
		||||
@@ -823,6 +849,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
 | 
			
		||||
 | 
			
		||||
    if (selfref) def->name = janet_unwrap_symbol(head);
 | 
			
		||||
    janet_def_addflags(def);
 | 
			
		||||
    defindex = janetc_addfuncdef(c, def);
 | 
			
		||||
 | 
			
		||||
    /* Ensure enough slots for vararg function. */
 | 
			
		||||
@@ -852,6 +879,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) 2021 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										208
									
								
								src/core/state.h
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								src/core/state.h
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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,76 +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;
 | 
			
		||||
 | 
			
		||||
/* 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 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) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -62,7 +62,7 @@ int janet_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
 | 
			
		||||
    int32_t ylen = janet_string_length(rhs);
 | 
			
		||||
    int32_t len = xlen > ylen ? ylen : xlen;
 | 
			
		||||
    int res = memcmp(lhs, rhs, len);
 | 
			
		||||
    if (res) return res;
 | 
			
		||||
    if (res) return res > 0 ? 1 : -1;
 | 
			
		||||
    if (xlen == ylen) return 0;
 | 
			
		||||
    return xlen < ylen ? -1 : 1;
 | 
			
		||||
}
 | 
			
		||||
@@ -108,7 +108,7 @@ static void kmp_init(
 | 
			
		||||
    if (patlen == 0) {
 | 
			
		||||
        janet_panic("expected non-empty pattern");
 | 
			
		||||
    }
 | 
			
		||||
    int32_t *lookup = calloc(patlen, sizeof(int32_t));
 | 
			
		||||
    int32_t *lookup = janet_calloc(patlen, sizeof(int32_t));
 | 
			
		||||
    if (!lookup) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -131,7 +131,7 @@ static void kmp_init(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void kmp_deinit(struct kmp_state *state) {
 | 
			
		||||
    free(state->lookup);
 | 
			
		||||
    janet_free(state->lookup);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void kmp_seti(struct kmp_state *state, int32_t i) {
 | 
			
		||||
@@ -170,13 +170,37 @@ static int32_t kmp_next(struct kmp_state *state) {
 | 
			
		||||
 | 
			
		||||
/* CFuns */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_slice,
 | 
			
		||||
              "(string/slice bytes &opt start end)",
 | 
			
		||||
              "Returns a substring from a byte sequence. The substring is from "
 | 
			
		||||
              "index start inclusive to index end exclusive. All indexing "
 | 
			
		||||
              "is from 0. 'start' and 'end' can also be negative to indicate indexing "
 | 
			
		||||
              "from the end of the string. Note that index -1 is synonymous with "
 | 
			
		||||
              "index (length bytes) to allow a full negative slice range. ") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_stringv(view.bytes + range.start, range.end - range.start);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_repeat(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_symbol_slice,
 | 
			
		||||
              "(symbol/slice bytes &opt start end)",
 | 
			
		||||
              "Same a string/slice, but returns a symbol.") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_symbolv(view.bytes + range.start, range.end - range.start);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_keyword_slice,
 | 
			
		||||
              "(keyword/slice bytes &opt start end)",
 | 
			
		||||
              "Same a string/slice, but returns a keyword.") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_keywordv(view.bytes + range.start, range.end - range.start);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_repeat,
 | 
			
		||||
              "(string/repeat bytes n)",
 | 
			
		||||
              "Returns a string that is n copies of bytes concatenated.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    int32_t rep = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -192,7 +216,9 @@ static Janet cfun_string_repeat(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(newbuf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_bytes(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_bytes,
 | 
			
		||||
              "(string/bytes str)",
 | 
			
		||||
              "Returns a tuple of integers that are the byte values of the string.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    Janet *tup = janet_tuple_begin(view.len);
 | 
			
		||||
@@ -203,7 +229,10 @@ static Janet cfun_string_bytes(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_end(tup));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_frombytes(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_frombytes,
 | 
			
		||||
              "(string/from-bytes & byte-vals)",
 | 
			
		||||
              "Creates a string from integer parameters with byte values. All integers "
 | 
			
		||||
              "will be coerced to the range of 1 byte 0-255.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    uint8_t *buf = janet_string_begin(argc);
 | 
			
		||||
    for (i = 0; i < argc; i++) {
 | 
			
		||||
@@ -213,7 +242,11 @@ static Janet cfun_string_frombytes(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_asciilower(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_asciilower,
 | 
			
		||||
              "(string/ascii-lower str)",
 | 
			
		||||
              "Returns a new string where all bytes are replaced with the "
 | 
			
		||||
              "lowercase version of themselves in ASCII. Does only a very simple "
 | 
			
		||||
              "case check, meaning no unicode support.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    uint8_t *buf = janet_string_begin(view.len);
 | 
			
		||||
@@ -228,7 +261,11 @@ static Janet cfun_string_asciilower(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_asciiupper(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_asciiupper,
 | 
			
		||||
              "(string/ascii-upper str)",
 | 
			
		||||
              "Returns a new string where all bytes are replaced with the "
 | 
			
		||||
              "uppercase version of themselves in ASCII. Does only a very simple "
 | 
			
		||||
              "case check, meaning no unicode support.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    uint8_t *buf = janet_string_begin(view.len);
 | 
			
		||||
@@ -243,7 +280,9 @@ static Janet cfun_string_asciiupper(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_string(janet_string_end(buf));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_reverse(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_reverse,
 | 
			
		||||
              "(string/reverse str)",
 | 
			
		||||
              "Returns a string that is the reversed version of str.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    uint8_t *buf = janet_string_begin(view.len);
 | 
			
		||||
@@ -267,7 +306,11 @@ static void findsetup(int32_t argc, Janet *argv, struct kmp_state *s, int32_t ex
 | 
			
		||||
    s->i = start;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_find(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_find,
 | 
			
		||||
              "(string/find patt str &opt start-index)",
 | 
			
		||||
              "Searches for the first instance of pattern patt in string "
 | 
			
		||||
              "str. Returns the index of the first character in patt if found, "
 | 
			
		||||
              "otherwise returns nil.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct kmp_state state;
 | 
			
		||||
    findsetup(argc, argv, &state, 0);
 | 
			
		||||
@@ -278,7 +321,9 @@ static Janet cfun_string_find(int32_t argc, Janet *argv) {
 | 
			
		||||
           : janet_wrap_integer(result);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_hasprefix(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_hasprefix,
 | 
			
		||||
              "(string/has-prefix? pfx str)",
 | 
			
		||||
              "Tests whether str starts with pfx.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView prefix = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView str = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -287,7 +332,9 @@ static Janet cfun_string_hasprefix(int32_t argc, Janet *argv) {
 | 
			
		||||
           : janet_wrap_boolean(memcmp(prefix.bytes, str.bytes, prefix.len) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_hassuffix(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_hassuffix,
 | 
			
		||||
              "(string/has-suffix? sfx str)",
 | 
			
		||||
              "Tests whether str ends with sfx.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView suffix = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView str = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -298,7 +345,12 @@ static Janet cfun_string_hassuffix(int32_t argc, Janet *argv) {
 | 
			
		||||
                                       suffix.len) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_string_findall(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_findall,
 | 
			
		||||
              "(string/find-all patt str &opt start-index)",
 | 
			
		||||
              "Searches for all instances of pattern patt in string "
 | 
			
		||||
              "str. Returns an array of all indices of found patterns. Overlapping "
 | 
			
		||||
              "instances of the pattern are counted individually, meaning a byte in str "
 | 
			
		||||
              "may contribute to multiple found patterns.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct kmp_state state;
 | 
			
		||||
    findsetup(argc, argv, &state, 0);
 | 
			
		||||
@@ -332,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;
 | 
			
		||||
@@ -352,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;
 | 
			
		||||
@@ -372,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;
 | 
			
		||||
@@ -386,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));
 | 
			
		||||
@@ -393,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);
 | 
			
		||||
@@ -415,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;
 | 
			
		||||
@@ -455,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 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);
 | 
			
		||||
@@ -495,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);
 | 
			
		||||
@@ -505,153 +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. ")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "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) 2021 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) 2021 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) 2021 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) 2021 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) 2021 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();
 | 
			
		||||
@@ -173,7 +166,7 @@ Janet janet_table_rawget(JanetTable *t, Janet key) {
 | 
			
		||||
Janet janet_table_remove(JanetTable *t, Janet key) {
 | 
			
		||||
    JanetKV *bucket = janet_table_find(t, key);
 | 
			
		||||
    if (NULL != bucket && !janet_checktype(bucket->key, JANET_NIL)) {
 | 
			
		||||
        Janet ret = bucket->key;
 | 
			
		||||
        Janet ret = bucket->value;
 | 
			
		||||
        t->count--;
 | 
			
		||||
        t->deleted++;
 | 
			
		||||
        bucket->key = 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 to go in 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);
 | 
			
		||||
    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 a 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 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.") {
 | 
			
		||||
    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,731 +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;
 | 
			
		||||
        clock_gettime(CLOCK_REALTIME, &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, keep lock */
 | 
			
		||||
                janet_vm_jmp_buf = old_buf;
 | 
			
		||||
            } 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);
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
    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 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.")
 | 
			
		||||
    },
 | 
			
		||||
    {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) 2021 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
 | 
			
		||||
							
								
								
									
										469
									
								
								src/core/util.c
									
									
									
									
									
								
							
							
						
						
									
										469
									
								
								src/core/util.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -26,6 +26,14 @@
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#else
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
@@ -94,7 +102,7 @@ const char *const janet_status_names[16] = {
 | 
			
		||||
    "alive"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_NO_PRF
 | 
			
		||||
#ifndef JANET_PRF
 | 
			
		||||
 | 
			
		||||
int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
 | 
			
		||||
    const uint8_t *end = str + len;
 | 
			
		||||
@@ -219,19 +227,21 @@ int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
 | 
			
		||||
/* Computes hash of an array of values */
 | 
			
		||||
int32_t janet_array_calchash(const Janet *array, int32_t len) {
 | 
			
		||||
    const Janet *end = array + len;
 | 
			
		||||
    uint32_t hash = 5381;
 | 
			
		||||
    while (array < end)
 | 
			
		||||
        hash = (hash << 5) + hash + janet_hash(*array++);
 | 
			
		||||
    uint32_t hash = 0;
 | 
			
		||||
    while (array < end) {
 | 
			
		||||
        uint32_t elem = janet_hash(*array++);
 | 
			
		||||
        hash ^= elem + 0x9e3779b9 + (hash << 6) + (hash >> 2);
 | 
			
		||||
    }
 | 
			
		||||
    return (int32_t) hash;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Computes hash of an array of values */
 | 
			
		||||
int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len) {
 | 
			
		||||
    const JanetKV *end = kvs + len;
 | 
			
		||||
    uint32_t hash = 5381;
 | 
			
		||||
    uint32_t hash = 0;
 | 
			
		||||
    while (kvs < end) {
 | 
			
		||||
        hash = (hash << 5) + hash + janet_hash(kvs->key);
 | 
			
		||||
        hash = (hash << 5) + hash + janet_hash(kvs->value);
 | 
			
		||||
        hash ^= janet_hash(kvs->key) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
 | 
			
		||||
        hash ^= janet_hash(kvs->value) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
 | 
			
		||||
        kvs++;
 | 
			
		||||
    }
 | 
			
		||||
    return (int32_t) hash;
 | 
			
		||||
@@ -352,104 +362,208 @@ const void *janet_strbinsearch(
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Register a value in the global registry */
 | 
			
		||||
void janet_register(const char *name, JanetCFunction cfun) {
 | 
			
		||||
    Janet key = janet_wrap_cfunction(cfun);
 | 
			
		||||
    Janet value = janet_csymbolv(name);
 | 
			
		||||
    janet_table_put(janet_vm_registry, key, value);
 | 
			
		||||
/* Add sourcemapping and documentation to a binding table */
 | 
			
		||||
static void janet_add_meta(JanetTable *table, const char *doc, const char *source_file, int32_t source_line) {
 | 
			
		||||
    if (doc) {
 | 
			
		||||
        janet_table_put(table, janet_ckeywordv("doc"), janet_cstringv(doc));
 | 
			
		||||
    }
 | 
			
		||||
    if (source_file && source_line) {
 | 
			
		||||
        Janet triple[3];
 | 
			
		||||
        triple[0] = janet_cstringv(source_file);
 | 
			
		||||
        triple[1] = janet_wrap_integer(source_line);
 | 
			
		||||
        triple[2] = janet_wrap_integer(1);
 | 
			
		||||
        Janet value = janet_wrap_tuple(janet_tuple_n(triple, 3));
 | 
			
		||||
        janet_table_put(table, janet_ckeywordv("source-map"), value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add a def to an environment */
 | 
			
		||||
void janet_def(JanetTable *env, const char *name, Janet val, const char *doc) {
 | 
			
		||||
void janet_def_sm(JanetTable *env, const char *name, Janet val, const char *doc, const char *source_file, int32_t source_line) {
 | 
			
		||||
    JanetTable *subt = janet_table(2);
 | 
			
		||||
    janet_table_put(subt, janet_ckeywordv("value"), val);
 | 
			
		||||
    if (doc)
 | 
			
		||||
        janet_table_put(subt, janet_ckeywordv("doc"), janet_cstringv(doc));
 | 
			
		||||
    janet_add_meta(subt, doc, source_file, source_line);
 | 
			
		||||
    janet_table_put(env, janet_csymbolv(name), janet_wrap_table(subt));
 | 
			
		||||
}
 | 
			
		||||
void janet_def(JanetTable *env, const char *name, Janet value, const char *doc) {
 | 
			
		||||
    janet_def_sm(env, name, value, doc, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add a var to the environment */
 | 
			
		||||
void janet_var(JanetTable *env, const char *name, Janet val, const char *doc) {
 | 
			
		||||
void janet_var_sm(JanetTable *env, const char *name, Janet val, const char *doc, const char *source_file, int32_t source_line) {
 | 
			
		||||
    JanetArray *array = janet_array(1);
 | 
			
		||||
    JanetTable *subt = janet_table(2);
 | 
			
		||||
    janet_array_push(array, val);
 | 
			
		||||
    janet_table_put(subt, janet_ckeywordv("ref"), janet_wrap_array(array));
 | 
			
		||||
    if (doc)
 | 
			
		||||
        janet_table_put(subt, janet_ckeywordv("doc"), janet_cstringv(doc));
 | 
			
		||||
    janet_add_meta(subt, doc, source_file, source_line);
 | 
			
		||||
    janet_table_put(env, janet_csymbolv(name), janet_wrap_table(subt));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load many cfunctions at once */
 | 
			
		||||
static void _janet_cfuns_prefix(JanetTable *env, const char *regprefix, const JanetReg *cfuns, int defprefix) {
 | 
			
		||||
    uint8_t *longname_buffer = NULL;
 | 
			
		||||
    size_t prefixlen = 0;
 | 
			
		||||
    size_t bufsize = 0;
 | 
			
		||||
    if (NULL != regprefix) {
 | 
			
		||||
        prefixlen = strlen(regprefix);
 | 
			
		||||
        bufsize = prefixlen + 256;
 | 
			
		||||
        longname_buffer = malloc(bufsize);
 | 
			
		||||
        if (NULL == longname_buffer) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        safe_memcpy(longname_buffer, regprefix, prefixlen);
 | 
			
		||||
        longname_buffer[prefixlen] = '/';
 | 
			
		||||
        prefixlen++;
 | 
			
		||||
    }
 | 
			
		||||
    while (cfuns->name) {
 | 
			
		||||
        Janet name;
 | 
			
		||||
        if (NULL != regprefix) {
 | 
			
		||||
            int32_t nmlen = 0;
 | 
			
		||||
            while (cfuns->name[nmlen]) nmlen++;
 | 
			
		||||
            int32_t totallen = (int32_t) prefixlen + nmlen;
 | 
			
		||||
            if ((size_t) totallen > bufsize) {
 | 
			
		||||
                bufsize = (size_t)(totallen) + 128;
 | 
			
		||||
                longname_buffer = realloc(longname_buffer, bufsize);
 | 
			
		||||
                if (NULL == longname_buffer) {
 | 
			
		||||
                    JANET_OUT_OF_MEMORY;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            safe_memcpy(longname_buffer + prefixlen, cfuns->name, nmlen);
 | 
			
		||||
            name = janet_wrap_symbol(janet_symbol(longname_buffer, totallen));
 | 
			
		||||
        } else {
 | 
			
		||||
            name = janet_csymbolv(cfuns->name);
 | 
			
		||||
        }
 | 
			
		||||
        Janet fun = janet_wrap_cfunction(cfuns->cfun);
 | 
			
		||||
        if (defprefix) {
 | 
			
		||||
            JanetTable *subt = janet_table(2);
 | 
			
		||||
            janet_table_put(subt, janet_ckeywordv("value"), fun);
 | 
			
		||||
            if (cfuns->documentation)
 | 
			
		||||
                janet_table_put(subt, janet_ckeywordv("doc"), janet_cstringv(cfuns->documentation));
 | 
			
		||||
            janet_table_put(env, name, janet_wrap_table(subt));
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_def(env, cfuns->name, fun, cfuns->documentation);
 | 
			
		||||
        }
 | 
			
		||||
        janet_table_put(janet_vm_registry, fun, name);
 | 
			
		||||
        cfuns++;
 | 
			
		||||
    }
 | 
			
		||||
    free(longname_buffer);
 | 
			
		||||
void janet_var(JanetTable *env, const char *name, Janet val, const char *doc) {
 | 
			
		||||
    janet_var_sm(env, name, val, doc, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_cfuns_prefix(JanetTable *env, const char *regprefix, const JanetReg *cfuns) {
 | 
			
		||||
    _janet_cfuns_prefix(env, regprefix, cfuns, 1);
 | 
			
		||||
/* Registry functions */
 | 
			
		||||
 | 
			
		||||
/* Put the registry in sorted order. */
 | 
			
		||||
static void janet_registry_sort(void) {
 | 
			
		||||
    for (size_t i = 1; i < janet_vm.registry_count; i++) {
 | 
			
		||||
        JanetCFunRegistry reg = janet_vm.registry[i];
 | 
			
		||||
        size_t j;
 | 
			
		||||
        for (j = i; j > 0; j--) {
 | 
			
		||||
            if ((void *)(janet_vm.registry[j - 1].cfun) < (void *)(reg.cfun)) break;
 | 
			
		||||
            janet_vm.registry[j] = janet_vm.registry[j - 1];
 | 
			
		||||
        }
 | 
			
		||||
        janet_vm.registry[j] = reg;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm.registry_dirty = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_registry_put(
 | 
			
		||||
    JanetCFunction key,
 | 
			
		||||
    const char *name,
 | 
			
		||||
    const char *name_prefix,
 | 
			
		||||
    const char *source_file,
 | 
			
		||||
    int32_t source_line) {
 | 
			
		||||
    if (janet_vm.registry_count == janet_vm.registry_cap) {
 | 
			
		||||
        size_t newcap = (janet_vm.registry_count + 1) * 2;
 | 
			
		||||
        /* Size it nicely with core by default */
 | 
			
		||||
        if (newcap < 512) {
 | 
			
		||||
            newcap = 512;
 | 
			
		||||
        }
 | 
			
		||||
        void *newmem = janet_realloc(janet_vm.registry, newcap * sizeof(JanetCFunRegistry));
 | 
			
		||||
        if (NULL == newmem) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        janet_vm.registry = newmem;
 | 
			
		||||
        janet_vm.registry_cap = newcap;
 | 
			
		||||
    }
 | 
			
		||||
    JanetCFunRegistry value = {
 | 
			
		||||
        key,
 | 
			
		||||
        name,
 | 
			
		||||
        name_prefix,
 | 
			
		||||
        source_file,
 | 
			
		||||
        source_line
 | 
			
		||||
    };
 | 
			
		||||
    janet_vm.registry[janet_vm.registry_count++] = value;
 | 
			
		||||
    janet_vm.registry_dirty = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetCFunRegistry *janet_registry_get(JanetCFunction key) {
 | 
			
		||||
    if (janet_vm.registry_dirty) {
 | 
			
		||||
        janet_registry_sort();
 | 
			
		||||
    }
 | 
			
		||||
    for (size_t i = 0; i < janet_vm.registry_count; i++) {
 | 
			
		||||
        if (janet_vm.registry[i].cfun == key) {
 | 
			
		||||
            return janet_vm.registry + i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetCFunRegistry *lo = janet_vm.registry;
 | 
			
		||||
    JanetCFunRegistry *hi = lo + janet_vm.registry_count;
 | 
			
		||||
    while (lo < hi) {
 | 
			
		||||
        JanetCFunRegistry *mid = lo + (hi - lo) / 2;
 | 
			
		||||
        if (mid->cfun == key) {
 | 
			
		||||
            return mid;
 | 
			
		||||
        }
 | 
			
		||||
        if ((void *)(mid->cfun) > (void *)(key)) {
 | 
			
		||||
            hi = mid;
 | 
			
		||||
        } else {
 | 
			
		||||
            lo = mid + 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    char *buf;
 | 
			
		||||
    size_t plen;
 | 
			
		||||
} NameBuf;
 | 
			
		||||
 | 
			
		||||
static void namebuf_init(NameBuf *namebuf, const char *prefix) {
 | 
			
		||||
    size_t plen = strlen(prefix);
 | 
			
		||||
    namebuf->plen = plen;
 | 
			
		||||
    namebuf->buf = janet_malloc(namebuf->plen + 256);
 | 
			
		||||
    if (NULL == namebuf->buf) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    memcpy(namebuf->buf, prefix, plen);
 | 
			
		||||
    namebuf->buf[plen] = '/';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void namebuf_deinit(NameBuf *namebuf) {
 | 
			
		||||
    janet_free(namebuf->buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *namebuf_name(NameBuf *namebuf, const char *suffix) {
 | 
			
		||||
    size_t slen = strlen(suffix);
 | 
			
		||||
    namebuf->buf = janet_realloc(namebuf->buf, namebuf->plen + 2 + slen);
 | 
			
		||||
    if (NULL == namebuf->buf) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    memcpy(namebuf->buf + namebuf->plen + 1, suffix, slen);
 | 
			
		||||
    namebuf->buf[namebuf->plen + 1 + slen] = '\0';
 | 
			
		||||
    return (char *)(namebuf->buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns) {
 | 
			
		||||
    _janet_cfuns_prefix(env, regprefix, cfuns, 0);
 | 
			
		||||
    while (cfuns->name) {
 | 
			
		||||
        Janet fun = janet_wrap_cfunction(cfuns->cfun);
 | 
			
		||||
        if (env) janet_def(env, cfuns->name, fun, cfuns->documentation);
 | 
			
		||||
        janet_registry_put(cfuns->cfun, cfuns->name, regprefix, NULL, 0);
 | 
			
		||||
        cfuns++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns) {
 | 
			
		||||
    while (cfuns->name) {
 | 
			
		||||
        Janet fun = janet_wrap_cfunction(cfuns->cfun);
 | 
			
		||||
        if (env) janet_def_sm(env, cfuns->name, fun, cfuns->documentation, cfuns->source_file, cfuns->source_line);
 | 
			
		||||
        janet_registry_put(cfuns->cfun, cfuns->name, regprefix, cfuns->source_file, cfuns->source_line);
 | 
			
		||||
        cfuns++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_cfuns_prefix(JanetTable *env, const char *regprefix, const JanetReg *cfuns) {
 | 
			
		||||
    NameBuf nb;
 | 
			
		||||
    if (env) namebuf_init(&nb, regprefix);
 | 
			
		||||
    while (cfuns->name) {
 | 
			
		||||
        Janet fun = janet_wrap_cfunction(cfuns->cfun);
 | 
			
		||||
        if (env) janet_def(env, namebuf_name(&nb, cfuns->name), fun, cfuns->documentation);
 | 
			
		||||
        janet_registry_put(cfuns->cfun, cfuns->name, regprefix, NULL, 0);
 | 
			
		||||
        cfuns++;
 | 
			
		||||
    }
 | 
			
		||||
    if (env) namebuf_deinit(&nb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_cfuns_ext_prefix(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns) {
 | 
			
		||||
    NameBuf nb;
 | 
			
		||||
    if (env) namebuf_init(&nb, regprefix);
 | 
			
		||||
    while (cfuns->name) {
 | 
			
		||||
        Janet fun = janet_wrap_cfunction(cfuns->cfun);
 | 
			
		||||
        if (env) janet_def_sm(env, namebuf_name(&nb, cfuns->name), fun, cfuns->documentation, cfuns->source_file, cfuns->source_line);
 | 
			
		||||
        janet_registry_put(cfuns->cfun, cfuns->name, regprefix, cfuns->source_file, cfuns->source_line);
 | 
			
		||||
        cfuns++;
 | 
			
		||||
    }
 | 
			
		||||
    if (env) namebuf_deinit(&nb);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Register a value in the global registry */
 | 
			
		||||
void janet_register(const char *name, JanetCFunction cfun) {
 | 
			
		||||
    janet_registry_put(cfun, name, NULL, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Abstract type introspection */
 | 
			
		||||
 | 
			
		||||
void janet_register_abstract_type(const JanetAbstractType *at) {
 | 
			
		||||
    Janet sym = janet_csymbolv(at->name);
 | 
			
		||||
    if (!(janet_checktype(janet_table_get(janet_vm_abstract_registry, sym), JANET_NIL))) {
 | 
			
		||||
    Janet check = janet_table_get(janet_vm.abstract_registry, sym);
 | 
			
		||||
    if (!janet_checktype(check, JANET_NIL) && at != janet_unwrap_pointer(check)) {
 | 
			
		||||
        janet_panicf("cannot register abstract type %s, "
 | 
			
		||||
                     "a type with the same name exists", at->name);
 | 
			
		||||
    }
 | 
			
		||||
    janet_table_put(janet_vm_abstract_registry, sym, janet_wrap_pointer((void *) at));
 | 
			
		||||
    janet_table_put(janet_vm.abstract_registry, sym, janet_wrap_pointer((void *) at));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType *janet_get_abstract_type(Janet key) {
 | 
			
		||||
    Janet wrapped = janet_table_get(janet_vm_abstract_registry, key);
 | 
			
		||||
    Janet wrapped = janet_table_get(janet_vm.abstract_registry, key);
 | 
			
		||||
    if (janet_checktype(wrapped, JANET_NIL)) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
@@ -457,46 +571,82 @@ const JanetAbstractType *janet_get_abstract_type(Janet key) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_BOOTSTRAP
 | 
			
		||||
void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p) {
 | 
			
		||||
void janet_core_def_sm(JanetTable *env, const char *name, Janet x, const void *p, const void *sf, int32_t sl) {
 | 
			
		||||
    (void) sf;
 | 
			
		||||
    (void) sl;
 | 
			
		||||
    (void) p;
 | 
			
		||||
    Janet key = janet_csymbolv(name);
 | 
			
		||||
    janet_table_put(env, key, x);
 | 
			
		||||
    if (janet_checktype(x, JANET_CFUNCTION)) {
 | 
			
		||||
        janet_table_put(janet_vm_registry, x, key);
 | 
			
		||||
        janet_registry_put(janet_unwrap_cfunction(x), name, NULL, NULL, 0);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns) {
 | 
			
		||||
void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns) {
 | 
			
		||||
    (void) regprefix;
 | 
			
		||||
    while (cfuns->name) {
 | 
			
		||||
        Janet fun = janet_wrap_cfunction(cfuns->cfun);
 | 
			
		||||
        janet_core_def(env, cfuns->name, fun, cfuns->documentation);
 | 
			
		||||
        janet_table_put(env, janet_csymbolv(cfuns->name), fun);
 | 
			
		||||
        janet_registry_put(cfuns->cfun, cfuns->name, regprefix, cfuns->source_file, cfuns->source_line);
 | 
			
		||||
        cfuns++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Resolve a symbol in the environment */
 | 
			
		||||
JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out) {
 | 
			
		||||
JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
 | 
			
		||||
    Janet ref;
 | 
			
		||||
    JanetTable *entry_table;
 | 
			
		||||
    Janet entry = janet_table_get(env, janet_wrap_symbol(sym));
 | 
			
		||||
    JanetBinding binding = {
 | 
			
		||||
        JANET_BINDING_NONE,
 | 
			
		||||
        janet_wrap_nil(),
 | 
			
		||||
        JANET_BINDING_DEP_NONE
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /* Check environment for entry */
 | 
			
		||||
    if (!janet_checktype(entry, JANET_TABLE))
 | 
			
		||||
        return JANET_BINDING_NONE;
 | 
			
		||||
        return binding;
 | 
			
		||||
    entry_table = janet_unwrap_table(entry);
 | 
			
		||||
 | 
			
		||||
    /* deprecation check */
 | 
			
		||||
    Janet deprecate = janet_table_get(entry_table, janet_ckeywordv("deprecated"));
 | 
			
		||||
    if (janet_checktype(deprecate, JANET_KEYWORD)) {
 | 
			
		||||
        JanetKeyword depkw = janet_unwrap_keyword(deprecate);
 | 
			
		||||
        if (!janet_cstrcmp(depkw, "relaxed")) {
 | 
			
		||||
            binding.deprecation = JANET_BINDING_DEP_RELAXED;
 | 
			
		||||
        } else if (!janet_cstrcmp(depkw, "normal")) {
 | 
			
		||||
            binding.deprecation = JANET_BINDING_DEP_NORMAL;
 | 
			
		||||
        } else if (!janet_cstrcmp(depkw, "strict")) {
 | 
			
		||||
            binding.deprecation = JANET_BINDING_DEP_STRICT;
 | 
			
		||||
        }
 | 
			
		||||
    } else if (!janet_checktype(deprecate, JANET_NIL)) {
 | 
			
		||||
        binding.deprecation = JANET_BINDING_DEP_NORMAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!janet_checktype(
 | 
			
		||||
                janet_table_get(entry_table, janet_ckeywordv("macro")),
 | 
			
		||||
                JANET_NIL)) {
 | 
			
		||||
        *out = janet_table_get(entry_table, janet_ckeywordv("value"));
 | 
			
		||||
        return JANET_BINDING_MACRO;
 | 
			
		||||
        binding.value = janet_table_get(entry_table, janet_ckeywordv("value"));
 | 
			
		||||
        binding.type = JANET_BINDING_MACRO;
 | 
			
		||||
        return binding;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ref = janet_table_get(entry_table, janet_ckeywordv("ref"));
 | 
			
		||||
    if (janet_checktype(ref, JANET_ARRAY)) {
 | 
			
		||||
        *out = ref;
 | 
			
		||||
        return JANET_BINDING_VAR;
 | 
			
		||||
        binding.value = ref;
 | 
			
		||||
        binding.type = JANET_BINDING_VAR;
 | 
			
		||||
        return binding;
 | 
			
		||||
    }
 | 
			
		||||
    *out = janet_table_get(entry_table, janet_ckeywordv("value"));
 | 
			
		||||
    return JANET_BINDING_DEF;
 | 
			
		||||
 | 
			
		||||
    binding.value = janet_table_get(entry_table, janet_ckeywordv("value"));
 | 
			
		||||
    binding.type = JANET_BINDING_DEF;
 | 
			
		||||
    return binding;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out) {
 | 
			
		||||
    JanetBinding binding = janet_resolve_ext(env, sym);
 | 
			
		||||
    *out = binding.value;
 | 
			
		||||
    return binding.type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Resolve a symbol in the core environment. */
 | 
			
		||||
@@ -574,8 +724,12 @@ int janet_checksize(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_NUMBER))
 | 
			
		||||
        return 0;
 | 
			
		||||
    double dval = janet_unwrap_number(x);
 | 
			
		||||
    return dval == (double)((size_t) dval) &&
 | 
			
		||||
           dval <= SIZE_MAX;
 | 
			
		||||
    if (dval != (double)((size_t) dval)) return 0;
 | 
			
		||||
    if (SIZE_MAX > JANET_INTMAX_INT64) {
 | 
			
		||||
        return dval <= JANET_INTMAX_INT64;
 | 
			
		||||
    } else {
 | 
			
		||||
        return dval <= SIZE_MAX;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_get_core_table(const char *name) {
 | 
			
		||||
@@ -586,3 +740,140 @@ JanetTable *janet_get_core_table(const char *name) {
 | 
			
		||||
    if (!janet_checktype(out, JANET_TABLE)) return NULL;
 | 
			
		||||
    return janet_unwrap_table(out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Sort keys of a dictionary type */
 | 
			
		||||
int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffer) {
 | 
			
		||||
 | 
			
		||||
    /* First, put populated indices into index_buffer */
 | 
			
		||||
    int32_t next_index = 0;
 | 
			
		||||
    for (int32_t i = 0; i < cap; i++) {
 | 
			
		||||
        if (!janet_checktype(dict[i].key, JANET_NIL)) {
 | 
			
		||||
            index_buffer[next_index++] = i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Next, sort those (simple insertion sort here for now) */
 | 
			
		||||
    for (int32_t i = 1; i < next_index; i++) {
 | 
			
		||||
        int32_t index_to_insert = index_buffer[i];
 | 
			
		||||
        Janet lhs = dict[index_to_insert].key;
 | 
			
		||||
        for (int32_t j = i - 1; j >= 0; j--) {
 | 
			
		||||
            index_buffer[j + 1] = index_buffer[j];
 | 
			
		||||
            Janet rhs = dict[index_buffer[j]].key;
 | 
			
		||||
            if (janet_compare(lhs, rhs) >= 0) {
 | 
			
		||||
                index_buffer[j + 1] = index_to_insert;
 | 
			
		||||
                break;
 | 
			
		||||
            } else if (j == 0) {
 | 
			
		||||
                index_buffer[0] = index_to_insert;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Return number of indices found */
 | 
			
		||||
    return next_index;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clock shims for various platforms */
 | 
			
		||||
#ifdef JANET_GETTIME
 | 
			
		||||
/* For macos */
 | 
			
		||||
#ifdef __MACH__
 | 
			
		||||
#include <mach/clock.h>
 | 
			
		||||
#include <mach/mach.h>
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    FILETIME ftime;
 | 
			
		||||
    GetSystemTimeAsFileTime(&ftime);
 | 
			
		||||
    int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
 | 
			
		||||
    /* Windows epoch is January 1, 1601 apparently */
 | 
			
		||||
    wintime -= 116444736000000000LL;
 | 
			
		||||
    spec->tv_sec  = wintime / 10000000LL;
 | 
			
		||||
    /* Resolution is 100 nanoseconds. */
 | 
			
		||||
    spec->tv_nsec = wintime % 10000000LL * 100;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
#elif defined(__MACH__)
 | 
			
		||||
int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    clock_serv_t cclock;
 | 
			
		||||
    mach_timespec_t mts;
 | 
			
		||||
    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
 | 
			
		||||
    clock_get_time(cclock, &mts);
 | 
			
		||||
    mach_port_deallocate(mach_task_self(), cclock);
 | 
			
		||||
    spec->tv_sec = mts.tv_sec;
 | 
			
		||||
    spec->tv_nsec = mts.tv_nsec;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    return clock_gettime(CLOCK_REALTIME, spec);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Setting C99 standard makes this not available, but it should
 | 
			
		||||
 * work/link properly if we detect a BSD */
 | 
			
		||||
#if defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
 | 
			
		||||
void arc4random_buf(void *buf, size_t nbytes);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int janet_cryptorand(uint8_t *out, size_t n) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    for (size_t i = 0; i < n; i += sizeof(unsigned int)) {
 | 
			
		||||
        unsigned int v;
 | 
			
		||||
        if (rand_s(&v))
 | 
			
		||||
            return -1;
 | 
			
		||||
        for (int32_t j = 0; (j < sizeof(unsigned int)) && (i + j < n); j++) {
 | 
			
		||||
            out[i + j] = v & 0xff;
 | 
			
		||||
            v = v >> 8;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
#elif defined(JANET_LINUX) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) )
 | 
			
		||||
    /* We should be able to call getrandom on linux, but it doesn't seem
 | 
			
		||||
       to be uniformly supported on linux distros.
 | 
			
		||||
       On Mac, arc4random_buf wasn't available on until 10.7.
 | 
			
		||||
       In these cases, use this fallback path for now... */
 | 
			
		||||
    int rc;
 | 
			
		||||
    int randfd;
 | 
			
		||||
    RETRY_EINTR(randfd, open("/dev/urandom", O_RDONLY | O_CLOEXEC));
 | 
			
		||||
    if (randfd < 0)
 | 
			
		||||
        return -1;
 | 
			
		||||
    while (n > 0) {
 | 
			
		||||
        ssize_t nread;
 | 
			
		||||
        RETRY_EINTR(nread, read(randfd, out, n));
 | 
			
		||||
        if (nread <= 0) {
 | 
			
		||||
            RETRY_EINTR(rc, close(randfd));
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        out += nread;
 | 
			
		||||
        n -= nread;
 | 
			
		||||
    }
 | 
			
		||||
    RETRY_EINTR(rc, close(randfd));
 | 
			
		||||
    return 0;
 | 
			
		||||
#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
 | 
			
		||||
    arc4random_buf(out, n);
 | 
			
		||||
    return 0;
 | 
			
		||||
#else
 | 
			
		||||
    (void) n;
 | 
			
		||||
    (void) out;
 | 
			
		||||
    return -1;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Alloc function macro fills */
 | 
			
		||||
void *(janet_malloc)(size_t size) {
 | 
			
		||||
    return janet_malloc(size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void (janet_free)(void *ptr) {
 | 
			
		||||
    janet_free(ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *(janet_calloc)(size_t nmemb, size_t size) {
 | 
			
		||||
    return janet_calloc(nmemb, size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *(janet_realloc)(void *ptr, size_t size) {
 | 
			
		||||
    return janet_realloc(ptr, size);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -26,11 +26,17 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#if !defined(JANET_REDUCED_OS) || !defined(JANET_SINGLE_THREADED)
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#define JANET_GETTIME
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Handle runtime errors */
 | 
			
		||||
#ifndef JANET_EXIT
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
@@ -43,26 +49,16 @@
 | 
			
		||||
} while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define JANET_MARSHAL_DECREF 0x40000
 | 
			
		||||
 | 
			
		||||
#define janet_assert(c, m) do { \
 | 
			
		||||
    if (!(c)) JANET_EXIT((m)); \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
/* What to do when out of memory */
 | 
			
		||||
#ifndef JANET_OUT_OF_MEMORY
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Omit docstrings in some builds */
 | 
			
		||||
#ifndef JANET_BOOTSTRAP
 | 
			
		||||
#define JDOC(x) NULL
 | 
			
		||||
#define JANET_NO_BOOTSTRAP
 | 
			
		||||
#else
 | 
			
		||||
#define JDOC(x) x
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Utils */
 | 
			
		||||
#define janet_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1))
 | 
			
		||||
int janet_valid_utf8(const uint8_t *str, int32_t len);
 | 
			
		||||
int janet_is_symbol_char(uint8_t c);
 | 
			
		||||
extern const char janet_base64[65];
 | 
			
		||||
int32_t janet_array_calchash(const Janet *array, int32_t len);
 | 
			
		||||
int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len);
 | 
			
		||||
@@ -71,10 +67,10 @@ int32_t janet_tablen(int32_t n);
 | 
			
		||||
void safe_memcpy(void *dest, const void *src, size_t len);
 | 
			
		||||
void janet_buffer_push_types(JanetBuffer *buffer, int types);
 | 
			
		||||
const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key);
 | 
			
		||||
Janet janet_dict_get(const JanetKV *buckets, int32_t cap, Janet key);
 | 
			
		||||
void janet_memempty(JanetKV *mem, int32_t count);
 | 
			
		||||
void *janet_memalloc_empty(int32_t count);
 | 
			
		||||
JanetTable *janet_get_core_table(const char *name);
 | 
			
		||||
void janet_def_addflags(JanetFuncDef *def);
 | 
			
		||||
const void *janet_strbinsearch(
 | 
			
		||||
    const void *tab,
 | 
			
		||||
    size_t tabcount,
 | 
			
		||||
@@ -86,17 +82,45 @@ void janet_buffer_format(
 | 
			
		||||
    int32_t argstart,
 | 
			
		||||
    int32_t argc,
 | 
			
		||||
    Janet *argv);
 | 
			
		||||
Janet janet_next_impl(Janet ds, Janet key, int is_interpreter);
 | 
			
		||||
 | 
			
		||||
/* Registry functions */
 | 
			
		||||
void janet_registry_put(
 | 
			
		||||
    JanetCFunction key,
 | 
			
		||||
    const char *name,
 | 
			
		||||
    const char *name_prefix,
 | 
			
		||||
    const char *source_file,
 | 
			
		||||
    int32_t source_line);
 | 
			
		||||
JanetCFunRegistry *janet_registry_get(JanetCFunction key);
 | 
			
		||||
 | 
			
		||||
/* Inside the janet core, defining globals is different
 | 
			
		||||
 * at bootstrap time and normal runtime */
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
#define janet_core_def janet_def
 | 
			
		||||
#define janet_core_cfuns janet_cfuns
 | 
			
		||||
#define JANET_CORE_REG JANET_REG
 | 
			
		||||
#define JANET_CORE_FN JANET_FN
 | 
			
		||||
#define JANET_CORE_DEF JANET_DEF
 | 
			
		||||
#define janet_core_def_sm janet_def_sm
 | 
			
		||||
#define janet_core_cfuns_ext janet_cfuns_ext
 | 
			
		||||
#else
 | 
			
		||||
void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p);
 | 
			
		||||
void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns);
 | 
			
		||||
#define JANET_CORE_REG JANET_REG_S
 | 
			
		||||
#define JANET_CORE_FN JANET_FN_S
 | 
			
		||||
#define JANET_CORE_DEF(ENV, NAME, X, DOC) janet_core_def_sm(ENV, NAME, X, DOC, NULL, 0)
 | 
			
		||||
void janet_core_def_sm(JanetTable *env, const char *name, Janet x, const void *p, const void *sf, int32_t sl);
 | 
			
		||||
void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Clock gettime */
 | 
			
		||||
#ifdef JANET_GETTIME
 | 
			
		||||
int janet_gettime(struct timespec *spec);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* strdup */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#define strdup(x) _strdup(x)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
 | 
			
		||||
 | 
			
		||||
/* Initialize builtin libraries */
 | 
			
		||||
void janet_lib_io(JanetTable *env);
 | 
			
		||||
void janet_lib_math(JanetTable *env);
 | 
			
		||||
@@ -104,6 +128,7 @@ void janet_lib_array(JanetTable *env);
 | 
			
		||||
void janet_lib_tuple(JanetTable *env);
 | 
			
		||||
void janet_lib_buffer(JanetTable *env);
 | 
			
		||||
void janet_lib_table(JanetTable *env);
 | 
			
		||||
void janet_lib_struct(JanetTable *env);
 | 
			
		||||
void janet_lib_fiber(JanetTable *env);
 | 
			
		||||
void janet_lib_os(JanetTable *env);
 | 
			
		||||
void janet_lib_string(JanetTable *env);
 | 
			
		||||
@@ -123,13 +148,14 @@ void janet_lib_typed_array(JanetTable *env);
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
void janet_lib_inttypes(JanetTable *env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
void janet_lib_thread(JanetTable *env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
void janet_lib_net(JanetTable *env);
 | 
			
		||||
void janet_net_deinit(void);
 | 
			
		||||
void janet_net_markloop(void);
 | 
			
		||||
extern const JanetAbstractType janet_address_type;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
void janet_lib_ev(JanetTable *env);
 | 
			
		||||
void janet_ev_mark(void);
 | 
			
		||||
int janet_make_pipe(JanetHandle handles[2], int mode);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										136
									
								
								src/core/value.c
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								src/core/value.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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,12 +25,11 @@
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#include "fiber.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_THREAD_LOCAL JanetTraversalNode *janet_vm_traversal = NULL;
 | 
			
		||||
JANET_THREAD_LOCAL JanetTraversalNode *janet_vm_traversal_top = NULL;
 | 
			
		||||
JANET_THREAD_LOCAL JanetTraversalNode *janet_vm_traversal_base = NULL;
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
static void push_traversal_node(void *lhs, void *rhs, int32_t index2) {
 | 
			
		||||
    JanetTraversalNode node;
 | 
			
		||||
@@ -38,21 +37,22 @@ static void push_traversal_node(void *lhs, void *rhs, int32_t index2) {
 | 
			
		||||
    node.other = (JanetGCObject *) rhs;
 | 
			
		||||
    node.index = 0;
 | 
			
		||||
    node.index2 = index2;
 | 
			
		||||
    if (janet_vm_traversal + 1 >= janet_vm_traversal_top) {
 | 
			
		||||
        size_t oldsize = janet_vm_traversal - janet_vm_traversal_base;
 | 
			
		||||
    int is_new = janet_vm.traversal_base == NULL;
 | 
			
		||||
    if (is_new || (janet_vm.traversal + 1 >= janet_vm.traversal_top)) {
 | 
			
		||||
        size_t oldsize = is_new ? 0 : (janet_vm.traversal - janet_vm.traversal_base);
 | 
			
		||||
        size_t newsize = 2 * oldsize + 1;
 | 
			
		||||
        if (newsize < 128) {
 | 
			
		||||
            newsize = 128;
 | 
			
		||||
        }
 | 
			
		||||
        JanetTraversalNode *tn = realloc(janet_vm_traversal_base, newsize * sizeof(JanetTraversalNode));
 | 
			
		||||
        JanetTraversalNode *tn = janet_realloc(janet_vm.traversal_base, newsize * sizeof(JanetTraversalNode));
 | 
			
		||||
        if (tn == NULL) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        janet_vm_traversal_base = tn;
 | 
			
		||||
        janet_vm_traversal_top = janet_vm_traversal_base + newsize;
 | 
			
		||||
        janet_vm_traversal = janet_vm_traversal_base + oldsize;
 | 
			
		||||
        janet_vm.traversal_base = tn;
 | 
			
		||||
        janet_vm.traversal_top = janet_vm.traversal_base + newsize;
 | 
			
		||||
        janet_vm.traversal = janet_vm.traversal_base + oldsize;
 | 
			
		||||
    }
 | 
			
		||||
    *(++janet_vm_traversal) = node;
 | 
			
		||||
    *(++janet_vm.traversal) = node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -64,8 +64,8 @@ static void push_traversal_node(void *lhs, void *rhs, int32_t index2) {
 | 
			
		||||
 * 3 - early stop - lhs > rhs
 | 
			
		||||
 */
 | 
			
		||||
static int traversal_next(Janet *x, Janet *y) {
 | 
			
		||||
    JanetTraversalNode *t = janet_vm_traversal;
 | 
			
		||||
    while (t && t > janet_vm_traversal_base) {
 | 
			
		||||
    JanetTraversalNode *t = janet_vm.traversal;
 | 
			
		||||
    while (t && t > janet_vm.traversal_base) {
 | 
			
		||||
        JanetGCObject *self = t->self;
 | 
			
		||||
        JanetTupleHead *tself = (JanetTupleHead *)self;
 | 
			
		||||
        JanetStructHead *sself = (JanetStructHead *)self;
 | 
			
		||||
@@ -78,7 +78,7 @@ static int traversal_next(Janet *x, Janet *y) {
 | 
			
		||||
                int32_t index = t->index++;
 | 
			
		||||
                *x = tself->data[index];
 | 
			
		||||
                *y = tother->data[index];
 | 
			
		||||
                janet_vm_traversal = t;
 | 
			
		||||
                janet_vm.traversal = t;
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            if (t->index2 && tself->length != tother->length) {
 | 
			
		||||
@@ -91,20 +91,31 @@ static int traversal_next(Janet *x, Janet *y) {
 | 
			
		||||
                int32_t index = t->index++;
 | 
			
		||||
                *x = sself->data[index].value;
 | 
			
		||||
                *y = sother->data[index].value;
 | 
			
		||||
                janet_vm_traversal = t;
 | 
			
		||||
                janet_vm.traversal = t;
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            for (int32_t i = t->index; i < sself->capacity; i++) {
 | 
			
		||||
                t->index2 = 1;
 | 
			
		||||
                *x = sself->data[t->index].key;
 | 
			
		||||
                *y = sother->data[t->index].key;
 | 
			
		||||
                janet_vm_traversal = t;
 | 
			
		||||
                janet_vm.traversal = t;
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            /* Traverse prototype */
 | 
			
		||||
            JanetStruct sproto = sself->proto;
 | 
			
		||||
            JanetStruct oproto = sother->proto;
 | 
			
		||||
            if (sproto && !oproto) return 3;
 | 
			
		||||
            if (!sproto && oproto) return 1;
 | 
			
		||||
            if (oproto && sproto) {
 | 
			
		||||
                *x = janet_wrap_struct(sproto);
 | 
			
		||||
                *y = janet_wrap_struct(oproto);
 | 
			
		||||
                janet_vm.traversal = t - 1;
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        t--;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_traversal = t;
 | 
			
		||||
    janet_vm.traversal = t;
 | 
			
		||||
    return 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -113,6 +124,10 @@ static int traversal_next(Janet *x, Janet *y) {
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
Janet janet_next(Janet ds, Janet key) {
 | 
			
		||||
    return janet_next_impl(ds, key, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_next_impl(Janet ds, Janet key, int is_interpreter) {
 | 
			
		||||
    JanetType t = janet_type(ds);
 | 
			
		||||
    switch (t) {
 | 
			
		||||
        default:
 | 
			
		||||
@@ -175,6 +190,44 @@ Janet janet_next(Janet ds, Janet key) {
 | 
			
		||||
            if (NULL == at->next) break;
 | 
			
		||||
            return at->next(abst, key);
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FIBER: {
 | 
			
		||||
            JanetFiber *child = janet_unwrap_fiber(ds);
 | 
			
		||||
            Janet retreg;
 | 
			
		||||
            JanetFiberStatus status = janet_fiber_status(child);
 | 
			
		||||
            if (status == JANET_STATUS_ALIVE ||
 | 
			
		||||
                    status == JANET_STATUS_DEAD ||
 | 
			
		||||
                    status == JANET_STATUS_ERROR ||
 | 
			
		||||
                    status == JANET_STATUS_USER0 ||
 | 
			
		||||
                    status == JANET_STATUS_USER1 ||
 | 
			
		||||
                    status == JANET_STATUS_USER2 ||
 | 
			
		||||
                    status == JANET_STATUS_USER3 ||
 | 
			
		||||
                    status == JANET_STATUS_USER4) {
 | 
			
		||||
                return janet_wrap_nil();
 | 
			
		||||
            }
 | 
			
		||||
            janet_vm.fiber->child = child;
 | 
			
		||||
            JanetSignal sig = janet_continue(child, janet_wrap_nil(), &retreg);
 | 
			
		||||
            if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
 | 
			
		||||
                if (is_interpreter) {
 | 
			
		||||
                    janet_signalv(sig, retreg);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_vm.fiber->child = NULL;
 | 
			
		||||
                    janet_panicv(retreg);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            janet_vm.fiber->child = NULL;
 | 
			
		||||
            if (sig == JANET_SIGNAL_OK ||
 | 
			
		||||
                    sig == JANET_SIGNAL_ERROR ||
 | 
			
		||||
                    sig == JANET_SIGNAL_USER0 ||
 | 
			
		||||
                    sig == JANET_SIGNAL_USER1 ||
 | 
			
		||||
                    sig == JANET_SIGNAL_USER2 ||
 | 
			
		||||
                    sig == JANET_SIGNAL_USER3 ||
 | 
			
		||||
                    sig == JANET_SIGNAL_USER4) {
 | 
			
		||||
                /* Fiber cannot be resumed, so discard last value. */
 | 
			
		||||
                return janet_wrap_nil();
 | 
			
		||||
            } else {
 | 
			
		||||
                return janet_wrap_integer(0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
@@ -194,7 +247,7 @@ static int janet_compare_abstract(JanetAbstract xx, JanetAbstract yy) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_equals(Janet x, Janet y) {
 | 
			
		||||
    janet_vm_traversal = janet_vm_traversal_base;
 | 
			
		||||
    janet_vm.traversal = janet_vm.traversal_base;
 | 
			
		||||
    do {
 | 
			
		||||
        if (janet_type(x) != janet_type(y)) return 0;
 | 
			
		||||
        switch (janet_type(x)) {
 | 
			
		||||
@@ -231,6 +284,8 @@ int janet_equals(Janet x, Janet y) {
 | 
			
		||||
                if (s1 == s2) break;
 | 
			
		||||
                if (janet_struct_hash(s1) != janet_struct_hash(s2)) return 0;
 | 
			
		||||
                if (janet_struct_length(s1) != janet_struct_length(s2)) return 0;
 | 
			
		||||
                if (janet_struct_proto(s1) && !janet_struct_proto(s2)) return 0;
 | 
			
		||||
                if (!janet_struct_proto(s1) && janet_struct_proto(s2)) return 0;
 | 
			
		||||
                push_traversal_node(janet_struct_head(s1), janet_struct_head(s2), 0);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -261,6 +316,17 @@ int32_t janet_hash(Janet x) {
 | 
			
		||||
        case JANET_STRUCT:
 | 
			
		||||
            hash = janet_struct_hash(janet_unwrap_struct(x));
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_NUMBER: {
 | 
			
		||||
            union {
 | 
			
		||||
                double d;
 | 
			
		||||
                uint64_t u;
 | 
			
		||||
            } as;
 | 
			
		||||
            as.d = janet_unwrap_number(x);
 | 
			
		||||
            uint32_t lo = (uint32_t)(as.u & 0xFFFFFFFF);
 | 
			
		||||
            uint32_t hi = (uint32_t)(as.u >> 32);
 | 
			
		||||
            hash = (int32_t)(hi ^ (lo >> 3));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_ABSTRACT: {
 | 
			
		||||
            JanetAbstract xx = janet_unwrap_abstract(x);
 | 
			
		||||
            const JanetAbstractType *at = janet_abstract_type(xx);
 | 
			
		||||
@@ -271,14 +337,12 @@ int32_t janet_hash(Janet x) {
 | 
			
		||||
        }
 | 
			
		||||
        /* fallthrough */
 | 
			
		||||
        default:
 | 
			
		||||
            /* TODO - test performance with different hash functions */
 | 
			
		||||
            if (sizeof(double) == sizeof(void *)) {
 | 
			
		||||
                /* Assuming 8 byte pointer */
 | 
			
		||||
                uint64_t i = janet_u64(x);
 | 
			
		||||
                hash = (int32_t)(i & 0xFFFFFFFF);
 | 
			
		||||
                /* Get a bit more entropy by shifting the low bits out */
 | 
			
		||||
                hash >>= 3;
 | 
			
		||||
                hash ^= (int32_t)(i >> 32);
 | 
			
		||||
                uint32_t lo = (uint32_t)(i & 0xFFFFFFFF);
 | 
			
		||||
                uint32_t hi = (uint32_t)(i >> 32);
 | 
			
		||||
                hash = (int32_t)(hi ^ (lo >> 3));
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Assuming 4 byte pointer (or smaller) */
 | 
			
		||||
                hash = (int32_t)((char *)janet_unwrap_pointer(x) - (char *)0);
 | 
			
		||||
@@ -293,7 +357,7 @@ int32_t janet_hash(Janet x) {
 | 
			
		||||
 * If y is less, returns 1. All types are comparable
 | 
			
		||||
 * and should have strict ordering, excepts NaNs. */
 | 
			
		||||
int janet_compare(Janet x, Janet y) {
 | 
			
		||||
    janet_vm_traversal = janet_vm_traversal_base;
 | 
			
		||||
    janet_vm.traversal = janet_vm.traversal_base;
 | 
			
		||||
    int status;
 | 
			
		||||
    do {
 | 
			
		||||
        JanetType tx = janet_type(x);
 | 
			
		||||
@@ -419,6 +483,14 @@ Janet janet_in(Janet ds, Janet key) {
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FIBER: {
 | 
			
		||||
            /* Bit of a hack to allow iterating over fibers. */
 | 
			
		||||
            if (janet_equals(key, janet_wrap_integer(0))) {
 | 
			
		||||
                return janet_unwrap_fiber(ds)->last_value;
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_panicf("expected key 0, got %v", key);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
@@ -474,6 +546,14 @@ Janet janet_get(Janet ds, Janet key) {
 | 
			
		||||
            const JanetKV *st = janet_unwrap_struct(ds);
 | 
			
		||||
            return janet_struct_get(st, key);
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FIBER: {
 | 
			
		||||
            /* Bit of a hack to allow iterating over fibers. */
 | 
			
		||||
            if (janet_equals(key, janet_wrap_integer(0))) {
 | 
			
		||||
                return janet_unwrap_fiber(ds)->last_value;
 | 
			
		||||
            } else {
 | 
			
		||||
                return janet_wrap_nil();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -530,6 +610,14 @@ Janet janet_getindex(Janet ds, int32_t index) {
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FIBER: {
 | 
			
		||||
            if (index == 0) {
 | 
			
		||||
                value = janet_unwrap_fiber(ds)->last_value;
 | 
			
		||||
            } else {
 | 
			
		||||
                value = janet_wrap_nil();
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return value;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -43,7 +43,7 @@ void *janet_v_flattenmem(void *v, int32_t itemsize) {
 | 
			
		||||
    int32_t *p;
 | 
			
		||||
    if (NULL == v) return NULL;
 | 
			
		||||
    size_t size = (size_t) itemsize * janet_v__cnt(v);
 | 
			
		||||
    p = malloc(size);
 | 
			
		||||
    p = janet_malloc(size);
 | 
			
		||||
    if (NULL != p) {
 | 
			
		||||
        safe_memcpy(p, v, size);
 | 
			
		||||
        return p;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										389
									
								
								src/core/vm.c
									
									
									
									
									
								
							
							
						
						
									
										389
									
								
								src/core/vm.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -32,16 +32,6 @@
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
/* VM state */
 | 
			
		||||
JANET_THREAD_LOCAL JanetTable *janet_vm_core_env;
 | 
			
		||||
JANET_THREAD_LOCAL JanetTable *janet_vm_registry;
 | 
			
		||||
JANET_THREAD_LOCAL JanetTable *janet_vm_abstract_registry;
 | 
			
		||||
JANET_THREAD_LOCAL int janet_vm_stackn = 0;
 | 
			
		||||
JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber = NULL;
 | 
			
		||||
JANET_THREAD_LOCAL JanetFiber *janet_vm_root_fiber = NULL;
 | 
			
		||||
JANET_THREAD_LOCAL Janet *janet_vm_return_reg = NULL;
 | 
			
		||||
JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
 | 
			
		||||
 | 
			
		||||
/* Virtual registers
 | 
			
		||||
 *
 | 
			
		||||
 * One instruction word
 | 
			
		||||
@@ -90,14 +80,18 @@ JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
 | 
			
		||||
    func = janet_stack_frame(stack)->func; \
 | 
			
		||||
} while (0)
 | 
			
		||||
#define vm_return(sig, val) do { \
 | 
			
		||||
    janet_vm_return_reg[0] = (val); \
 | 
			
		||||
    janet_vm.return_reg[0] = (val); \
 | 
			
		||||
    vm_commit(); \
 | 
			
		||||
    return (sig); \
 | 
			
		||||
} while (0)
 | 
			
		||||
#define vm_return_no_restore(sig, val) do { \
 | 
			
		||||
    janet_vm.return_reg[0] = (val); \
 | 
			
		||||
    return (sig); \
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
/* Next instruction variations */
 | 
			
		||||
#define maybe_collect() do {\
 | 
			
		||||
    if (janet_vm_next_collection >= janet_vm_gc_interval) janet_collect(); } while (0)
 | 
			
		||||
    if (janet_vm.next_collection >= janet_vm.gc_interval) janet_collect(); } while (0)
 | 
			
		||||
#define vm_checkgc_next() maybe_collect(); vm_next()
 | 
			
		||||
#define vm_pcnext() pc++; vm_next()
 | 
			
		||||
#define vm_checkgc_pcnext() maybe_collect(); vm_pcnext()
 | 
			
		||||
@@ -117,6 +111,17 @@ JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
 | 
			
		||||
        janet_panicf("expected %T, got %v", (TS), (X)); \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
#ifdef JANET_NO_INTERPRETER_INTERRUPT
 | 
			
		||||
#define vm_maybe_auto_suspend(COND)
 | 
			
		||||
#else
 | 
			
		||||
#define vm_maybe_auto_suspend(COND) do { \
 | 
			
		||||
    if ((COND) && janet_vm.auto_suspend) { \
 | 
			
		||||
        janet_vm.auto_suspend = 0; \
 | 
			
		||||
        fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \
 | 
			
		||||
        vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \
 | 
			
		||||
    } \
 | 
			
		||||
} while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Templates for certain patterns in opcodes */
 | 
			
		||||
#define vm_binop_immediate(op)\
 | 
			
		||||
@@ -197,6 +202,20 @@ JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf = NULL;
 | 
			
		||||
            vm_checkgc_pcnext();\
 | 
			
		||||
        }\
 | 
			
		||||
    }
 | 
			
		||||
#define vm_compop_imm(op) \
 | 
			
		||||
    {\
 | 
			
		||||
        Janet op1 = stack[B];\
 | 
			
		||||
        if (janet_checktype(op1, JANET_NUMBER)) {\
 | 
			
		||||
            double x1 = janet_unwrap_number(op1);\
 | 
			
		||||
            double x2 = (double) CS; \
 | 
			
		||||
            stack[A] = janet_wrap_boolean(x1 op x2);\
 | 
			
		||||
            vm_pcnext();\
 | 
			
		||||
        } else {\
 | 
			
		||||
            vm_commit();\
 | 
			
		||||
            stack[A] = janet_wrap_boolean(janet_compare(op1, janet_wrap_integer(CS)) op 0);\
 | 
			
		||||
            vm_checkgc_pcnext();\
 | 
			
		||||
        }\
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
/* Trace a function call */
 | 
			
		||||
static void vm_do_trace(JanetFunction *func, int32_t argc, const Janet *argv) {
 | 
			
		||||
@@ -256,11 +275,16 @@ static Janet call_nonfn(JanetFiber *fiber, Janet callee) {
 | 
			
		||||
    return janet_method_invoke(callee, argc, fiber->data + fiber->stacktop);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Method lookup could potentially handle tables specially... */
 | 
			
		||||
static Janet method_to_fun(Janet method, Janet obj) {
 | 
			
		||||
    return janet_get(obj, method);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a callable from a keyword method name and ensure that it is valid. */
 | 
			
		||||
static Janet resolve_method(Janet name, JanetFiber *fiber) {
 | 
			
		||||
    int32_t argc = fiber->stacktop - fiber->stackstart;
 | 
			
		||||
    if (argc < 1) janet_panicf("method call (%v) takes at least 1 argument, got 0", name);
 | 
			
		||||
    Janet callee = janet_get(fiber->data[fiber->stackstart], name);
 | 
			
		||||
    Janet callee = method_to_fun(name, fiber->data[fiber->stackstart]);
 | 
			
		||||
    if (janet_checktype(callee, JANET_NIL))
 | 
			
		||||
        janet_panicf("unknown method %v invoked on %v", name, fiber->data[fiber->stackstart]);
 | 
			
		||||
    return callee;
 | 
			
		||||
@@ -268,8 +292,7 @@ static Janet resolve_method(Janet name, JanetFiber *fiber) {
 | 
			
		||||
 | 
			
		||||
/* Lookup method on value x */
 | 
			
		||||
static Janet janet_method_lookup(Janet x, const char *name) {
 | 
			
		||||
    Janet kname = janet_ckeywordv(name);
 | 
			
		||||
    return janet_get(x, kname);
 | 
			
		||||
    return method_to_fun(janet_ckeywordv(name), x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Call a method first on the righthand side, and then on the left hand side with a prefix */
 | 
			
		||||
@@ -373,9 +396,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        &&label_JOP_GREATER_THAN_EQUAL,
 | 
			
		||||
        &&label_JOP_LESS_THAN_EQUAL,
 | 
			
		||||
        &&label_JOP_NEXT,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_JOP_NOT_EQUALS,
 | 
			
		||||
        &&label_JOP_NOT_EQUALS_IMMEDIATE,
 | 
			
		||||
        &&label_JOP_CANCEL,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
@@ -563,6 +586,15 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    register Janet *stack;
 | 
			
		||||
    register uint32_t *pc;
 | 
			
		||||
    register JanetFunction *func;
 | 
			
		||||
 | 
			
		||||
    if (fiber->flags & JANET_FIBER_RESUME_SIGNAL) {
 | 
			
		||||
        JanetSignal sig = (fiber->gc.flags & JANET_FIBER_STATUS_MASK) >> JANET_FIBER_STATUS_OFFSET;
 | 
			
		||||
        fiber->gc.flags &= ~JANET_FIBER_STATUS_MASK;
 | 
			
		||||
        fiber->flags &= ~(JANET_FIBER_RESUME_SIGNAL | JANET_FIBER_FLAG_MASK);
 | 
			
		||||
        janet_vm.return_reg[0] = in;
 | 
			
		||||
        return sig;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vm_restore();
 | 
			
		||||
 | 
			
		||||
    if (fiber->flags & JANET_FIBER_DID_LONGJUMP) {
 | 
			
		||||
@@ -613,7 +645,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        Janet retval = stack[D];
 | 
			
		||||
        int entrance_frame = janet_stack_frame(stack)->flags & JANET_STACKFRAME_ENTRANCE;
 | 
			
		||||
        janet_fiber_popframe(fiber);
 | 
			
		||||
        if (entrance_frame) vm_return(JANET_SIGNAL_OK, retval);
 | 
			
		||||
        if (entrance_frame) vm_return_no_restore(JANET_SIGNAL_OK, retval);
 | 
			
		||||
        vm_restore();
 | 
			
		||||
        stack[A] = retval;
 | 
			
		||||
        vm_checkgc_pcnext();
 | 
			
		||||
@@ -623,7 +655,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        Janet retval = janet_wrap_nil();
 | 
			
		||||
        int entrance_frame = janet_stack_frame(stack)->flags & JANET_STACKFRAME_ENTRANCE;
 | 
			
		||||
        janet_fiber_popframe(fiber);
 | 
			
		||||
        if (entrance_frame) vm_return(JANET_SIGNAL_OK, retval);
 | 
			
		||||
        if (entrance_frame) vm_return_no_restore(JANET_SIGNAL_OK, retval);
 | 
			
		||||
        vm_restore();
 | 
			
		||||
        stack[A] = retval;
 | 
			
		||||
        vm_checkgc_pcnext();
 | 
			
		||||
@@ -725,11 +757,13 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_JUMP)
 | 
			
		||||
    pc += DS;
 | 
			
		||||
    vm_maybe_auto_suspend(DS < 0);
 | 
			
		||||
    vm_next();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_JUMP_IF)
 | 
			
		||||
    if (janet_truthy(stack[A])) {
 | 
			
		||||
        pc += ES;
 | 
			
		||||
        vm_maybe_auto_suspend(ES < 0);
 | 
			
		||||
    } else {
 | 
			
		||||
        pc++;
 | 
			
		||||
    }
 | 
			
		||||
@@ -740,12 +774,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        pc++;
 | 
			
		||||
    } else {
 | 
			
		||||
        pc += ES;
 | 
			
		||||
        vm_maybe_auto_suspend(ES < 0);
 | 
			
		||||
    }
 | 
			
		||||
    vm_next();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_JUMP_IF_NIL)
 | 
			
		||||
    if (janet_checktype(stack[A], JANET_NIL)) {
 | 
			
		||||
        pc += ES;
 | 
			
		||||
        vm_maybe_auto_suspend(ES < 0);
 | 
			
		||||
    } else {
 | 
			
		||||
        pc++;
 | 
			
		||||
    }
 | 
			
		||||
@@ -756,6 +792,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        pc++;
 | 
			
		||||
    } else {
 | 
			
		||||
        pc += ES;
 | 
			
		||||
        vm_maybe_auto_suspend(ES < 0);
 | 
			
		||||
    }
 | 
			
		||||
    vm_next();
 | 
			
		||||
 | 
			
		||||
@@ -766,8 +803,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    vm_compop( <=);
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_LESS_THAN_IMMEDIATE)
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_unwrap_integer(stack[B]) < CS);
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
    vm_compop_imm( <);
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_GREATER_THAN)
 | 
			
		||||
    vm_compop( >);
 | 
			
		||||
@@ -776,15 +812,22 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    vm_compop( >=);
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_GREATER_THAN_IMMEDIATE)
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_unwrap_integer(stack[B]) > CS);
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
    vm_compop_imm( >);
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_EQUALS)
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_equals(stack[B], stack[C]));
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_EQUALS_IMMEDIATE)
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_unwrap_integer(stack[B]) == CS);
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) == (double) CS);
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_NOT_EQUALS)
 | 
			
		||||
    stack[A] = janet_wrap_boolean(!janet_equals(stack[B], stack[C]));
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_NOT_EQUALS_IMMEDIATE)
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) != (double) CS);
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_COMPARE)
 | 
			
		||||
@@ -792,7 +835,12 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_NEXT)
 | 
			
		||||
    stack[A] = janet_next(stack[B], stack[C]);
 | 
			
		||||
    vm_commit();
 | 
			
		||||
    {
 | 
			
		||||
        Janet temp = janet_next_impl(stack[B], stack[C], 1);
 | 
			
		||||
        vm_restore();
 | 
			
		||||
        stack[A] = temp;
 | 
			
		||||
    }
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_LOAD_NIL)
 | 
			
		||||
@@ -918,6 +966,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    vm_checkgc_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_CALL) {
 | 
			
		||||
        vm_maybe_auto_suspend(1);
 | 
			
		||||
        Janet callee = stack[E];
 | 
			
		||||
        if (fiber->stacktop > fiber->maxstack) {
 | 
			
		||||
            vm_throw("stack overflow");
 | 
			
		||||
@@ -957,6 +1006,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_TAILCALL) {
 | 
			
		||||
        vm_maybe_auto_suspend(1);
 | 
			
		||||
        Janet callee = stack[D];
 | 
			
		||||
        if (fiber->stacktop > fiber->maxstack) {
 | 
			
		||||
            vm_throw("stack overflow");
 | 
			
		||||
@@ -992,8 +1042,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
                retreg = call_nonfn(fiber, callee);
 | 
			
		||||
            }
 | 
			
		||||
            janet_fiber_popframe(fiber);
 | 
			
		||||
            if (entrance_frame)
 | 
			
		||||
                vm_return(JANET_SIGNAL_OK, retreg);
 | 
			
		||||
            if (entrance_frame) {
 | 
			
		||||
                vm_return_no_restore(JANET_SIGNAL_OK, retreg);
 | 
			
		||||
            }
 | 
			
		||||
            vm_restore();
 | 
			
		||||
            stack[A] = retreg;
 | 
			
		||||
            vm_checkgc_pcnext();
 | 
			
		||||
@@ -1002,6 +1053,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_RESUME) {
 | 
			
		||||
        Janet retreg;
 | 
			
		||||
        vm_maybe_auto_suspend(1);
 | 
			
		||||
        vm_assert_type(stack[B], JANET_FIBER);
 | 
			
		||||
        JanetFiber *child = janet_unwrap_fiber(stack[B]);
 | 
			
		||||
        if (janet_check_can_resume(child, &retreg)) {
 | 
			
		||||
@@ -1040,6 +1092,25 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        vm_return((int) sub_status, stack[B]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_CANCEL) {
 | 
			
		||||
        Janet retreg;
 | 
			
		||||
        vm_assert_type(stack[B], JANET_FIBER);
 | 
			
		||||
        JanetFiber *child = janet_unwrap_fiber(stack[B]);
 | 
			
		||||
        if (janet_check_can_resume(child, &retreg)) {
 | 
			
		||||
            vm_commit();
 | 
			
		||||
            janet_panicv(retreg);
 | 
			
		||||
        }
 | 
			
		||||
        fiber->child = child;
 | 
			
		||||
        JanetSignal sig = janet_continue_signal(child, stack[C], &retreg, JANET_SIGNAL_ERROR);
 | 
			
		||||
        if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
 | 
			
		||||
            vm_return(sig, retreg);
 | 
			
		||||
        }
 | 
			
		||||
        fiber->child = NULL;
 | 
			
		||||
        stack = fiber->data + fiber->frame;
 | 
			
		||||
        stack[A] = retreg;
 | 
			
		||||
        vm_checkgc_pcnext();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_PUT)
 | 
			
		||||
    vm_commit();
 | 
			
		||||
    fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
 | 
			
		||||
@@ -1216,9 +1287,9 @@ JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) {
 | 
			
		||||
 | 
			
		||||
Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
 | 
			
		||||
    /* Check entry conditions */
 | 
			
		||||
    if (!janet_vm_fiber)
 | 
			
		||||
    if (!janet_vm.fiber)
 | 
			
		||||
        janet_panic("janet_call failed because there is no current fiber");
 | 
			
		||||
    if (janet_vm_stackn >= JANET_RECURSION_GUARD)
 | 
			
		||||
    if (janet_vm.stackn >= JANET_RECURSION_GUARD)
 | 
			
		||||
        janet_panic("C stack recursed too deeply");
 | 
			
		||||
 | 
			
		||||
    /* Tracing */
 | 
			
		||||
@@ -1227,35 +1298,42 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Push frame */
 | 
			
		||||
    janet_fiber_pushn(janet_vm_fiber, argv, argc);
 | 
			
		||||
    if (janet_fiber_funcframe(janet_vm_fiber, fun)) {
 | 
			
		||||
        janet_panicf("arity mismatch in %v", janet_wrap_function(fun));
 | 
			
		||||
    janet_fiber_pushn(janet_vm.fiber, argv, argc);
 | 
			
		||||
    if (janet_fiber_funcframe(janet_vm.fiber, fun)) {
 | 
			
		||||
        int32_t min = fun->def->min_arity;
 | 
			
		||||
        int32_t max = fun->def->max_arity;
 | 
			
		||||
        Janet funv = janet_wrap_function(fun);
 | 
			
		||||
        if (min == max && min != argc)
 | 
			
		||||
            janet_panicf("arity mismatch in %v, expected %d, got %d", funv, min, argc);
 | 
			
		||||
        if (min >= 0 && argc < min)
 | 
			
		||||
            janet_panicf("arity mismatch in %v, expected at least %d, got %d", funv, min, argc);
 | 
			
		||||
        janet_panicf("arity mismatch in %v, expected at most %d, got %d", funv, max, argc);
 | 
			
		||||
    }
 | 
			
		||||
    janet_fiber_frame(janet_vm_fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
 | 
			
		||||
    janet_fiber_frame(janet_vm.fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
 | 
			
		||||
 | 
			
		||||
    /* Set up */
 | 
			
		||||
    int32_t oldn = janet_vm_stackn++;
 | 
			
		||||
    int32_t oldn = janet_vm.stackn++;
 | 
			
		||||
    int handle = janet_gclock();
 | 
			
		||||
 | 
			
		||||
    /* Run vm */
 | 
			
		||||
    janet_vm_fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
 | 
			
		||||
    JanetSignal signal = run_vm(janet_vm_fiber, janet_wrap_nil());
 | 
			
		||||
    janet_vm.fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
 | 
			
		||||
    JanetSignal signal = run_vm(janet_vm.fiber, janet_wrap_nil());
 | 
			
		||||
 | 
			
		||||
    /* Teardown */
 | 
			
		||||
    janet_vm_stackn = oldn;
 | 
			
		||||
    janet_vm.stackn = oldn;
 | 
			
		||||
    janet_gcunlock(handle);
 | 
			
		||||
 | 
			
		||||
    if (signal != JANET_SIGNAL_OK) {
 | 
			
		||||
        janet_panicv(*janet_vm_return_reg);
 | 
			
		||||
        janet_panicv(*janet_vm.return_reg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *janet_vm_return_reg;
 | 
			
		||||
    return *janet_vm.return_reg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out) {
 | 
			
		||||
    /* Check conditions */
 | 
			
		||||
    JanetFiberStatus old_status = janet_fiber_status(fiber);
 | 
			
		||||
    if (janet_vm_stackn >= JANET_RECURSION_GUARD) {
 | 
			
		||||
    if (janet_vm.stackn >= JANET_RECURSION_GUARD) {
 | 
			
		||||
        janet_fiber_set_status(fiber, JANET_STATUS_ERROR);
 | 
			
		||||
        *out = janet_cstringv("C stack recursed too deeply");
 | 
			
		||||
        return JANET_SIGNAL_ERROR;
 | 
			
		||||
@@ -1272,23 +1350,68 @@ static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out) {
 | 
			
		||||
    return JANET_SIGNAL_OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_try_init(JanetTryState *state) {
 | 
			
		||||
    state->stackn = janet_vm.stackn++;
 | 
			
		||||
    state->gc_handle = janet_vm.gc_suspend;
 | 
			
		||||
    state->vm_fiber = janet_vm.fiber;
 | 
			
		||||
    state->vm_jmp_buf = janet_vm.signal_buf;
 | 
			
		||||
    state->vm_return_reg = janet_vm.return_reg;
 | 
			
		||||
    janet_vm.return_reg = &(state->payload);
 | 
			
		||||
    janet_vm.signal_buf = &(state->buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_restore(JanetTryState *state) {
 | 
			
		||||
    janet_vm.stackn = state->stackn;
 | 
			
		||||
    janet_vm.gc_suspend = state->gc_handle;
 | 
			
		||||
    janet_vm.fiber = state->vm_fiber;
 | 
			
		||||
    janet_vm.signal_buf = state->vm_jmp_buf;
 | 
			
		||||
    janet_vm.return_reg = state->vm_return_reg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *out) {
 | 
			
		||||
    jmp_buf buf;
 | 
			
		||||
 | 
			
		||||
    JanetFiberStatus old_status = janet_fiber_status(fiber);
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_fiber_did_resume(fiber);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Clear last value */
 | 
			
		||||
    fiber->last_value = janet_wrap_nil();
 | 
			
		||||
 | 
			
		||||
    /* Continue child fiber if it exists */
 | 
			
		||||
    if (fiber->child) {
 | 
			
		||||
        if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber;
 | 
			
		||||
        if (janet_vm.root_fiber == NULL) janet_vm.root_fiber = fiber;
 | 
			
		||||
        JanetFiber *child = fiber->child;
 | 
			
		||||
        janet_vm_stackn++;
 | 
			
		||||
        uint32_t instr = (janet_stack_frame(fiber->data + fiber->frame)->pc)[0];
 | 
			
		||||
        janet_vm.stackn++;
 | 
			
		||||
        JanetSignal sig = janet_continue(child, in, &in);
 | 
			
		||||
        janet_vm_stackn--;
 | 
			
		||||
        if (janet_vm_root_fiber == fiber) janet_vm_root_fiber = NULL;
 | 
			
		||||
        janet_vm.stackn--;
 | 
			
		||||
        if (janet_vm.root_fiber == fiber) janet_vm.root_fiber = NULL;
 | 
			
		||||
        if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
 | 
			
		||||
            *out = in;
 | 
			
		||||
            janet_fiber_set_status(fiber, sig);
 | 
			
		||||
            return sig;
 | 
			
		||||
        }
 | 
			
		||||
        /* Check if we need any special handling for certain opcodes */
 | 
			
		||||
        switch (instr & 0x7F) {
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
            case JOP_NEXT: {
 | 
			
		||||
                if (sig == JANET_SIGNAL_OK ||
 | 
			
		||||
                        sig == JANET_SIGNAL_ERROR ||
 | 
			
		||||
                        sig == JANET_SIGNAL_USER0 ||
 | 
			
		||||
                        sig == JANET_SIGNAL_USER1 ||
 | 
			
		||||
                        sig == JANET_SIGNAL_USER2 ||
 | 
			
		||||
                        sig == JANET_SIGNAL_USER3 ||
 | 
			
		||||
                        sig == JANET_SIGNAL_USER4) {
 | 
			
		||||
                    in = janet_wrap_nil();
 | 
			
		||||
                } else {
 | 
			
		||||
                    in = janet_wrap_integer(0);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fiber->child = NULL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1306,47 +1429,24 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Save global state */
 | 
			
		||||
    int32_t oldn = janet_vm_stackn++;
 | 
			
		||||
    int handle = janet_vm_gc_suspend;
 | 
			
		||||
    JanetFiber *old_vm_fiber = janet_vm_fiber;
 | 
			
		||||
    jmp_buf *old_vm_jmp_buf = janet_vm_jmp_buf;
 | 
			
		||||
    Janet *old_vm_return_reg = janet_vm_return_reg;
 | 
			
		||||
 | 
			
		||||
    /* Setup fiber */
 | 
			
		||||
    if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber;
 | 
			
		||||
    janet_vm_fiber = fiber;
 | 
			
		||||
    janet_gcroot(janet_wrap_fiber(fiber));
 | 
			
		||||
    janet_fiber_set_status(fiber, JANET_STATUS_ALIVE);
 | 
			
		||||
    janet_vm_return_reg = out;
 | 
			
		||||
    janet_vm_jmp_buf = &buf;
 | 
			
		||||
 | 
			
		||||
    /* Run loop */
 | 
			
		||||
    JanetSignal signal;
 | 
			
		||||
    int jmpsig;
 | 
			
		||||
#if defined(JANET_BSD) || defined(JANET_APPLE)
 | 
			
		||||
    jmpsig = _setjmp(buf);
 | 
			
		||||
#else
 | 
			
		||||
    jmpsig = setjmp(buf);
 | 
			
		||||
#endif
 | 
			
		||||
    if (jmpsig) {
 | 
			
		||||
        signal = (JanetSignal) jmpsig;
 | 
			
		||||
    } else {
 | 
			
		||||
        signal = run_vm(fiber, in);
 | 
			
		||||
    JanetTryState tstate;
 | 
			
		||||
    JanetSignal sig = janet_try(&tstate);
 | 
			
		||||
    if (!sig) {
 | 
			
		||||
        /* Normal setup */
 | 
			
		||||
        if (janet_vm.root_fiber == NULL) janet_vm.root_fiber = fiber;
 | 
			
		||||
        janet_vm.fiber = fiber;
 | 
			
		||||
        janet_fiber_set_status(fiber, JANET_STATUS_ALIVE);
 | 
			
		||||
        sig = run_vm(fiber, in);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Tear down fiber */
 | 
			
		||||
    janet_fiber_set_status(fiber, signal);
 | 
			
		||||
    janet_gcunroot(janet_wrap_fiber(fiber));
 | 
			
		||||
    /* Restore */
 | 
			
		||||
    if (janet_vm.root_fiber == fiber) janet_vm.root_fiber = NULL;
 | 
			
		||||
    janet_fiber_set_status(fiber, sig);
 | 
			
		||||
    janet_restore(&tstate);
 | 
			
		||||
    fiber->last_value = tstate.payload;
 | 
			
		||||
    *out = tstate.payload;
 | 
			
		||||
 | 
			
		||||
    /* Restore global state */
 | 
			
		||||
    if (janet_vm_root_fiber == fiber) janet_vm_root_fiber = NULL;
 | 
			
		||||
    janet_vm_gc_suspend = handle;
 | 
			
		||||
    janet_vm_fiber = old_vm_fiber;
 | 
			
		||||
    janet_vm_stackn = oldn;
 | 
			
		||||
    janet_vm_return_reg = old_vm_return_reg;
 | 
			
		||||
    janet_vm_jmp_buf = old_vm_jmp_buf;
 | 
			
		||||
 | 
			
		||||
    return signal;
 | 
			
		||||
    return sig;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Enter the main vm loop */
 | 
			
		||||
@@ -1357,6 +1457,20 @@ JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
 | 
			
		||||
    return janet_continue_no_check(fiber, in, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Enter the main vm loop but immediately raise a signal */
 | 
			
		||||
JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig) {
 | 
			
		||||
    JanetSignal tmp_signal = janet_check_can_resume(fiber, out);
 | 
			
		||||
    if (tmp_signal) return tmp_signal;
 | 
			
		||||
    if (sig != JANET_SIGNAL_OK) {
 | 
			
		||||
        JanetFiber *child = fiber;
 | 
			
		||||
        while (child->child) child = child->child;
 | 
			
		||||
        child->gc.flags &= ~JANET_FIBER_STATUS_MASK;
 | 
			
		||||
        child->gc.flags |= sig << JANET_FIBER_STATUS_OFFSET;
 | 
			
		||||
        child->flags |= JANET_FIBER_RESUME_SIGNAL;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_continue_no_check(fiber, in, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetSignal janet_pcall(
 | 
			
		||||
    JanetFunction *fun,
 | 
			
		||||
    int32_t argc,
 | 
			
		||||
@@ -1379,7 +1493,9 @@ JanetSignal janet_pcall(
 | 
			
		||||
 | 
			
		||||
Janet janet_mcall(const char *name, int32_t argc, Janet *argv) {
 | 
			
		||||
    /* At least 1 argument */
 | 
			
		||||
    if (argc < 1) janet_panicf("method :%s expected at least 1 argument");
 | 
			
		||||
    if (argc < 1) {
 | 
			
		||||
        janet_panicf("method :%s expected at least 1 argument", name);
 | 
			
		||||
    }
 | 
			
		||||
    /* Find method */
 | 
			
		||||
    Janet method = janet_method_lookup(argv[0], name);
 | 
			
		||||
    if (janet_checktype(method, JANET_NIL)) {
 | 
			
		||||
@@ -1391,43 +1507,63 @@ Janet janet_mcall(const char *name, int32_t argc, Janet *argv) {
 | 
			
		||||
 | 
			
		||||
/* Setup VM */
 | 
			
		||||
int janet_init(void) {
 | 
			
		||||
 | 
			
		||||
    /* Garbage collection */
 | 
			
		||||
    janet_vm_blocks = NULL;
 | 
			
		||||
    janet_vm_next_collection = 0;
 | 
			
		||||
    /* Setting memoryInterval to zero forces
 | 
			
		||||
     * a collection pretty much every cycle, which is
 | 
			
		||||
     * incredibly horrible for performance, but can help ensure
 | 
			
		||||
     * there are no memory bugs during development */
 | 
			
		||||
    janet_vm_gc_interval = 0x10000;
 | 
			
		||||
    janet_vm.blocks = NULL;
 | 
			
		||||
    janet_vm.next_collection = 0;
 | 
			
		||||
    janet_vm.gc_interval = 0x400000;
 | 
			
		||||
    janet_vm.block_count = 0;
 | 
			
		||||
 | 
			
		||||
    janet_symcache_init();
 | 
			
		||||
 | 
			
		||||
    /* Initialize gc roots */
 | 
			
		||||
    janet_vm_roots = NULL;
 | 
			
		||||
    janet_vm_root_count = 0;
 | 
			
		||||
    janet_vm_root_capacity = 0;
 | 
			
		||||
    janet_vm.roots = NULL;
 | 
			
		||||
    janet_vm.root_count = 0;
 | 
			
		||||
    janet_vm.root_capacity = 0;
 | 
			
		||||
 | 
			
		||||
    /* Scratch memory */
 | 
			
		||||
    janet_scratch_mem = NULL;
 | 
			
		||||
    janet_scratch_len = 0;
 | 
			
		||||
    janet_scratch_cap = 0;
 | 
			
		||||
    janet_vm.user = NULL;
 | 
			
		||||
    janet_vm.scratch_mem = NULL;
 | 
			
		||||
    janet_vm.scratch_len = 0;
 | 
			
		||||
    janet_vm.scratch_cap = 0;
 | 
			
		||||
 | 
			
		||||
    /* Initialize registry */
 | 
			
		||||
    janet_vm_registry = janet_table(0);
 | 
			
		||||
    janet_vm_abstract_registry = janet_table(0);
 | 
			
		||||
    janet_gcroot(janet_wrap_table(janet_vm_registry));
 | 
			
		||||
    janet_gcroot(janet_wrap_table(janet_vm_abstract_registry));
 | 
			
		||||
    janet_vm.registry = NULL;
 | 
			
		||||
    janet_vm.registry_cap = 0;
 | 
			
		||||
    janet_vm.registry_count = 0;
 | 
			
		||||
    janet_vm.registry_dirty = 0;
 | 
			
		||||
 | 
			
		||||
    /* Intialize abstract registry */
 | 
			
		||||
    janet_vm.abstract_registry = janet_table(0);
 | 
			
		||||
    janet_gcroot(janet_wrap_table(janet_vm.abstract_registry));
 | 
			
		||||
 | 
			
		||||
    /* Traversal */
 | 
			
		||||
    janet_vm_traversal = NULL;
 | 
			
		||||
    janet_vm_traversal_base = NULL;
 | 
			
		||||
    janet_vm_traversal_top = NULL;
 | 
			
		||||
    janet_vm.traversal = NULL;
 | 
			
		||||
    janet_vm.traversal_base = NULL;
 | 
			
		||||
    janet_vm.traversal_top = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Core env */
 | 
			
		||||
    janet_vm_core_env = NULL;
 | 
			
		||||
    janet_vm.core_env = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Auto suspension */
 | 
			
		||||
    janet_vm.auto_suspend = 0;
 | 
			
		||||
 | 
			
		||||
    /* Dynamic bindings */
 | 
			
		||||
    janet_vm.top_dyns = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Seed RNG */
 | 
			
		||||
    janet_rng_seed(janet_default_rng(), 0);
 | 
			
		||||
 | 
			
		||||
    /* Fibers */
 | 
			
		||||
    janet_vm_fiber = NULL;
 | 
			
		||||
    janet_vm_root_fiber = NULL;
 | 
			
		||||
    janet_vm_stackn = 0;
 | 
			
		||||
    /* Threads */
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
    janet_threads_init();
 | 
			
		||||
    janet_vm.fiber = NULL;
 | 
			
		||||
    janet_vm.root_fiber = NULL;
 | 
			
		||||
    janet_vm.stackn = 0;
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_ev_init();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    janet_net_init();
 | 
			
		||||
#endif
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -1436,18 +1572,21 @@ int janet_init(void) {
 | 
			
		||||
void janet_deinit(void) {
 | 
			
		||||
    janet_clear_memory();
 | 
			
		||||
    janet_symcache_deinit();
 | 
			
		||||
    free(janet_vm_roots);
 | 
			
		||||
    janet_vm_roots = NULL;
 | 
			
		||||
    janet_vm_root_count = 0;
 | 
			
		||||
    janet_vm_root_capacity = 0;
 | 
			
		||||
    janet_vm_registry = NULL;
 | 
			
		||||
    janet_vm_abstract_registry = NULL;
 | 
			
		||||
    janet_vm_core_env = NULL;
 | 
			
		||||
    free(janet_vm_traversal_base);
 | 
			
		||||
    janet_vm_fiber = NULL;
 | 
			
		||||
    janet_vm_root_fiber = NULL;
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
    janet_threads_deinit();
 | 
			
		||||
    janet_free(janet_vm.roots);
 | 
			
		||||
    janet_vm.roots = NULL;
 | 
			
		||||
    janet_vm.root_count = 0;
 | 
			
		||||
    janet_vm.root_capacity = 0;
 | 
			
		||||
    janet_vm.abstract_registry = NULL;
 | 
			
		||||
    janet_vm.core_env = NULL;
 | 
			
		||||
    janet_vm.top_dyns = NULL;
 | 
			
		||||
    janet_vm.user = NULL;
 | 
			
		||||
    janet_free(janet_vm.traversal_base);
 | 
			
		||||
    janet_vm.fiber = NULL;
 | 
			
		||||
    janet_vm.root_fiber = NULL;
 | 
			
		||||
    janet_free(janet_vm.registry);
 | 
			
		||||
    janet_vm.registry = NULL;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_ev_deinit();
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    janet_net_deinit();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -162,8 +162,8 @@ Janet(janet_wrap_number)(double x) {
 | 
			
		||||
 | 
			
		||||
void *janet_memalloc_empty(int32_t count) {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    void *mem = malloc((size_t) count * sizeof(JanetKV));
 | 
			
		||||
    janet_vm_next_collection += (size_t) count * sizeof(JanetKV);
 | 
			
		||||
    void *mem = janet_malloc((size_t) count * sizeof(JanetKV));
 | 
			
		||||
    janet_vm.next_collection += (size_t) count * sizeof(JanetKV);
 | 
			
		||||
    if (NULL == mem) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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,6 +20,8 @@
 | 
			
		||||
* IN THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "janetconf.h"
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_H_defined
 | 
			
		||||
#define JANET_H_defined
 | 
			
		||||
 | 
			
		||||
@@ -35,8 +37,6 @@ extern "C" {
 | 
			
		||||
 | 
			
		||||
/***** START SECTION CONFIG *****/
 | 
			
		||||
 | 
			
		||||
#include "janetconf.h"
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_VERSION
 | 
			
		||||
#define JANET_VERSION "latest"
 | 
			
		||||
#endif
 | 
			
		||||
@@ -127,6 +127,12 @@ extern "C" {
 | 
			
		||||
#define JANET_LITTLE_ENDIAN 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Limits for converting doubles to 64 bit integers */
 | 
			
		||||
#define JANET_INTMAX_DOUBLE 9007199254740992.0
 | 
			
		||||
#define JANET_INTMIN_DOUBLE (-9007199254740992.0)
 | 
			
		||||
#define JANET_INTMAX_INT64 9007199254740992
 | 
			
		||||
#define JANET_INTMIN_INT64 (-9007199254740992)
 | 
			
		||||
 | 
			
		||||
/* Check emscripten */
 | 
			
		||||
#ifdef __EMSCRIPTEN__
 | 
			
		||||
#define JANET_NO_DYNAMIC_MODULES
 | 
			
		||||
@@ -139,16 +145,17 @@ extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Define how global janet state is declared */
 | 
			
		||||
/* Also enable the thread library only if not single-threaded */
 | 
			
		||||
#ifdef JANET_SINGLE_THREADED
 | 
			
		||||
#define JANET_THREAD_LOCAL
 | 
			
		||||
#undef JANET_THREADS
 | 
			
		||||
#elif defined(__GNUC__)
 | 
			
		||||
#define JANET_THREAD_LOCAL __thread
 | 
			
		||||
#define JANET_THREADS
 | 
			
		||||
#elif defined(_MSC_BUILD)
 | 
			
		||||
#define JANET_THREAD_LOCAL __declspec(thread)
 | 
			
		||||
#define JANET_THREADS
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_THREAD_LOCAL
 | 
			
		||||
#undef JANET_THREADS
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable dynamic module loading. Enabled by default. */
 | 
			
		||||
@@ -166,13 +173,13 @@ extern "C" {
 | 
			
		||||
#define JANET_PEG
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable the typedarray module */
 | 
			
		||||
#ifndef JANET_NO_TYPED_ARRAY
 | 
			
		||||
#define JANET_TYPED_ARRAY
 | 
			
		||||
/* Enable or disable event loop */
 | 
			
		||||
#if !defined(JANET_NO_EV) && !defined(__EMSCRIPTEN__)
 | 
			
		||||
#define JANET_EV
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable networking */
 | 
			
		||||
#if !defined(JANET_NO_NET) && !defined(__EMSCRIPTEN__)
 | 
			
		||||
#if defined(JANET_EV) && !defined(JANET_NO_NET) && !defined(__EMSCRIPTEN__)
 | 
			
		||||
#define JANET_NET
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -181,6 +188,21 @@ extern "C" {
 | 
			
		||||
#define JANET_INT_TYPES
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable epoll on Linux */
 | 
			
		||||
#if defined(JANET_LINUX) && !defined(JANET_EV_NO_EPOLL)
 | 
			
		||||
#define JANET_EV_EPOLL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable kqueue on BSD */
 | 
			
		||||
#if defined(JANET_BSD) && !defined(JANET_EV_NO_KQUEUE)
 | 
			
		||||
#define JANET_EV_KQUEUE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable kqueue on Apple */
 | 
			
		||||
#if defined(JANET_APPLE) && !defined(JANET_EV_NO_KQUEUE)
 | 
			
		||||
#define JANET_EV_KQUEUE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* How to export symbols */
 | 
			
		||||
#ifndef JANET_API
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
@@ -195,7 +217,7 @@ extern "C" {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#define JANET_NO_RETURN __declspec(noreturn)
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_NO_RETURN __attribute__ ((noreturn))
 | 
			
		||||
#define JANET_NO_RETURN __attribute__((noreturn))
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -219,6 +241,11 @@ extern "C" {
 | 
			
		||||
 * To turn of nanboxing, for debugging purposes or for certain
 | 
			
		||||
 * architectures (Nanboxing only tested on x86 and x64), comment out
 | 
			
		||||
 * the JANET_NANBOX define.*/
 | 
			
		||||
 | 
			
		||||
#if defined(_M_ARM64) || defined(_M_ARM) || defined(__aarch64__)
 | 
			
		||||
#define JANET_NO_NANBOX
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_NO_NANBOX
 | 
			
		||||
#ifdef JANET_32
 | 
			
		||||
#define JANET_NANBOX_32
 | 
			
		||||
@@ -256,20 +283,37 @@ typedef struct {
 | 
			
		||||
} JanetBuildConfig;
 | 
			
		||||
 | 
			
		||||
/* Get config of current compilation unit. */
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
/* C++11 syntax */
 | 
			
		||||
#define janet_config_current() (JanetBuildConfig { \
 | 
			
		||||
    JANET_VERSION_MAJOR, \
 | 
			
		||||
    JANET_VERSION_MINOR, \
 | 
			
		||||
    JANET_VERSION_PATCH, \
 | 
			
		||||
    JANET_CURRENT_CONFIG_BITS })
 | 
			
		||||
#else
 | 
			
		||||
/* C99 syntax */
 | 
			
		||||
#define janet_config_current() ((JanetBuildConfig){ \
 | 
			
		||||
    JANET_VERSION_MAJOR, \
 | 
			
		||||
    JANET_VERSION_MINOR, \
 | 
			
		||||
    JANET_VERSION_PATCH, \
 | 
			
		||||
    JANET_CURRENT_CONFIG_BITS })
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* What to do when out of memory */
 | 
			
		||||
#ifndef JANET_OUT_OF_MEMORY
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/***** END SECTION CONFIG *****/
 | 
			
		||||
 | 
			
		||||
/***** START SECTION TYPES *****/
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
// Must be defined before including stdlib.h
 | 
			
		||||
/* Must be defined before including stdlib.h */
 | 
			
		||||
#define _CRT_RAND_S
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
@@ -278,6 +322,25 @@ typedef struct {
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
/* Some extra includes if EV is enabled */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
typedef struct JanetDudCriticalSection {
 | 
			
		||||
    /* Avoid including windows.h here - instead, create a structure of the same size */
 | 
			
		||||
    /* Needs to be same size as crtical section see WinNT.h for CRITCIAL_SECTION definition */
 | 
			
		||||
    void *debug_info;
 | 
			
		||||
    long lock_count;
 | 
			
		||||
    long recursion_count;
 | 
			
		||||
    void *owning_thread;
 | 
			
		||||
    void *lock_semaphore;
 | 
			
		||||
    unsigned long spin_count;
 | 
			
		||||
} JanetOSMutex;
 | 
			
		||||
#else
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
typedef pthread_mutex_t JanetOSMutex;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BSD
 | 
			
		||||
int _setjmp(jmp_buf);
 | 
			
		||||
JANET_NO_RETURN void _longjmp(jmp_buf, int);
 | 
			
		||||
@@ -288,6 +351,15 @@ JANET_API extern const char *const janet_type_names[16];
 | 
			
		||||
JANET_API extern const char *const janet_signal_names[14];
 | 
			
		||||
JANET_API extern const char *const janet_status_names[16];
 | 
			
		||||
 | 
			
		||||
/* For various IO routines, we want to use an int on posix and HANDLE on windows */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
typedef void *JanetHandle;
 | 
			
		||||
#define JANET_HANDLE_NONE NULL
 | 
			
		||||
#else
 | 
			
		||||
typedef int JanetHandle;
 | 
			
		||||
#define JANET_HANDLE_NONE (-1)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Fiber signals */
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_SIGNAL_OK,
 | 
			
		||||
@@ -307,6 +379,7 @@ typedef enum {
 | 
			
		||||
} JanetSignal;
 | 
			
		||||
 | 
			
		||||
#define JANET_SIGNAL_EVENT JANET_SIGNAL_USER9
 | 
			
		||||
#define JANET_SIGNAL_INTERRUPT JANET_SIGNAL_USER8
 | 
			
		||||
 | 
			
		||||
/* Fiber statuses - mostly corresponds to signals. */
 | 
			
		||||
typedef enum {
 | 
			
		||||
@@ -328,6 +401,9 @@ typedef enum {
 | 
			
		||||
    JANET_STATUS_ALIVE
 | 
			
		||||
} JanetFiberStatus;
 | 
			
		||||
 | 
			
		||||
/* For encapsulating all thread-local Janet state (except natives) */
 | 
			
		||||
typedef struct JanetVM JanetVM;
 | 
			
		||||
 | 
			
		||||
/* Use type punning for GC objects */
 | 
			
		||||
typedef struct JanetGCObject JanetGCObject;
 | 
			
		||||
 | 
			
		||||
@@ -351,6 +427,7 @@ typedef struct JanetKV JanetKV;
 | 
			
		||||
typedef struct JanetStackFrame JanetStackFrame;
 | 
			
		||||
typedef struct JanetAbstractType JanetAbstractType;
 | 
			
		||||
typedef struct JanetReg JanetReg;
 | 
			
		||||
typedef struct JanetRegExt JanetRegExt;
 | 
			
		||||
typedef struct JanetMethod JanetMethod;
 | 
			
		||||
typedef struct JanetSourceMapping JanetSourceMapping;
 | 
			
		||||
typedef struct JanetView JanetView;
 | 
			
		||||
@@ -461,6 +538,75 @@ typedef void *JanetAbstract;
 | 
			
		||||
#define JANET_TFLAG_CALLABLE (JANET_TFLAG_FUNCTION | JANET_TFLAG_CFUNCTION | \
 | 
			
		||||
        JANET_TFLAG_LENGTHABLE | JANET_TFLAG_ABSTRACT)
 | 
			
		||||
 | 
			
		||||
/* Event Loop Types */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
 | 
			
		||||
#define JANET_STREAM_CLOSED 0x1
 | 
			
		||||
#define JANET_STREAM_SOCKET 0x2
 | 
			
		||||
#define JANET_STREAM_IOCP 0x4
 | 
			
		||||
#define JANET_STREAM_READABLE 0x200
 | 
			
		||||
#define JANET_STREAM_WRITABLE 0x400
 | 
			
		||||
#define JANET_STREAM_ACCEPTABLE 0x800
 | 
			
		||||
#define JANET_STREAM_UDPSERVER 0x1000
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_ASYNC_EVENT_INIT,
 | 
			
		||||
    JANET_ASYNC_EVENT_MARK,
 | 
			
		||||
    JANET_ASYNC_EVENT_DEINIT,
 | 
			
		||||
    JANET_ASYNC_EVENT_CLOSE,
 | 
			
		||||
    JANET_ASYNC_EVENT_ERR,
 | 
			
		||||
    JANET_ASYNC_EVENT_HUP,
 | 
			
		||||
    JANET_ASYNC_EVENT_READ,
 | 
			
		||||
    JANET_ASYNC_EVENT_WRITE,
 | 
			
		||||
    JANET_ASYNC_EVENT_CANCEL,
 | 
			
		||||
    JANET_ASYNC_EVENT_COMPLETE, /* Used on windows for IOCP */
 | 
			
		||||
    JANET_ASYNC_EVENT_USER
 | 
			
		||||
} JanetAsyncEvent;
 | 
			
		||||
 | 
			
		||||
#define JANET_ASYNC_LISTEN_READ (1 << JANET_ASYNC_EVENT_READ)
 | 
			
		||||
#define JANET_ASYNC_LISTEN_WRITE (1 << JANET_ASYNC_EVENT_WRITE)
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_ASYNC_STATUS_NOT_DONE,
 | 
			
		||||
    JANET_ASYNC_STATUS_DONE
 | 
			
		||||
} JanetAsyncStatus;
 | 
			
		||||
 | 
			
		||||
/* Typedefs */
 | 
			
		||||
typedef struct JanetListenerState JanetListenerState;
 | 
			
		||||
typedef struct JanetStream JanetStream;
 | 
			
		||||
typedef JanetAsyncStatus(*JanetListener)(JanetListenerState *state, JanetAsyncEvent event);
 | 
			
		||||
 | 
			
		||||
/* Wrapper around file descriptors and HANDLEs that can be polled. */
 | 
			
		||||
struct JanetStream {
 | 
			
		||||
    JanetHandle handle;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    /* Linked list of all in-flight IO routines for this stream */
 | 
			
		||||
    JanetListenerState *state;
 | 
			
		||||
    const void *methods; /* Methods for this stream */
 | 
			
		||||
    /* internal - used to disallow multiple concurrent reads / writes on the same stream.
 | 
			
		||||
     * this constraint may be lifted later but allowing such would require more internal book keeping
 | 
			
		||||
     * for some implementations. You can read and write at the same time on the same stream, though. */
 | 
			
		||||
    int _mask;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Interface for state machine based event loop */
 | 
			
		||||
struct JanetListenerState {
 | 
			
		||||
    JanetListener machine;
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
    JanetStream *stream;
 | 
			
		||||
    void *event; /* Used to pass data from asynchronous IO event. Contents depend on both
 | 
			
		||||
                    implementation of the event loop and the particular event. */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    void *tag; /* Used to associate listeners with an overlapped structure */
 | 
			
		||||
    int bytes; /* Used to track how many bytes were transfered. */
 | 
			
		||||
#endif
 | 
			
		||||
    /* internal */
 | 
			
		||||
    size_t _index;
 | 
			
		||||
    int _mask;
 | 
			
		||||
    JanetListenerState *_next;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* We provide three possible implementations of Janets. The preferred
 | 
			
		||||
 * nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the
 | 
			
		||||
 * application must interact through exposed interface. */
 | 
			
		||||
@@ -550,14 +696,14 @@ JANET_API Janet janet_wrap_integer(int32_t x);
 | 
			
		||||
#define janet_nanbox_tag(type) (janet_nanbox_lowtag(type) << 47)
 | 
			
		||||
#define janet_type(x) \
 | 
			
		||||
    (isnan((x).number) \
 | 
			
		||||
        ? (((x).u64 >> 47) & 0xF) \
 | 
			
		||||
        ? (JanetType) (((x).u64 >> 47) & 0xF) \
 | 
			
		||||
        : JANET_NUMBER)
 | 
			
		||||
 | 
			
		||||
#define janet_nanbox_checkauxtype(x, type) \
 | 
			
		||||
    (((x).u64 & JANET_NANBOX_TAGBITS) == janet_nanbox_tag((type)))
 | 
			
		||||
 | 
			
		||||
#define janet_nanbox_isnumber(x) \
 | 
			
		||||
    (!isnan((x).number) || janet_nanbox_checkauxtype((x), JANET_NUMBER))
 | 
			
		||||
    (!isnan((x).number) || ((((x).u64 >> 47) & 0xF) == JANET_NUMBER))
 | 
			
		||||
 | 
			
		||||
#define janet_checktype(x, t) \
 | 
			
		||||
    (((t) == JANET_NUMBER) \
 | 
			
		||||
@@ -629,7 +775,7 @@ JANET_API Janet janet_nanbox_from_bits(uint64_t bits);
 | 
			
		||||
#define JANET_DOUBLE_OFFSET 0xFFFF
 | 
			
		||||
 | 
			
		||||
#define janet_u64(x) ((x).u64)
 | 
			
		||||
#define janet_type(x) (((x).tagged.type < JANET_DOUBLE_OFFSET) ? (x).tagged.type : JANET_NUMBER)
 | 
			
		||||
#define janet_type(x) (((x).tagged.type < JANET_DOUBLE_OFFSET) ? (JanetType)((x).tagged.type) : JANET_NUMBER)
 | 
			
		||||
#define janet_checktype(x, t) ((t) == JANET_NUMBER \
 | 
			
		||||
        ? (x).tagged.type >= JANET_DOUBLE_OFFSET \
 | 
			
		||||
        : (x).tagged.type == (t))
 | 
			
		||||
@@ -706,7 +852,7 @@ JANET_API int janet_checkint64(Janet x);
 | 
			
		||||
JANET_API int janet_checksize(Janet x);
 | 
			
		||||
JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at);
 | 
			
		||||
#define janet_checkintrange(x) ((x) >= INT32_MIN && (x) <= INT32_MAX && (x) == (int32_t)(x))
 | 
			
		||||
#define janet_checkint64range(x) ((x) >= INT64_MIN && (x) <= INT64_MAX && (x) == (int64_t)(x))
 | 
			
		||||
#define janet_checkint64range(x) ((x) >= JANET_INTMIN_DOUBLE && (x) <= JANET_INTMAX_DOUBLE && (x) == (int64_t)(x))
 | 
			
		||||
#define janet_unwrap_integer(x) ((int32_t) janet_unwrap_number(x))
 | 
			
		||||
#define janet_wrap_integer(x) janet_wrap_number((int32_t)(x))
 | 
			
		||||
 | 
			
		||||
@@ -717,7 +863,10 @@ JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at
 | 
			
		||||
 * list of blocks, which is naive but works. */
 | 
			
		||||
struct JanetGCObject {
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
    JanetGCObject *next;
 | 
			
		||||
    union {
 | 
			
		||||
        JanetGCObject *next;
 | 
			
		||||
        int32_t refcount; /* For threaded abstract types */
 | 
			
		||||
    } data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* A lightweight green thread in janet. Does not correspond to
 | 
			
		||||
@@ -728,11 +877,21 @@ struct JanetFiber {
 | 
			
		||||
    int32_t frame; /* Index of the stack frame */
 | 
			
		||||
    int32_t stackstart; /* Beginning of next args */
 | 
			
		||||
    int32_t stacktop; /* Top of stack. Where values are pushed and popped from. */
 | 
			
		||||
    int32_t capacity;
 | 
			
		||||
    int32_t capacity; /* How big is the stack memory */
 | 
			
		||||
    int32_t maxstack; /* Arbitrary defined limit for stack overflow */
 | 
			
		||||
    JanetTable *env; /* Dynamic bindings table (usually current environment). */
 | 
			
		||||
    Janet *data;
 | 
			
		||||
    Janet *data; /* Dynamically resized stack memory */
 | 
			
		||||
    JanetFiber *child; /* Keep linked list of fibers for restarting pending fibers */
 | 
			
		||||
    Janet last_value; /* Last returned value from a fiber */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* These fields are only relevant for fibers that are used as "root fibers" -
 | 
			
		||||
     * that is, fibers that are scheduled on the event loop and behave much like threads
 | 
			
		||||
     * in a multi-tasking system. It would be possible to move these fields to a new
 | 
			
		||||
     * type, say "JanetTask", that as separate from fibers to save a bit of space. */
 | 
			
		||||
    JanetListenerState *waiting;
 | 
			
		||||
    uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */
 | 
			
		||||
    void *supervisor_channel; /* Channel to push self to when complete */
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Mark if a stack frame is a tail call for debugging */
 | 
			
		||||
@@ -802,6 +961,7 @@ struct JanetStructHead {
 | 
			
		||||
    int32_t length;
 | 
			
		||||
    int32_t hash;
 | 
			
		||||
    int32_t capacity;
 | 
			
		||||
    const JanetKV *proto;
 | 
			
		||||
    const JanetKV data[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -962,6 +1122,14 @@ struct JanetReg {
 | 
			
		||||
    const char *documentation;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct JanetRegExt {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    JanetCFunction cfun;
 | 
			
		||||
    const char *documentation;
 | 
			
		||||
    const char *source_file;
 | 
			
		||||
    int32_t source_line;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct JanetMethod {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    JanetCFunction cfun;
 | 
			
		||||
@@ -996,9 +1164,22 @@ struct JanetRNG {
 | 
			
		||||
typedef struct JanetFile JanetFile;
 | 
			
		||||
struct JanetFile {
 | 
			
		||||
    FILE *file;
 | 
			
		||||
    int flags;
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* For janet_try and janet_restore */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    /* old state */
 | 
			
		||||
    int32_t stackn;
 | 
			
		||||
    int gc_handle;
 | 
			
		||||
    JanetFiber *vm_fiber;
 | 
			
		||||
    jmp_buf *vm_jmp_buf;
 | 
			
		||||
    Janet *vm_return_reg;
 | 
			
		||||
    /* new state */
 | 
			
		||||
    jmp_buf buf;
 | 
			
		||||
    Janet payload;
 | 
			
		||||
} JanetTryState;
 | 
			
		||||
 | 
			
		||||
/* Thread types */
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
typedef struct JanetThread JanetThread;
 | 
			
		||||
@@ -1118,6 +1299,9 @@ enum JanetOpCode {
 | 
			
		||||
    JOP_GREATER_THAN_EQUAL,
 | 
			
		||||
    JOP_LESS_THAN_EQUAL,
 | 
			
		||||
    JOP_NEXT,
 | 
			
		||||
    JOP_NOT_EQUALS,
 | 
			
		||||
    JOP_NOT_EQUALS_IMMEDIATE,
 | 
			
		||||
    JOP_CANCEL,
 | 
			
		||||
    JOP_INSTRUCTION_COUNT
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -1128,9 +1312,152 @@ extern enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT];
 | 
			
		||||
 | 
			
		||||
/***** START SECTION MAIN *****/
 | 
			
		||||
 | 
			
		||||
/* Event Loop */
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
 | 
			
		||||
extern JANET_API const JanetAbstractType janet_stream_type;
 | 
			
		||||
extern JANET_API const JanetAbstractType janet_channel_type;
 | 
			
		||||
 | 
			
		||||
/* Run the event loop */
 | 
			
		||||
JANET_API void janet_loop(void);
 | 
			
		||||
 | 
			
		||||
/* Run the event loop, but allow for user scheduled interrupts triggered
 | 
			
		||||
 * by janet_loop1_interrupt being called in library code, a signal handler, or
 | 
			
		||||
 * another thread.
 | 
			
		||||
 *
 | 
			
		||||
 * Example:
 | 
			
		||||
 *
 | 
			
		||||
 * while (!janet_loop_done()) {
 | 
			
		||||
 *   // One turn of the event loop
 | 
			
		||||
 *   JanetFiber *interrupted_fiber = janet_loop1();
 | 
			
		||||
 *   // interrupted_fiber may be NULL
 | 
			
		||||
 *   // do some work here periodically...
 | 
			
		||||
 *   if (NULL != interrupted_fiber) {
 | 
			
		||||
 *     if (cancel_interrupted_fiber) {
 | 
			
		||||
 *       janet_cancel(interrupted_fiber, janet_cstringv("fiber was interrupted for [reason]"));
 | 
			
		||||
 *     } else {
 | 
			
		||||
 *       janet_schedule(interrupted_fiber, janet_wrap_nil());
 | 
			
		||||
 *     }
 | 
			
		||||
 *   }
 | 
			
		||||
 * }
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
JANET_API int janet_loop_done(void);
 | 
			
		||||
JANET_API JanetFiber *janet_loop1(void);
 | 
			
		||||
JANET_API void janet_loop1_interrupt(JanetVM *vm);
 | 
			
		||||
 | 
			
		||||
/* Wrapper around streams */
 | 
			
		||||
JANET_API JanetStream *janet_stream(JanetHandle handle, uint32_t flags, const JanetMethod *methods);
 | 
			
		||||
JANET_API void janet_stream_close(JanetStream *stream);
 | 
			
		||||
JANET_API Janet janet_cfun_stream_close(int32_t argc, Janet *argv);
 | 
			
		||||
JANET_API Janet janet_cfun_stream_read(int32_t argc, Janet *argv);
 | 
			
		||||
JANET_API Janet janet_cfun_stream_chunk(int32_t argc, Janet *argv);
 | 
			
		||||
JANET_API Janet janet_cfun_stream_write(int32_t argc, Janet *argv);
 | 
			
		||||
JANET_API void janet_stream_flags(JanetStream *stream, uint32_t flags);
 | 
			
		||||
 | 
			
		||||
/* Queue a fiber to run on the event loop */
 | 
			
		||||
JANET_API void janet_schedule(JanetFiber *fiber, Janet value);
 | 
			
		||||
JANET_API void janet_cancel(JanetFiber *fiber, Janet value);
 | 
			
		||||
JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig);
 | 
			
		||||
 | 
			
		||||
/* Start a state machine listening for events from a stream */
 | 
			
		||||
JANET_API JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user);
 | 
			
		||||
 | 
			
		||||
/* Shorthand for yielding to event loop in C */
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_await(void);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_sleep_await(double sec);
 | 
			
		||||
 | 
			
		||||
/* For use inside listeners - adds a timeout to the current fiber, such that
 | 
			
		||||
 * it will be resumed after sec seconds if no other event schedules the current fiber. */
 | 
			
		||||
JANET_API void janet_addtimeout(double sec);
 | 
			
		||||
JANET_API void janet_ev_inc_refcount(void);
 | 
			
		||||
JANET_API void janet_ev_dec_refcount(void);
 | 
			
		||||
 | 
			
		||||
/* Thread aware abstract types and helpers */
 | 
			
		||||
JANET_API void *janet_abstract_begin_threaded(const JanetAbstractType *atype, size_t size);
 | 
			
		||||
JANET_API void *janet_abstract_end_threaded(void *x);
 | 
			
		||||
JANET_API void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size);
 | 
			
		||||
JANET_API int32_t janet_abstract_incref(void *abst);
 | 
			
		||||
JANET_API int32_t janet_abstract_decref(void *abst);
 | 
			
		||||
 | 
			
		||||
/* Expose some OS sync primitives to make portable abstract types easier to implement */
 | 
			
		||||
JANET_API void janet_os_mutex_init(JanetOSMutex *mutex);
 | 
			
		||||
JANET_API void janet_os_mutex_deinit(JanetOSMutex *mutex);
 | 
			
		||||
JANET_API void janet_os_mutex_lock(JanetOSMutex *mutex);
 | 
			
		||||
JANET_API void janet_os_mutex_unlock(JanetOSMutex *mutex);
 | 
			
		||||
 | 
			
		||||
/* Get last error from an IO operation */
 | 
			
		||||
JANET_API Janet janet_ev_lasterr(void);
 | 
			
		||||
 | 
			
		||||
/* Async service for calling a function or syscall in a background thread. This is not
 | 
			
		||||
 * as efficient in the slightest as using Streams but can be used for arbitrary blocking
 | 
			
		||||
 * functions and syscalls. */
 | 
			
		||||
 | 
			
		||||
/* Used to pass data between the main thread and worker threads for simple tasks.
 | 
			
		||||
 * We could just use a pointer but this prevents malloc/free in the common case
 | 
			
		||||
 * of only a handful of arguments. */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int tag;
 | 
			
		||||
    int argi;
 | 
			
		||||
    void *argp;
 | 
			
		||||
    Janet argj;
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
} JanetEVGenericMessage;
 | 
			
		||||
 | 
			
		||||
/* How to resume or cancel after a threaded call. Not exhaustive of the possible
 | 
			
		||||
 * ways one might want to resume after returning from a threaded call, but should
 | 
			
		||||
 * cover most of the common cases. For something more complicated, such as resuming
 | 
			
		||||
 * with an abstract type or a struct, one should use janet_ev_threaded_call instead
 | 
			
		||||
 * of janet_ev_threaded_await with a custom callback. */
 | 
			
		||||
 | 
			
		||||
#define JANET_EV_TCTAG_NIL 0          /* resume with nil */
 | 
			
		||||
#define JANET_EV_TCTAG_INTEGER 1      /* resume with janet_wrap_integer(argi) */
 | 
			
		||||
#define JANET_EV_TCTAG_STRING 2       /* resume with janet_cstringv((const char *) argp) */
 | 
			
		||||
#define JANET_EV_TCTAG_STRINGF 3      /* resume with janet_cstringv((const char *) argp), then call free on argp. */
 | 
			
		||||
#define JANET_EV_TCTAG_KEYWORD 4      /* resume with janet_ckeywordv((const char *) argp) */
 | 
			
		||||
#define JANET_EV_TCTAG_ERR_STRING 5   /* cancel with janet_cstringv((const char *) argp) */
 | 
			
		||||
#define JANET_EV_TCTAG_ERR_STRINGF 6  /* cancel with janet_cstringv((const char *) argp), then call free on argp. */
 | 
			
		||||
#define JANET_EV_TCTAG_ERR_KEYWORD 7  /* cancel with janet_ckeywordv((const char *) argp) */
 | 
			
		||||
#define JANET_EV_TCTAG_BOOLEAN 8      /* resume with janet_wrap_boolean(argi) */
 | 
			
		||||
 | 
			
		||||
/* Function pointer that is run in the thread pool */
 | 
			
		||||
typedef JanetEVGenericMessage(*JanetThreadedSubroutine)(JanetEVGenericMessage arguments);
 | 
			
		||||
 | 
			
		||||
/* Handler for events posted to the event loop */
 | 
			
		||||
typedef void (*JanetCallback)(JanetEVGenericMessage return_value);
 | 
			
		||||
 | 
			
		||||
/* Handler that is run in the main thread with the result of the JanetAsyncSubroutine (same as JanetCallback) */
 | 
			
		||||
typedef void (*JanetThreadedCallback)(JanetEVGenericMessage return_value);
 | 
			
		||||
 | 
			
		||||
/* API calls for quickly offloading some work in C to a new thread or thread pool. */
 | 
			
		||||
JANET_API void janet_ev_threaded_call(JanetThreadedSubroutine fp, JanetEVGenericMessage arguments, JanetThreadedCallback cb);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_threaded_await(JanetThreadedSubroutine fp, int tag, int argi, void *argp);
 | 
			
		||||
 | 
			
		||||
/* Post callback + userdata to an event loop. Takes the vm parameter to allow posting from other
 | 
			
		||||
 * threads or signal handlers. Use NULL to post to the current thread. */
 | 
			
		||||
JANET_API void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGenericMessage msg);
 | 
			
		||||
 | 
			
		||||
/* Callback used by janet_ev_threaded_await */
 | 
			
		||||
JANET_API void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value);
 | 
			
		||||
 | 
			
		||||
/* Read async from a stream */
 | 
			
		||||
JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Write async to a stream */
 | 
			
		||||
JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf);
 | 
			
		||||
JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str);
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags);
 | 
			
		||||
JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags);
 | 
			
		||||
JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags);
 | 
			
		||||
JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Parsing */
 | 
			
		||||
@@ -1140,6 +1467,7 @@ JANET_API void janet_parser_deinit(JanetParser *parser);
 | 
			
		||||
JANET_API void janet_parser_consume(JanetParser *parser, uint8_t c);
 | 
			
		||||
JANET_API enum JanetParserStatus janet_parser_status(JanetParser *parser);
 | 
			
		||||
JANET_API Janet janet_parser_produce(JanetParser *parser);
 | 
			
		||||
JANET_API Janet janet_parser_produce_wrapped(JanetParser *parser);
 | 
			
		||||
JANET_API const char *janet_parser_error(JanetParser *parser);
 | 
			
		||||
JANET_API void janet_parser_flush(JanetParser *parser);
 | 
			
		||||
JANET_API void janet_parser_eof(JanetParser *parser);
 | 
			
		||||
@@ -1176,15 +1504,26 @@ struct JanetCompileResult {
 | 
			
		||||
    enum JanetCompileStatus status;
 | 
			
		||||
};
 | 
			
		||||
JANET_API JanetCompileResult janet_compile(Janet source, JanetTable *env, JanetString where);
 | 
			
		||||
JANET_API JanetCompileResult janet_compile_lint(
 | 
			
		||||
    Janet source,
 | 
			
		||||
    JanetTable *env,
 | 
			
		||||
    JanetString where,
 | 
			
		||||
    JanetArray *lints);
 | 
			
		||||
 | 
			
		||||
/* Get the default environment for janet */
 | 
			
		||||
JANET_API JanetTable *janet_core_env(JanetTable *replacements);
 | 
			
		||||
JANET_API JanetTable *janet_core_lookup_table(JanetTable *replacements);
 | 
			
		||||
 | 
			
		||||
/* Execute strings */
 | 
			
		||||
JANET_API int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out);
 | 
			
		||||
JANET_API int janet_dostring(JanetTable *env, const char *str, const char *sourcePath, Janet *out);
 | 
			
		||||
 | 
			
		||||
/* Run the entrypoint of a wrapped program */
 | 
			
		||||
JANET_API int janet_loop_fiber(JanetFiber *fiber);
 | 
			
		||||
 | 
			
		||||
/* Number scanning */
 | 
			
		||||
JANET_API int janet_scan_number(const uint8_t *str, int32_t len, double *out);
 | 
			
		||||
JANET_API int janet_scan_number_base(const uint8_t *str, int32_t len, int32_t base, double *out);
 | 
			
		||||
JANET_API int janet_scan_int64(const uint8_t *str, int32_t len, int64_t *out);
 | 
			
		||||
JANET_API int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out);
 | 
			
		||||
 | 
			
		||||
@@ -1201,6 +1540,7 @@ JANET_API JanetRNG *janet_default_rng(void);
 | 
			
		||||
JANET_API void janet_rng_seed(JanetRNG *rng, uint32_t seed);
 | 
			
		||||
JANET_API void janet_rng_longseed(JanetRNG *rng, const uint8_t *bytes, int32_t len);
 | 
			
		||||
JANET_API uint32_t janet_rng_u32(JanetRNG *rng);
 | 
			
		||||
JANET_API double janet_rng_double(JanetRNG *rng);
 | 
			
		||||
 | 
			
		||||
/* Array functions */
 | 
			
		||||
JANET_API JanetArray *janet_array(int32_t capacity);
 | 
			
		||||
@@ -1281,16 +1621,20 @@ JANET_API JanetSymbol janet_symbol_gen(void);
 | 
			
		||||
#define janet_struct_length(t) (janet_struct_head(t)->length)
 | 
			
		||||
#define janet_struct_capacity(t) (janet_struct_head(t)->capacity)
 | 
			
		||||
#define janet_struct_hash(t) (janet_struct_head(t)->hash)
 | 
			
		||||
#define janet_struct_proto(t) (janet_struct_head(t)->proto)
 | 
			
		||||
JANET_API JanetKV *janet_struct_begin(int32_t count);
 | 
			
		||||
JANET_API void janet_struct_put(JanetKV *st, Janet key, Janet value);
 | 
			
		||||
JANET_API JanetStruct janet_struct_end(JanetKV *st);
 | 
			
		||||
JANET_API Janet janet_struct_get(JanetStruct st, Janet key);
 | 
			
		||||
JANET_API Janet janet_struct_rawget(JanetStruct st, Janet key);
 | 
			
		||||
JANET_API Janet janet_struct_get_ex(JanetStruct st, Janet key, JanetStruct *which);
 | 
			
		||||
JANET_API JanetTable *janet_struct_to_table(JanetStruct st);
 | 
			
		||||
JANET_API const JanetKV *janet_struct_find(JanetStruct st, Janet key);
 | 
			
		||||
 | 
			
		||||
/* Table functions */
 | 
			
		||||
JANET_API JanetTable *janet_table(int32_t capacity);
 | 
			
		||||
JANET_API JanetTable *janet_table_init(JanetTable *table, int32_t capacity);
 | 
			
		||||
JANET_API JanetTable *janet_table_init_raw(JanetTable *table, int32_t capacity);
 | 
			
		||||
JANET_API void janet_table_deinit(JanetTable *table);
 | 
			
		||||
JANET_API Janet janet_table_get(JanetTable *t, Janet key);
 | 
			
		||||
JANET_API Janet janet_table_get_ex(JanetTable *t, Janet key, JanetTable **which);
 | 
			
		||||
@@ -1302,6 +1646,7 @@ JANET_API void janet_table_merge_table(JanetTable *table, JanetTable *other);
 | 
			
		||||
JANET_API void janet_table_merge_struct(JanetTable *table, JanetStruct other);
 | 
			
		||||
JANET_API JanetKV *janet_table_find(JanetTable *t, Janet key);
 | 
			
		||||
JANET_API JanetTable *janet_table_clone(JanetTable *table);
 | 
			
		||||
JANET_API void janet_table_clear(JanetTable *table);
 | 
			
		||||
 | 
			
		||||
/* Fiber */
 | 
			
		||||
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv);
 | 
			
		||||
@@ -1372,10 +1717,17 @@ JANET_API int janet_verify(JanetFuncDef *def);
 | 
			
		||||
JANET_API JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, Janet x);
 | 
			
		||||
 | 
			
		||||
/* Misc */
 | 
			
		||||
#ifndef JANET_NO_PRF
 | 
			
		||||
#ifdef JANET_PRF
 | 
			
		||||
#define JANET_HASH_KEY_SIZE 16
 | 
			
		||||
JANET_API void janet_init_hash_key(uint8_t key[JANET_HASH_KEY_SIZE]);
 | 
			
		||||
#endif
 | 
			
		||||
JANET_API void janet_try_init(JanetTryState *state);
 | 
			
		||||
#if defined(JANET_BSD) || defined(JANET_APPLE)
 | 
			
		||||
#define janet_try(state) (janet_try_init(state), (JanetSignal) _setjmp((state)->buf))
 | 
			
		||||
#else
 | 
			
		||||
#define janet_try(state) (janet_try_init(state), (JanetSignal) setjmp((state)->buf))
 | 
			
		||||
#endif
 | 
			
		||||
JANET_API void janet_restore(JanetTryState *state);
 | 
			
		||||
JANET_API int janet_equals(Janet x, Janet y);
 | 
			
		||||
JANET_API int32_t janet_hash(Janet x);
 | 
			
		||||
JANET_API int janet_compare(Janet x, Janet y);
 | 
			
		||||
@@ -1393,16 +1745,25 @@ JANET_API Janet janet_wrap_number_safe(double x);
 | 
			
		||||
JANET_API int janet_keyeq(Janet x, const char *cstring);
 | 
			
		||||
JANET_API int janet_streq(Janet x, const char *cstring);
 | 
			
		||||
JANET_API int janet_symeq(Janet x, const char *cstring);
 | 
			
		||||
JANET_API int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffer);
 | 
			
		||||
 | 
			
		||||
/* VM functions */
 | 
			
		||||
JANET_API int janet_init(void);
 | 
			
		||||
JANET_API void janet_deinit(void);
 | 
			
		||||
JANET_API JanetVM *janet_vm_alloc(void);
 | 
			
		||||
JANET_API JanetVM *janet_local_vm(void);
 | 
			
		||||
JANET_API void janet_vm_free(JanetVM *vm);
 | 
			
		||||
JANET_API void janet_vm_save(JanetVM *into);
 | 
			
		||||
JANET_API void janet_vm_load(JanetVM *from);
 | 
			
		||||
JANET_API void janet_interpreter_interrupt(JanetVM *vm);
 | 
			
		||||
JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out);
 | 
			
		||||
JANET_API JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig);
 | 
			
		||||
JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);
 | 
			
		||||
JANET_API JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out);
 | 
			
		||||
JANET_API Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv);
 | 
			
		||||
JANET_API Janet janet_mcall(const char *name, int32_t argc, Janet *argv);
 | 
			
		||||
JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err);
 | 
			
		||||
JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix);
 | 
			
		||||
 | 
			
		||||
/* Scratch Memory API */
 | 
			
		||||
typedef void (*JanetScratchFinalizer)(void *);
 | 
			
		||||
@@ -1420,26 +1781,111 @@ typedef enum {
 | 
			
		||||
    JANET_BINDING_VAR,
 | 
			
		||||
    JANET_BINDING_MACRO
 | 
			
		||||
} JanetBindingType;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    JanetBindingType type;
 | 
			
		||||
    Janet value;
 | 
			
		||||
    enum {
 | 
			
		||||
        JANET_BINDING_DEP_NONE,
 | 
			
		||||
        JANET_BINDING_DEP_RELAXED,
 | 
			
		||||
        JANET_BINDING_DEP_NORMAL,
 | 
			
		||||
        JANET_BINDING_DEP_STRICT,
 | 
			
		||||
    } deprecation;
 | 
			
		||||
} JanetBinding;
 | 
			
		||||
 | 
			
		||||
JANET_API void janet_def(JanetTable *env, const char *name, Janet val, const char *documentation);
 | 
			
		||||
JANET_API void janet_var(JanetTable *env, const char *name, Janet val, const char *documentation);
 | 
			
		||||
JANET_API void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns);
 | 
			
		||||
JANET_API void janet_cfuns_prefix(JanetTable *env, const char *regprefix, const JanetReg *cfuns);
 | 
			
		||||
JANET_API JanetBindingType janet_resolve(JanetTable *env, JanetSymbol sym, Janet *out);
 | 
			
		||||
JANET_API void janet_register(const char *name, JanetCFunction cfun);
 | 
			
		||||
JANET_API JanetBinding janet_resolve_ext(JanetTable *env, JanetSymbol sym);
 | 
			
		||||
 | 
			
		||||
/* Get values from the core environment. */
 | 
			
		||||
JANET_API Janet janet_resolve_core(const char *name);
 | 
			
		||||
 | 
			
		||||
/* New C API */
 | 
			
		||||
 | 
			
		||||
/* Shorthand for janet C function declarations */
 | 
			
		||||
#define JANET_CFUN(name) Janet name (int32_t argc, Janet *argv)
 | 
			
		||||
 | 
			
		||||
/* Declare a C function with documentation and source mapping */
 | 
			
		||||
#define JANET_REG_END {NULL, NULL, NULL, NULL, 0}
 | 
			
		||||
 | 
			
		||||
/* no docstrings or sourcemaps */
 | 
			
		||||
#define JANET_REG_(JNAME, CNAME) {JNAME, CNAME, NULL, NULL, 0}
 | 
			
		||||
#define JANET_FN_(CNAME, USAGE, DOCSTRING) \
 | 
			
		||||
    Janet CNAME (int32_t argc, Janet *argv)
 | 
			
		||||
#define JANET_DEF_(ENV, JNAME, VAL, DOC) \
 | 
			
		||||
    janet_def(ENV, JNAME, VAL, NULL)
 | 
			
		||||
 | 
			
		||||
/* sourcemaps only */
 | 
			
		||||
#define JANET_REG_S(JNAME, CNAME) {JNAME, CNAME, NULL, __FILE__, CNAME##_sourceline_}
 | 
			
		||||
#define JANET_FN_S(CNAME, USAGE, DOCSTRING) \
 | 
			
		||||
    static int32_t CNAME##_sourceline_ = __LINE__; \
 | 
			
		||||
    Janet CNAME (int32_t argc, Janet *argv)
 | 
			
		||||
#define JANET_DEF_S(ENV, JNAME, VAL, DOC) \
 | 
			
		||||
    janet_def_sm(ENV, JNAME, VAL, NULL, __FILE__, __LINE__)
 | 
			
		||||
 | 
			
		||||
/* docstring only */
 | 
			
		||||
#define JANET_REG_D(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, NULL, 0}
 | 
			
		||||
#define JANET_FN_D(CNAME, USAGE, DOCSTRING) \
 | 
			
		||||
    static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \
 | 
			
		||||
    Janet CNAME (int32_t argc, Janet *argv)
 | 
			
		||||
#define JANET_DEF_D(ENV, JNAME, VAL, DOC) \
 | 
			
		||||
    janet_def(ENV, JNAME, VAL, DOC)
 | 
			
		||||
 | 
			
		||||
/* sourcemaps and docstrings */
 | 
			
		||||
#define JANET_REG_SD(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, __FILE__, CNAME##_sourceline_}
 | 
			
		||||
#define JANET_FN_SD(CNAME, USAGE, DOCSTRING) \
 | 
			
		||||
    static int32_t CNAME##_sourceline_ = __LINE__; \
 | 
			
		||||
    static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \
 | 
			
		||||
    Janet CNAME (int32_t argc, Janet *argv)
 | 
			
		||||
#define JANET_DEF_SD(ENV, JNAME, VAL, DOC) \
 | 
			
		||||
    janet_def_sm(ENV, JNAME, VAL, DOC, __FILE__, __LINE__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Choose defaults for source mapping and docstring based on config defs */
 | 
			
		||||
#if defined(JANET_NO_SOURCEMAPS) && defined(JANET_NO_DOCSTRINGS)
 | 
			
		||||
#define JANET_REG JANET_REG_
 | 
			
		||||
#define JANET_FN JANET_FN_
 | 
			
		||||
#define JANET_DEF JANET_DEF_
 | 
			
		||||
#elif defined(JANET_NO_SOURCEMAPS) && !defined(JANET_NO_DOCSTRINGS)
 | 
			
		||||
#define JANET_REG JANET_REG_D
 | 
			
		||||
#define JANET_FN JANET_FN_D
 | 
			
		||||
#define JANET_DEF JANET_DEF_D
 | 
			
		||||
#elif !defined(JANET_NO_SOURCEMAPS) && defined(JANET_NO_DOCSTRINGS)
 | 
			
		||||
#define JANET_REG JANET_REG_S
 | 
			
		||||
#define JANET_FN JANET_FN_S
 | 
			
		||||
#define JANET_DEF JANET_DEF_S
 | 
			
		||||
#elif !defined(JANET_NO_SOURCEMAPS) && !defined(JANET_NO_DOCSTRINGS)
 | 
			
		||||
#define JANET_REG JANET_REG_SD
 | 
			
		||||
#define JANET_FN JANET_FN_SD
 | 
			
		||||
#define JANET_DEF JANET_DEF_SD
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Define things with source mapping information */
 | 
			
		||||
JANET_API void janet_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns);
 | 
			
		||||
JANET_API void janet_cfuns_ext_prefix(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns);
 | 
			
		||||
JANET_API void janet_def_sm(JanetTable *env, const char *name, Janet val, const char *documentation, const char *source_file, int32_t source_line);
 | 
			
		||||
JANET_API void janet_var_sm(JanetTable *env, const char *name, Janet val, const char *documentation, const char *source_file, int32_t source_line);
 | 
			
		||||
 | 
			
		||||
/* Legacy definition of C functions */
 | 
			
		||||
JANET_API void janet_register(const char *name, JanetCFunction cfun);
 | 
			
		||||
 | 
			
		||||
/* Allow setting entry name for static libraries */
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
#define JANET_MODULE_PREFIX extern "C"
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_MODULE_PREFIX
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef JANET_ENTRY_NAME
 | 
			
		||||
#define JANET_MODULE_ENTRY \
 | 
			
		||||
    JANET_API JanetBuildConfig _janet_mod_config(void) { \
 | 
			
		||||
    JANET_MODULE_PREFIX JANET_API JanetBuildConfig _janet_mod_config(void) { \
 | 
			
		||||
        return janet_config_current(); \
 | 
			
		||||
    } \
 | 
			
		||||
    JANET_API void _janet_init
 | 
			
		||||
    JANET_MODULE_PREFIX JANET_API void _janet_init
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_MODULE_ENTRY JANET_API void JANET_ENTRY_NAME
 | 
			
		||||
#define JANET_MODULE_ENTRY JANET_MODULE_PREFIX JANET_API void JANET_ENTRY_NAME
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_signalv(JanetSignal signal, Janet message);
 | 
			
		||||
@@ -1456,6 +1902,7 @@ JANET_API void janet_arity(int32_t arity, int32_t min, int32_t max);
 | 
			
		||||
JANET_API void janet_fixarity(int32_t arity, int32_t fix);
 | 
			
		||||
 | 
			
		||||
JANET_API int janet_getmethod(JanetKeyword method, const JanetMethod *methods, Janet *out);
 | 
			
		||||
JANET_API Janet janet_nextmethod(const JanetMethod *methods, Janet key);
 | 
			
		||||
 | 
			
		||||
JANET_API double janet_getnumber(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetArray *janet_getarray(const Janet *argv, int32_t n);
 | 
			
		||||
@@ -1524,12 +1971,18 @@ extern JANET_API const JanetAbstractType janet_file_type;
 | 
			
		||||
#define JANET_FILE_BINARY 64
 | 
			
		||||
#define JANET_FILE_SERIALIZABLE 128
 | 
			
		||||
#define JANET_FILE_PIPED 256
 | 
			
		||||
#define JANET_FILE_NONIL 512
 | 
			
		||||
 | 
			
		||||
JANET_API Janet janet_makefile(FILE *f, int flags);
 | 
			
		||||
JANET_API FILE *janet_getfile(const Janet *argv, int32_t n, int *flags);
 | 
			
		||||
JANET_API Janet janet_makefile(FILE *f, int32_t flags);
 | 
			
		||||
JANET_API JanetFile *janet_makejfile(FILE *f, int32_t flags);
 | 
			
		||||
JANET_API FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags);
 | 
			
		||||
JANET_API FILE *janet_dynfile(const char *name, FILE *def);
 | 
			
		||||
JANET_API JanetFile *janet_getjfile(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetAbstract janet_checkfile(Janet j);
 | 
			
		||||
JANET_API FILE *janet_unwrapfile(Janet j, int *flags);
 | 
			
		||||
JANET_API FILE *janet_unwrapfile(Janet j, int32_t *flags);
 | 
			
		||||
JANET_API int janet_file_close(JanetFile *file);
 | 
			
		||||
 | 
			
		||||
JANET_API int janet_cryptorand(uint8_t *out, size_t n);
 | 
			
		||||
 | 
			
		||||
/* Marshal API */
 | 
			
		||||
JANET_API void janet_marshal_size(JanetMarshalContext *ctx, size_t value);
 | 
			
		||||
@@ -1548,6 +2001,7 @@ JANET_API uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx);
 | 
			
		||||
JANET_API void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, size_t len);
 | 
			
		||||
JANET_API Janet janet_unmarshal_janet(JanetMarshalContext *ctx);
 | 
			
		||||
JANET_API JanetAbstract janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size);
 | 
			
		||||
JANET_API void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p);
 | 
			
		||||
 | 
			
		||||
JANET_API void janet_register_abstract_type(const JanetAbstractType *at);
 | 
			
		||||
JANET_API const JanetAbstractType *janet_get_abstract_type(Janet key);
 | 
			
		||||
@@ -1585,70 +2039,23 @@ typedef enum {
 | 
			
		||||
    RULE_TO,           /* [rule] */
 | 
			
		||||
    RULE_THRU,         /* [rule] */
 | 
			
		||||
    RULE_LENPREFIX,    /* [rule_a, rule_b (repeat rule_b rule_a times)] */
 | 
			
		||||
} JanetPegOpcode;
 | 
			
		||||
    RULE_READINT,      /* [(signedness << 4) | (endianess << 5) | bytewidth, tag] */
 | 
			
		||||
    RULE_LINE,         /* [tag] */
 | 
			
		||||
    RULE_COLUMN,       /* [tag] */
 | 
			
		||||
    RULE_UNREF,        /* [rule, tag] */
 | 
			
		||||
    RULE_CAPTURE_NUM   /* [rule, tag] */
 | 
			
		||||
} JanetPegOpcod;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint32_t *bytecode;
 | 
			
		||||
    Janet *constants;
 | 
			
		||||
    size_t bytecode_len;
 | 
			
		||||
    uint32_t num_constants;
 | 
			
		||||
    int has_backref;
 | 
			
		||||
} JanetPeg;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_TYPED_ARRAY
 | 
			
		||||
 | 
			
		||||
extern JANET_API const JanetAbstractType janet_ta_view_type;
 | 
			
		||||
extern JANET_API const JanetAbstractType janet_ta_buffer_type;
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_TARRAY_TYPE_U8,
 | 
			
		||||
    JANET_TARRAY_TYPE_S8,
 | 
			
		||||
    JANET_TARRAY_TYPE_U16,
 | 
			
		||||
    JANET_TARRAY_TYPE_S16,
 | 
			
		||||
    JANET_TARRAY_TYPE_U32,
 | 
			
		||||
    JANET_TARRAY_TYPE_S32,
 | 
			
		||||
    JANET_TARRAY_TYPE_U64,
 | 
			
		||||
    JANET_TARRAY_TYPE_S64,
 | 
			
		||||
    JANET_TARRAY_TYPE_F32,
 | 
			
		||||
    JANET_TARRAY_TYPE_F64
 | 
			
		||||
} JanetTArrayType;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint8_t *data;
 | 
			
		||||
    size_t size;
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
} JanetTArrayBuffer;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    union {
 | 
			
		||||
        void *pointer;
 | 
			
		||||
        uint8_t *u8;
 | 
			
		||||
        int8_t *s8;
 | 
			
		||||
        uint16_t *u16;
 | 
			
		||||
        int16_t *s16;
 | 
			
		||||
        uint32_t *u32;
 | 
			
		||||
        int32_t *s32;
 | 
			
		||||
        uint64_t *u64;
 | 
			
		||||
        int64_t *s64;
 | 
			
		||||
        float *f32;
 | 
			
		||||
        double *f64;
 | 
			
		||||
    } as;
 | 
			
		||||
    JanetTArrayBuffer *buffer;
 | 
			
		||||
    size_t size;
 | 
			
		||||
    size_t stride;
 | 
			
		||||
    JanetTArrayType type;
 | 
			
		||||
} JanetTArrayView;
 | 
			
		||||
 | 
			
		||||
JANET_API JanetTArrayBuffer *janet_tarray_buffer(size_t size);
 | 
			
		||||
JANET_API JanetTArrayView *janet_tarray_view(JanetTArrayType type, size_t size, size_t stride, size_t offset, JanetTArrayBuffer *buffer);
 | 
			
		||||
JANET_API int janet_is_tarray_view(Janet x, JanetTArrayType type);
 | 
			
		||||
JANET_API JanetTArrayBuffer *janet_gettarray_buffer(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetTArrayView *janet_gettarray_view(const Janet *argv, int32_t n, JanetTArrayType type);
 | 
			
		||||
JanetTArrayView *janet_gettarray_any(const Janet *argv, int32_t n);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
 | 
			
		||||
extern JANET_API const JanetAbstractType janet_s64_type;
 | 
			
		||||
@@ -1676,9 +2083,28 @@ extern JANET_API const JanetAbstractType janet_thread_type;
 | 
			
		||||
 | 
			
		||||
JANET_API int janet_thread_receive(Janet *msg_out, double timeout);
 | 
			
		||||
JANET_API int janet_thread_send(JanetThread *thread, Janet msg, double timeout);
 | 
			
		||||
JANET_API JanetThread *janet_thread_current(void);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Custom allocator support */
 | 
			
		||||
JANET_API void *(janet_malloc)(size_t);
 | 
			
		||||
JANET_API void *(janet_realloc)(void *, size_t);
 | 
			
		||||
JANET_API void *(janet_calloc)(size_t, size_t);
 | 
			
		||||
JANET_API void (janet_free)(void *);
 | 
			
		||||
#ifndef janet_malloc
 | 
			
		||||
#define janet_malloc(X) malloc((X))
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef janet_realloc
 | 
			
		||||
#define janet_realloc(X, Y) realloc((X), (Y))
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef janet_calloc
 | 
			
		||||
#define janet_calloc(X, Y) calloc((X), (Y))
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef janet_free
 | 
			
		||||
#define janet_free(X) free((X))
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/***** END SECTION MAIN *****/
 | 
			
		||||
 | 
			
		||||
/* Re-enable popped variable length array warnings */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2020 Calvin Rose
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -84,7 +84,7 @@ static void simpleline(JanetBuffer *buffer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Windows */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#if defined(JANET_WINDOWS) || defined(JANET_SIMPLE_GETLINE)
 | 
			
		||||
 | 
			
		||||
void janet_line_init() {
 | 
			
		||||
    ;
 | 
			
		||||
@@ -136,7 +136,6 @@ static JANET_THREAD_LOCAL int gbl_cols = 80;
 | 
			
		||||
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_history_count = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_historyi = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_sigint_flag = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL struct termios gbl_termios_start;
 | 
			
		||||
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_match_count = 0;
 | 
			
		||||
@@ -152,7 +151,7 @@ static const char *badterms[] = {
 | 
			
		||||
 | 
			
		||||
static char *sdup(const char *s) {
 | 
			
		||||
    size_t len = strlen(s) + 1;
 | 
			
		||||
    char *mem = malloc(len);
 | 
			
		||||
    char *mem = janet_malloc(len);
 | 
			
		||||
    if (!mem) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
@@ -170,7 +169,7 @@ static int rawmode(void) {
 | 
			
		||||
    t.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
 | 
			
		||||
    t.c_cc[VMIN] = 1;
 | 
			
		||||
    t.c_cc[VTIME] = 0;
 | 
			
		||||
    if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) < 0) goto fatal;
 | 
			
		||||
    if (tcsetattr(STDIN_FILENO, TCSADRAIN, &t) < 0) goto fatal;
 | 
			
		||||
    gbl_israwmode = 1;
 | 
			
		||||
    return 0;
 | 
			
		||||
fatal:
 | 
			
		||||
@@ -180,7 +179,7 @@ fatal:
 | 
			
		||||
 | 
			
		||||
/* Disable raw mode */
 | 
			
		||||
static void norawmode(void) {
 | 
			
		||||
    if (gbl_israwmode && tcsetattr(STDIN_FILENO, TCSAFLUSH, &gbl_termios_start) != -1)
 | 
			
		||||
    if (gbl_israwmode && tcsetattr(STDIN_FILENO, TCSADRAIN, &gbl_termios_start) != -1)
 | 
			
		||||
        gbl_israwmode = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -300,7 +299,7 @@ static int insert(char c, int draw) {
 | 
			
		||||
 | 
			
		||||
static void historymove(int delta) {
 | 
			
		||||
    if (gbl_history_count > 1) {
 | 
			
		||||
        free(gbl_history[gbl_historyi]);
 | 
			
		||||
        janet_free(gbl_history[gbl_historyi]);
 | 
			
		||||
        gbl_history[gbl_historyi] = sdup(gbl_buf);
 | 
			
		||||
 | 
			
		||||
        gbl_historyi += delta;
 | 
			
		||||
@@ -326,7 +325,7 @@ static void addhistory(void) {
 | 
			
		||||
        gbl_history[gbl_history_count++] = newline;
 | 
			
		||||
        len++;
 | 
			
		||||
    } else {
 | 
			
		||||
        free(gbl_history[JANET_HISTORY_MAX - 1]);
 | 
			
		||||
        janet_free(gbl_history[JANET_HISTORY_MAX - 1]);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = len - 1; i > 0; i--) {
 | 
			
		||||
        gbl_history[i] = gbl_history[i - 1];
 | 
			
		||||
@@ -338,7 +337,7 @@ static void replacehistory(void) {
 | 
			
		||||
    /* History count is always > 0 here */
 | 
			
		||||
    if (gbl_len == 0 || (gbl_history_count > 1 && !strcmp(gbl_buf, gbl_history[1]))) {
 | 
			
		||||
        /* Delete history */
 | 
			
		||||
        free(gbl_history[0]);
 | 
			
		||||
        janet_free(gbl_history[0]);
 | 
			
		||||
        for (int i = 1; i < gbl_history_count; i++) {
 | 
			
		||||
            gbl_history[i - 1] = gbl_history[i];
 | 
			
		||||
        }
 | 
			
		||||
@@ -346,7 +345,7 @@ static void replacehistory(void) {
 | 
			
		||||
    } else {
 | 
			
		||||
        char *newline = sdup(gbl_buf);
 | 
			
		||||
        if (!newline) return;
 | 
			
		||||
        free(gbl_history[0]);
 | 
			
		||||
        janet_free(gbl_history[0]);
 | 
			
		||||
        gbl_history[0] = newline;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -747,7 +746,7 @@ static int line() {
 | 
			
		||||
 | 
			
		||||
        switch (c) {
 | 
			
		||||
            default:
 | 
			
		||||
                if (c < 0x20) break;
 | 
			
		||||
                if ((unsigned char) c < 0x20) break;
 | 
			
		||||
                if (insert(c, 1)) return -1;
 | 
			
		||||
                break;
 | 
			
		||||
            case 1:     /* ctrl-a */
 | 
			
		||||
@@ -758,6 +757,10 @@ static int line() {
 | 
			
		||||
                kleft();
 | 
			
		||||
                break;
 | 
			
		||||
            case 3:     /* ctrl-c */
 | 
			
		||||
                norawmode();
 | 
			
		||||
                kill(getpid(), SIGINT);
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case 17:    /* ctrl-q */
 | 
			
		||||
                gbl_cancel_current_repl_form = 1;
 | 
			
		||||
                clearlines();
 | 
			
		||||
                return -1;
 | 
			
		||||
@@ -930,7 +933,7 @@ void janet_line_deinit() {
 | 
			
		||||
    int i;
 | 
			
		||||
    norawmode();
 | 
			
		||||
    for (i = 0; i < gbl_history_count; i++)
 | 
			
		||||
        free(gbl_history[i]);
 | 
			
		||||
        janet_free(gbl_history[i]);
 | 
			
		||||
    gbl_historyi = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -958,11 +961,7 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
 | 
			
		||||
    }
 | 
			
		||||
    if (line()) {
 | 
			
		||||
        norawmode();
 | 
			
		||||
        if (gbl_sigint_flag) {
 | 
			
		||||
            raise(SIGINT);
 | 
			
		||||
        } else {
 | 
			
		||||
            fputc('\n', out);
 | 
			
		||||
        }
 | 
			
		||||
        fputc('\n', out);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    fflush(stdin);
 | 
			
		||||
@@ -996,6 +995,27 @@ int main(int argc, char **argv) {
 | 
			
		||||
    SetConsoleOutputCP(65001);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(JANET_WINDOWS) && !defined(JANET_SIMPLE_GETLINE)
 | 
			
		||||
    /* Try and not leave the terminal in a bad state */
 | 
			
		||||
    atexit(norawmode);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(JANET_PRF)
 | 
			
		||||
    uint8_t hash_key[JANET_HASH_KEY_SIZE + 1];
 | 
			
		||||
#ifdef JANET_REDUCED_OS
 | 
			
		||||
    char *envvar = NULL;
 | 
			
		||||
#else
 | 
			
		||||
    char *envvar = getenv("JANET_HASHSEED");
 | 
			
		||||
#endif
 | 
			
		||||
    if (NULL != envvar) {
 | 
			
		||||
        strncpy((char *) hash_key, envvar, sizeof(hash_key) - 1);
 | 
			
		||||
    } else if (janet_cryptorand(hash_key, JANET_HASH_KEY_SIZE) != 0) {
 | 
			
		||||
        fputs("unable to initialize janet PRF hash function.\n", stderr);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    janet_init_hash_key(hash_key);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Set up VM */
 | 
			
		||||
    janet_init();
 | 
			
		||||
 | 
			
		||||
@@ -1016,20 +1036,14 @@ int main(int argc, char **argv) {
 | 
			
		||||
    janet_table_put(env, janet_ckeywordv("executable"), janet_cstringv(argv[0]));
 | 
			
		||||
 | 
			
		||||
    /* Run startup script */
 | 
			
		||||
    Janet mainfun, out;
 | 
			
		||||
    Janet mainfun;
 | 
			
		||||
    janet_resolve(env, janet_csymbol("cli-main"), &mainfun);
 | 
			
		||||
    Janet mainargs[1] = { janet_wrap_array(args) };
 | 
			
		||||
    JanetFiber *fiber = janet_fiber(janet_unwrap_function(mainfun), 64, 1, mainargs);
 | 
			
		||||
    fiber->env = env;
 | 
			
		||||
    status = janet_continue(fiber, janet_wrap_nil(), &out);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
 | 
			
		||||
        janet_stacktrace(fiber, out);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    status = JANET_SIGNAL_OK;
 | 
			
		||||
    janet_loop();
 | 
			
		||||
#endif
 | 
			
		||||
    /* Run the fiber in an event loop */
 | 
			
		||||
    status = janet_loop_fiber(fiber);
 | 
			
		||||
 | 
			
		||||
    /* Deinitialize vm */
 | 
			
		||||
    janet_deinit();
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user