mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 01:23:04 +00:00 
			
		
		
		
	Compare commits
	
		
			964 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					73334f3485 | ||
| 
						 | 
					a5b8da8d67 | ||
| 
						 | 
					e8cccfced5 | ||
| 
						 | 
					88984f7ffb | ||
| 
						 | 
					182170b3be | ||
| 
						 | 
					f92412841b | ||
| 
						 | 
					18c00e89da | ||
| 
						 | 
					7c38a55a9a | ||
| 
						 | 
					a15916ec9c | ||
| 
						 | 
					3583d4c92f | ||
| 
						 | 
					a456c67a7b | ||
| 
						 | 
					6e226e4073 | ||
| 
						 | 
					d23e6614b1 | ||
| 
						 | 
					ab6afa72fd | ||
| 
						 | 
					9538b8a77c | ||
| 
						 | 
					36d3804dd2 | ||
| 
						 | 
					a34b8ea68f | ||
| 
						 | 
					55c10f98bb | ||
| 
						 | 
					4b5a2a14c0 | ||
| 
						 | 
					665705d06d | ||
| 
						 | 
					cc8cd4bace | ||
| 
						 | 
					8f8b6ed001 | ||
| 
						 | 
					aa9efee868 | ||
| 
						 | 
					e0a0e2ed42 | ||
| 
						 | 
					39f5c539d7 | ||
| 
						 | 
					1b6437a4f8 | ||
| 
						 | 
					c62c1a58f0 | ||
| 
						 | 
					3441bcbd69 | ||
| 
						 | 
					2e6001316a | ||
| 
						 | 
					53bcc15207 | ||
| 
						 | 
					dd609bb1bb | ||
| 
						 | 
					5c56c7fa91 | ||
| 
						 | 
					9a892363a3 | ||
| 
						 | 
					5f550ea5d4 | ||
| 
						 | 
					1b6cc023a5 | ||
| 
						 | 
					410f8d69bc | ||
| 
						 | 
					6da44bdb6a | ||
| 
						 | 
					d30fd27575 | ||
| 
						 | 
					1b278fc657 | ||
| 
						 | 
					eecffe01a5 | ||
| 
						 | 
					f63a33884f | ||
| 
						 | 
					fa75a395cb | ||
| 
						 | 
					1f34ec9902 | ||
| 
						 | 
					f75c08a78e | ||
| 
						 | 
					5e93f0e34b | ||
| 
						 | 
					49f151e265 | ||
| 
						 | 
					2b73a15ad8 | ||
| 
						 | 
					06d581dde3 | ||
| 
						 | 
					2b49903c82 | ||
| 
						 | 
					a17ae977a5 | ||
| 
						 | 
					8a6b44cb4e | ||
| 
						 | 
					60d9f97750 | ||
| 
						 | 
					f252933f62 | ||
| 
						 | 
					6dbd7b476c | ||
| 
						 | 
					a47eb847fb | ||
| 
						 | 
					ba5990ef21 | ||
| 
						 | 
					753911fe2d | ||
| 
						 | 
					746ced5501 | ||
| 
						 | 
					1b49934e4f | ||
| 
						 | 
					682f0f584f | ||
| 
						 | 
					611b2a6c3a | ||
| 
						 | 
					8043caf581 | ||
| 
						 | 
					b2d2690eb9 | ||
| 
						 | 
					7f745a34c3 | ||
| 
						 | 
					b16cf17246 | ||
| 
						 | 
					67e8518ba6 | ||
| 
						 | 
					e94e8dc484 | ||
| 
						 | 
					1a24d4fc86 | ||
| 
						 | 
					6ee05785d1 | ||
| 
						 | 
					268ff666d2 | ||
| 
						 | 
					91bb34c3bf | ||
| 
						 | 
					17d5fb3210 | ||
| 
						 | 
					687b987f7e | ||
| 
						 | 
					4daecc9a41 | ||
| 
						 | 
					a85eacadda | ||
| 
						 | 
					ed63987fd1 | ||
| 
						 | 
					ff173047f4 | ||
| 
						 | 
					83e8aab289 | ||
| 
						 | 
					85cb35e68f | ||
| 
						 | 
					952906279c | ||
| 
						 | 
					5b79b48ae0 | ||
| 
						 | 
					7c44127bcb | ||
| 
						 | 
					9338312103 | ||
| 
						 | 
					a0eeb630e7 | ||
| 
						 | 
					6535d72bd4 | ||
| 
						 | 
					a7d424bc81 | ||
| 
						 | 
					2bceba4a7a | ||
| 
						 | 
					e3159bb0f5 | ||
| 
						 | 
					5d1bd8a932 | ||
| 
						 | 
					bafa6bfff0 | ||
| 
						 | 
					e2eb7ab4b2 | ||
| 
						 | 
					9f4497a5ae | ||
| 
						 | 
					70de8bf092 | ||
| 
						 | 
					e52575e23a | ||
| 
						 | 
					10994cbc6a | ||
| 
						 | 
					abad9d7db9 | ||
| 
						 | 
					5e443cd29d | ||
| 
						 | 
					7bf3a9d24c | ||
| 
						 | 
					d80a7094ae | ||
| 
						 | 
					ad77bc391c | ||
| 
						 | 
					2b84fb14b4 | ||
| 
						 | 
					07155ce657 | ||
| 
						 | 
					046d28662d | ||
| 
						 | 
					84dd3db620 | ||
| 
						 | 
					282f2671ea | ||
| 
						 | 
					3fc2be3e6e | ||
| 
						 | 
					d10c1fe759 | ||
| 
						 | 
					d18472b07d | ||
| 
						 | 
					43a68dcd2a | ||
| 
						 | 
					3d93028088 | ||
| 
						 | 
					99f0af92bd | ||
| 
						 | 
					71d81b14a2 | ||
| 
						 | 
					3894f4021a | ||
| 
						 | 
					72c659d1ee | ||
| 
						 | 
					8f879b4adc | ||
| 
						 | 
					18f2847dc1 | ||
| 
						 | 
					89b7ff9daf | ||
| 
						 | 
					26c263d6be | ||
| 
						 | 
					2570e0f7a0 | ||
| 
						 | 
					8084e4c728 | ||
| 
						 | 
					ee90f9df62 | ||
| 
						 | 
					906a982ace | ||
| 
						 | 
					88e60c309c | ||
| 
						 | 
					9694aee819 | ||
| 
						 | 
					2697b0e425 | ||
| 
						 | 
					c0d7a49b19 | ||
| 
						 | 
					f9a6f52d9c | ||
| 
						 | 
					c02c2e3f02 | ||
| 
						 | 
					1fcd47dd7b | ||
| 
						 | 
					384ee4f6a9 | ||
| 
						 | 
					e9deec8231 | ||
| 
						 | 
					2fc77a1b63 | ||
| 
						 | 
					442fe8209d | ||
| 
						 | 
					968a0dc4ac | ||
| 
						 | 
					40c93d0786 | ||
| 
						 | 
					83b0bc688c | ||
| 
						 | 
					6185b253be | ||
| 
						 | 
					17da53d0d9 | ||
| 
						 | 
					9ffec43d2b | ||
| 
						 | 
					e4f4a42751 | ||
| 
						 | 
					4f65c2707e | ||
| 
						 | 
					75bdea5155 | ||
| 
						 | 
					f553c5da47 | ||
| 
						 | 
					5f70a85f7e | ||
| 
						 | 
					c82fd106a7 | ||
| 
						 | 
					0e9b866b98 | ||
| 
						 | 
					67a8c6df09 | ||
| 
						 | 
					86cf8127b6 | ||
| 
						 | 
					828e0a07cd | ||
| 
						 | 
					90018b35c0 | ||
| 
						 | 
					5a199716cb | ||
| 
						 | 
					43ecd4f2d8 | ||
| 
						 | 
					c5a9602be9 | ||
| 
						 | 
					e88aab6d68 | ||
| 
						 | 
					ce528251d5 | ||
| 
						 | 
					9e334da2d6 | ||
| 
						 | 
					c0e508e334 | ||
| 
						 | 
					b63b3bef74 | ||
| 
						 | 
					05d0b5ac05 | ||
| 
						 | 
					c56d6e8fc1 | ||
| 
						 | 
					33d2f9a522 | ||
| 
						 | 
					e53d22fad2 | ||
| 
						 | 
					33f55dc32f | ||
| 
						 | 
					7e6aad2221 | ||
| 
						 | 
					3c0c22259c | ||
| 
						 | 
					42f6af4bf1 | ||
| 
						 | 
					f274b02653 | ||
| 
						 | 
					70c29b4e5d | ||
| 
						 | 
					84d43d1039 | ||
| 
						 | 
					5c67c1165d | ||
| 
						 | 
					85028967d8 | ||
| 
						 | 
					6ceff6ecc9 | ||
| 
						 | 
					06eec06ff0 | ||
| 
						 | 
					2dcc0adc0e | ||
| 
						 | 
					8ca1e44af1 | ||
| 
						 | 
					2aedc6beff | ||
| 
						 | 
					af2eb06298 | ||
| 
						 | 
					7ff545bd2e | ||
| 
						 | 
					a59b5765b6 | ||
| 
						 | 
					6bd58dd4c0 | ||
| 
						 | 
					e3406cd922 | ||
| 
						 | 
					ab70524d85 | ||
| 
						 | 
					ce36c4c0d6 | ||
| 
						 | 
					2b01b780da | ||
| 
						 | 
					f3048a3d6b | ||
| 
						 | 
					accac6c662 | ||
| 
						 | 
					631622aa48 | ||
| 
						 | 
					aaeaa3a944 | ||
| 
						 | 
					d1104b5a65 | ||
| 
						 | 
					1f074671ce | ||
| 
						 | 
					872b39cc32 | ||
| 
						 | 
					9eab57d194 | ||
| 
						 | 
					8edd873c3e | ||
| 
						 | 
					771956b5b6 | ||
| 
						 | 
					ecc4da5113 | ||
| 
						 | 
					f5555d21b9 | ||
| 
						 | 
					342a29c7be | ||
| 
						 | 
					368b891499 | ||
| 
						 | 
					f62539ad55 | ||
| 
						 | 
					4835ecb950 | ||
| 
						 | 
					31f0ff0d84 | ||
| 
						 | 
					b7b594205c | ||
| 
						 | 
					190056b863 | ||
| 
						 | 
					ae6b359109 | ||
| 
						 | 
					3078686f8f | ||
| 
						 | 
					0f4ecd93ab | ||
| 
						 | 
					4af187d0ca | ||
| 
						 | 
					a5d6b22838 | ||
| 
						 | 
					fda0a081f5 | ||
| 
						 | 
					94b7a69741 | ||
| 
						 | 
					6518257129 | ||
| 
						 | 
					dc325188d0 | ||
| 
						 | 
					0b51ab157d | ||
| 
						 | 
					f95de25b15 | ||
| 
						 | 
					f424f2936b | ||
| 
						 | 
					2d6c2ee7c0 | ||
| 
						 | 
					7cd106a10c | ||
| 
						 | 
					0d9e999113 | ||
| 
						 | 
					75710ccabd | ||
| 
						 | 
					0f60115f27 | ||
| 
						 | 
					16a3c85baa | ||
| 
						 | 
					92ff1d3be4 | ||
| 
						 | 
					58441dc49f | ||
| 
						 | 
					dbc5d688e2 | ||
| 
						 | 
					e2a8951f68 | ||
| 
						 | 
					f0f03ad519 | ||
| 
						 | 
					e37575e763 | ||
| 
						 | 
					f4fd481415 | ||
| 
						 | 
					8fca6b7af4 | ||
| 
						 | 
					600e822933 | ||
| 
						 | 
					2028ac8a20 | ||
| 
						 | 
					7bae7d9efd | ||
| 
						 | 
					cb54fb02c1 | ||
| 
						 | 
					7529abb542 | ||
| 
						 | 
					16ac681ed9 | ||
| 
						 | 
					74560ff805 | ||
| 
						 | 
					fe348187cc | ||
| 
						 | 
					fd5315793c | ||
| 
						 | 
					87db463f4e | ||
| 
						 | 
					1225cd31c8 | ||
| 
						 | 
					6998865d7b | ||
| 
						 | 
					b8aec50763 | ||
| 
						 | 
					7efb39d608 | ||
| 
						 | 
					f7c90bc1ff | ||
| 
						 | 
					aee077c1bd | ||
| 
						 | 
					6968275ddf | ||
| 
						 | 
					074ae4fc0d | ||
| 
						 | 
					6cd35ed9c8 | ||
| 
						 | 
					7911e74222 | ||
| 
						 | 
					2fafe2b5d1 | ||
| 
						 | 
					de977819ce | ||
| 
						 | 
					1844beecc3 | ||
| 
						 | 
					cb529bbd63 | ||
| 
						 | 
					25990867e2 | ||
| 
						 | 
					4fbc71c70d | ||
| 
						 | 
					eb21d4fff4 | ||
| 
						 | 
					6d5fc1d743 | ||
| 
						 | 
					e88042b2fa | ||
| 
						 | 
					750b448f75 | ||
| 
						 | 
					14d1dc8749 | ||
| 
						 | 
					8e0340252b | ||
| 
						 | 
					641a16c133 | ||
| 
						 | 
					533d78bffe | ||
| 
						 | 
					ae2c5820a1 | ||
| 
						 | 
					8334504f4e | ||
| 
						 | 
					2260a593bd | ||
| 
						 | 
					7d8af2f99a | ||
| 
						 | 
					46bdcece4d | ||
| 
						 | 
					7387a1d91e | ||
| 
						 | 
					ae4b8078df | ||
| 
						 | 
					60e0c8ea92 | ||
| 
						 | 
					7d3acc0ed6 | ||
| 
						 | 
					2637b33957 | ||
| 
						 | 
					58ccb66659 | ||
| 
						 | 
					634429cf61 | ||
| 
						 | 
					6ac65e603d | ||
| 
						 | 
					03166a745a | ||
| 
						 | 
					4d61ba20ce | ||
| 
						 | 
					751ff677fe | ||
| 
						 | 
					ace60e1898 | ||
| 
						 | 
					876b7f106f | ||
| 
						 | 
					809b6589a1 | ||
| 
						 | 
					02f53ca014 | ||
| 
						 | 
					0b03ddb21b | ||
| 
						 | 
					ea5d4fd3af | ||
| 
						 | 
					e6b73f8cd1 | ||
| 
						 | 
					af232ef729 | ||
| 
						 | 
					2e2f8abfc0 | ||
| 
						 | 
					91a583db27 | ||
| 
						 | 
					dc5cc630ff | ||
| 
						 | 
					c1647a74c5 | ||
| 
						 | 
					721f280966 | ||
| 
						 | 
					258ebb9145 | ||
| 
						 | 
					e914eaf055 | ||
| 
						 | 
					fe54013679 | ||
| 
						 | 
					fdaf2e1594 | ||
| 
						 | 
					f0092ef69b | ||
| 
						 | 
					a88ae7e1d9 | ||
| 
						 | 
					9946f3bdf4 | ||
| 
						 | 
					c747e8d16c | ||
| 
						 | 
					3e402d397e | ||
| 
						 | 
					0350834cd3 | ||
| 
						 | 
					980981c9ee | ||
| 
						 | 
					3c8346f24e | ||
| 
						 | 
					42bd27c24b | ||
| 
						 | 
					4a0f67f3bd | ||
| 
						 | 
					09b6fc4670 | ||
| 
						 | 
					4d9bcd6bcc | ||
| 
						 | 
					cd34b89977 | ||
| 
						 | 
					3151fa3988 | ||
| 
						 | 
					5e58110e19 | ||
| 
						 | 
					e1cdd0f8cc | ||
| 
						 | 
					1f39a0f180 | ||
| 
						 | 
					367c4b14f5 | ||
| 
						 | 
					9c437796d3 | ||
| 
						 | 
					60e22d9703 | ||
| 
						 | 
					ee7362e847 | ||
| 
						 | 
					369f96b80e | ||
| 
						 | 
					7c5ed04ab1 | ||
| 
						 | 
					4779a445e0 | ||
| 
						 | 
					f0f1b7ce9e | ||
| 
						 | 
					7c9157a0ed | ||
| 
						 | 
					522a6cb435 | ||
| 
						 | 
					d0d551d739 | ||
| 
						 | 
					71a123fef7 | ||
| 
						 | 
					3f40c8d7fb | ||
| 
						 | 
					983c2e5499 | ||
| 
						 | 
					eebb4c3ade | ||
| 
						 | 
					50425eac72 | ||
| 
						 | 
					382ff77bbe | ||
| 
						 | 
					bf680fb5d3 | ||
| 
						 | 
					4ed7db4f91 | ||
| 
						 | 
					bf19920d65 | ||
| 
						 | 
					174b5f6686 | ||
| 
						 | 
					4173645b81 | ||
| 
						 | 
					af511f1f55 | ||
| 
						 | 
					83c6080380 | ||
| 
						 | 
					2f0c789ea1 | ||
| 
						 | 
					a9b8f8e8a9 | ||
| 
						 | 
					f92f3eb6fa | ||
| 
						 | 
					89e74dca3e | ||
| 
						 | 
					f2e86d2f8d | ||
| 
						 | 
					623da131e5 | ||
| 
						 | 
					e89ec31ae5 | ||
| 
						 | 
					68a6ed208e | ||
| 
						 | 
					c01b32c4f3 | ||
| 
						 | 
					ee11ff9da9 | ||
| 
						 | 
					ed56d5d6ff | ||
| 
						 | 
					b317ab755c | ||
| 
						 | 
					9819994999 | ||
| 
						 | 
					e9dbaa81d2 | ||
| 
						 | 
					9f9146ffae | ||
| 
						 | 
					9d9732af97 | ||
| 
						 | 
					ebb8fa9787 | ||
| 
						 | 
					9e6abbf4d4 | ||
| 
						 | 
					6032a6d658 | ||
| 
						 | 
					c29ab22e6d | ||
| 
						 | 
					592ac4904c | ||
| 
						 | 
					03ae2ec153 | ||
| 
						 | 
					3bc42d0d37 | ||
| 
						 | 
					12630d3e54 | ||
| 
						 | 
					c9897f99c3 | ||
| 
						 | 
					e66dc14b3a | ||
| 
						 | 
					7a2868c147 | ||
| 
						 | 
					9e0daaee09 | ||
| 
						 | 
					c293c7de93 | ||
| 
						 | 
					49eb5f8563 | ||
| 
						 | 
					674b375b2c | ||
| 
						 | 
					7e94c091eb | ||
| 
						 | 
					5885ccba61 | ||
| 
						 | 
					431ecd3d1a | ||
| 
						 | 
					f6df8ff935 | ||
| 
						 | 
					3fd70f0951 | ||
| 
						 | 
					bebb635d4f | ||
| 
						 | 
					354896bc4b | ||
| 
						 | 
					5ddefff27e | ||
| 
						 | 
					91827eef4f | ||
| 
						 | 
					9c14c09962 | ||
| 
						 | 
					e85a84171f | ||
| 
						 | 
					3a4f86c3d7 | ||
| 
						 | 
					5e75963312 | ||
| 
						 | 
					184d9289b5 | ||
| 
						 | 
					b7ff9577c0 | ||
| 
						 | 
					942a1aaac6 | ||
| 
						 | 
					69f0fe004d | ||
| 
						 | 
					2a04347a42 | ||
| 
						 | 
					1394f1a5c0 | ||
| 
						 | 
					cf4d19a8ea | ||
| 
						 | 
					23b0fe9f8e | ||
| 
						 | 
					1ba718b15e | ||
| 
						 | 
					df5f79ff35 | ||
| 
						 | 
					6d7e8528ea | ||
| 
						 | 
					197bb73a62 | ||
| 
						 | 
					f91e599451 | ||
| 
						 | 
					5b9aa9237c | ||
| 
						 | 
					61f38fab37 | ||
| 
						 | 
					9142f38cbc | ||
| 
						 | 
					e8ed961572 | ||
| 
						 | 
					be11a2a1ad | ||
| 
						 | 
					ea75086300 | ||
| 
						 | 
					9eeefbd79a | ||
| 
						 | 
					c573a98363 | ||
| 
						 | 
					11d7af3f95 | ||
| 
						 | 
					a10b4f61d8 | ||
| 
						 | 
					a0cb7514f1 | ||
| 
						 | 
					b066edc116 | ||
| 
						 | 
					938f5a689e | ||
| 
						 | 
					772f4c26e8 | ||
| 
						 | 
					6b5d151beb | ||
| 
						 | 
					a9176a77e6 | ||
| 
						 | 
					16f409c6a9 | ||
| 
						 | 
					9593c930de | ||
| 
						 | 
					56f33f514b | ||
| 
						 | 
					1ccd544b94 | ||
| 
						 | 
					93c83a2ee2 | ||
| 
						 | 
					f459e32ada | ||
| 
						 | 
					9b640c8e9c | ||
| 
						 | 
					a3228f4997 | ||
| 
						 | 
					715eb69d92 | ||
| 
						 | 
					df2d5cb3d3 | ||
| 
						 | 
					3b189eab64 | ||
| 
						 | 
					609b629c22 | ||
| 
						 | 
					e74365fe38 | ||
| 
						 | 
					46b34833c2 | ||
| 
						 | 
					045c80869d | ||
| 
						 | 
					2ea2e72ddd | ||
| 
						 | 
					1b17e12fd6 | ||
| 
						 | 
					cc5beda0d2 | ||
| 
						 | 
					a363fd926d | ||
| 
						 | 
					21ebede529 | ||
| 
						 | 
					15d67e9191 | ||
| 
						 | 
					b5996f5f02 | ||
| 
						 | 
					83204dc293 | ||
| 
						 | 
					e3f4142d2a | ||
| 
						 | 
					f18ad36b1b | ||
| 
						 | 
					cb25a2ecd6 | ||
| 
						 | 
					741a5036e8 | ||
| 
						 | 
					549ee95f3d | ||
| 
						 | 
					6ae81058aa | ||
| 
						 | 
					267c603824 | ||
| 
						 | 
					a8f583a372 | ||
| 
						 | 
					2b5d90f73a | ||
| 
						 | 
					4139e426fe | ||
| 
						 | 
					a775a89e01 | ||
| 
						 | 
					990f6352e0 | ||
| 
						 | 
					b344702304 | ||
| 
						 | 
					d497612bce | ||
| 
						 | 
					2a3b101bd8 | ||
| 
						 | 
					63e93af421 | ||
| 
						 | 
					ab055b3ebe | ||
| 
						 | 
					a9a013473f | ||
| 
						 | 
					87de1e5766 | ||
| 
						 | 
					894aaef267 | ||
| 
						 | 
					e209e54ffe | ||
| 
						 | 
					7511eadaa7 | ||
| 
						 | 
					6c4906605a | ||
| 
						 | 
					8a9be9d837 | ||
| 
						 | 
					b72098cc71 | ||
| 
						 | 
					defe60e08b | ||
| 
						 | 
					7f852b8af4 | ||
| 
						 | 
					d71c100ca7 | ||
| 
						 | 
					5442c8e86d | ||
| 
						 | 
					cf4901e713 | ||
| 
						 | 
					4b8c1ac2d2 | ||
| 
						 | 
					555e0c0b85 | ||
| 
						 | 
					dc301305de | ||
| 
						 | 
					f1111c135b | ||
| 
						 | 
					3905e92965 | ||
| 
						 | 
					1418ada38f | ||
| 
						 | 
					9256a66b76 | ||
| 
						 | 
					e8c013a778 | ||
| 
						 | 
					fea8242ea7 | ||
| 
						 | 
					7bfb17c209 | ||
| 
						 | 
					e7e4341e70 | ||
| 
						 | 
					6186be4443 | ||
| 
						 | 
					d07f01d7cb | ||
| 
						 | 
					73291a30a0 | ||
| 
						 | 
					a3b129845b | ||
| 
						 | 
					0ff8f58be8 | ||
| 
						 | 
					66292beec9 | ||
| 
						 | 
					bf2af1051f | ||
| 
						 | 
					b6e3020d4c | ||
| 
						 | 
					8f516a1e28 | ||
| 
						 | 
					5f2e287efd | ||
| 
						 | 
					8c0d65cf9f | ||
| 
						 | 
					fa609a5079 | ||
| 
						 | 
					c708ff9708 | ||
| 
						 | 
					2ea90334a3 | ||
| 
						 | 
					eea8aa555f | ||
| 
						 | 
					51a75e1872 | ||
| 
						 | 
					af7ed4322e | ||
| 
						 | 
					7cdd7cf6eb | ||
| 
						 | 
					26aa622afc | ||
| 
						 | 
					84ad161f1e | ||
| 
						 | 
					6efb965dab | ||
| 
						 | 
					8c90a12e0f | ||
| 
						 | 
					2d54e88e74 | ||
| 
						 | 
					16ea5323e0 | ||
| 
						 | 
					7a23ce2367 | ||
| 
						 | 
					e05bc7eb54 | ||
| 
						 | 
					b3a6e25ce0 | ||
| 
						 | 
					b63d41102e | ||
| 
						 | 
					964295b59d | ||
| 
						 | 
					d19db30f3d | ||
| 
						 | 
					d12464fc0e | ||
| 
						 | 
					a96971c8a7 | ||
| 
						 | 
					f6f769503a | ||
| 
						 | 
					82917ac6e3 | ||
| 
						 | 
					a6ffafb1a2 | ||
| 
						 | 
					fb8c529f2e | ||
| 
						 | 
					1ee98e1e66 | ||
| 
						 | 
					81f35f5dd1 | ||
| 
						 | 
					1b402347cd | ||
| 
						 | 
					7599656784 | ||
| 
						 | 
					dccb60ba35 | ||
| 
						 | 
					ae642ceca0 | ||
| 
						 | 
					471b6f9966 | ||
| 
						 | 
					5dd18bac2c | ||
| 
						 | 
					018f4e0891 | ||
| 
						 | 
					e85809a98a | ||
| 
						 | 
					e6e9bd8147 | ||
| 
						 | 
					221645d2ce | ||
| 
						 | 
					2f4a6214a2 | ||
| 
						 | 
					e00a461c26 | ||
| 
						 | 
					c31314be38 | ||
| 
						 | 
					ee142c4be0 | ||
| 
						 | 
					aeacc0b31b | ||
| 
						 | 
					7b4c3bdbcc | ||
| 
						 | 
					910b9cf1fd | ||
| 
						 | 
					b10aaceab0 | ||
| 
						 | 
					169bd812c9 | ||
| 
						 | 
					34767f1e13 | ||
| 
						 | 
					4f642c0843 | ||
| 
						 | 
					4e5889ed59 | ||
| 
						 | 
					a1b848ad76 | ||
| 
						 | 
					dbcc1fad3e | ||
| 
						 | 
					db366558e7 | ||
| 
						 | 
					a23c03fbd0 | ||
| 
						 | 
					ff18b92eb0 | ||
| 
						 | 
					7f148522ab | ||
| 
						 | 
					159c612924 | ||
| 
						 | 
					b95dfd4bdf | ||
| 
						 | 
					e69954af2f | ||
| 
						 | 
					a5ff26f602 | ||
| 
						 | 
					a7536268e1 | ||
| 
						 | 
					541469371a | ||
| 
						 | 
					a13aeaf955 | ||
| 
						 | 
					9cf674cdcb | ||
| 
						 | 
					51c0cf97bc | ||
| 
						 | 
					4cb1f616c5 | ||
| 
						 | 
					645109048b | ||
| 
						 | 
					f969fb69e1 | ||
| 
						 | 
					bfb60fdb84 | ||
| 
						 | 
					2f43cb843e | ||
| 
						 | 
					874fd2aba7 | ||
| 
						 | 
					33d1371186 | ||
| 
						 | 
					d2dd241e6b | ||
| 
						 | 
					4ecadfabf4 | ||
| 
						 | 
					ffd79c6097 | ||
| 
						 | 
					35a8d2a519 | ||
| 
						 | 
					21eab7e9cc | ||
| 
						 | 
					d9605c2856 | ||
| 
						 | 
					70a467d469 | ||
| 
						 | 
					6e8979336d | ||
| 
						 | 
					ee01045db5 | ||
| 
						 | 
					b7f8224588 | ||
| 
						 | 
					ca4c1e4259 | ||
| 
						 | 
					91712add3d | ||
| 
						 | 
					7198dcb416 | ||
| 
						 | 
					08e20e912d | ||
| 
						 | 
					f45571033c | ||
| 
						 | 
					2ac36a0572 | ||
| 
						 | 
					3df1d54847 | ||
| 
						 | 
					f3969b6066 | ||
| 
						 | 
					6222f35bc8 | ||
| 
						 | 
					2f178963c0 | ||
| 
						 | 
					15760b0950 | ||
| 
						 | 
					43a6a70e1e | ||
| 
						 | 
					cd36f1ef5f | ||
| 
						 | 
					cdd7083c86 | ||
| 
						 | 
					8df7364319 | ||
| 
						 | 
					63023722d1 | ||
| 
						 | 
					79c12e5116 | ||
| 
						 | 
					53e16944a1 | ||
| 
						 | 
					7475362c85 | ||
| 
						 | 
					9238b82cde | ||
| 
						 | 
					7049f658ec | ||
| 
						 | 
					701913fb19 | ||
| 
						 | 
					831f41a62b | ||
| 
						 | 
					0ea1da80e7 | ||
| 
						 | 
					06eea74b98 | ||
| 
						 | 
					c8c0e112bc | ||
| 
						 | 
					7417e82c51 | ||
| 
						 | 
					ecc4d80a5a | ||
| 
						 | 
					3df24c52f4 | ||
| 
						 | 
					8a70fb95b5 | ||
| 
						 | 
					d8b45ecd61 | ||
| 
						 | 
					61712bae9c | ||
| 
						 | 
					4ff81a5a25 | ||
| 
						 | 
					08f0e55d8f | ||
| 
						 | 
					080b37cb31 | ||
| 
						 | 
					bbdcd035ba | ||
| 
						 | 
					f9233ef90b | ||
| 
						 | 
					cd3573a4d2 | ||
| 
						 | 
					738fe24e6d | ||
| 
						 | 
					c2e55b5486 | ||
| 
						 | 
					989f0726e3 | ||
| 
						 | 
					bdefd3ba1e | ||
| 
						 | 
					4efcff33bd | ||
| 
						 | 
					8183cc5a8d | ||
| 
						 | 
					f3bda1536d | ||
| 
						 | 
					3b6371e03d | ||
| 
						 | 
					b5d3c87253 | ||
| 
						 | 
					f73b8c550a | ||
| 
						 | 
					5437744126 | ||
| 
						 | 
					5a5e70b001 | ||
| 
						 | 
					348a5bc0a9 | ||
| 
						 | 
					026c64fa01 | ||
| 
						 | 
					e38663c457 | ||
| 
						 | 
					117c741c29 | ||
| 
						 | 
					9bc5bec9f1 | ||
| 
						 | 
					a5f4e4d328 | ||
| 
						 | 
					db0abfde72 | ||
| 
						 | 
					edf263bcb5 | ||
| 
						 | 
					60fba585e3 | ||
| 
						 | 
					ebb6fe5be3 | ||
| 
						 | 
					d91c95bf92 | ||
| 
						 | 
					2007438424 | ||
| 
						 | 
					81423635ad | ||
| 
						 | 
					58d297364a | ||
| 
						 | 
					db902c90c4 | ||
| 
						 | 
					42ccd0f790 | ||
| 
						 | 
					20ec6f574e | ||
| 
						 | 
					b3db367ae7 | ||
| 
						 | 
					8a62c742e6 | ||
| 
						 | 
					b125cbeac9 | ||
| 
						 | 
					3f7a2c2197 | ||
| 
						 | 
					f6248369fe | ||
| 
						 | 
					c83f3ec097 | ||
| 
						 | 
					0cd00da354 | ||
| 
						 | 
					4b7b285aa9 | ||
| 
						 | 
					d63379e777 | ||
| 
						 | 
					b219b146fa | ||
| 
						 | 
					ff90b81ec3 | ||
| 
						 | 
					9120eaef79 | ||
| 
						 | 
					1ccd879916 | ||
| 
						 | 
					f977ace7f8 | ||
| 
						 | 
					c3f4dc0c15 | ||
| 
						 | 
					78eed9b11c | ||
| 
						 | 
					3a4d56afca | ||
| 
						 | 
					63bb93fc07 | ||
| 
						 | 
					5a39a04a79 | ||
| 
						 | 
					2fde34b519 | ||
| 
						 | 
					1ef5c038db | ||
| 
						 | 
					e2459cfb47 | ||
| 
						 | 
					cfffc0bcf1 | ||
| 
						 | 
					7272f43191 | ||
| 
						 | 
					2a7ea27bb7 | ||
| 
						 | 
					32c5b816ae | ||
| 
						 | 
					e54ea7a1d8 | ||
| 
						 | 
					1077efd03a | ||
| 
						 | 
					f9ab91511d | ||
| 
						 | 
					2c3ca2984e | ||
| 
						 | 
					94722e566c | ||
| 
						 | 
					163f7ee85d | ||
| 
						 | 
					52d3470cbe | ||
| 
						 | 
					0bd6e85c61 | ||
| 
						 | 
					e35c6b876f | ||
| 
						 | 
					9a2897e741 | ||
| 
						 | 
					70b2e8179d | ||
| 
						 | 
					5317edc65d | ||
| 
						 | 
					866d83579e | ||
| 
						 | 
					a238391b36 | ||
| 
						 | 
					5e152d30db | ||
| 
						 | 
					57c954783d | ||
| 
						 | 
					b5407ac708 | ||
| 
						 | 
					472ec730b5 | ||
| 
						 | 
					8c819b1f91 | ||
| 
						 | 
					528a516390 | ||
| 
						 | 
					6509e37c84 | ||
| 
						 | 
					649173f661 | ||
| 
						 | 
					1efb0adb35 | ||
| 
						 | 
					88a8e2c1df | ||
| 
						 | 
					bb4ff05d35 | ||
| 
						 | 
					dd3b601c87 | ||
| 
						 | 
					e22d101a62 | ||
| 
						 | 
					4b3c813f5a | ||
| 
						 | 
					67f375bea2 | ||
| 
						 | 
					88ba99b87e | ||
| 
						 | 
					53447e9d0b | ||
| 
						 | 
					c4c86f8671 | ||
| 
						 | 
					658941d26d | ||
| 
						 | 
					e4bf27b01c | ||
| 
						 | 
					7d48b75f81 | ||
| 
						 | 
					5f56bf836c | ||
| 
						 | 
					c0f5f97ddb | ||
| 
						 | 
					15177ac2e9 | ||
| 
						 | 
					8360bc93ac | ||
| 
						 | 
					e0ea844d50 | ||
| 
						 | 
					9675411f35 | ||
| 
						 | 
					e97299fc65 | ||
| 
						 | 
					26a113927e | ||
| 
						 | 
					d0aa7ef590 | ||
| 
						 | 
					5de889419f | ||
| 
						 | 
					0fcbda2da7 | ||
| 
						 | 
					14e33c295f | ||
| 
						 | 
					644ac8caf8 | ||
| 
						 | 
					77189b6e66 | ||
| 
						 | 
					4f8f7f66ee | ||
| 
						 | 
					b099bd97f2 | ||
| 
						 | 
					961c6ea15a | ||
| 
						 | 
					9c97d8f648 | ||
| 
						 | 
					ad7bf80611 | ||
| 
						 | 
					40080b23ae | ||
| 
						 | 
					7acb5c63e0 | ||
| 
						 | 
					fcca9bbab3 | ||
| 
						 | 
					dbb2187425 | ||
| 
						 | 
					82e51f9e81 | ||
| 
						 | 
					4782a76bca | ||
| 
						 | 
					d13788a4ed | ||
| 
						 | 
					e64a0175b1 | ||
| 
						 | 
					4aca94154f | ||
| 
						 | 
					ac5f118dac | ||
| 
						 | 
					a2812ec5eb | ||
| 
						 | 
					70f13f1b62 | ||
| 
						 | 
					77e62a25cb | ||
| 
						 | 
					09345ec786 | ||
| 
						 | 
					bad73baf98 | ||
| 
						 | 
					3602f5aa5d | ||
| 
						 | 
					672b705faf | ||
| 
						 | 
					64e3cdeb2b | ||
| 
						 | 
					909c906080 | ||
| 
						 | 
					71bde11e95 | ||
| 
						 | 
					fc20fbed92 | ||
| 
						 | 
					e6b7c85c37 | ||
| 
						 | 
					b3a92363f8 | ||
| 
						 | 
					e9f2d1aca7 | ||
| 
						 | 
					b4e3dbf331 | ||
| 
						 | 
					c3620786cf | ||
| 
						 | 
					41943746e4 | ||
| 
						 | 
					176e816b8c | ||
| 
						 | 
					50a19bd870 | ||
| 
						 | 
					57b751b994 | ||
| 
						 | 
					77732a8f44 | ||
| 
						 | 
					c47c2e538d | ||
| 
						 | 
					cc5545277d | ||
| 
						 | 
					63353b98cd | ||
| 
						 | 
					4dfc869b8a | ||
| 
						 | 
					b4b1c7d80b | ||
| 
						 | 
					e53c03028f | ||
| 
						 | 
					8680aef42f | ||
| 
						 | 
					c3fd71d643 | ||
| 
						 | 
					30c47d685d | ||
| 
						 | 
					80db682109 | ||
| 
						 | 
					e8e5f66f4c | ||
| 
						 | 
					aaf3d08bcd | ||
| 
						 | 
					61132d6c40 | ||
| 
						 | 
					9cc0645a1e | ||
| 
						 | 
					fc8c6a429e | ||
| 
						 | 
					2f966883d9 | ||
| 
						 | 
					320ba80ca1 | ||
| 
						 | 
					b621d4dd2e | ||
| 
						 | 
					56d927c72d | ||
| 
						 | 
					53afc2e50a | ||
| 
						 | 
					89debac8f6 | ||
| 
						 | 
					f2197fa2d8 | ||
| 
						 | 
					a6a097c111 | ||
| 
						 | 
					c3e28bc924 | ||
| 
						 | 
					8d78fb1f6b | ||
| 
						 | 
					148917d4ca | ||
| 
						 | 
					d8cf9bf942 | ||
| 
						 | 
					d6f5a060ed | ||
| 
						 | 
					692b6ef8ac | ||
| 
						 | 
					ac5f1fe1be | ||
| 
						 | 
					0f35acade1 | ||
| 
						 | 
					56d72ec4c5 | ||
| 
						 | 
					71d51c160d | ||
| 
						 | 
					0b58e505ee | ||
| 
						 | 
					2a6c615bec | ||
| 
						 | 
					ab8c5a0b5f | ||
| 
						 | 
					68c35feaea | ||
| 
						 | 
					88d0c2ca0f | ||
| 
						 | 
					398833ebe3 | ||
| 
						 | 
					358f5a03bf | ||
| 
						 | 
					fba1fdabe4 | ||
| 
						 | 
					d42afd21e5 | ||
| 
						 | 
					20ada86761 | ||
| 
						 | 
					3b353f1855 | ||
| 
						 | 
					1467ab4f93 | ||
| 
						 | 
					7e65c2bdad | ||
| 
						 | 
					84a4e3e98a | ||
| 
						 | 
					bcbeedb001 | ||
| 
						 | 
					e04b103b5d | ||
| 
						 | 
					ac75b94679 | ||
| 
						 | 
					d3bb06cfd6 | ||
| 
						 | 
					5cd729c4c1 | ||
| 
						 | 
					c9fd2bdf39 | ||
| 
						 | 
					e4be5992b3 | ||
| 
						 | 
					2ac4988f1b | ||
| 
						 | 
					19f14adb9e | ||
| 
						 | 
					86de039492 | ||
| 
						 | 
					2360164e4f | ||
| 
						 | 
					c93ddceadb | ||
| 
						 | 
					cd19dec44a | ||
| 
						 | 
					53ba9c800a | ||
| 
						 | 
					cabbaded68 | ||
| 
						 | 
					9bb589f827 | ||
| 
						 | 
					c3a06686c2 | ||
| 
						 | 
					7d57f87007 | ||
| 
						 | 
					4cc4a9d38b | ||
| 
						 | 
					02c7cd0194 | ||
| 
						 | 
					696efcb9e2 | ||
| 
						 | 
					6e9cde8ac1 | ||
| 
						 | 
					a9fae49671 | ||
| 
						 | 
					440af9fd64 | ||
| 
						 | 
					347721ae40 | ||
| 
						 | 
					daea91044c | ||
| 
						 | 
					4ed3f2c662 | ||
| 
						 | 
					3641c8f60a | ||
| 
						 | 
					e4b68cd940 | ||
| 
						 | 
					b8c936e2fe | ||
| 
						 | 
					83cd519702 | ||
| 
						 | 
					54b54f85f3 | ||
| 
						 | 
					ccd874fe4e | ||
| 
						 | 
					9dc7e8ed3a | ||
| 
						 | 
					485099fd6e | ||
| 
						 | 
					d359c6b43e | ||
| 
						 | 
					d9ed7a77f8 | ||
| 
						 | 
					4238a4ca6a | ||
| 
						 | 
					0902a5a981 | ||
| 
						 | 
					f3192303ab | ||
| 
						 | 
					bef5bd72c2 | ||
| 
						 | 
					b6175e4296 | ||
| 
						 | 
					3858b2e177 | ||
| 
						 | 
					9a76e77981 | ||
| 
						 | 
					8182d640cd | ||
| 
						 | 
					1c6fda1a5c | ||
| 
						 | 
					c51db1cf2f | ||
| 
						 | 
					4e7930fc4c | ||
| 
						 | 
					3563f8ccdb | ||
| 
						 | 
					575af763f6 | ||
| 
						 | 
					8b16b9b246 | ||
| 
						 | 
					01aab66667 | ||
| 
						 | 
					aa5c987a94 | ||
| 
						 | 
					75229332c8 | ||
| 
						 | 
					9d5b1ba838 | ||
| 
						 | 
					f27b225b34 | ||
| 
						 | 
					3c523d66e9 | ||
| 
						 | 
					1144c27c54 | ||
| 
						 | 
					b442b21d3f | ||
| 
						 | 
					746ff5307d | ||
| 
						 | 
					ef85b24d8f | ||
| 
						 | 
					c55d93512b | ||
| 
						 | 
					2e38f9ba61 | ||
| 
						 | 
					1cadff8e58 | ||
| 
						 | 
					d1eba60ba8 | ||
| 
						 | 
					057dccad8f | ||
| 
						 | 
					4285200b4b | ||
| 
						 | 
					73c2fbbc2a | ||
| 
						 | 
					37b7e170fa | ||
| 
						 | 
					b032d94877 | ||
| 
						 | 
					9476016741 | ||
| 
						 | 
					7a1c9c7798 | ||
| 
						 | 
					c7fb7b4451 | ||
| 
						 | 
					67c474fc7a | ||
| 
						 | 
					4e8154cf8a | ||
| 
						 | 
					9582d3c623 | ||
| 
						 | 
					0079500713 | ||
| 
						 | 
					55af6ce834 | ||
| 
						 | 
					3e82fdc125 | ||
| 
						 | 
					7344a6cfc0 | ||
| 
						 | 
					0aded71343 | ||
| 
						 | 
					7663b1e703 | ||
| 
						 | 
					282546c03f | ||
| 
						 | 
					f4bc89d1c0 | ||
| 
						 | 
					fa277c3797 | ||
| 
						 | 
					c0c8ab25e6 | ||
| 
						 | 
					b685bf3026 | ||
| 
						 | 
					ce31db09e4 | ||
| 
						 | 
					624a6cf619 | ||
| 
						 | 
					587aa87d28 | ||
| 
						 | 
					88813c4f87 | ||
| 
						 | 
					dacbe29771 | ||
| 
						 | 
					244833cfa1 | ||
| 
						 | 
					05e7f974e3 | ||
| 
						 | 
					0dbef65a73 | ||
| 
						 | 
					9106228787 | ||
| 
						 | 
					6ae3bdb25c | ||
| 
						 | 
					310bcec260 | ||
| 
						 | 
					8c4cc4e671 | ||
| 
						 | 
					c6eaaa83ed | ||
| 
						 | 
					8f598d6f96 | ||
| 
						 | 
					20bc323d17 | ||
| 
						 | 
					8b0bcf4db9 | ||
| 
						 | 
					8955e6f536 | ||
| 
						 | 
					f8ddea6452 | ||
| 
						 | 
					987e04086d | ||
| 
						 | 
					85f2acbf52 | ||
| 
						 | 
					1acf4c3ab7 | ||
| 
						 | 
					07a3158fba | ||
| 
						 | 
					2f8bed9d82 | ||
| 
						 | 
					a490937cd9 | ||
| 
						 | 
					8ee5942481 | ||
| 
						 | 
					93b469885a | ||
| 
						 | 
					d8d1de2dcb | ||
| 
						 | 
					ab224514f0 | ||
| 
						 | 
					75179de8da | ||
| 
						 | 
					c28df14e6b | ||
| 
						 | 
					b73855b193 | ||
| 
						 | 
					2093ab2baa | ||
| 
						 | 
					a0f40042cb | ||
| 
						 | 
					3254c2c477 | ||
| 
						 | 
					0a8eb9e3ba | ||
| 
						 | 
					70e0c6f9ef | ||
| 
						 | 
					a8a78d4525 | ||
| 
						 | 
					57e6ee963d | ||
| 
						 | 
					ce6bfb8420 | ||
| 
						 | 
					f0672bdc59 | ||
| 
						 | 
					23de953fbd | ||
| 
						 | 
					03c496bdd8 | ||
| 
						 | 
					d5ee6cf521 | ||
| 
						 | 
					fb7981e053 | ||
| 
						 | 
					846123ecab | ||
| 
						 | 
					373cb444fe | ||
| 
						 | 
					90f212df92 | ||
| 
						 | 
					12286e4246 | ||
| 
						 | 
					aa60c1f36a | ||
| 
						 | 
					c731f01067 | ||
| 
						 | 
					6c9c1cdb30 | ||
| 
						 | 
					9ba2b40e87 | ||
| 
						 | 
					7a3d055012 | ||
| 
						 | 
					0824f45e29 | ||
| 
						 | 
					4debe3446c | ||
| 
						 | 
					07fe9bcdf6 | ||
| 
						 | 
					6a557a73f5 | ||
| 
						 | 
					8d1cfe0c56 | ||
| 
						 | 
					a3a42eebea | ||
| 
						 | 
					76be8006a4 | ||
| 
						 | 
					bfcfd58259 | ||
| 
						 | 
					914a4360e7 | ||
| 
						 | 
					8c31874eeb | ||
| 
						 | 
					ef7afeb2ea | ||
| 
						 | 
					4067f883a2 | ||
| 
						 | 
					c8974fffbe | ||
| 
						 | 
					b75fb8dc9e | ||
| 
						 | 
					57356781a9 | ||
| 
						 | 
					e43eab5fd6 | ||
| 
						 | 
					894cd0e022 | ||
| 
						 | 
					db2c63fffc | ||
| 
						 | 
					60e0f32f1a | ||
| 
						 | 
					e731996a68 | ||
| 
						 | 
					2f69cd4209 | ||
| 
						 | 
					fd59de25c5 | ||
| 
						 | 
					af12c3d41a | ||
| 
						 | 
					e78a3d1c19 | ||
| 
						 | 
					c099ec05ee | ||
| 
						 | 
					a20612478e | ||
| 
						 | 
					f778e8bbd1 | ||
| 
						 | 
					7203c046f9 | ||
| 
						 | 
					754b61c593 | ||
| 
						 | 
					927e9e4e4d | ||
| 
						 | 
					699f9622d7 | ||
| 
						 | 
					765eb84c33 | ||
| 
						 | 
					12a1849090 | 
@@ -1,4 +1,4 @@
 | 
			
		||||
image: freebsd/12.x
 | 
			
		||||
image: freebsd/14.x
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
@@ -9,3 +9,4 @@ tasks:
 | 
			
		||||
    gmake
 | 
			
		||||
    gmake test
 | 
			
		||||
    sudo gmake install
 | 
			
		||||
    sudo gmake uninstall
 | 
			
		||||
 
 | 
			
		||||
@@ -19,3 +19,8 @@ tasks:
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
    sudo ninja install
 | 
			
		||||
- meson_min: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
 | 
			
		||||
    cd build_meson_min
 | 
			
		||||
    ninja
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
image: openbsd/latest
 | 
			
		||||
image: openbsd/7.4
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
@@ -11,6 +11,7 @@ tasks:
 | 
			
		||||
    gmake test
 | 
			
		||||
    doas gmake install
 | 
			
		||||
    gmake test-install
 | 
			
		||||
    doas gmake uninstall
 | 
			
		||||
- meson_min: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
 | 
			
		||||
@@ -29,4 +30,3 @@ tasks:
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
    doas ninja install
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								.github/cosmo/build
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.github/cosmo/build
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
set -eux
 | 
			
		||||
 | 
			
		||||
COSMO_DIR="/sc/cosmocc"
 | 
			
		||||
 | 
			
		||||
# build x86_64
 | 
			
		||||
X86_64_CC="/sc/cosmocc/bin/x86_64-unknown-cosmo-cc"
 | 
			
		||||
X86_64_AR="/sc/cosmocc/bin/x86_64-unknown-cosmo-ar"
 | 
			
		||||
mkdir -p /sc/cosmocc/x86_64
 | 
			
		||||
make -j CC="$X86_64_CC" AR="$X86_64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
 | 
			
		||||
cp build/janet /sc/cosmocc/x86_64/janet
 | 
			
		||||
make clean
 | 
			
		||||
 | 
			
		||||
# build aarch64
 | 
			
		||||
AARCH64_CC="/sc/cosmocc/bin/aarch64-unknown-cosmo-cc"
 | 
			
		||||
AARCH64_AR="/sc/cosmocc/bin/aarch64-unknown-cosmo-ar"
 | 
			
		||||
mkdir -p /sc/cosmocc/aarch64
 | 
			
		||||
make -j CC="$AARCH64_CC" AR="$AARCH64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
 | 
			
		||||
cp build/janet /sc/cosmocc/aarch64/janet
 | 
			
		||||
make clean
 | 
			
		||||
 | 
			
		||||
# fat binary
 | 
			
		||||
apefat () {
 | 
			
		||||
    OUTPUT="$1"
 | 
			
		||||
    OLDNAME_X86_64="$(basename -- "$2")"
 | 
			
		||||
    OLDNAME_AARCH64="$(basename -- "$3")"
 | 
			
		||||
    TARG_FOLD="$(dirname "$OUTPUT")"
 | 
			
		||||
    "$COSMO_DIR/bin/apelink" -l "$COSMO_DIR/bin/ape-x86_64.elf" \
 | 
			
		||||
        -l "$COSMO_DIR/bin/ape-aarch64.elf" \
 | 
			
		||||
        -M "$COSMO_DIR/bin/ape-m1.c" \
 | 
			
		||||
        -o "$OUTPUT" \
 | 
			
		||||
        "$2" \
 | 
			
		||||
        "$3"
 | 
			
		||||
    cp "$2" "$TARG_FOLD/$OLDNAME_X86_64.x86_64"
 | 
			
		||||
    cp "$3" "$TARG_FOLD/$OLDNAME_AARCH64.aarch64"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apefat /sc/cosmocc/janet.com /sc/cosmocc/x86_64/janet /sc/cosmocc/aarch64/janet
 | 
			
		||||
							
								
								
									
										21
									
								
								.github/cosmo/setup
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.github/cosmo/setup
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
sudo apt update
 | 
			
		||||
sudo apt-get install -y ca-certificates libssl-dev\
 | 
			
		||||
    qemu qemu-utils qemu-user-static\
 | 
			
		||||
    texinfo groff\
 | 
			
		||||
    cmake ninja-build bison zip\
 | 
			
		||||
    pkg-config build-essential autoconf re2c
 | 
			
		||||
 | 
			
		||||
# download cosmocc
 | 
			
		||||
cd /sc
 | 
			
		||||
wget https://github.com/jart/cosmopolitan/releases/download/3.3.3/cosmocc-3.3.3.zip
 | 
			
		||||
mkdir -p cosmocc
 | 
			
		||||
cd cosmocc
 | 
			
		||||
unzip ../cosmocc-3.3.3.zip
 | 
			
		||||
 | 
			
		||||
# register
 | 
			
		||||
cd /sc/cosmocc
 | 
			
		||||
sudo cp ./bin/ape-x86_64.elf /usr/bin/ape
 | 
			
		||||
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
 | 
			
		||||
							
								
								
									
										42
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								.github/workflows/codeql.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
name: "CodeQL"
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [ "master" ]
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches: [ "master" ]
 | 
			
		||||
  schedule:
 | 
			
		||||
    - cron: "2 7 * * 4"
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  analyze:
 | 
			
		||||
    name: Analyze
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    permissions:
 | 
			
		||||
      actions: read
 | 
			
		||||
      contents: read
 | 
			
		||||
      security-events: write
 | 
			
		||||
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        language: [ cpp ]
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
      - name: Initialize CodeQL
 | 
			
		||||
        uses: github/codeql-action/init@v3
 | 
			
		||||
        with:
 | 
			
		||||
          languages: ${{ matrix.language }}
 | 
			
		||||
          queries: +security-and-quality
 | 
			
		||||
          tools: linked
 | 
			
		||||
 | 
			
		||||
      - name: Autobuild
 | 
			
		||||
        uses: github/codeql-action/autobuild@v3
 | 
			
		||||
 | 
			
		||||
      - name: Perform CodeQL Analysis
 | 
			
		||||
        uses: github/codeql-action/analyze@v3
 | 
			
		||||
        with:
 | 
			
		||||
          category: "/language:${{ matrix.language }}"
 | 
			
		||||
							
								
								
									
										58
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -17,7 +17,7 @@ jobs:
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ ubuntu-latest, macos-latest ]
 | 
			
		||||
        os: [ ubuntu-latest, macos-13 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
@@ -39,6 +39,35 @@ jobs:
 | 
			
		||||
            build/c/janet.c
 | 
			
		||||
            build/c/shell.c
 | 
			
		||||
 | 
			
		||||
  release-arm:
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write  # for softprops/action-gh-release to create GitHub release
 | 
			
		||||
    name: Build release binaries
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ 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 }}-aarch64.tar.gz
 | 
			
		||||
      - name: Draft the release
 | 
			
		||||
        uses: softprops/action-gh-release@v1
 | 
			
		||||
        with:
 | 
			
		||||
          draft: true
 | 
			
		||||
          files: |
 | 
			
		||||
            build/*.gz
 | 
			
		||||
            build/janet.h
 | 
			
		||||
            build/c/janet.c
 | 
			
		||||
            build/c/shell.c
 | 
			
		||||
 | 
			
		||||
  release-windows:
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write  # for softprops/action-gh-release to create GitHub release
 | 
			
		||||
@@ -60,3 +89,30 @@ jobs:
 | 
			
		||||
            ./dist/*.zip
 | 
			
		||||
            ./*.zip
 | 
			
		||||
            ./*.msi
 | 
			
		||||
 | 
			
		||||
  release-cosmo:
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write  # for softprops/action-gh-release to create GitHub release
 | 
			
		||||
    name: Build release binaries for Cosmo
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: create build folder
 | 
			
		||||
        run: |
 | 
			
		||||
          sudo mkdir -p /sc
 | 
			
		||||
          sudo chmod -R 0777 /sc
 | 
			
		||||
      - name: setup Cosmopolitan Libc
 | 
			
		||||
        run: bash ./.github/cosmo/setup
 | 
			
		||||
      - name: Set the version
 | 
			
		||||
        run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set the platform
 | 
			
		||||
        run: echo "platform=cosmo" >> $GITHUB_ENV
 | 
			
		||||
      - name: build Janet APE binary
 | 
			
		||||
        run: bash ./.github/cosmo/build
 | 
			
		||||
      - name: push binary to github
 | 
			
		||||
        uses: softprops/action-gh-release@v1
 | 
			
		||||
        with:
 | 
			
		||||
          draft: true
 | 
			
		||||
          files: |
 | 
			
		||||
            /sc/cosmocc/janet.com
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										105
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							@@ -12,7 +12,7 @@ jobs:
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ ubuntu-latest, macos-latest ]
 | 
			
		||||
        os: [ ubuntu-latest, macos-latest, macos-13 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
@@ -23,7 +23,10 @@ jobs:
 | 
			
		||||
 | 
			
		||||
  test-windows:
 | 
			
		||||
    name: Build and test on Windows
 | 
			
		||||
    runs-on: windows-latest
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ windows-latest, windows-2019 ]
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
@@ -35,3 +38,101 @@ jobs:
 | 
			
		||||
      - name: Test the project
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: build_win test
 | 
			
		||||
      - name: Test installer build
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: build_win dist
 | 
			
		||||
 | 
			
		||||
  test-windows-min:
 | 
			
		||||
    name: Build and test on Windows Minimal build
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ windows-2019 ]
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Setup MSVC
 | 
			
		||||
        uses: ilammy/msvc-dev-cmd@v1
 | 
			
		||||
      - name: Setup Python
 | 
			
		||||
        uses: actions/setup-python@v2
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: '3.x'
 | 
			
		||||
      - name: Install Python Dependencies
 | 
			
		||||
        run: pip install meson ninja
 | 
			
		||||
      - name: Build
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: |
 | 
			
		||||
          meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
 | 
			
		||||
          cd build_meson_min
 | 
			
		||||
          ninja
 | 
			
		||||
 | 
			
		||||
  test-mingw:
 | 
			
		||||
    name: Build on Windows with Mingw
 | 
			
		||||
    runs-on: windows-latest
 | 
			
		||||
    defaults:
 | 
			
		||||
      run:
 | 
			
		||||
        shell: msys2 {0}
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        msystem: [ UCRT64, CLANG64 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Setup Mingw
 | 
			
		||||
        uses: msys2/setup-msys2@v2
 | 
			
		||||
        with:
 | 
			
		||||
          msystem: ${{ matrix.msystem }}
 | 
			
		||||
          update: true
 | 
			
		||||
          install: >-
 | 
			
		||||
            base-devel
 | 
			
		||||
            git
 | 
			
		||||
            gcc
 | 
			
		||||
      - name: Build
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: make -j4 CC=gcc
 | 
			
		||||
      - name: Test
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: make -j4 CC=gcc test
 | 
			
		||||
 | 
			
		||||
  test-mingw-linux:
 | 
			
		||||
    name: Build and test with Mingw on Linux + Wine
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Setup Mingw and wine
 | 
			
		||||
        run: |
 | 
			
		||||
          sudo dpkg --add-architecture i386
 | 
			
		||||
          sudo apt-get update
 | 
			
		||||
          sudo apt-get install libstdc++6:i386 libgcc-s1:i386
 | 
			
		||||
          sudo apt-get install gcc-mingw-w64-x86-64-win32 wine wine32 wine64
 | 
			
		||||
      - name: Compile the project
 | 
			
		||||
        run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine
 | 
			
		||||
      - name: Test the project
 | 
			
		||||
        run: make test UNAME=MINGW RUN=wine VERBOSE=1
 | 
			
		||||
 | 
			
		||||
  test-arm-linux:
 | 
			
		||||
    name: Build and test ARM32 cross compilation
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Setup qemu and cross compiler
 | 
			
		||||
        run: |
 | 
			
		||||
          sudo apt-get update
 | 
			
		||||
          sudo apt-get install gcc-arm-linux-gnueabi qemu-user
 | 
			
		||||
      - name: Compile the project
 | 
			
		||||
        run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc
 | 
			
		||||
      - name: Test the project
 | 
			
		||||
        run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test VERBOSE=1
 | 
			
		||||
 | 
			
		||||
  test-s390x-linux:
 | 
			
		||||
    name: Build and test s390x in qemu
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout the repository
 | 
			
		||||
      uses: actions/checkout@master
 | 
			
		||||
    - name: Enable qemu
 | 
			
		||||
      run: docker run --privileged --rm tonistiigi/binfmt --install s390x
 | 
			
		||||
    - name: Build and run on emulated architecture
 | 
			
		||||
      run: docker run --rm -v .:/janet --platform linux/s390x alpine sh -c "apk update && apk add --no-interactive git build-base && cd /janet && make -j3 && make test"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -34,8 +34,11 @@ local
 | 
			
		||||
 | 
			
		||||
# Common test files I use.
 | 
			
		||||
temp.janet
 | 
			
		||||
temp*.janet
 | 
			
		||||
temp.c
 | 
			
		||||
temp*janet
 | 
			
		||||
temp*.c
 | 
			
		||||
scratch.janet
 | 
			
		||||
scratch.c
 | 
			
		||||
 | 
			
		||||
# Emscripten
 | 
			
		||||
*.bc
 | 
			
		||||
@@ -45,6 +48,8 @@ janet.wasm
 | 
			
		||||
# Generated files
 | 
			
		||||
*.gen.h
 | 
			
		||||
*.gen.c
 | 
			
		||||
*.tmp
 | 
			
		||||
temp.*
 | 
			
		||||
 | 
			
		||||
# Generate test files
 | 
			
		||||
*.out
 | 
			
		||||
@@ -57,6 +62,7 @@ xxd.exe
 | 
			
		||||
# VSCode
 | 
			
		||||
.vs
 | 
			
		||||
.clangd
 | 
			
		||||
.cache
 | 
			
		||||
 | 
			
		||||
# Swap files
 | 
			
		||||
*.swp
 | 
			
		||||
@@ -68,10 +74,13 @@ tags
 | 
			
		||||
vgcore.*
 | 
			
		||||
*.out.*
 | 
			
		||||
 | 
			
		||||
# Wix artifacts
 | 
			
		||||
# WiX artifacts
 | 
			
		||||
*.msi
 | 
			
		||||
*.wixpdb
 | 
			
		||||
 | 
			
		||||
# Makefile config
 | 
			
		||||
/config.mk
 | 
			
		||||
 | 
			
		||||
# Created by https://www.gitignore.io/api/c
 | 
			
		||||
 | 
			
		||||
### C ###
 | 
			
		||||
@@ -119,6 +128,9 @@ vgcore.*
 | 
			
		||||
*.idb
 | 
			
		||||
*.pdb
 | 
			
		||||
 | 
			
		||||
# GGov
 | 
			
		||||
*.gcov
 | 
			
		||||
 | 
			
		||||
# Kernel Module Compile Results
 | 
			
		||||
*.mod*
 | 
			
		||||
*.cmd
 | 
			
		||||
@@ -127,6 +139,9 @@ Module.symvers
 | 
			
		||||
Mkfile.old
 | 
			
		||||
dkms.conf
 | 
			
		||||
 | 
			
		||||
# Coverage files
 | 
			
		||||
*.cov
 | 
			
		||||
 | 
			
		||||
# End of https://www.gitignore.io/api/c
 | 
			
		||||
 | 
			
		||||
# Created by https://www.gitignore.io/api/cmake
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										215
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										215
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,12 +1,217 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## 1.38.0 - 2025-03-18
 | 
			
		||||
- Add `bundle/replace`
 | 
			
		||||
- Add CLI flags for the `bundle/` module to install and manage bundles.
 | 
			
		||||
- Improve `?` peg special termination behavior
 | 
			
		||||
- Add IEEE hex floats to grammar.
 | 
			
		||||
- Add buffer peg literal support
 | 
			
		||||
- Improve `split` peg special edge case behavior
 | 
			
		||||
- Add Arm64 .msi support
 | 
			
		||||
- Add `no-reuse` argument to `net/listen` to disable reusing server sockets
 | 
			
		||||
- Add `struct/rawget`
 | 
			
		||||
- Fix `deep=` and `deep-not=` to better handle degenerate cases with mutable table keys
 | 
			
		||||
- Long strings will now dedent on `\r\n` instead of just `\n`.
 | 
			
		||||
- Add `ev/to-file` for synchronous resource operations
 | 
			
		||||
- Improve `file/open` error message by including path
 | 
			
		||||
 | 
			
		||||
## 1.37.1 - 2024-12-05
 | 
			
		||||
- Fix meson cross compilation
 | 
			
		||||
- Update timeout documentation for networking APIs: timeouts raise errors and do not return nil.
 | 
			
		||||
- Add `janet_addtimeout_nil(double sec);` to the C API.
 | 
			
		||||
- Change string hashing.
 | 
			
		||||
- Fix string equality bug.
 | 
			
		||||
- Add `assertf`
 | 
			
		||||
- Change how JANET_PROFILE is loaded to allow more easily customizing the environment.
 | 
			
		||||
- Add `*repl-prompt*` dynamic binding to allow customizing the built in repl.
 | 
			
		||||
- Add multiple path support in the `JANET_PATH` environment variables. This lets
 | 
			
		||||
  user more easily import modules from many directories.
 | 
			
		||||
- Add `nth` and `only-tags` PEG specials to select from sub-captures while
 | 
			
		||||
  dropping the rest.
 | 
			
		||||
 | 
			
		||||
## 1.36.0 - 2024-09-07
 | 
			
		||||
- Improve error messages in `bundle/add*` functions.
 | 
			
		||||
- Add CI testing and verify tests pass on the s390x architecture.
 | 
			
		||||
- Save `:source-form` in environment entries when `*debug*` is set.
 | 
			
		||||
- Add experimental `filewatch/` module for listening to file system changes on Linux and Windows.
 | 
			
		||||
- Add `bundle/who-is` to query which bundle a file on disk was installed by.
 | 
			
		||||
- Add `geomean` function
 | 
			
		||||
- Add `:R` and `:W` flags to `os/pipe` to create blocking pipes on Posix and Windows systems.
 | 
			
		||||
  These streams cannot be directly read to and written from, but can be passed to subprocesses.
 | 
			
		||||
- Add `array/join`
 | 
			
		||||
- Add `tuple/join`
 | 
			
		||||
- Add `bundle/add-bin` to make installing scripts easier. This also establishes a packaging convention for it.
 | 
			
		||||
- Fix marshalling weak tables and weak arrays.
 | 
			
		||||
- Fix bug in `ev/` module that could accidentally close sockets on accident.
 | 
			
		||||
- Expose C functions for constructing weak tables in janet.h
 | 
			
		||||
- Let range take non-integer values.
 | 
			
		||||
 | 
			
		||||
## 1.35.2 - 2024-06-16
 | 
			
		||||
- Fix some documentation typos.
 | 
			
		||||
- Allow using `:only` in import without quoting.
 | 
			
		||||
 | 
			
		||||
## 1.35.0 - 2024-06-15
 | 
			
		||||
- Add `:only` argument to `import` to allow for easier control over imported bindings.
 | 
			
		||||
- Add extra optional `env` argument to `eval` and `eval-string`.
 | 
			
		||||
- Allow naming function literals with a keyword. This allows better stacktraces for macros without
 | 
			
		||||
  accidentally adding new bindings.
 | 
			
		||||
- Add `bundle/` module for managing packages within Janet. This should replace the jpm packaging
 | 
			
		||||
  format eventually and is much simpler and amenable to more complicated builds.
 | 
			
		||||
- Add macros `ev/with-lock`, `ev/with-rlock`, and `ev/with-wlock` for using mutexes and rwlocks.
 | 
			
		||||
- Add `with-env`
 | 
			
		||||
- Add *module-make-env* dynamic binding
 | 
			
		||||
- Add buffer/format-at
 | 
			
		||||
- Add long form command line options for readable CLI usage
 | 
			
		||||
- Fix bug with `net/accept-loop` that would sometimes miss connections.
 | 
			
		||||
 | 
			
		||||
## 1.34.0 - 2024-03-22
 | 
			
		||||
- Add a new (split) PEG special by @ianthehenry
 | 
			
		||||
- Add buffer/push-* sized int and float by @pnelson
 | 
			
		||||
- Documentation improvements: @amano-kenji, @llmII, @MaxGyver83, @pepe, @sogaiu.
 | 
			
		||||
- Expose _exit to skip certain cleanup with os/exit.
 | 
			
		||||
- Swap set / body order for each by @sogaiu.
 | 
			
		||||
- Abort on assert failure instead of exit.
 | 
			
		||||
- Fix: os/proc-wait by @llmII.
 | 
			
		||||
- Fix macex1 to keep syntax location for all tuples.
 | 
			
		||||
- Restore if-let tail calls.
 | 
			
		||||
- Don't try and resume fibers that can't be resumed.
 | 
			
		||||
- Register stream on unmarshal.
 | 
			
		||||
- Fix asm roundtrip issue.
 | 
			
		||||
 | 
			
		||||
## 1.33.0 - 2024-01-07
 | 
			
		||||
- Add more + and * keywords to default-peg-grammar by @sogaiu.
 | 
			
		||||
- Use libc strlen in janet_buffer_push_cstring by @williewillus.
 | 
			
		||||
- Be a bit safer with reference counting.
 | 
			
		||||
- Add support for atomic loads in Janet's atomic abstraction.
 | 
			
		||||
- Fix poll event loop CPU usage issue.
 | 
			
		||||
- Add ipv6, shared, and cryptorand options to meson.
 | 
			
		||||
- Add more ipv6 feature detection.
 | 
			
		||||
- Fix loop for forever loop.
 | 
			
		||||
- Cleaned up unused NetStateConnect, fixed janet_async_end() ev refcount by @zevv.
 | 
			
		||||
- Fix warnings w/ MSVC and format.
 | 
			
		||||
- Fix marshal_one_env w/ JANET_MARSHAL_UNSAFE.
 | 
			
		||||
- Fix `(default)`.
 | 
			
		||||
- Fix cannot marshal fiber with c stackframe, in a dynamic way that is fairly conservative.
 | 
			
		||||
- Fix typo for SIGALARM in os/proc-kill.
 | 
			
		||||
- Prevent bytecode optimization from remove mk* instructions.
 | 
			
		||||
- Fix arity typo in peg.c by @pepe.
 | 
			
		||||
- Update Makefile for MinGW.
 | 
			
		||||
- Fix canceling waiting fiber.
 | 
			
		||||
- Add a new (sub) PEG special by @ianthehenry.
 | 
			
		||||
- Fix if net/server's handler has incorrect arity.
 | 
			
		||||
- Fix macex raising on ().
 | 
			
		||||
 | 
			
		||||
## 1.32.1 - 2023-10-15
 | 
			
		||||
- Fix return value from C function `janet_dobytes` when called on Janet functions that yield to event loop.
 | 
			
		||||
- Change C API for event loop interaction - get rid of JanetListener and instead use `janet_async_start` and `janet_async_end`.
 | 
			
		||||
- Rework event loop to make fewer system calls on kqueue and epoll.
 | 
			
		||||
- Expose atomic refcount abstraction in janet.h
 | 
			
		||||
- Add `array/weak` for weak references in arrays
 | 
			
		||||
- Add support for weak tables via `table/weak`, `table/weak-keys`, and `table/weak-values`.
 | 
			
		||||
- Fix compiler bug with using the result of `(break x)` expression in some contexts.
 | 
			
		||||
- Rework internal event loop code to be better behaved on Windows
 | 
			
		||||
- Update meson build to work better on windows
 | 
			
		||||
 | 
			
		||||
## 1.31.0 - 2023-09-17
 | 
			
		||||
- Report line and column when using `janet_dobytes`
 | 
			
		||||
- Add `:unless` loop modifier
 | 
			
		||||
- Allow calling `reverse` on generators.
 | 
			
		||||
- Improve performance of a number of core functions including `partition`, `mean`, `keys`, `values`, `pairs`, `interleave`.
 | 
			
		||||
- Add `lengthable?`
 | 
			
		||||
- Add `os/sigaction`
 | 
			
		||||
- Change `every?` and `any?` to behave like the functional versions of the `and` and `or` macros.
 | 
			
		||||
- Fix bug with garbage collecting threaded abstract types.
 | 
			
		||||
- Add `:signal` to the `sandbox` function to allow intercepting signals.
 | 
			
		||||
 | 
			
		||||
## 1.30.0 - 2023-08-05
 | 
			
		||||
- Change indexing of `array/remove` to start from -1 at the end instead of -2.
 | 
			
		||||
- Add new string escape sequences `\\a`, `\\b`, `\\?`, and `\\'`.
 | 
			
		||||
- Fix bug with marshalling channels
 | 
			
		||||
- Add `div` for floored division
 | 
			
		||||
- Make `div` and `mod` variadic
 | 
			
		||||
- Support `bnot` for integer types.
 | 
			
		||||
- Define `(mod x 0)` as `x`
 | 
			
		||||
- Add `ffi/pointer-cfunction` to convert pointers to cfunctions
 | 
			
		||||
 | 
			
		||||
## 1.29.1 - 2023-06-19
 | 
			
		||||
- Add support for passing booleans to PEGs for "always" and "never" matching.
 | 
			
		||||
- Allow dictionary types for `take` and `drop`
 | 
			
		||||
- Fix bug with closing channels while other fibers were waiting on them - `ev/take`, `ev/give`, and `ev/select`  will now return the correct (documented) value when another fiber closes the channel.
 | 
			
		||||
- Add `ffi/calling-conventions` to show all available calling conventions for FFI.
 | 
			
		||||
- Add `net/setsockopt`
 | 
			
		||||
- Add `signal` argument to `os/proc-kill` to send signals besides `SIGKILL` on Posix.
 | 
			
		||||
- Add `source` argument to `os/clock` to get different time sources.
 | 
			
		||||
- Various combinator functions now are variadic like `map`
 | 
			
		||||
- Add `file/lines` to iterate over lines in a file lazily.
 | 
			
		||||
- Reorganize test suite to be sorted by module rather than pseudo-randomly.
 | 
			
		||||
- Add `*task-id*`
 | 
			
		||||
- Add `env` argument to `fiber/new`.
 | 
			
		||||
- Add `JANET_NO_AMALG` flag to Makefile to properly incremental builds
 | 
			
		||||
- Optimize bytecode compiler to generate fewer instructions and improve loops.
 | 
			
		||||
- Fix bug with `ev/gather` and hung fibers.
 | 
			
		||||
- Add `os/isatty`
 | 
			
		||||
- Add `has-key?` and `has-value?`
 | 
			
		||||
- Make imperative arithmetic macros variadic
 | 
			
		||||
- `ev/connect` now yields to the event loop instead of blocking while waiting for an ACK.
 | 
			
		||||
 | 
			
		||||
## 1.28.0 - 2023-05-13
 | 
			
		||||
- Various bug fixes
 | 
			
		||||
- Make nested short-fn's behave a bit more predictably (it is still not recommended to nest short-fns).
 | 
			
		||||
- Add `os/strftime` for date formatting.
 | 
			
		||||
- Fix `ev/select` on threaded channels sometimes live-locking.
 | 
			
		||||
- Support the `NO_COLOR` environment variable to turn off VT100 color codes in repl (and in scripts).
 | 
			
		||||
  See http://no-color.org/
 | 
			
		||||
- Disallow using `(splice x)` in contexts where it doesn't make sense rather than silently coercing to `x`.
 | 
			
		||||
  Instead, raise a compiler error.
 | 
			
		||||
- Change the names of `:user8` and `:user9` signals to `:interrupt` and `:await`
 | 
			
		||||
- Change the names of `:user8` and `:user9` fiber statuses to `:interrupted` and `:suspended`.
 | 
			
		||||
- Add `ev/all-tasks` to see all currently suspended fibers.
 | 
			
		||||
- Add `keep-syntax` and `keep-syntax!` functions to make writing macros easier.
 | 
			
		||||
 | 
			
		||||
## 1.27.0 - 2023-03-05
 | 
			
		||||
- Change semantics around bracket tuples to no longer be equal to regular tuples.
 | 
			
		||||
- Add `index` argument to `ffi/write` for symmetry with `ffi/read`.
 | 
			
		||||
- Add `buffer/push-at`
 | 
			
		||||
- Add `ffi/pointer-buffer` to convert pointers to buffers the cannot be reallocated. This
 | 
			
		||||
  allows easier manipulation of FFI memory, memory mapped files, and buffer memory shared between threads.
 | 
			
		||||
- Calling `ev/cancel` on a fiber waiting on `ev/gather` will correctly
 | 
			
		||||
  cancel the child fibers.
 | 
			
		||||
- Add `(sandbox ...)` function to core for permission based security. Also add `janet_sandbox` to C API.
 | 
			
		||||
  The sandbox allows limiting access to the file system, network, ffi, and OS resources at runtime.
 | 
			
		||||
- Add `(.locals)` function to debugger to see currently bound local symbols.
 | 
			
		||||
- Track symbol -> slot mapping so debugger can get symbolic information. This exposes local bindings
 | 
			
		||||
  in `debug/stack` and `disasm`.
 | 
			
		||||
- Add `os/compiler` to detect what host compiler was used to compile the interpreter
 | 
			
		||||
- Add support for mingw and cygwin builds (mingw support also added in jpm).
 | 
			
		||||
 | 
			
		||||
## 1.26.0 - 2023-01-07
 | 
			
		||||
- Add `ffi/malloc` and `ffi/free`. Useful as tools of last resort.
 | 
			
		||||
- Add `ffi/jitfn` to allow calling function pointers generated at runtime from machine code.
 | 
			
		||||
  Bring your own assembler, though.
 | 
			
		||||
- Channels can now be marshalled. Pending state is not saved, only items in the channel.
 | 
			
		||||
- Use the new `.length` function pointer on abstract types for lengths. Adding
 | 
			
		||||
  a `length` method will still work as well.
 | 
			
		||||
- Support byte views on abstract types with the `.bytes` function pointer.
 | 
			
		||||
- Add the `u` format specifier to printf family functions.
 | 
			
		||||
- Allow printing 64 integer types in `printf` and `string/format` family functions.
 | 
			
		||||
- Allow importing modules from custom directories more easily with the `@` prefix
 | 
			
		||||
  to module paths. For example, if there is a dynamic binding :custom-modules that
 | 
			
		||||
  is a file system path to a directory of modules, import from that directory with
 | 
			
		||||
  `(import @custom-modules/mymod)`.
 | 
			
		||||
- Fix error message bug in FFI library.
 | 
			
		||||
 | 
			
		||||
## 1.25.1 - 2022-10-29
 | 
			
		||||
- Add `memcmp` function to core library.
 | 
			
		||||
- Fix bug in `os/open` with `:rw` permissions not correct on Linux.
 | 
			
		||||
- Support config.mk for more easily configuring the Makefile.
 | 
			
		||||
 | 
			
		||||
## 1.25.0 - 2022-10-10
 | 
			
		||||
- Windows FFI fixes.
 | 
			
		||||
- Fix PEG `if-not` combinator with captures in the condition
 | 
			
		||||
- Fix bug with `os/date` with nil first argument
 | 
			
		||||
- Fix bug with `net/accept` on Linux that could leak file descriptors to subprocesses
 | 
			
		||||
- Reduce number of hash collisiions from pointer hashing
 | 
			
		||||
- Reduce number of hash collisions from pointer hashing
 | 
			
		||||
- Add optional parameter to `marshal` to skip cycle checking code
 | 
			
		||||
 | 
			
		||||
## 1.24.1 - 2022-08-24
 | 
			
		||||
@@ -130,7 +335,7 @@ All notable changes to this project will be documented in this file.
 | 
			
		||||
- 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 `janet_interpreter_interrupt` 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
 | 
			
		||||
@@ -184,7 +389,7 @@ saving and restoring the entire VM state.
 | 
			
		||||
- Sort keys in pretty printing output.
 | 
			
		||||
 | 
			
		||||
## 1.15.3 - 2021-02-28
 | 
			
		||||
- Fix a fiber bug that occured in deeply nested fibers
 | 
			
		||||
- Fix a fiber bug that occurred in deeply nested fibers
 | 
			
		||||
- Add `unref` combinator to pegs.
 | 
			
		||||
- Small docstring changes.
 | 
			
		||||
 | 
			
		||||
@@ -334,13 +539,13 @@ saving and restoring the entire VM state.
 | 
			
		||||
- Add `symbol/slice`
 | 
			
		||||
- Add `keyword/slice`
 | 
			
		||||
- Allow cross compilation with Makefile.
 | 
			
		||||
- Change `compare-primitve` to `cmp` and make it more efficient.
 | 
			
		||||
- Change `compare-primitive` 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.
 | 
			
		||||
- Add `:h`, `:h+`, and `:h*` in `default-peg-grammar` for hexadecimal digits.
 | 
			
		||||
- Fix `%j` formatter to print numbers precisely (using the `%.17g` format string to printf).
 | 
			
		||||
 | 
			
		||||
## 1.10.1 - 2020-06-18
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
Copyright (c) 2021 Calvin Rose and contributors
 | 
			
		||||
Copyright (c) 2025 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										134
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2022 Calvin Rose
 | 
			
		||||
# Copyright (c) 2025 Calvin Rose
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -21,40 +21,58 @@
 | 
			
		||||
################################
 | 
			
		||||
##### Set global variables #####
 | 
			
		||||
################################
 | 
			
		||||
 | 
			
		||||
sinclude config.mk
 | 
			
		||||
PREFIX?=/usr/local
 | 
			
		||||
 | 
			
		||||
JANETCONF_HEADER?=src/conf/janetconf.h
 | 
			
		||||
INCLUDEDIR?=$(PREFIX)/include
 | 
			
		||||
BINDIR?=$(PREFIX)/bin
 | 
			
		||||
LIBDIR?=$(PREFIX)/lib
 | 
			
		||||
JANET_BUILD?="\"$(shell git log --pretty=format:'%h' -n 1 2> /dev/null || echo local)\""
 | 
			
		||||
CLIBS=-lm -lpthread
 | 
			
		||||
JANET_TARGET=build/janet
 | 
			
		||||
JANET_BOOT=build/janet_boot
 | 
			
		||||
JANET_IMPORT_LIB=build/janet.lib
 | 
			
		||||
JANET_LIBRARY_IMPORT_LIB=build/libjanet.lib
 | 
			
		||||
JANET_LIBRARY=build/libjanet.so
 | 
			
		||||
JANET_STATIC_LIBRARY=build/libjanet.a
 | 
			
		||||
JANET_PATH?=$(LIBDIR)/janet
 | 
			
		||||
JANET_MANPATH?=$(PREFIX)/share/man/man1/
 | 
			
		||||
JANET_PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig
 | 
			
		||||
JANET_DIST_DIR?=janet-dist
 | 
			
		||||
JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)'
 | 
			
		||||
JANET_TARGET_OBJECTS=build/janet.o build/shell.o
 | 
			
		||||
JPM_TAG?=master
 | 
			
		||||
SPORK_TAG?=master
 | 
			
		||||
HAS_SHARED?=1
 | 
			
		||||
DEBUGGER=gdb
 | 
			
		||||
SONAME_SETTER=-Wl,-soname,
 | 
			
		||||
 | 
			
		||||
# For cross compilation
 | 
			
		||||
HOSTCC?=$(CC)
 | 
			
		||||
HOSTAR?=$(AR)
 | 
			
		||||
CFLAGS?=-O2
 | 
			
		||||
# Symbols are (optionally) removed later, keep -g as default!
 | 
			
		||||
CFLAGS?=-O2 -g
 | 
			
		||||
LDFLAGS?=-rdynamic
 | 
			
		||||
LIBJANET_LDFLAGS?=$(LD_FLAGS)
 | 
			
		||||
RUN:=$(RUN)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
 | 
			
		||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 -g $(COMMON_CFLAGS)
 | 
			
		||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 $(COMMON_CFLAGS) -g
 | 
			
		||||
BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS)
 | 
			
		||||
 | 
			
		||||
# Disable amalgamated build
 | 
			
		||||
ifeq ($(JANET_NO_AMALG), 1)
 | 
			
		||||
	JANET_TARGET_OBJECTS+=$(patsubst src/%.c,build/%.bin.o,$(JANET_CORE_SOURCES))
 | 
			
		||||
	JANET_BOOT_FLAGS+=image-only
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# For installation
 | 
			
		||||
LDCONFIG:=ldconfig "$(LIBDIR)"
 | 
			
		||||
 | 
			
		||||
# Check OS
 | 
			
		||||
UNAME:=$(shell uname -s)
 | 
			
		||||
UNAME?=$(shell uname -s)
 | 
			
		||||
ifeq ($(UNAME), Darwin)
 | 
			
		||||
	CLIBS:=$(CLIBS) -ldl
 | 
			
		||||
	SONAME_SETTER:=-Wl,-install_name,
 | 
			
		||||
@@ -76,14 +94,33 @@ ifeq ($(shell uname -o), Android)
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
$(shell mkdir -p build/core build/c build/boot)
 | 
			
		||||
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
 | 
			
		||||
# Mingw
 | 
			
		||||
MINGW_COMPILER=
 | 
			
		||||
ifeq ($(findstring MINGW,$(UNAME)), MINGW)
 | 
			
		||||
	MINGW_COMPILER=gcc
 | 
			
		||||
	CLIBS:=-lws2_32 -lpsapi -lwsock32
 | 
			
		||||
	LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB)
 | 
			
		||||
	LIBJANET_LDFLAGS:=-Wl,--out-implib,$(JANET_LIBRARY_IMPORT_LIB)
 | 
			
		||||
	JANET_TARGET:=$(JANET_TARGET).exe
 | 
			
		||||
	JANET_BOOT:=$(JANET_BOOT).exe
 | 
			
		||||
	COMPILER_VERSION:=$(shell $(CC) --version)
 | 
			
		||||
	ifeq ($(findstring clang,$(COMPILER_VERSION)), clang)
 | 
			
		||||
		MINGW_COMPILER=clang
 | 
			
		||||
	endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$(shell mkdir -p build/core build/c build/boot build/mainclient)
 | 
			
		||||
all: $(JANET_TARGET) $(JANET_STATIC_LIBRARY) build/janet.h
 | 
			
		||||
ifeq ($(HAS_SHARED), 1)
 | 
			
		||||
all: $(JANET_LIBRARY)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
######################
 | 
			
		||||
##### Name Files #####
 | 
			
		||||
######################
 | 
			
		||||
 | 
			
		||||
JANET_HEADERS=src/include/janet.h src/conf/janetconf.h
 | 
			
		||||
JANET_HEADERS=src/include/janet.h $(JANETCONF_HEADER)
 | 
			
		||||
 | 
			
		||||
JANET_LOCAL_HEADERS=src/core/features.h \
 | 
			
		||||
					src/core/util.h \
 | 
			
		||||
@@ -110,6 +147,7 @@ JANET_CORE_SOURCES=src/core/abstract.c \
 | 
			
		||||
				   src/core/ev.c \
 | 
			
		||||
				   src/core/ffi.c \
 | 
			
		||||
				   src/core/fiber.c \
 | 
			
		||||
				   src/core/filewatch.c \
 | 
			
		||||
				   src/core/gc.c \
 | 
			
		||||
				   src/core/inttypes.c \
 | 
			
		||||
				   src/core/io.c \
 | 
			
		||||
@@ -155,46 +193,58 @@ $(JANET_BOOT_OBJECTS): $(JANET_BOOT_HEADERS)
 | 
			
		||||
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
 | 
			
		||||
	$(CC) $(BOOT_CFLAGS) -o $@ -c $<
 | 
			
		||||
 | 
			
		||||
build/janet_boot: $(JANET_BOOT_OBJECTS)
 | 
			
		||||
$(JANET_BOOT): $(JANET_BOOT_OBJECTS)
 | 
			
		||||
	$(CC) $(BOOT_CFLAGS) -o $@ $(JANET_BOOT_OBJECTS) $(CLIBS)
 | 
			
		||||
 | 
			
		||||
# Now the reason we bootstrap in the first place
 | 
			
		||||
build/c/janet.c: build/janet_boot src/boot/boot.janet
 | 
			
		||||
	build/janet_boot . JANET_PATH '$(JANET_PATH)' > $@
 | 
			
		||||
build/c/janet.c: $(JANET_BOOT) src/boot/boot.janet
 | 
			
		||||
	$(RUN) $(JANET_BOOT) $(JANET_BOOT_FLAGS) > $@
 | 
			
		||||
	cksum $@
 | 
			
		||||
 | 
			
		||||
##################
 | 
			
		||||
##### Quicky #####
 | 
			
		||||
##################
 | 
			
		||||
 | 
			
		||||
build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -o $@ -c $<
 | 
			
		||||
 | 
			
		||||
########################
 | 
			
		||||
##### Amalgamation #####
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
ifeq ($(UNAME), Darwin)
 | 
			
		||||
SONAME=libjanet.1.25.dylib
 | 
			
		||||
SONAME=libjanet.1.38.dylib
 | 
			
		||||
else
 | 
			
		||||
SONAME=libjanet.so.1.25
 | 
			
		||||
SONAME=libjanet.so.1.38
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(MINGW_COMPILER), clang)
 | 
			
		||||
	SONAME=
 | 
			
		||||
	SONAME_SETTER=
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
build/c/shell.c: src/mainclient/shell.c
 | 
			
		||||
	cp $< $@
 | 
			
		||||
 | 
			
		||||
build/janet.h: $(JANET_TARGET) src/include/janet.h src/conf/janetconf.h
 | 
			
		||||
	./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h src/conf/janetconf.h $@
 | 
			
		||||
build/janet.h: $(JANET_TARGET) src/include/janet.h $(JANETCONF_HEADER)
 | 
			
		||||
	$(RUN) ./$(JANET_TARGET) tools/patch-header.janet src/include/janet.h $(JANETCONF_HEADER) $@
 | 
			
		||||
 | 
			
		||||
build/janetconf.h: src/conf/janetconf.h
 | 
			
		||||
build/janetconf.h: $(JANETCONF_HEADER)
 | 
			
		||||
	cp $< $@
 | 
			
		||||
 | 
			
		||||
build/janet.o: build/c/janet.c src/conf/janetconf.h src/include/janet.h
 | 
			
		||||
build/janet.o: build/c/janet.c $(JANETCONF_HEADER) src/include/janet.h
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
build/shell.o: build/c/shell.c src/conf/janetconf.h src/include/janet.h
 | 
			
		||||
build/shell.o: build/c/shell.c $(JANETCONF_HEADER) src/include/janet.h
 | 
			
		||||
	$(HOSTCC) $(BUILD_CFLAGS) -c $< -o $@
 | 
			
		||||
 | 
			
		||||
$(JANET_TARGET): build/janet.o build/shell.o
 | 
			
		||||
$(JANET_TARGET): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_LIBRARY): build/janet.o build/shell.o
 | 
			
		||||
	$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
 | 
			
		||||
$(JANET_LIBRARY): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTCC) $(LIBJANET_LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
 | 
			
		||||
 | 
			
		||||
$(JANET_STATIC_LIBRARY): build/janet.o build/shell.o
 | 
			
		||||
$(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
 | 
			
		||||
	$(HOSTAR) rcs $@ $^
 | 
			
		||||
 | 
			
		||||
###################
 | 
			
		||||
@@ -206,19 +256,19 @@ $(JANET_STATIC_LIBRARY): build/janet.o build/shell.o
 | 
			
		||||
TEST_SCRIPTS=$(wildcard test/suite*.janet)
 | 
			
		||||
 | 
			
		||||
repl: $(JANET_TARGET)
 | 
			
		||||
	./$(JANET_TARGET)
 | 
			
		||||
	$(RUN) ./$(JANET_TARGET)
 | 
			
		||||
 | 
			
		||||
debug: $(JANET_TARGET)
 | 
			
		||||
	$(DEBUGGER) ./$(JANET_TARGET)
 | 
			
		||||
 | 
			
		||||
VALGRIND_COMMAND=valgrind --leak-check=full
 | 
			
		||||
VALGRIND_COMMAND=valgrind --leak-check=full --quiet
 | 
			
		||||
 | 
			
		||||
valgrind: $(JANET_TARGET)
 | 
			
		||||
	$(VALGRIND_COMMAND) ./$(JANET_TARGET)
 | 
			
		||||
 | 
			
		||||
test: $(JANET_TARGET) $(TEST_PROGRAMS)
 | 
			
		||||
	for f in test/suite*.janet; do ./$(JANET_TARGET) "$$f" || exit; done
 | 
			
		||||
	for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
 | 
			
		||||
	for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
 | 
			
		||||
	for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
 | 
			
		||||
 | 
			
		||||
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
 | 
			
		||||
	for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
 | 
			
		||||
@@ -235,20 +285,25 @@ dist: build/janet-dist.tar.gz
 | 
			
		||||
 | 
			
		||||
build/janet-%.tar.gz: $(JANET_TARGET) \
 | 
			
		||||
	build/janet.h \
 | 
			
		||||
	janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
 | 
			
		||||
	janet.1 LICENSE CONTRIBUTING.md $(JANET_STATIC_LIBRARY) \
 | 
			
		||||
	README.md build/c/janet.c build/c/shell.c
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/bin
 | 
			
		||||
	cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/
 | 
			
		||||
	strip -x -S 'build/$(JANET_DIST_DIR)/bin/janet'
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/include
 | 
			
		||||
	cp build/janet.h build/$(JANET_DIST_DIR)/include/
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/lib/
 | 
			
		||||
	cp $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
 | 
			
		||||
	cp $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
 | 
			
		||||
	cp $(JANET_LIBRARY) build/$(JANET_DIST_DIR)/lib/ || true
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/man/man1/
 | 
			
		||||
	cp janet.1 build/$(JANET_DIST_DIR)/man/man1/janet.1
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/src/
 | 
			
		||||
	cp build/c/janet.c build/c/shell.c build/$(JANET_DIST_DIR)/src/
 | 
			
		||||
	cp CONTRIBUTING.md LICENSE README.md build/$(JANET_DIST_DIR)/
 | 
			
		||||
	cd build && tar -czvf ../$@ ./$(JANET_DIST_DIR)
 | 
			
		||||
ifeq ($(HAS_SHARED), 1)
 | 
			
		||||
build/janet-%.tar.gz: $(JANET_LIBRARY)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
#########################
 | 
			
		||||
##### Documentation #####
 | 
			
		||||
@@ -257,7 +312,7 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
 | 
			
		||||
docs: build/doc.html
 | 
			
		||||
 | 
			
		||||
build/doc.html: $(JANET_TARGET) tools/gendoc.janet
 | 
			
		||||
	$(JANET_TARGET) tools/gendoc.janet > build/doc.html
 | 
			
		||||
	$(RUN) $(JANET_TARGET) tools/gendoc.janet > build/doc.html
 | 
			
		||||
 | 
			
		||||
########################
 | 
			
		||||
##### Installation #####
 | 
			
		||||
@@ -273,7 +328,7 @@ build/janet.pc: $(JANET_TARGET)
 | 
			
		||||
	echo "Name: janet" >> $@
 | 
			
		||||
	echo "Url: https://janet-lang.org" >> $@
 | 
			
		||||
	echo "Description: Library for the Janet programming language." >> $@
 | 
			
		||||
	$(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@
 | 
			
		||||
	$(RUN) $(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@
 | 
			
		||||
	echo 'Cflags: -I$${includedir}' >> $@
 | 
			
		||||
	echo 'Libs: -L$${libdir} -ljanet' >> $@
 | 
			
		||||
	echo 'Libs.private: $(CLIBS)' >> $@
 | 
			
		||||
@@ -281,9 +336,10 @@ build/janet.pc: $(JANET_TARGET)
 | 
			
		||||
install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(BINDIR)'
 | 
			
		||||
	cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
 | 
			
		||||
	strip -x -S '$(DESTDIR)$(BINDIR)/janet'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd
 | 
			
		||||
	ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(LIBDIR)'
 | 
			
		||||
	if test $(UNAME) = Darwin ; then \
 | 
			
		||||
@@ -300,6 +356,8 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
 | 
			
		||||
	cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
 | 
			
		||||
	cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	cp '$(JANET_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
 | 
			
		||||
	cp '$(JANET_LIBRARY_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
 | 
			
		||||
 | 
			
		||||
install-jpm-git: $(JANET_TARGET)
 | 
			
		||||
@@ -312,7 +370,13 @@ install-jpm-git: $(JANET_TARGET)
 | 
			
		||||
		JANET_HEADERPATH='$(INCLUDEDIR)/janet' \
 | 
			
		||||
		JANET_BINPATH='$(BINDIR)' \
 | 
			
		||||
		JANET_LIBPATH='$(LIBDIR)' \
 | 
			
		||||
		../../$(JANET_TARGET) ./bootstrap.janet
 | 
			
		||||
		$(RUN) ../../$(JANET_TARGET) ./bootstrap.janet
 | 
			
		||||
 | 
			
		||||
install-spork-git: $(JANET_TARGET)
 | 
			
		||||
	mkdir -p build
 | 
			
		||||
	rm -rf build/spork
 | 
			
		||||
	git clone --depth=1 --branch='$(SPORK_TAG)' https://github.com/janet-lang/spork.git build/spork
 | 
			
		||||
	$(JANET_TARGET) -e '(bundle/install "build/spork")'
 | 
			
		||||
 | 
			
		||||
uninstall:
 | 
			
		||||
	-rm '$(DESTDIR)$(BINDIR)/janet'
 | 
			
		||||
@@ -328,14 +392,14 @@ uninstall:
 | 
			
		||||
#################
 | 
			
		||||
 | 
			
		||||
format:
 | 
			
		||||
	tools/format.sh
 | 
			
		||||
	sh tools/format.sh
 | 
			
		||||
 | 
			
		||||
grammar: build/janet.tmLanguage
 | 
			
		||||
build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
 | 
			
		||||
	$(JANET_TARGET) $< > $@
 | 
			
		||||
	$(RUN) $(JANET_TARGET) $< > $@
 | 
			
		||||
 | 
			
		||||
compile-commands:
 | 
			
		||||
	# Requires pip install copmiledb
 | 
			
		||||
	# Requires pip install compiledb
 | 
			
		||||
	compiledb make
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										240
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										240
									
								
								README.md
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
[](https://gitter.im/janet-language/community)
 | 
			
		||||
[](https://janet.zulipchat.com)
 | 
			
		||||
 
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/openbsd.yml?)
 | 
			
		||||
@@ -6,58 +6,128 @@
 | 
			
		||||
 | 
			
		||||
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">
 | 
			
		||||
 | 
			
		||||
**Janet** is a functional and imperative programming language and bytecode interpreter. It is a
 | 
			
		||||
lisp-like language, but lists are replaced
 | 
			
		||||
by other data structures (arrays, tables (hash table), struct (immutable hash table), tuples).
 | 
			
		||||
The language also supports bridging to native code written in C, meta-programming with macros, and bytecode assembly.
 | 
			
		||||
**Janet** is a programming language for system scripting, expressive automation, and
 | 
			
		||||
extending programs written in C or C++ with user scripting capabilities.
 | 
			
		||||
 | 
			
		||||
Janet makes a good system scripting language, or a language to embed in other programs.
 | 
			
		||||
It's like Lua and GNU Guile in that regard. It has more built-in functionality and a richer core language than
 | 
			
		||||
Lua, but smaller than GNU Guile or Python. However, it is much easier to embed and port than Python or Guile.
 | 
			
		||||
 | 
			
		||||
There is a REPL for trying out the language, as well as the ability
 | 
			
		||||
to run script files. This client program is separate from the core runtime, so
 | 
			
		||||
Janet can be embedded in other programs. Try Janet in your browser at
 | 
			
		||||
[https://janet-lang.org](https://janet-lang.org).
 | 
			
		||||
 | 
			
		||||
If you'd like to financially support the ongoing development of Janet, consider
 | 
			
		||||
[sponsoring its primary author](https://github.com/sponsors/bakpakin) through GitHub.
 | 
			
		||||
<https://janet-lang.org>.
 | 
			
		||||
 | 
			
		||||
<br>
 | 
			
		||||
 | 
			
		||||
## Use Cases
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
Janet makes a good system scripting language, or a language to embed in other programs.
 | 
			
		||||
It's like Lua and Guile in that regard. It has more built-in functionality and a richer core language than
 | 
			
		||||
Lua, but smaller than GNU Guile or Python.
 | 
			
		||||
See the examples directory for all provided example programs.
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
### Game of Life
 | 
			
		||||
 | 
			
		||||
* Configurable at build time - turn features on or off for a smaller or more featureful build
 | 
			
		||||
* Minimal setup - one binary and you are good to go!
 | 
			
		||||
```janet
 | 
			
		||||
# John Conway's Game of Life
 | 
			
		||||
 | 
			
		||||
(def- window
 | 
			
		||||
  (seq [x :range [-1 2]
 | 
			
		||||
         y :range [-1 2]
 | 
			
		||||
         :when (not (and (zero? x) (zero? y)))]
 | 
			
		||||
       [x y]))
 | 
			
		||||
 | 
			
		||||
(defn- neighbors
 | 
			
		||||
  [[x y]]
 | 
			
		||||
  (map (fn [[x1 y1]] [(+ x x1) (+ y y1)]) window))
 | 
			
		||||
 | 
			
		||||
(defn tick
 | 
			
		||||
  "Get the next state in the Game Of Life."
 | 
			
		||||
  [state]
 | 
			
		||||
  (def cell-set (frequencies state))
 | 
			
		||||
  (def neighbor-set (frequencies (mapcat neighbors state)))
 | 
			
		||||
  (seq [coord :keys neighbor-set
 | 
			
		||||
         :let [count (get neighbor-set coord)]
 | 
			
		||||
         :when (or (= count 3) (and (get cell-set coord) (= count 2)))]
 | 
			
		||||
      coord))
 | 
			
		||||
 | 
			
		||||
(defn draw
 | 
			
		||||
  "Draw cells in the game of life from (x1, y1) to (x2, y2)"
 | 
			
		||||
  [state x1 y1 x2 y2]
 | 
			
		||||
  (def cellset @{})
 | 
			
		||||
  (each cell state (put cellset cell true))
 | 
			
		||||
  (loop [x :range [x1 (+ 1 x2)]
 | 
			
		||||
         :after (print)
 | 
			
		||||
         y :range [y1 (+ 1 y2)]]
 | 
			
		||||
    (file/write stdout (if (get cellset [x y]) "X " ". ")))
 | 
			
		||||
  (print))
 | 
			
		||||
 | 
			
		||||
# Print the first 20 generations of a glider
 | 
			
		||||
(var *state* '[(0 0) (-1 0) (1 0) (1 1) (0 2)])
 | 
			
		||||
(for i 0 20
 | 
			
		||||
  (print "generation " i)
 | 
			
		||||
  (draw *state* -7 -7 7 7)
 | 
			
		||||
  (set *state* (tick *state*)))
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### TCP Echo Server
 | 
			
		||||
 | 
			
		||||
```janet
 | 
			
		||||
# A simple TCP echo server using the built-in socket networking and event loop.
 | 
			
		||||
 | 
			
		||||
(defn handler
 | 
			
		||||
  "Simple handler for connections."
 | 
			
		||||
  [stream]
 | 
			
		||||
  (defer (:close stream)
 | 
			
		||||
    (def id (gensym))
 | 
			
		||||
    (def b @"")
 | 
			
		||||
    (print "Connection " id "!")
 | 
			
		||||
    (while (:read stream 1024 b)
 | 
			
		||||
      (printf " %v -> %v" id b)
 | 
			
		||||
      (:write stream b)
 | 
			
		||||
      (buffer/clear b))
 | 
			
		||||
    (printf "Done %v!" id)
 | 
			
		||||
    (ev/sleep 0.5)))
 | 
			
		||||
 | 
			
		||||
(net/server "127.0.0.1" "8000" handler)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Windows FFI Hello, World!
 | 
			
		||||
 | 
			
		||||
```janet
 | 
			
		||||
# Use the FFI to popup a Windows message box - no C required
 | 
			
		||||
 | 
			
		||||
(ffi/context "user32.dll")
 | 
			
		||||
 | 
			
		||||
(ffi/defbind MessageBoxA :int
 | 
			
		||||
  [w :ptr text :string cap :string typ :int])
 | 
			
		||||
 | 
			
		||||
(MessageBoxA nil "Hello, World!" "Test" 0)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Language Features
 | 
			
		||||
 | 
			
		||||
* 600+ functions and macros in the core library
 | 
			
		||||
* Built-in socket networking, threading, subprocesses, and file system functions.
 | 
			
		||||
* Parsing Expression Grammars (PEG) engine as a more robust Regex alternative
 | 
			
		||||
* Macros and compile-time computation
 | 
			
		||||
* Per-thread event loop for efficient IO (epoll/IOCP/kqueue)
 | 
			
		||||
* First-class green threads (continuations) as well as OS threads
 | 
			
		||||
* Erlang-style supervision trees that integrate with the event loop
 | 
			
		||||
* First-class closures
 | 
			
		||||
* Garbage collection
 | 
			
		||||
* First-class green threads (continuations)
 | 
			
		||||
* Distributed as janet.c and janet.h for embedding into a larger program.
 | 
			
		||||
* Python-style generators (implemented as a plain macro)
 | 
			
		||||
* Mutable and immutable arrays (array/tuple)
 | 
			
		||||
* Mutable and immutable hashtables (table/struct)
 | 
			
		||||
* Mutable and immutable strings (buffer/string)
 | 
			
		||||
* Macros
 | 
			
		||||
* Multithreading
 | 
			
		||||
* Per-thread event loop for efficient evented IO
 | 
			
		||||
* Byte code interpreter with an assembly interface, as well as bytecode verification
 | 
			
		||||
* Tail call Optimization
 | 
			
		||||
* Direct interop with C via abstract types and C functions
 | 
			
		||||
* Dynamically load C libraries
 | 
			
		||||
* Functional and imperative standard library
 | 
			
		||||
* Lexical scoping
 | 
			
		||||
* Imperative programming as well as functional
 | 
			
		||||
* REPL
 | 
			
		||||
* Parsing Expression Grammars built into the core library
 | 
			
		||||
* 400+ functions and macros in the core library
 | 
			
		||||
* Embedding Janet in other programs
 | 
			
		||||
* Interactive environment with detailed stack traces
 | 
			
		||||
* Tail recursion
 | 
			
		||||
* Interface with C functions and dynamically load plugins ("natives").
 | 
			
		||||
* Built-in C FFI for when the native bindings are too much work
 | 
			
		||||
* REPL development with debugger and inspectable runtime
 | 
			
		||||
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
* For a quick tutorial, see [the introduction](https://janet-lang.org/docs/index.html) for more details.
 | 
			
		||||
* For the full API for all functions in the core library, see [the core API doc](https://janet-lang.org/api/index.html)
 | 
			
		||||
* For the full API for all functions in the core library, see [the core API doc](https://janet-lang.org/api/index.html).
 | 
			
		||||
 | 
			
		||||
Documentation is also available locally in the REPL.
 | 
			
		||||
Use the `(doc symbol-name)` macro to get API
 | 
			
		||||
@@ -65,7 +135,7 @@ documentation for symbols in the core library. For example,
 | 
			
		||||
```
 | 
			
		||||
(doc apply)
 | 
			
		||||
```
 | 
			
		||||
Shows documentation for the `apply` function.
 | 
			
		||||
shows documentation for the `apply` function.
 | 
			
		||||
 | 
			
		||||
To get a list of all bindings in the default
 | 
			
		||||
environment, use the `(all-bindings)` function. You
 | 
			
		||||
@@ -84,7 +154,7 @@ the SourceHut mirror is actively maintained.
 | 
			
		||||
 | 
			
		||||
The Makefile is non-portable and requires GNU-flavored make.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```sh
 | 
			
		||||
cd somewhere/my/projects/janet
 | 
			
		||||
make
 | 
			
		||||
make test
 | 
			
		||||
@@ -100,7 +170,7 @@ Find out more about the available make targets by running `make help`.
 | 
			
		||||
32-bit Haiku build instructions are the same as the UNIX-like build instructions,
 | 
			
		||||
but you need to specify an alternative compiler, such as `gcc-x86`.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```sh
 | 
			
		||||
cd somewhere/my/projects/janet
 | 
			
		||||
make CC=gcc-x86
 | 
			
		||||
make test
 | 
			
		||||
@@ -112,10 +182,9 @@ make install-jpm-git
 | 
			
		||||
### FreeBSD
 | 
			
		||||
 | 
			
		||||
FreeBSD build instructions are the same as the UNIX-like build instructions,
 | 
			
		||||
but you need `gmake` to compile. Alternatively, install directly from
 | 
			
		||||
packages, using `pkg install lang/janet`.
 | 
			
		||||
but you need `gmake` to compile. Alternatively, install the package directly with `pkg install lang/janet`.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```sh
 | 
			
		||||
cd somewhere/my/projects/janet
 | 
			
		||||
gmake
 | 
			
		||||
gmake test
 | 
			
		||||
@@ -127,19 +196,19 @@ gmake install-jpm-git
 | 
			
		||||
### NetBSD
 | 
			
		||||
 | 
			
		||||
NetBSD build instructions are the same as the FreeBSD build instructions.
 | 
			
		||||
Alternatively, install directly from packages, using `pkgin install janet`.
 | 
			
		||||
Alternatively, install the package directly with `pkgin install janet`.
 | 
			
		||||
 | 
			
		||||
### Windows
 | 
			
		||||
 | 
			
		||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#)
 | 
			
		||||
2. Run a Visual Studio Command Prompt (cl.exe and link.exe need to be on the PATH) and cd to the directory with janet.
 | 
			
		||||
3. Run `build_win` to compile janet.
 | 
			
		||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#).
 | 
			
		||||
2. Run a Visual Studio Command Prompt (`cl.exe` and `link.exe` need to be on your PATH) and `cd` to the directory with Janet.
 | 
			
		||||
3. Run `build_win` to compile Janet.
 | 
			
		||||
4. Run `build_win test` to make sure everything is working.
 | 
			
		||||
 | 
			
		||||
To build an `.msi` installer executable, in addition to the above steps, you will have to:
 | 
			
		||||
 | 
			
		||||
5. Install, or otherwise add to your PATH the [WiX 3.11 Toolset](https://github.com/wixtoolset/wix3/releases)
 | 
			
		||||
6. run `build_win dist`
 | 
			
		||||
5. Install, or otherwise add to your PATH the [WiX 3.14 Toolset](https://github.com/wixtoolset/wix3/releases).
 | 
			
		||||
6. Run `build_win dist`.
 | 
			
		||||
 | 
			
		||||
Now you should have an `.msi`. You can run `build_win install` to install the `.msi`, or execute the file itself.
 | 
			
		||||
 | 
			
		||||
@@ -175,19 +244,21 @@ ninja -C build install
 | 
			
		||||
 | 
			
		||||
Janet can be hacked on with pretty much any environment you like, but for IDE
 | 
			
		||||
lovers, [Gnome Builder](https://wiki.gnome.org/Apps/Builder) is probably the
 | 
			
		||||
best option, as it has excellent meson integration. It also offers code completion
 | 
			
		||||
best option, as it has excellent Meson integration. It also offers code completion
 | 
			
		||||
for Janet's C API right out of the box, which is very useful for exploring. VSCode, Vim,
 | 
			
		||||
Emacs, and Atom will have syntax packages for the Janet language, though.
 | 
			
		||||
Emacs, and Atom each have syntax packages for the Janet language, though.
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
See the [Introduction](https://janet-lang.org/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.
 | 
			
		||||
If you just want to try out the language, you don't need to install anything.
 | 
			
		||||
In this case you can also move the `janet` executable wherever you want on
 | 
			
		||||
your system and run it.  However, for a fuller setup, please see the
 | 
			
		||||
[Introduction](https://janet-lang.org/docs/index.html) for more details.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
A REPL is launched when the binary is invoked with no arguments. Pass the -h flag
 | 
			
		||||
to display the usage information. Individual scripts can be run with `./janet myscript.janet`
 | 
			
		||||
A REPL is launched when the binary is invoked with no arguments. Pass the `-h` flag
 | 
			
		||||
to display the usage information. Individual scripts can be run with `./janet myscript.janet`.
 | 
			
		||||
 | 
			
		||||
If you are looking to explore, you can print a list of all available macros, functions, and constants
 | 
			
		||||
by entering the command `(all-bindings)` into the REPL.
 | 
			
		||||
@@ -202,20 +273,26 @@ Hello, World!
 | 
			
		||||
nil
 | 
			
		||||
janet:3:> (os/exit)
 | 
			
		||||
$ janet -h
 | 
			
		||||
usage: build/janet [options] script args...
 | 
			
		||||
usage: janet [options] script args...
 | 
			
		||||
Options are:
 | 
			
		||||
  -h : Show this help
 | 
			
		||||
  -v : Print the version string
 | 
			
		||||
  -s : Use raw stdin instead of getline like functionality
 | 
			
		||||
  -e code : Execute a string of janet
 | 
			
		||||
  -E code arguments... : Evaluate an expression as a short-fn with arguments
 | 
			
		||||
  -d : Set the debug flag in the REPL
 | 
			
		||||
  -r : Enter the REPL after running all scripts
 | 
			
		||||
  -R : Disables loading profile.janet when JANET_PROFILE is present
 | 
			
		||||
  -p : Keep on executing if there is a top-level error (persistent)
 | 
			
		||||
  -q : Hide prompt, logo, and REPL output (quiet)
 | 
			
		||||
  -q : Hide logo (quiet)
 | 
			
		||||
  -k : Compile scripts but do not execute (flycheck)
 | 
			
		||||
  -m syspath : Set system path for loading global modules
 | 
			
		||||
  -c source output : Compile janet source code into an image
 | 
			
		||||
  -i : Load the script argument as an image file instead of source code
 | 
			
		||||
  -n : Disable ANSI color output in the REPL
 | 
			
		||||
  -l path : Execute code in a file before running the main script
 | 
			
		||||
  -l lib : Use a module before processing more arguments
 | 
			
		||||
  -w level : Set the lint warning level - default is "normal"
 | 
			
		||||
  -x level : Set the lint error level - default is "none"
 | 
			
		||||
  -- : Stop handling options
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
@@ -226,8 +303,8 @@ If installed, you can also run `man janet` to get usage information.
 | 
			
		||||
Janet can be embedded in a host program very easily. The normal build
 | 
			
		||||
will create a file `build/janet.c`, which is a single C file
 | 
			
		||||
that contains all the source to Janet. This file, along with
 | 
			
		||||
`src/include/janet.h` and `src/conf/janetconf.h` can be dragged into any C
 | 
			
		||||
project and compiled into the project. Janet should be compiled with `-std=c99`
 | 
			
		||||
`src/include/janet.h` and `src/conf/janetconf.h`, can be dragged into any C
 | 
			
		||||
project and compiled into it. Janet should be compiled with `-std=c99`
 | 
			
		||||
on most compilers, and will need to be linked to the math library, `-lm`, and
 | 
			
		||||
the dynamic linker, `-ldl`, if one wants to be able to load dynamic modules. If
 | 
			
		||||
there is no need for dynamic modules, add the define
 | 
			
		||||
@@ -235,26 +312,35 @@ there is no need for dynamic modules, add the define
 | 
			
		||||
 | 
			
		||||
See the [Embedding Section](https://janet-lang.org/capi/embedding.html) on the website for more information.
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
See the examples directory for some example janet code.
 | 
			
		||||
 | 
			
		||||
## Discussion
 | 
			
		||||
 | 
			
		||||
Feel free to ask questions and join the discussion on the [Janet Gitter Channel](https://gitter.im/janet-language/community).
 | 
			
		||||
Gitter provides Matrix and irc bridges as well.
 | 
			
		||||
Feel free to ask questions and join the discussion on the [Janet Zulip Instance](https://janet.zulipchat.com/)
 | 
			
		||||
 | 
			
		||||
## FAQ
 | 
			
		||||
 | 
			
		||||
### How fast is it?
 | 
			
		||||
 | 
			
		||||
It is about the same speed as most interpreted languages without a JIT compiler. Tight, critical
 | 
			
		||||
loops should probably be written in C or C++ . Programs tend to be a bit faster than
 | 
			
		||||
they would be in a language like Python due to the discouragement of slow Object-Oriented abstraction
 | 
			
		||||
with lots of hash-table lookups, and making late-binding explicit. All values are boxed in an 8-byte
 | 
			
		||||
representation by default and allocated on the heap, with the exception of numbers, nils and booleans. The
 | 
			
		||||
PEG engine is a specialized interpreter that can efficiently process string and buffer data.
 | 
			
		||||
 | 
			
		||||
The GC is simple and stop-the-world, but GC knobs are exposed in the core library and separate threads
 | 
			
		||||
have isolated heaps and garbage collectors. Data that is shared between threads is reference counted.
 | 
			
		||||
 | 
			
		||||
YMMV.
 | 
			
		||||
 | 
			
		||||
### Where is (favorite feature from other language)?
 | 
			
		||||
 | 
			
		||||
It may exist, it may not. If you want to propose major language features, go ahead and open an issue, but
 | 
			
		||||
they will likely by closed as "will not implement". Often, such features make one usecase simpler at the expense
 | 
			
		||||
It may exist, it may not. If you want to propose a major language feature, go ahead and open an issue, but
 | 
			
		||||
it will likely be closed as "will not implement". Often, such features make one usecase simpler at the expense
 | 
			
		||||
of 5 others by making the language more complicated.
 | 
			
		||||
 | 
			
		||||
### Is there a language spec?
 | 
			
		||||
 | 
			
		||||
There is not currently a spec besides the documentation at https://janet-lang.org.
 | 
			
		||||
There is not currently a spec besides the documentation at <https://janet-lang.org>.
 | 
			
		||||
 | 
			
		||||
### Is this Scheme/Common Lisp? Where are the cons cells?
 | 
			
		||||
 | 
			
		||||
@@ -263,20 +349,20 @@ 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.
 | 
			
		||||
Internally, Janet is not at all like Clojure, Scheme, or Common Lisp.
 | 
			
		||||
 | 
			
		||||
### Are the immutable data structures (tuples and structs) implemented as hash tries?
 | 
			
		||||
 | 
			
		||||
No. They are immutable arrays and hash tables. Don't try and use them like Clojure's vectors
 | 
			
		||||
and maps, instead they work well as table keys or other identifiers.
 | 
			
		||||
 | 
			
		||||
### Can I do Object Oriented programming with Janet?
 | 
			
		||||
### Can I do object-oriented programming with Janet?
 | 
			
		||||
 | 
			
		||||
To some extent, yes. However, it is not the recommended method of abstraction, and performance may suffer.
 | 
			
		||||
That said, tables can be used to make mutable objects with inheritance and polymorphism, where object
 | 
			
		||||
methods are implemeted with keywords.
 | 
			
		||||
methods are implemented with keywords.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```clj
 | 
			
		||||
(def Car @{:honk (fn [self msg] (print "car " self " goes " msg)) })
 | 
			
		||||
(def my-car (table/setproto @{} Car))
 | 
			
		||||
(:honk my-car "Beep!")
 | 
			
		||||
@@ -287,17 +373,25 @@ methods are implemeted with keywords.
 | 
			
		||||
Usually, one of a few reasons:
 | 
			
		||||
- Often, it already exists in a different form and the Clojure port would be redundant.
 | 
			
		||||
- Clojure programs often generate a lot of garbage and rely on the JVM to clean it up.
 | 
			
		||||
  Janet does not run on the JVM, and has a more primitive garbage collector.
 | 
			
		||||
- We want to keep the Janet core small. With Lisps, usually a feature can be added as a library
 | 
			
		||||
  without feeling "bolted on", especially when compared to ALGOL like languages. Adding features
 | 
			
		||||
  Janet does not run on the JVM and has a more primitive garbage collector.
 | 
			
		||||
- We want to keep the Janet core small. With Lisps, a feature can usually be added as a library
 | 
			
		||||
  without feeling "bolted on", especially when compared to ALGOL-like languages. Adding features
 | 
			
		||||
  to the core also makes it a bit more difficult to keep Janet maximally portable.
 | 
			
		||||
 | 
			
		||||
### Can I bind to Rust/Zig/Go/Java/Nim/C++/D/Pascal/Fortran/Odin/Jai/(Some new "Systems" Programming Language)?
 | 
			
		||||
 | 
			
		||||
Probably, if that language has a good interface with C. But the programmer may need to do
 | 
			
		||||
some extra work to map Janet's internal memory model to that of the bound language. Janet
 | 
			
		||||
also uses `setjmp`/`longjmp` for non-local returns internally. This
 | 
			
		||||
approach is out of favor with many programmers now and doesn't always play well with other languages
 | 
			
		||||
that have exceptions or stack-unwinding.
 | 
			
		||||
 | 
			
		||||
### Why is my terminal spitting out junk when I run the REPL?
 | 
			
		||||
 | 
			
		||||
Make sure your terminal supports ANSI escape codes. Most modern terminals will
 | 
			
		||||
support these, but some older terminals, Windows consoles, or embedded terminals
 | 
			
		||||
will not. If your terminal does not support ANSI escape codes, run the REPL with
 | 
			
		||||
the `-n` flag, which disables color output. You can also try the `-s` if further issues
 | 
			
		||||
the `-n` flag, which disables color output. You can also try the `-s` flag if further issues
 | 
			
		||||
ensue.
 | 
			
		||||
 | 
			
		||||
## Why is it called "Janet"?
 | 
			
		||||
 
 | 
			
		||||
@@ -41,32 +41,34 @@ if not exist build\boot mkdir build\boot
 | 
			
		||||
@rem Build the bootstrap interpreter
 | 
			
		||||
for %%f in (src\core\*.c) do (
 | 
			
		||||
    %JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
 | 
			
		||||
    @if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
    @if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
)
 | 
			
		||||
for %%f in (src\boot\*.c) do (
 | 
			
		||||
    %JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
 | 
			
		||||
    @if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
    @if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
)
 | 
			
		||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
@if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
build\janet_boot . > build\c\janet.c
 | 
			
		||||
@if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
 | 
			
		||||
@rem Build the sources
 | 
			
		||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
@if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
%JANET_COMPILE% /Fobuild\shell.obj src\mainclient\shell.c
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
@if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
 | 
			
		||||
@rem Build the resources
 | 
			
		||||
rc /nologo /fobuild\janet_win.res janet_win.rc
 | 
			
		||||
@if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
 | 
			
		||||
@rem Link everything to main client
 | 
			
		||||
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
@if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
 | 
			
		||||
@rem Build static library (libjanet.a)
 | 
			
		||||
@rem Build static library (libjanet.lib)
 | 
			
		||||
%JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
@if not errorlevel 0 goto :BUILDFAIL
 | 
			
		||||
 | 
			
		||||
echo === Successfully built janet.exe for Windows ===
 | 
			
		||||
echo === Run 'build_win test' to run tests. ==
 | 
			
		||||
@@ -89,16 +91,18 @@ exit /b 0
 | 
			
		||||
 | 
			
		||||
@rem Clean build artifacts
 | 
			
		||||
:CLEAN
 | 
			
		||||
del *.exe *.lib *.exp
 | 
			
		||||
del *.exe *.lib *.exp *.msi *.wixpdb
 | 
			
		||||
rd /s /q build
 | 
			
		||||
rd /s /q dist
 | 
			
		||||
if exist dist (
 | 
			
		||||
    rd /s /q dist
 | 
			
		||||
)
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
@rem Run tests
 | 
			
		||||
:TEST
 | 
			
		||||
for %%f in (test/suite*.janet) do (
 | 
			
		||||
    janet.exe test\%%f
 | 
			
		||||
    @if errorlevel 1 goto TESTFAIL
 | 
			
		||||
    @if not errorlevel 0 goto TESTFAIL
 | 
			
		||||
)
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
@@ -134,11 +138,18 @@ if defined APPVEYOR_REPO_TAG_NAME (
 | 
			
		||||
    set RELEASE_VERSION=%JANET_VERSION%
 | 
			
		||||
)
 | 
			
		||||
if defined CI (
 | 
			
		||||
    set WIXBIN="c:\Program Files (x86)\WiX Toolset v3.11\bin\"
 | 
			
		||||
    set WIXBIN="%WIX%bin\"
 | 
			
		||||
    echo WIXBIN = %WIXBIN%
 | 
			
		||||
) else (
 | 
			
		||||
    set WIXBIN=
 | 
			
		||||
)
 | 
			
		||||
%WIXBIN%candle.exe tools\msi\janet.wxs -arch %BUILDARCH% -out build\
 | 
			
		||||
 | 
			
		||||
set WIXARCH=%BUILDARCH%
 | 
			
		||||
if "%WIXARCH%"=="aarch64" (
 | 
			
		||||
    set WIXARCH=arm64
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
%WIXBIN%candle.exe tools\msi\janet.wxs -arch %WIXARCH% -out build\
 | 
			
		||||
%WIXBIN%light.exe "-sice:ICE38" -b tools\msi -ext WixUIExtension build\janet.wixobj -out janet-%RELEASE_VERSION%-windows-%BUILDARCH%-installer.msi
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								examples/chatserver.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								examples/chatserver.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
(def conmap @{})
 | 
			
		||||
 | 
			
		||||
(defn broadcast [em msg]
 | 
			
		||||
  (eachk par conmap
 | 
			
		||||
         (if (not= par em)
 | 
			
		||||
           (if-let [tar (get conmap par)]
 | 
			
		||||
             (net/write tar (string/format "[%s]:%s" em msg))))))
 | 
			
		||||
 | 
			
		||||
(defn handler
 | 
			
		||||
  [connection]
 | 
			
		||||
  (print "connection: " connection)
 | 
			
		||||
  (net/write connection "Whats your name?\n")
 | 
			
		||||
  (def name (string/trim (string (ev/read connection 100))))
 | 
			
		||||
  (print name " connected")
 | 
			
		||||
  (if (get conmap name)
 | 
			
		||||
    (do
 | 
			
		||||
      (net/write connection "Name already taken!")
 | 
			
		||||
      (:close connection))
 | 
			
		||||
    (do
 | 
			
		||||
      (put conmap name connection)
 | 
			
		||||
      (net/write connection (string/format "Welcome %s\n" name))
 | 
			
		||||
      (defer (do
 | 
			
		||||
               (put conmap name nil)
 | 
			
		||||
               (:close connection))
 | 
			
		||||
        (while (def msg (ev/read connection 100))
 | 
			
		||||
          (broadcast name (string msg)))
 | 
			
		||||
        (print name " disconnected")))))
 | 
			
		||||
 | 
			
		||||
(defn main [& args]
 | 
			
		||||
  (printf "STARTING SERVER...")
 | 
			
		||||
  (flush)
 | 
			
		||||
  (def my-server (net/listen "127.0.0.1" "8000"))
 | 
			
		||||
  (forever
 | 
			
		||||
   (def connection (net/accept my-server))
 | 
			
		||||
   (ev/call handler connection)))
 | 
			
		||||
@@ -35,6 +35,11 @@ typedef struct {
 | 
			
		||||
    int c;
 | 
			
		||||
} intintint;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint64_t a;
 | 
			
		||||
    uint64_t b;
 | 
			
		||||
} uint64pair;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int64_t a;
 | 
			
		||||
    int64_t b;
 | 
			
		||||
@@ -78,7 +83,6 @@ double double_lots(
 | 
			
		||||
    return i + j;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_lots_2(
 | 
			
		||||
    double a,
 | 
			
		||||
@@ -205,4 +209,19 @@ int sixints_fn_3(SixInts s, int x) {
 | 
			
		||||
    return x + s.u + s.v + s.w + s.x + s.y + s.z;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
intint stack_spill_fn(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
 | 
			
		||||
                      uint8_t e, uint8_t f, uint8_t g, uint8_t h,
 | 
			
		||||
                      float i, float j, float k, float l,
 | 
			
		||||
                      float m, float n, float o, float p,
 | 
			
		||||
                      float s1, int8_t s2, uint8_t s3, double s4, uint8_t s5, intint s6) {
 | 
			
		||||
    return (intint) {
 | 
			
		||||
        (a | b | c | d | e | f | g | h) + (i + j + k + l + m + n + o + p),
 | 
			
		||||
        s1 *s6.a + s2 *s6.b + s3 *s4 *s5
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double stack_spill_fn_2(uint64pair a, uint64pair b, uint64pair c, int8_t d, uint64pair e, int8_t f) {
 | 
			
		||||
    return (double)(a.a * c.a + a.b * c.b + b.a * e.a) * f - (double)(b.b * e.b) + d;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,11 +8,13 @@
 | 
			
		||||
 | 
			
		||||
(if is-windows
 | 
			
		||||
  (os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
 | 
			
		||||
  (os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px))
 | 
			
		||||
  (os/execute ["cc" ffi/source-loc "-g" "-shared" "-o" ffi/loc] :px))
 | 
			
		||||
 | 
			
		||||
(ffi/context ffi/loc)
 | 
			
		||||
 | 
			
		||||
(def intint (ffi/struct :int :int))
 | 
			
		||||
(def intintint (ffi/struct :int :int :int))
 | 
			
		||||
(def uint64pair (ffi/struct :u64 :u64))
 | 
			
		||||
(def big (ffi/struct :s64 :s64 :s64))
 | 
			
		||||
(def split (ffi/struct :int :int :float :float))
 | 
			
		||||
(def split-flip (ffi/struct :float :float :int :int))
 | 
			
		||||
@@ -55,6 +57,14 @@
 | 
			
		||||
(ffi/defbind sixints-fn six-ints [])
 | 
			
		||||
(ffi/defbind sixints-fn-2 :int [x :int s six-ints])
 | 
			
		||||
(ffi/defbind sixints-fn-3 :int [s six-ints x :int])
 | 
			
		||||
(ffi/defbind stack-spill-fn intint
 | 
			
		||||
             [a :u8 b :u8 c :u8 d :u8
 | 
			
		||||
              e :u8 f :u8 g :u8 h :u8
 | 
			
		||||
              i :float j :float k :float l :float
 | 
			
		||||
              m :float n :float o :float p :float
 | 
			
		||||
              s1 :float s2 :s8 s3 :u8 s4 :double s5 :u8 s6 intint])
 | 
			
		||||
(ffi/defbind stack-spill-fn-2 :double [a uint64pair b uint64pair c uint64pair d :s8 e uint64pair f :s8])
 | 
			
		||||
(ffi/defbind-alias int-fn int-fn-aliased :int [a :int b :int])
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Struct reading and writing
 | 
			
		||||
@@ -119,6 +129,7 @@
 | 
			
		||||
(tracev (return-struct 42))
 | 
			
		||||
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
 | 
			
		||||
(tracev (struct-big 11 99.5))
 | 
			
		||||
(tracev (int-fn-aliased 10 20))
 | 
			
		||||
 | 
			
		||||
(assert (= [10 10 12 12] (split-ret-fn 10 12)))
 | 
			
		||||
(assert (= [12 12 10 10] (split-flip-ret-fn 10 12)))
 | 
			
		||||
@@ -130,5 +141,10 @@
 | 
			
		||||
(assert (= 21 (math/round (double-many 1 2 3 4 5 6.01))))
 | 
			
		||||
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
 | 
			
		||||
(assert (= 204 (float-fn 8 4 17)))
 | 
			
		||||
(assert (= [0 38534415] (stack-spill-fn
 | 
			
		||||
                          0 0 0 0 0 0 0 0
 | 
			
		||||
                          0 0 0 0 0 0 0 0
 | 
			
		||||
                          1.5 -32 196 65536.5 3 [-15 32])))
 | 
			
		||||
(assert (= -2806 (stack-spill-fn-2 [2 3] [5 7] [9 11] -19 [13 17] -23)))
 | 
			
		||||
 | 
			
		||||
(print "Done.")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								examples/jitfn/hello.bin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/jitfn/hello.bin
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										17
									
								
								examples/jitfn/hello.nasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								examples/jitfn/hello.nasm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
BITS 64
 | 
			
		||||
 | 
			
		||||
;;;
 | 
			
		||||
;;; Code
 | 
			
		||||
;;;
 | 
			
		||||
mov rax, 1          ; write(
 | 
			
		||||
mov rdi, 1          ;   STDOUT_FILENO,
 | 
			
		||||
lea rsi, [rel msg]  ;   msg,
 | 
			
		||||
mov rdx, msglen     ;   sizeof(msg)
 | 
			
		||||
syscall             ; );
 | 
			
		||||
ret                 ; return;
 | 
			
		||||
 | 
			
		||||
;;;
 | 
			
		||||
;;; Constants
 | 
			
		||||
;;;
 | 
			
		||||
msg: db "Hello, world!", 10
 | 
			
		||||
msglen: equ $ - msg
 | 
			
		||||
							
								
								
									
										13
									
								
								examples/jitfn/jitfn.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								examples/jitfn/jitfn.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
###
 | 
			
		||||
### Relies on NASM being installed to assemble code.
 | 
			
		||||
### Only works on x86-64 Linux.
 | 
			
		||||
###
 | 
			
		||||
### Before running, compile hello.nasm to hello.bin with
 | 
			
		||||
### $ nasm hello.nasm -o hello.bin
 | 
			
		||||
 | 
			
		||||
(def bin (slurp "hello.bin"))
 | 
			
		||||
(def f (ffi/jitfn bin))
 | 
			
		||||
(def signature (ffi/signature :default :void))
 | 
			
		||||
(ffi/call f signature)
 | 
			
		||||
(print "called a jitted function with FFI!")
 | 
			
		||||
(print "machine code: " (describe (string/slice f)))
 | 
			
		||||
							
								
								
									
										5
									
								
								examples/posix-exec.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								examples/posix-exec.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
# Switch to python
 | 
			
		||||
 | 
			
		||||
(print "running in Janet")
 | 
			
		||||
(os/posix-exec ["python"] :p)
 | 
			
		||||
(print "will not print")
 | 
			
		||||
							
								
								
									
										1
									
								
								examples/sample-bad-bundle/badmod.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								examples/sample-bad-bundle/badmod.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
(def abc 123)
 | 
			
		||||
							
								
								
									
										7
									
								
								examples/sample-bad-bundle/bundle.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/sample-bad-bundle/bundle.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
(defn install
 | 
			
		||||
  [manifest &]
 | 
			
		||||
  (bundle/add-file manifest "badmod.janet"))
 | 
			
		||||
 | 
			
		||||
(defn check
 | 
			
		||||
  [&]
 | 
			
		||||
  (error "Check failed!"))
 | 
			
		||||
							
								
								
									
										1
									
								
								examples/sample-bundle-aliases/aliases-mod.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								examples/sample-bundle-aliases/aliases-mod.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
(defn fun [x] (range x))
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/sample-bundle-aliases/bundle.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-bundle-aliases/bundle.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
(defn install
 | 
			
		||||
  [manifest &]
 | 
			
		||||
  (bundle/add-file manifest "aliases-mod.janet"))
 | 
			
		||||
							
								
								
									
										4
									
								
								examples/sample-bundle-aliases/info.jdn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								examples/sample-bundle-aliases/info.jdn
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
@{
 | 
			
		||||
  :name "sample-bundle-aliases"
 | 
			
		||||
  :dependencies ["sample-dep1" "sample-dep2"]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								examples/sample-bundle/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								examples/sample-bundle/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
@{
 | 
			
		||||
  :name "sample-bundle"
 | 
			
		||||
  :dependencies ["sample-dep1" "sample-dep2"]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/sample-bundle/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-bundle/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
(defn install
 | 
			
		||||
  [manifest &]
 | 
			
		||||
  (bundle/add-file manifest "mymod.janet"))
 | 
			
		||||
							
								
								
									
										7
									
								
								examples/sample-bundle/mymod.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/sample-bundle/mymod.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
(import dep1)
 | 
			
		||||
(import dep2)
 | 
			
		||||
 | 
			
		||||
(defn myfn
 | 
			
		||||
  [x]
 | 
			
		||||
  (def y (dep2/function x))
 | 
			
		||||
  (dep1/function y))
 | 
			
		||||
							
								
								
									
										4
									
								
								examples/sample-dep1/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								examples/sample-dep1/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
@{
 | 
			
		||||
  :name "sample-dep1"
 | 
			
		||||
  :dependencies ["sample-dep2"]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/sample-dep1/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep1/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
(defn install
 | 
			
		||||
  [manifest &]
 | 
			
		||||
  (bundle/add-file manifest "dep1.janet"))
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/sample-dep1/dep1.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep1/dep1.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
(defn function
 | 
			
		||||
  [x]
 | 
			
		||||
  (+ x x))
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/sample-dep2/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep2/bundle/info.jdn
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
@{
 | 
			
		||||
  :name "sample-dep2"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/sample-dep2/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep2/bundle/init.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
(defn install
 | 
			
		||||
  [manifest &]
 | 
			
		||||
  (bundle/add-file manifest "dep2.janet"))
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/sample-dep2/dep2.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/sample-dep2/dep2.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
(defn function
 | 
			
		||||
  [x]
 | 
			
		||||
  (* x x))
 | 
			
		||||
							
								
								
									
										41
									
								
								examples/sigaction.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								examples/sigaction.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
###
 | 
			
		||||
### Usage: janet examples/sigaction.janet 1|2|3|4 &
 | 
			
		||||
###
 | 
			
		||||
### Then at shell: kill -s SIGTERM $!
 | 
			
		||||
###
 | 
			
		||||
 | 
			
		||||
(defn action
 | 
			
		||||
  []
 | 
			
		||||
  (print "Handled SIGTERM!")
 | 
			
		||||
  (flush)
 | 
			
		||||
  (os/exit 1))
 | 
			
		||||
 | 
			
		||||
(defn main1
 | 
			
		||||
  []
 | 
			
		||||
  (os/sigaction :term action true)
 | 
			
		||||
  (forever))
 | 
			
		||||
 | 
			
		||||
(defn main2
 | 
			
		||||
  []
 | 
			
		||||
  (os/sigaction :term action)
 | 
			
		||||
  (forever))
 | 
			
		||||
 | 
			
		||||
(defn main3
 | 
			
		||||
  []
 | 
			
		||||
  (os/sigaction :term action true)
 | 
			
		||||
  (forever (ev/sleep math/inf)))
 | 
			
		||||
 | 
			
		||||
(defn main4
 | 
			
		||||
  []
 | 
			
		||||
  (os/sigaction :term action)
 | 
			
		||||
  (forever (ev/sleep math/inf)))
 | 
			
		||||
 | 
			
		||||
(defn main
 | 
			
		||||
  [& args]
 | 
			
		||||
  (def which (scan-number (get args 1 "1")))
 | 
			
		||||
  (case which
 | 
			
		||||
    1 (main1) # should work
 | 
			
		||||
    2 (main2) # will not work
 | 
			
		||||
    3 (main3) # should work
 | 
			
		||||
    4 (main4) # should work
 | 
			
		||||
    (error "bad main")))
 | 
			
		||||
							
								
								
									
										20
									
								
								examples/weak-tables.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								examples/weak-tables.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
(def weak-k (table/weak-keys 10))
 | 
			
		||||
(def weak-v (table/weak-values 10))
 | 
			
		||||
(def weak-kv (table/weak 10))
 | 
			
		||||
 | 
			
		||||
(put weak-kv (gensym) 10)
 | 
			
		||||
(put weak-kv :hello :world)
 | 
			
		||||
(put weak-k :abc123zz77asda :stuff)
 | 
			
		||||
(put weak-k true :abc123zz77asda)
 | 
			
		||||
(put weak-k :zyzzyz false)
 | 
			
		||||
(put weak-v (gensym) 10)
 | 
			
		||||
(put weak-v 20 (gensym))
 | 
			
		||||
(print "before gc")
 | 
			
		||||
(tracev weak-k)
 | 
			
		||||
(tracev weak-v)
 | 
			
		||||
(tracev weak-kv)
 | 
			
		||||
(gccollect)
 | 
			
		||||
(print "after gc")
 | 
			
		||||
(tracev weak-k)
 | 
			
		||||
(tracev weak-v)
 | 
			
		||||
(tracev weak-kv)
 | 
			
		||||
							
								
								
									
										13
									
								
								janet.1
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								janet.1
									
									
									
									
									
								
							@@ -183,6 +183,10 @@ default repl.
 | 
			
		||||
.BR \-n
 | 
			
		||||
Disable ANSI colors in the repl. Has no effect if no repl is run.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-N
 | 
			
		||||
Enable ANSI colors in the repl. Has no effect if no repl is run.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-r
 | 
			
		||||
Open a REPL (Read Eval Print Loop) after executing all sources. By default, if Janet is called with no
 | 
			
		||||
@@ -251,7 +255,8 @@ and then arguments to the script.
 | 
			
		||||
.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.
 | 
			
		||||
the default location set at compile time. This should be a list of as well as a colon
 | 
			
		||||
separate list of such directories.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_PROFILE
 | 
			
		||||
@@ -268,5 +273,11 @@ This variable does nothing in the default configuration of Janet, as PRF is disa
 | 
			
		||||
cannot be defined for this variable to have an effect.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B NO_COLOR
 | 
			
		||||
.RS
 | 
			
		||||
Turn off color by default in the repl and in the error handler of scripts. This can be changed at runtime
 | 
			
		||||
via dynamic bindings *err-color* and *pretty-format*, or via the command line parameters -n and -N.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.SH AUTHOR
 | 
			
		||||
Written by Calvin Rose <calsrose@gmail.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										153
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								meson.build
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2021 Calvin Rose and contributors
 | 
			
		||||
# Copyright (c) 2025 Calvin Rose and contributors
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -20,16 +20,34 @@
 | 
			
		||||
 | 
			
		||||
project('janet', 'c',
 | 
			
		||||
  default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
 | 
			
		||||
  version : '1.25.0')
 | 
			
		||||
  version : '1.38.0')
 | 
			
		||||
 | 
			
		||||
# Global settings
 | 
			
		||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
 | 
			
		||||
header_path = join_paths(get_option('prefix'), get_option('includedir'), 'janet')
 | 
			
		||||
 | 
			
		||||
# Link math library on all systems
 | 
			
		||||
# Compilers
 | 
			
		||||
cc = meson.get_compiler('c')
 | 
			
		||||
native_cc = meson.get_compiler('c', native : true)
 | 
			
		||||
 | 
			
		||||
# Native deps
 | 
			
		||||
native_m_dep = native_cc.find_library('m', required : false)
 | 
			
		||||
native_dl_dep = native_cc.find_library('dl', required : false)
 | 
			
		||||
native_android_spawn_dep = native_cc.find_library('android-spawn', required : false)
 | 
			
		||||
native_thread_dep = dependency('threads', native : true)
 | 
			
		||||
 | 
			
		||||
# Deps
 | 
			
		||||
m_dep = cc.find_library('m', required : false)
 | 
			
		||||
dl_dep = cc.find_library('dl', required : false)
 | 
			
		||||
 | 
			
		||||
# for MINGW/MSYS2
 | 
			
		||||
native_ws2_dep = native_cc.find_library('ws2_32', required: false)
 | 
			
		||||
native_psapi_dep = native_cc.find_library('psapi', required: false)
 | 
			
		||||
native_wsock_dep = native_cc.find_library('wsock32', required: false)
 | 
			
		||||
ws2_dep = cc.find_library('ws2_32', required: false)
 | 
			
		||||
psapi_dep = cc.find_library('psapi', required: false)
 | 
			
		||||
wsock_dep = cc.find_library('wsock32', required: false)
 | 
			
		||||
 | 
			
		||||
android_spawn_dep = cc.find_library('android-spawn', required : false)
 | 
			
		||||
thread_dep = dependency('threads')
 | 
			
		||||
 | 
			
		||||
@@ -61,6 +79,7 @@ conf.set('JANET_NO_SOURCEMAPS', not get_option('sourcemaps'))
 | 
			
		||||
conf.set('JANET_NO_ASSEMBLER', not get_option('assembler'))
 | 
			
		||||
conf.set('JANET_NO_PEG', not get_option('peg'))
 | 
			
		||||
conf.set('JANET_NO_NET', not get_option('net'))
 | 
			
		||||
conf.set('JANET_NO_IPV6', not get_option('ipv6'))
 | 
			
		||||
conf.set('JANET_NO_EV', not get_option('ev') or get_option('single_threaded'))
 | 
			
		||||
conf.set('JANET_REDUCED_OS', get_option('reduced_os'))
 | 
			
		||||
conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
 | 
			
		||||
@@ -77,6 +96,9 @@ conf.set('JANET_EV_NO_EPOLL', not get_option('epoll'))
 | 
			
		||||
conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
 | 
			
		||||
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
 | 
			
		||||
conf.set('JANET_NO_FFI', not get_option('ffi'))
 | 
			
		||||
conf.set('JANET_NO_FFI_JIT', not get_option('ffi_jit'))
 | 
			
		||||
conf.set('JANET_NO_FILEWATCH', not get_option('filewatch'))
 | 
			
		||||
conf.set('JANET_NO_CRYPTORAND', not get_option('cryptorand'))
 | 
			
		||||
if get_option('os_name') != ''
 | 
			
		||||
  conf.set('JANET_OS_NAME', get_option('os_name'))
 | 
			
		||||
endif
 | 
			
		||||
@@ -119,6 +141,7 @@ core_src = [
 | 
			
		||||
  'src/core/ev.c',
 | 
			
		||||
  'src/core/ffi.c',
 | 
			
		||||
  'src/core/fiber.c',
 | 
			
		||||
  'src/core/filewatch.c',
 | 
			
		||||
  'src/core/gc.c',
 | 
			
		||||
  'src/core/inttypes.c',
 | 
			
		||||
  'src/core/io.c',
 | 
			
		||||
@@ -159,16 +182,23 @@ mainclient_src = [
 | 
			
		||||
  'src/mainclient/shell.c'
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
janet_dependencies = [m_dep, dl_dep, android_spawn_dep, ws2_dep, psapi_dep, wsock_dep]
 | 
			
		||||
janet_native_dependencies = [native_m_dep, native_dl_dep, native_android_spawn_dep, native_ws2_dep, native_psapi_dep, native_wsock_dep]
 | 
			
		||||
if not get_option('single_threaded')
 | 
			
		||||
  janet_dependencies += thread_dep
 | 
			
		||||
  janet_native_dependencies += native_thread_dep
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Build boot binary
 | 
			
		||||
janet_boot = executable('janet-boot', core_src, boot_src,
 | 
			
		||||
  include_directories : incdir,
 | 
			
		||||
  c_args : '-DJANET_BOOTSTRAP',
 | 
			
		||||
  dependencies : [m_dep, dl_dep, thread_dep, android_spawn_dep],
 | 
			
		||||
  dependencies : janet_native_dependencies,
 | 
			
		||||
  native : true)
 | 
			
		||||
 | 
			
		||||
# Build janet.c
 | 
			
		||||
janetc = custom_target('janetc',
 | 
			
		||||
  input : [janet_boot],
 | 
			
		||||
  input : [janet_boot, 'src/boot/boot.janet'],
 | 
			
		||||
  output : 'janet.c',
 | 
			
		||||
  capture : true,
 | 
			
		||||
  command : [
 | 
			
		||||
@@ -176,30 +206,41 @@ janetc = custom_target('janetc',
 | 
			
		||||
    'JANET_PATH', janet_path
 | 
			
		||||
  ])
 | 
			
		||||
 | 
			
		||||
janet_dependencies = [m_dep, dl_dep, android_spawn_dep]
 | 
			
		||||
if not get_option('single_threaded')
 | 
			
		||||
  janet_dependencies += thread_dep
 | 
			
		||||
# Allow building with no shared library
 | 
			
		||||
if cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
  lib_cflags = ['-fvisibility=hidden']
 | 
			
		||||
else
 | 
			
		||||
  lib_cflags = []
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
libjanet = library('janet', janetc,
 | 
			
		||||
  include_directories : incdir,
 | 
			
		||||
  dependencies : janet_dependencies,
 | 
			
		||||
  version: meson.project_version(),
 | 
			
		||||
  soversion: version_parts[0] + '.' + version_parts[1],
 | 
			
		||||
  install : true)
 | 
			
		||||
 | 
			
		||||
if get_option('shared')
 | 
			
		||||
  libjanet = library('janet', janetc,
 | 
			
		||||
    include_directories : incdir,
 | 
			
		||||
    dependencies : janet_dependencies,
 | 
			
		||||
    version: meson.project_version(),
 | 
			
		||||
    soversion: version_parts[0] + '.' + version_parts[1],
 | 
			
		||||
    c_args : lib_cflags,
 | 
			
		||||
    install : true)
 | 
			
		||||
# Extra c flags - adding -fvisibility=hidden matches the Makefile and
 | 
			
		||||
# shaves off about 10k on linux x64, likely similar on other platforms.
 | 
			
		||||
if cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
  extra_cflags = ['-fvisibility=hidden']
 | 
			
		||||
  if cc.has_argument('-fvisibility=hidden')
 | 
			
		||||
    extra_cflags = ['-fvisibility=hidden', '-DJANET_DLL_IMPORT']
 | 
			
		||||
  else
 | 
			
		||||
    extra_cflags = ['-DJANET_DLL_IMPORT']
 | 
			
		||||
  endif
 | 
			
		||||
  janet_mainclient = executable('janet', mainclient_src,
 | 
			
		||||
    include_directories : incdir,
 | 
			
		||||
    dependencies : janet_dependencies,
 | 
			
		||||
    link_with: [libjanet],
 | 
			
		||||
    c_args : extra_cflags,
 | 
			
		||||
    install : true)
 | 
			
		||||
else
 | 
			
		||||
  extra_cflags = []
 | 
			
		||||
  # No shared library
 | 
			
		||||
  janet_mainclient = executable('janet', mainclient_src, janetc,
 | 
			
		||||
    include_directories : incdir,
 | 
			
		||||
    dependencies : janet_dependencies,
 | 
			
		||||
    c_args : lib_cflags,
 | 
			
		||||
    install : true)
 | 
			
		||||
endif
 | 
			
		||||
janet_mainclient = executable('janet', janetc, mainclient_src,
 | 
			
		||||
  include_directories : incdir,
 | 
			
		||||
  dependencies : janet_dependencies,
 | 
			
		||||
  c_args : extra_cflags,
 | 
			
		||||
  install : true)
 | 
			
		||||
 | 
			
		||||
if meson.is_cross_build()
 | 
			
		||||
  native_cc = meson.get_compiler('c', native: true)
 | 
			
		||||
@@ -210,7 +251,7 @@ if meson.is_cross_build()
 | 
			
		||||
  endif
 | 
			
		||||
  janet_nativeclient = executable('janet-native', janetc, mainclient_src,
 | 
			
		||||
    include_directories : incdir,
 | 
			
		||||
    dependencies : janet_dependencies,
 | 
			
		||||
    dependencies : janet_native_dependencies,
 | 
			
		||||
    c_args : extra_native_cflags,
 | 
			
		||||
    native : true)
 | 
			
		||||
else
 | 
			
		||||
@@ -226,17 +267,37 @@ docs = custom_target('docs',
 | 
			
		||||
 | 
			
		||||
# Tests
 | 
			
		||||
test_files = [
 | 
			
		||||
  'test/suite0000.janet',
 | 
			
		||||
  'test/suite0001.janet',
 | 
			
		||||
  'test/suite0002.janet',
 | 
			
		||||
  'test/suite0003.janet',
 | 
			
		||||
  'test/suite0004.janet',
 | 
			
		||||
  'test/suite0005.janet',
 | 
			
		||||
  'test/suite0006.janet',
 | 
			
		||||
  'test/suite0007.janet',
 | 
			
		||||
  'test/suite0008.janet',
 | 
			
		||||
  'test/suite0009.janet',
 | 
			
		||||
  'test/suite0010.janet'
 | 
			
		||||
  'test/suite-array.janet',
 | 
			
		||||
  'test/suite-asm.janet',
 | 
			
		||||
  'test/suite-boot.janet',
 | 
			
		||||
  'test/suite-buffer.janet',
 | 
			
		||||
  'test/suite-bundle.janet',
 | 
			
		||||
  'test/suite-capi.janet',
 | 
			
		||||
  'test/suite-cfuns.janet',
 | 
			
		||||
  'test/suite-compile.janet',
 | 
			
		||||
  'test/suite-corelib.janet',
 | 
			
		||||
  'test/suite-debug.janet',
 | 
			
		||||
  'test/suite-ev.janet',
 | 
			
		||||
  'test/suite-ffi.janet',
 | 
			
		||||
  'test/suite-filewatch.janet',
 | 
			
		||||
  'test/suite-inttypes.janet',
 | 
			
		||||
  'test/suite-io.janet',
 | 
			
		||||
  'test/suite-marsh.janet',
 | 
			
		||||
  'test/suite-math.janet',
 | 
			
		||||
  'test/suite-os.janet',
 | 
			
		||||
  'test/suite-parse.janet',
 | 
			
		||||
  'test/suite-peg.janet',
 | 
			
		||||
  'test/suite-pp.janet',
 | 
			
		||||
  'test/suite-specials.janet',
 | 
			
		||||
  'test/suite-string.janet',
 | 
			
		||||
  'test/suite-strtod.janet',
 | 
			
		||||
  'test/suite-struct.janet',
 | 
			
		||||
  'test/suite-symcache.janet',
 | 
			
		||||
  'test/suite-table.janet',
 | 
			
		||||
  'test/suite-tuple.janet',
 | 
			
		||||
  'test/suite-unknown.janet',
 | 
			
		||||
  'test/suite-value.janet',
 | 
			
		||||
  'test/suite-vm.janet'
 | 
			
		||||
]
 | 
			
		||||
foreach t : test_files
 | 
			
		||||
  test(t, janet_nativeclient, args : files([t]), workdir : meson.current_source_dir())
 | 
			
		||||
@@ -246,14 +307,15 @@ endforeach
 | 
			
		||||
run_target('repl', command : [janet_nativeclient])
 | 
			
		||||
 | 
			
		||||
# For use as meson subproject (wrap)
 | 
			
		||||
janet_dep = declare_dependency(include_directories : incdir,
 | 
			
		||||
  link_with : libjanet)
 | 
			
		||||
 | 
			
		||||
if get_option('shared')
 | 
			
		||||
  janet_dep = declare_dependency(include_directories : incdir,
 | 
			
		||||
    link_with : libjanet)
 | 
			
		||||
# pkgconfig
 | 
			
		||||
pkg = import('pkgconfig')
 | 
			
		||||
pkg.generate(libjanet,
 | 
			
		||||
  subdirs: 'janet',
 | 
			
		||||
  description: 'Library for the Janet programming language.')
 | 
			
		||||
  pkg = import('pkgconfig')
 | 
			
		||||
  pkg.generate(libjanet,
 | 
			
		||||
    subdirs: 'janet',
 | 
			
		||||
    description: 'Library for the Janet programming language.')
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# Installation
 | 
			
		||||
install_man('janet.1')
 | 
			
		||||
@@ -263,11 +325,12 @@ patched_janet = custom_target('patched-janeth',
 | 
			
		||||
  install : true,
 | 
			
		||||
  install_dir : join_paths(get_option('includedir'), 'janet'),
 | 
			
		||||
  build_by_default : true,
 | 
			
		||||
  output : ['janet.h'],
 | 
			
		||||
  output : ['janet_' + meson.project_version() + '.h'],
 | 
			
		||||
  command : [janet_nativeclient, '@INPUT@', '@OUTPUT@'])
 | 
			
		||||
 | 
			
		||||
# Create a version of the janet.h header that matches what jpm often expects
 | 
			
		||||
if meson.version().version_compare('>=0.61')
 | 
			
		||||
  install_symlink('janet.h', pointing_to: 'janet/janet.h', install_dir: get_option('includedir'))
 | 
			
		||||
  install_symlink('janet.h', pointing_to: 'janet/janet_' + meson.project_version() + '.h', install_dir: get_option('includedir'))
 | 
			
		||||
  install_symlink('janet.h', pointing_to: 'janet_' + meson.project_version() + '.h', install_dir: join_paths(get_option('includedir'), 'janet'))
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,15 +11,18 @@ option('peg', type : 'boolean', value : true)
 | 
			
		||||
option('int_types', type : 'boolean', value : true)
 | 
			
		||||
option('prf', type : 'boolean', value : false)
 | 
			
		||||
option('net', type : 'boolean', value : true)
 | 
			
		||||
option('ipv6', type : 'boolean', value : true)
 | 
			
		||||
option('ev', type : 'boolean', value : true)
 | 
			
		||||
option('processes', type : 'boolean', value : true)
 | 
			
		||||
option('umask', type : 'boolean', value : true)
 | 
			
		||||
option('realpath', type : 'boolean', value : true)
 | 
			
		||||
option('simple_getline', type : 'boolean', value : false)
 | 
			
		||||
option('epoll', type : 'boolean', value : false)
 | 
			
		||||
option('kqueue', type : 'boolean', value : false)
 | 
			
		||||
option('interpreter_interrupt', type : 'boolean', value : false)
 | 
			
		||||
option('epoll', type : 'boolean', value : true)
 | 
			
		||||
option('kqueue', type : 'boolean', value : true)
 | 
			
		||||
option('interpreter_interrupt', type : 'boolean', value : true)
 | 
			
		||||
option('ffi', type : 'boolean', value : true)
 | 
			
		||||
option('ffi_jit', type : 'boolean', value : true)
 | 
			
		||||
option('filewatch', type : 'boolean', value : true)
 | 
			
		||||
 | 
			
		||||
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
 | 
			
		||||
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)
 | 
			
		||||
@@ -28,3 +31,5 @@ option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7f
 | 
			
		||||
 | 
			
		||||
option('arch_name', type : 'string', value: '')
 | 
			
		||||
option('os_name', type : 'string', value: '')
 | 
			
		||||
option('shared', type : 'boolean', value: true)
 | 
			
		||||
option('cryptorand', type : 'boolean', value: true)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1958
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
							
						
						
									
										1958
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -22,7 +22,7 @@
 | 
			
		||||
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#include "tests.h"
 | 
			
		||||
@@ -35,6 +35,11 @@ int system_test() {
 | 
			
		||||
    assert(sizeof(void *) == 8);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Check the version defines are self consistent */
 | 
			
		||||
    char version_combined[256];
 | 
			
		||||
    sprintf(version_combined, "%d.%d.%d%s", JANET_VERSION_MAJOR, JANET_VERSION_MINOR, JANET_VERSION_PATCH, JANET_VERSION_EXTRA);
 | 
			
		||||
    assert(!strcmp(JANET_VERSION, version_combined));
 | 
			
		||||
 | 
			
		||||
    /* Reflexive testing and nanbox testing */
 | 
			
		||||
    assert(janet_equals(janet_wrap_nil(), janet_wrap_nil()));
 | 
			
		||||
    assert(janet_equals(janet_wrap_false(), janet_wrap_false()));
 | 
			
		||||
@@ -70,6 +75,5 @@ int system_test() {
 | 
			
		||||
 | 
			
		||||
    assert(janet_equals(tuple1, tuple2));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,10 @@
 | 
			
		||||
#define JANETCONF_H
 | 
			
		||||
 | 
			
		||||
#define JANET_VERSION_MAJOR 1
 | 
			
		||||
#define JANET_VERSION_MINOR 25
 | 
			
		||||
#define JANET_VERSION_MINOR 38
 | 
			
		||||
#define JANET_VERSION_PATCH 0
 | 
			
		||||
#define JANET_VERSION_EXTRA ""
 | 
			
		||||
#define JANET_VERSION "1.25.0"
 | 
			
		||||
#define JANET_VERSION "1.38.0"
 | 
			
		||||
 | 
			
		||||
/* #define JANET_BUILD "local" */
 | 
			
		||||
 | 
			
		||||
@@ -29,10 +29,13 @@
 | 
			
		||||
/* #define JANET_NO_NET */
 | 
			
		||||
/* #define JANET_NO_INT_TYPES */
 | 
			
		||||
/* #define JANET_NO_EV */
 | 
			
		||||
/* #define JANET_NO_FILEWATCH */
 | 
			
		||||
/* #define JANET_NO_REALPATH */
 | 
			
		||||
/* #define JANET_NO_SYMLINKS */
 | 
			
		||||
/* #define JANET_NO_UMASK */
 | 
			
		||||
/* #define JANET_NO_THREADS */
 | 
			
		||||
/* #define JANET_NO_FFI */
 | 
			
		||||
/* #define JANET_NO_FFI_JIT */
 | 
			
		||||
 | 
			
		||||
/* Other settings */
 | 
			
		||||
/* #define JANET_DEBUG */
 | 
			
		||||
@@ -50,6 +53,9 @@
 | 
			
		||||
/* #define JANET_EV_NO_EPOLL */
 | 
			
		||||
/* #define JANET_EV_NO_KQUEUE */
 | 
			
		||||
/* #define JANET_NO_INTERPRETER_INTERRUPT */
 | 
			
		||||
/* #define JANET_NO_IPV6 */
 | 
			
		||||
/* #define JANET_NO_CRYPTORAND */
 | 
			
		||||
/* #define JANET_USE_STDATOMIC */
 | 
			
		||||
 | 
			
		||||
/* Custom vm allocator support */
 | 
			
		||||
/* #include <mimalloc.h> */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -95,14 +95,6 @@ size_t janet_os_rwlock_size(void) {
 | 
			
		||||
    return sizeof(void *);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_incref(JanetAbstractHead *ab) {
 | 
			
		||||
    return InterlockedIncrement(&ab->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_decref(JanetAbstractHead *ab) {
 | 
			
		||||
    return InterlockedDecrement(&ab->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
 | 
			
		||||
    InitializeCriticalSection((CRITICAL_SECTION *) mutex);
 | 
			
		||||
}
 | 
			
		||||
@@ -155,14 +147,6 @@ size_t janet_os_rwlock_size(void) {
 | 
			
		||||
    return sizeof(pthread_rwlock_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_incref(JanetAbstractHead *ab) {
 | 
			
		||||
    return __atomic_add_fetch(&ab->gc.data.refcount, 1, __ATOMIC_RELAXED);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_decref(JanetAbstractHead *ab) {
 | 
			
		||||
    return __atomic_add_fetch(&ab->gc.data.refcount, -1, __ATOMIC_RELAXED);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutexattr_t attr;
 | 
			
		||||
    pthread_mutexattr_init(&attr);
 | 
			
		||||
@@ -210,11 +194,11 @@ void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int32_t janet_abstract_incref(void *abst) {
 | 
			
		||||
    return janet_incref(janet_abstract_head(abst));
 | 
			
		||||
    return janet_atomic_inc(&janet_abstract_head(abst)->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t janet_abstract_decref(void *abst) {
 | 
			
		||||
    return janet_decref(janet_abstract_head(abst));
 | 
			
		||||
    return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -30,9 +30,7 @@
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
/* Creates a new array */
 | 
			
		||||
JanetArray *janet_array(int32_t capacity) {
 | 
			
		||||
    JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
 | 
			
		||||
static void janet_array_impl(JanetArray *array, int32_t capacity) {
 | 
			
		||||
    Janet *data = NULL;
 | 
			
		||||
    if (capacity > 0) {
 | 
			
		||||
        janet_vm.next_collection += capacity * sizeof(Janet);
 | 
			
		||||
@@ -44,6 +42,19 @@ JanetArray *janet_array(int32_t capacity) {
 | 
			
		||||
    array->count = 0;
 | 
			
		||||
    array->capacity = capacity;
 | 
			
		||||
    array->data = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Creates a new array */
 | 
			
		||||
JanetArray *janet_array(int32_t capacity) {
 | 
			
		||||
    JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
 | 
			
		||||
    janet_array_impl(array, capacity);
 | 
			
		||||
    return array;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Creates a new array with weak references */
 | 
			
		||||
JanetArray *janet_array_weak(int32_t capacity) {
 | 
			
		||||
    JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY_WEAK, sizeof(JanetArray));
 | 
			
		||||
    janet_array_impl(array, capacity);
 | 
			
		||||
    return array;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -132,6 +143,15 @@ JANET_CORE_FN(cfun_array_new,
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_array_weak,
 | 
			
		||||
              "(array/weak capacity)",
 | 
			
		||||
              "Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getinteger(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array_weak(cap);
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_array_new_filled,
 | 
			
		||||
              "(array/new-filled count &opt value)",
 | 
			
		||||
              "Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") {
 | 
			
		||||
@@ -177,8 +197,8 @@ JANET_CORE_FN(cfun_array_peek,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_array_push,
 | 
			
		||||
              "(array/push arr x)",
 | 
			
		||||
              "Insert an element in the end of an array. Modifies the input array and returns it.") {
 | 
			
		||||
              "(array/push arr & xs)",
 | 
			
		||||
              "Push all the elements of xs to the end of an array. Modifies the input array and returns it.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    if (INT32_MAX - argc + 1 <= array->count) {
 | 
			
		||||
@@ -211,7 +231,7 @@ JANET_CORE_FN(cfun_array_slice,
 | 
			
		||||
              "Takes a slice of array or tuple from `start` to `end`. The range is half open, "
 | 
			
		||||
              "[start, end). Indexes can also be negative, indicating indexing from the "
 | 
			
		||||
              "end of the array. By default, `start` is 0 and `end` is the length of the array. "
 | 
			
		||||
              "Note that index -1 is synonymous with index `(length arrtup)` to allow a full "
 | 
			
		||||
              "Note that if the range is negative, it is taken as (start, end] to allow a full "
 | 
			
		||||
              "negative slice range. Returns a new array.") {
 | 
			
		||||
    JanetView view = janet_getindexed(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
@@ -255,12 +275,37 @@ JANET_CORE_FN(cfun_array_concat,
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_array_join,
 | 
			
		||||
              "(array/join arr & parts)",
 | 
			
		||||
              "Join a variable number of arrays and tuples into the first argument, "
 | 
			
		||||
              "which must be an array. "
 | 
			
		||||
              "Return the modified array `arr`.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    for (i = 1; i < argc; i++) {
 | 
			
		||||
        int32_t j, len = 0;
 | 
			
		||||
        const Janet *vals = NULL;
 | 
			
		||||
        if (!janet_indexed_view(argv[i], &vals, &len)) {
 | 
			
		||||
            janet_panicf("expected indexed type for argument %d, got %v", i, argv[i]);
 | 
			
		||||
        }
 | 
			
		||||
        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]);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_array_insert,
 | 
			
		||||
              "(array/insert arr at & xs)",
 | 
			
		||||
              "Insert all `xs` into array `arr` at index `at`. `at` should be an integer between "
 | 
			
		||||
              "0 and the length of the array. A negative value for `at` will index backwards from "
 | 
			
		||||
              "the end of the array, such that inserting at -1 appends to the array. "
 | 
			
		||||
              "Returns the array.") {
 | 
			
		||||
              "the end of the array, inserting after the index such that inserting at -1 appends to "
 | 
			
		||||
              "the array. Returns the array.") {
 | 
			
		||||
    size_t chunksize, restsize;
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
@@ -297,7 +342,7 @@ JANET_CORE_FN(cfun_array_remove,
 | 
			
		||||
    int32_t at = janet_getinteger(argv, 1);
 | 
			
		||||
    int32_t n = 1;
 | 
			
		||||
    if (at < 0) {
 | 
			
		||||
        at = array->count + at + 1;
 | 
			
		||||
        at = array->count + at;
 | 
			
		||||
    }
 | 
			
		||||
    if (at < 0 || at > array->count)
 | 
			
		||||
        janet_panicf("removal index %d out of range [0,%d]", at, array->count);
 | 
			
		||||
@@ -352,6 +397,7 @@ JANET_CORE_FN(cfun_array_clear,
 | 
			
		||||
void janet_lib_array(JanetTable *env) {
 | 
			
		||||
    JanetRegExt array_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("array/new", cfun_array_new),
 | 
			
		||||
        JANET_CORE_REG("array/weak", cfun_array_weak),
 | 
			
		||||
        JANET_CORE_REG("array/new-filled", cfun_array_new_filled),
 | 
			
		||||
        JANET_CORE_REG("array/fill", cfun_array_fill),
 | 
			
		||||
        JANET_CORE_REG("array/pop", cfun_array_pop),
 | 
			
		||||
@@ -364,6 +410,7 @@ void janet_lib_array(JanetTable *env) {
 | 
			
		||||
        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_CORE_REG("array/join", cfun_array_join),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, array_cfuns);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										108
									
								
								src/core/asm.c
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								src/core/asm.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -75,6 +75,7 @@ static const JanetInstructionDef janet_ops[] = {
 | 
			
		||||
    {"cmp", JOP_COMPARE},
 | 
			
		||||
    {"cncl", JOP_CANCEL},
 | 
			
		||||
    {"div", JOP_DIVIDE},
 | 
			
		||||
    {"divf", JOP_DIVIDE_FLOOR},
 | 
			
		||||
    {"divim", JOP_DIVIDE_IMMEDIATE},
 | 
			
		||||
    {"eq", JOP_EQUALS},
 | 
			
		||||
    {"eqim", JOP_EQUALS_IMMEDIATE},
 | 
			
		||||
@@ -137,6 +138,7 @@ static const JanetInstructionDef janet_ops[] = {
 | 
			
		||||
    {"sru", JOP_SHIFT_RIGHT_UNSIGNED},
 | 
			
		||||
    {"sruim", JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE},
 | 
			
		||||
    {"sub", JOP_SUBTRACT},
 | 
			
		||||
    {"subim", JOP_SUBTRACT_IMMEDIATE},
 | 
			
		||||
    {"tcall", JOP_TAILCALL},
 | 
			
		||||
    {"tchck", JOP_TYPECHECK}
 | 
			
		||||
};
 | 
			
		||||
@@ -187,7 +189,11 @@ static void janet_asm_longjmp(JanetAssembler *a) {
 | 
			
		||||
 | 
			
		||||
/* Throw some kind of assembly error */
 | 
			
		||||
static void janet_asm_error(JanetAssembler *a, const char *message) {
 | 
			
		||||
    a->errmessage = janet_formatc("%s, instruction %d", message, a->errindex);
 | 
			
		||||
    if (a->errindex < 0) {
 | 
			
		||||
        a->errmessage = janet_formatc("%s", message);
 | 
			
		||||
    } else {
 | 
			
		||||
        a->errmessage = janet_formatc("%s, instruction %d", message, a->errindex);
 | 
			
		||||
    }
 | 
			
		||||
    janet_asm_longjmp(a);
 | 
			
		||||
}
 | 
			
		||||
#define janet_asm_assert(a, c, m) do { if (!(c)) janet_asm_error((a), (m)); } while (0)
 | 
			
		||||
@@ -516,6 +522,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
#endif
 | 
			
		||||
        if (NULL != a.parent) {
 | 
			
		||||
            janet_asm_deinit(&a);
 | 
			
		||||
            a.parent->errmessage = a.errmessage;
 | 
			
		||||
            janet_asm_longjmp(a.parent);
 | 
			
		||||
        }
 | 
			
		||||
        result.funcdef = NULL;
 | 
			
		||||
@@ -553,6 +560,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("vararg"));
 | 
			
		||||
    if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
 | 
			
		||||
 | 
			
		||||
    /* Initialize slotcount */
 | 
			
		||||
    def->slotcount = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG) + def->arity;
 | 
			
		||||
 | 
			
		||||
    /* Check structarg */
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("structarg"));
 | 
			
		||||
    if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
 | 
			
		||||
@@ -601,6 +611,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
 | 
			
		||||
    /* Parse sub funcdefs */
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("closures"));
 | 
			
		||||
    if (janet_checktype(x, JANET_NIL)) {
 | 
			
		||||
        x = janet_get1(s, janet_ckeywordv("defs"));
 | 
			
		||||
    }
 | 
			
		||||
    if (janet_indexed_view(x, &arr, &count)) {
 | 
			
		||||
        int32_t i;
 | 
			
		||||
        for (i = 0; i < count; i++) {
 | 
			
		||||
@@ -713,16 +726,70 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set symbolmap */
 | 
			
		||||
    def->symbolmap = NULL;
 | 
			
		||||
    def->symbolmap_length = 0;
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("symbolmap"));
 | 
			
		||||
    if (janet_indexed_view(x, &arr, &count)) {
 | 
			
		||||
        def->symbolmap_length = count;
 | 
			
		||||
        def->symbolmap = janet_malloc(sizeof(JanetSymbolMap) * (size_t)count);
 | 
			
		||||
        if (NULL == def->symbolmap) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        for (i = 0; i < count; i++) {
 | 
			
		||||
            const Janet *tup;
 | 
			
		||||
            Janet entry = arr[i];
 | 
			
		||||
            JanetSymbolMap ss;
 | 
			
		||||
            if (!janet_checktype(entry, JANET_TUPLE)) {
 | 
			
		||||
                janet_asm_error(&a, "expected tuple");
 | 
			
		||||
            }
 | 
			
		||||
            tup = janet_unwrap_tuple(entry);
 | 
			
		||||
            if (janet_keyeq(tup[0], "upvalue")) {
 | 
			
		||||
                ss.birth_pc = UINT32_MAX;
 | 
			
		||||
            } else if (!janet_checkint(tup[0])) {
 | 
			
		||||
                janet_asm_error(&a, "expected integer");
 | 
			
		||||
            } else {
 | 
			
		||||
                ss.birth_pc = janet_unwrap_integer(tup[0]);
 | 
			
		||||
            }
 | 
			
		||||
            if (!janet_checkint(tup[1])) {
 | 
			
		||||
                janet_asm_error(&a, "expected integer");
 | 
			
		||||
            }
 | 
			
		||||
            if (!janet_checkint(tup[2])) {
 | 
			
		||||
                janet_asm_error(&a, "expected integer");
 | 
			
		||||
            }
 | 
			
		||||
            if (!janet_checktype(tup[3], JANET_SYMBOL)) {
 | 
			
		||||
                janet_asm_error(&a, "expected symbol");
 | 
			
		||||
            }
 | 
			
		||||
            ss.death_pc = janet_unwrap_integer(tup[1]);
 | 
			
		||||
            ss.slot_index = janet_unwrap_integer(tup[2]);
 | 
			
		||||
            ss.symbol = janet_unwrap_symbol(tup[3]);
 | 
			
		||||
            def->symbolmap[i] = ss;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (def->symbolmap_length) def->flags |= JANET_FUNCDEF_FLAG_HASSYMBOLMAP;
 | 
			
		||||
 | 
			
		||||
    /* Set environments */
 | 
			
		||||
    def->environments =
 | 
			
		||||
        janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
 | 
			
		||||
    if (NULL == def->environments) {
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("environments"));
 | 
			
		||||
    if (janet_indexed_view(x, &arr, &count)) {
 | 
			
		||||
        def->environments_length = count;
 | 
			
		||||
        if (def->environments_length) {
 | 
			
		||||
            def->environments = janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
 | 
			
		||||
        }
 | 
			
		||||
        for (int32_t i = 0; i < count; i++) {
 | 
			
		||||
            if (!janet_checkint(arr[i])) {
 | 
			
		||||
                janet_asm_error(&a, "expected integer");
 | 
			
		||||
            }
 | 
			
		||||
            def->environments[i] = janet_unwrap_integer(arr[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (def->environments_length && NULL == def->environments) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Verify the func def */
 | 
			
		||||
    if (janet_verify(def)) {
 | 
			
		||||
        janet_asm_error(&a, "invalid assembly");
 | 
			
		||||
    int verify_status = janet_verify(def);
 | 
			
		||||
    if (verify_status) {
 | 
			
		||||
        janet_asm_errorv(&a, janet_formatc("invalid assembly (%d)", verify_status));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Add final flags */
 | 
			
		||||
@@ -865,6 +932,29 @@ static Janet janet_disasm_slotcount(JanetFuncDef *def) {
 | 
			
		||||
    return janet_wrap_integer(def->slotcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_symbolslots(JanetFuncDef *def) {
 | 
			
		||||
    if (def->symbolmap == NULL) {
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    JanetArray *symbolslots = janet_array(def->symbolmap_length);
 | 
			
		||||
    Janet upvaluekw = janet_ckeywordv("upvalue");
 | 
			
		||||
    for (int32_t i = 0; i < def->symbolmap_length; i++) {
 | 
			
		||||
        JanetSymbolMap ss = def->symbolmap[i];
 | 
			
		||||
        Janet *t = janet_tuple_begin(4);
 | 
			
		||||
        if (ss.birth_pc == UINT32_MAX) {
 | 
			
		||||
            t[0] = upvaluekw;
 | 
			
		||||
        } else {
 | 
			
		||||
            t[0] = janet_wrap_integer(ss.birth_pc);
 | 
			
		||||
        }
 | 
			
		||||
        t[1] = janet_wrap_integer(ss.death_pc);
 | 
			
		||||
        t[2] = janet_wrap_integer(ss.slot_index);
 | 
			
		||||
        t[3] = janet_wrap_symbol(ss.symbol);
 | 
			
		||||
        symbolslots->data[i] = janet_wrap_tuple(janet_tuple_end(t));
 | 
			
		||||
    }
 | 
			
		||||
    symbolslots->count = def->symbolmap_length;
 | 
			
		||||
    return janet_wrap_array(symbolslots);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_bytecode(JanetFuncDef *def) {
 | 
			
		||||
    JanetArray *bcode = janet_array(def->bytecode_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
@@ -944,6 +1034,7 @@ Janet janet_disasm(JanetFuncDef *def) {
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("symbolmap"), janet_disasm_symbolslots(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("constants"), janet_disasm_constants(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("sourcemap"), janet_disasm_sourcemap(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("environments"), janet_disasm_environments(def));
 | 
			
		||||
@@ -961,7 +1052,7 @@ JANET_CORE_FN(cfun_asm,
 | 
			
		||||
    JanetAssembleResult res;
 | 
			
		||||
    res = janet_asm(argv[0], 0);
 | 
			
		||||
    if (res.status != JANET_ASSEMBLE_OK) {
 | 
			
		||||
        janet_panics(res.error);
 | 
			
		||||
        janet_panics(res.error ? res.error : janet_cstring("invalid assembly"));
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_function(janet_thunk(res.funcdef));
 | 
			
		||||
}
 | 
			
		||||
@@ -980,6 +1071,7 @@ JANET_CORE_FN(cfun_disasm,
 | 
			
		||||
              "* :source - name of source file that this function was compiled from.\n"
 | 
			
		||||
              "* :name - name of function.\n"
 | 
			
		||||
              "* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n"
 | 
			
		||||
              "* :symbolmap - all symbols and their slots.\n"
 | 
			
		||||
              "* :constants - an array of constants referenced by this function.\n"
 | 
			
		||||
              "* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n"
 | 
			
		||||
              "* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -28,8 +28,15 @@
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Allow for managed buffers that cannot realloc/free their backing memory */
 | 
			
		||||
static void janet_buffer_can_realloc(JanetBuffer *buffer) {
 | 
			
		||||
    if (buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC) {
 | 
			
		||||
        janet_panic("buffer cannot reallocate foreign memory");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize a buffer */
 | 
			
		||||
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
 | 
			
		||||
static JanetBuffer *janet_buffer_init_impl(JanetBuffer *buffer, int32_t capacity) {
 | 
			
		||||
    uint8_t *data = NULL;
 | 
			
		||||
    if (capacity < 4) capacity = 4;
 | 
			
		||||
    janet_gcpressure(capacity);
 | 
			
		||||
@@ -43,15 +50,37 @@ JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
 | 
			
		||||
    return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize a buffer */
 | 
			
		||||
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
 | 
			
		||||
    janet_buffer_init_impl(buffer, capacity);
 | 
			
		||||
    buffer->gc.data.next = NULL;
 | 
			
		||||
    buffer->gc.flags = JANET_MEM_DISABLED;
 | 
			
		||||
    return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize an unmanaged buffer */
 | 
			
		||||
JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t count) {
 | 
			
		||||
    if (count < 0) janet_panic("count < 0");
 | 
			
		||||
    if (capacity < count) janet_panic("capacity < count");
 | 
			
		||||
    JanetBuffer *buffer = janet_gcalloc(JANET_MEMORY_BUFFER, sizeof(JanetBuffer));
 | 
			
		||||
    buffer->gc.flags |= JANET_BUFFER_FLAG_NO_REALLOC;
 | 
			
		||||
    buffer->capacity = capacity;
 | 
			
		||||
    buffer->count = count;
 | 
			
		||||
    buffer->data = (uint8_t *) memory;
 | 
			
		||||
    return buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Deinitialize a buffer (free data memory) */
 | 
			
		||||
void janet_buffer_deinit(JanetBuffer *buffer) {
 | 
			
		||||
    janet_free(buffer->data);
 | 
			
		||||
    if (!(buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
 | 
			
		||||
        janet_free(buffer->data);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize a buffer */
 | 
			
		||||
JanetBuffer *janet_buffer(int32_t capacity) {
 | 
			
		||||
    JanetBuffer *buffer = janet_gcalloc(JANET_MEMORY_BUFFER, sizeof(JanetBuffer));
 | 
			
		||||
    return janet_buffer_init(buffer, capacity);
 | 
			
		||||
    return janet_buffer_init_impl(buffer, capacity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ensure that the buffer has enough internal capacity */
 | 
			
		||||
@@ -59,6 +88,7 @@ void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth)
 | 
			
		||||
    uint8_t *new_data;
 | 
			
		||||
    uint8_t *old = buffer->data;
 | 
			
		||||
    if (capacity <= buffer->capacity) return;
 | 
			
		||||
    janet_buffer_can_realloc(buffer);
 | 
			
		||||
    int64_t big_capacity = ((int64_t) capacity) * growth;
 | 
			
		||||
    capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity;
 | 
			
		||||
    janet_gcpressure(capacity - buffer->capacity);
 | 
			
		||||
@@ -91,6 +121,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
 | 
			
		||||
    }
 | 
			
		||||
    int32_t new_size = buffer->count + n;
 | 
			
		||||
    if (new_size > buffer->capacity) {
 | 
			
		||||
        janet_buffer_can_realloc(buffer);
 | 
			
		||||
        int32_t new_capacity = (new_size > (INT32_MAX / 2)) ? INT32_MAX : (new_size * 2);
 | 
			
		||||
        uint8_t *new_data = janet_realloc(buffer->data, new_capacity * sizeof(uint8_t));
 | 
			
		||||
        janet_gcpressure(new_capacity - buffer->capacity);
 | 
			
		||||
@@ -104,8 +135,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
 | 
			
		||||
 | 
			
		||||
/* Push a cstring to buffer */
 | 
			
		||||
void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) {
 | 
			
		||||
    int32_t len = 0;
 | 
			
		||||
    while (cstring[len]) ++len;
 | 
			
		||||
    int32_t len = (int32_t) strlen(cstring);
 | 
			
		||||
    janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -190,6 +220,20 @@ JANET_CORE_FN(cfun_buffer_new_filled,
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_frombytes,
 | 
			
		||||
              "(buffer/from-bytes & byte-vals)",
 | 
			
		||||
              "Creates a buffer from integer parameters with byte values. All integers "
 | 
			
		||||
              "will be coerced to the range of 1 byte 0-255.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(argc);
 | 
			
		||||
    for (i = 0; i < argc; i++) {
 | 
			
		||||
        int32_t c = janet_getinteger(argv, i);
 | 
			
		||||
        buffer->data[i] = c & 0xFF;
 | 
			
		||||
    }
 | 
			
		||||
    buffer->count = argc;
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_fill,
 | 
			
		||||
              "(buffer/fill buffer &opt byte)",
 | 
			
		||||
              "Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. "
 | 
			
		||||
@@ -212,6 +256,7 @@ JANET_CORE_FN(cfun_buffer_trim,
 | 
			
		||||
              "modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    janet_buffer_can_realloc(buffer);
 | 
			
		||||
    if (buffer->count < buffer->capacity) {
 | 
			
		||||
        int32_t newcap = buffer->count > 4 ? buffer->count : 4;
 | 
			
		||||
        uint8_t *newData = janet_realloc(buffer->data, newcap);
 | 
			
		||||
@@ -275,17 +320,135 @@ JANET_CORE_FN(cfun_buffer_chars,
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push,
 | 
			
		||||
              "(buffer/push buffer & xs)",
 | 
			
		||||
              "Push both individual bytes and byte sequences to a buffer. For each x in xs, "
 | 
			
		||||
              "push the byte if x is an integer, otherwise push the bytesequence to the buffer. "
 | 
			
		||||
              "Thus, this function behaves like both `buffer/push-string` and `buffer/push-byte`. "
 | 
			
		||||
              "Returns the modified buffer. "
 | 
			
		||||
              "Will throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
static int should_reverse_bytes(const Janet *argv, int32_t argc) {
 | 
			
		||||
    JanetKeyword order_kw = janet_getkeyword(argv, argc);
 | 
			
		||||
    if (!janet_cstrcmp(order_kw, "le")) {
 | 
			
		||||
#if JANET_BIG_ENDIAN
 | 
			
		||||
        return 1;
 | 
			
		||||
#endif
 | 
			
		||||
    } else if (!janet_cstrcmp(order_kw, "be")) {
 | 
			
		||||
#if JANET_LITTLE_ENDIAN
 | 
			
		||||
        return 1;
 | 
			
		||||
#endif
 | 
			
		||||
    } else if (!janet_cstrcmp(order_kw, "native")) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panicf("expected endianness :le, :be or :native, got %v", argv[1]);
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void reverse_u32(uint8_t bytes[4]) {
 | 
			
		||||
    uint8_t temp;
 | 
			
		||||
    temp = bytes[3];
 | 
			
		||||
    bytes[3] = bytes[0];
 | 
			
		||||
    bytes[0] = temp;
 | 
			
		||||
    temp = bytes[2];
 | 
			
		||||
    bytes[2] = bytes[1];
 | 
			
		||||
    bytes[1] = temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void reverse_u64(uint8_t bytes[8]) {
 | 
			
		||||
    uint8_t temp;
 | 
			
		||||
    temp = bytes[7];
 | 
			
		||||
    bytes[7] = bytes[0];
 | 
			
		||||
    bytes[0] = temp;
 | 
			
		||||
    temp = bytes[6];
 | 
			
		||||
    bytes[6] = bytes[1];
 | 
			
		||||
    bytes[1] = temp;
 | 
			
		||||
    temp = bytes[5];
 | 
			
		||||
    bytes[5] = bytes[2];
 | 
			
		||||
    bytes[2] = temp;
 | 
			
		||||
    temp = bytes[4];
 | 
			
		||||
    bytes[4] = bytes[3];
 | 
			
		||||
    bytes[3] = temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_uint16,
 | 
			
		||||
              "(buffer/push-uint16 buffer order data)",
 | 
			
		||||
              "Push a 16 bit unsigned integer data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    for (i = 1; i < argc; i++) {
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    uint16_t data = janet_getuinteger16(argv, 2);
 | 
			
		||||
    uint8_t bytes[sizeof(data)];
 | 
			
		||||
    memcpy(bytes, &data, sizeof(bytes));
 | 
			
		||||
    if (reverse) {
 | 
			
		||||
        uint8_t temp = bytes[1];
 | 
			
		||||
        bytes[1] = bytes[0];
 | 
			
		||||
        bytes[0] = temp;
 | 
			
		||||
    }
 | 
			
		||||
    janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_uint32,
 | 
			
		||||
              "(buffer/push-uint32 buffer order data)",
 | 
			
		||||
              "Push a 32 bit unsigned integer data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    uint32_t data = janet_getuinteger(argv, 2);
 | 
			
		||||
    uint8_t bytes[sizeof(data)];
 | 
			
		||||
    memcpy(bytes, &data, sizeof(bytes));
 | 
			
		||||
    if (reverse)
 | 
			
		||||
        reverse_u32(bytes);
 | 
			
		||||
    janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_uint64,
 | 
			
		||||
              "(buffer/push-uint64 buffer order data)",
 | 
			
		||||
              "Push a 64 bit unsigned integer data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    uint64_t data = janet_getuinteger64(argv, 2);
 | 
			
		||||
    uint8_t bytes[sizeof(data)];
 | 
			
		||||
    memcpy(bytes, &data, sizeof(bytes));
 | 
			
		||||
    if (reverse)
 | 
			
		||||
        reverse_u64(bytes);
 | 
			
		||||
    janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_float32,
 | 
			
		||||
              "(buffer/push-float32 buffer order data)",
 | 
			
		||||
              "Push the underlying bytes of a 32 bit float data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    float data = (float) janet_getnumber(argv, 2);
 | 
			
		||||
    uint8_t bytes[sizeof(data)];
 | 
			
		||||
    memcpy(bytes, &data, sizeof(bytes));
 | 
			
		||||
    if (reverse)
 | 
			
		||||
        reverse_u32(bytes);
 | 
			
		||||
    janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_float64,
 | 
			
		||||
              "(buffer/push-float64 buffer order data)",
 | 
			
		||||
              "Push the underlying bytes of a 64 bit float data onto the end of the buffer. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int reverse = should_reverse_bytes(argv, 1);
 | 
			
		||||
    double data = janet_getnumber(argv, 2);
 | 
			
		||||
    uint8_t bytes[sizeof(data)];
 | 
			
		||||
    memcpy(bytes, &data, sizeof(bytes));
 | 
			
		||||
    if (reverse)
 | 
			
		||||
        reverse_u64(bytes);
 | 
			
		||||
    janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offset, int32_t argc) {
 | 
			
		||||
    for (int32_t i = argc_offset; i < argc; i++) {
 | 
			
		||||
        if (janet_checktype(argv[i], JANET_NUMBER)) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, (uint8_t)(janet_getinteger(argv, i) & 0xFF));
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -297,9 +460,39 @@ JANET_CORE_FN(cfun_buffer_push,
 | 
			
		||||
            janet_buffer_push_bytes(buffer, view.bytes, view.len);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push_at,
 | 
			
		||||
              "(buffer/push-at buffer index & xs)",
 | 
			
		||||
              "Same as buffer/push, but copies the new data into the buffer "
 | 
			
		||||
              " at index `index`.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int32_t index = janet_getinteger(argv, 1);
 | 
			
		||||
    int32_t old_count = buffer->count;
 | 
			
		||||
    if (index < 0 || index > old_count) {
 | 
			
		||||
        janet_panicf("index out of range [0, %d)", old_count);
 | 
			
		||||
    }
 | 
			
		||||
    buffer->count = index;
 | 
			
		||||
    buffer_push_impl(buffer, argv, 2, argc);
 | 
			
		||||
    if (buffer->count < old_count) {
 | 
			
		||||
        buffer->count = old_count;
 | 
			
		||||
    }
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_push,
 | 
			
		||||
              "(buffer/push buffer & xs)",
 | 
			
		||||
              "Push both individual bytes and byte sequences to a buffer. For each x in xs, "
 | 
			
		||||
              "push the byte if x is an integer, otherwise push the bytesequence to the buffer. "
 | 
			
		||||
              "Thus, this function behaves like both `buffer/push-string` and `buffer/push-byte`. "
 | 
			
		||||
              "Returns the modified buffer. "
 | 
			
		||||
              "Will throw an error if the buffer overflows.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    buffer_push_impl(buffer, argv, 1, argc);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_clear,
 | 
			
		||||
              "(buffer/clear buffer)",
 | 
			
		||||
@@ -409,13 +602,15 @@ JANET_CORE_FN(cfun_buffer_blit,
 | 
			
		||||
    int same_buf = src.bytes == dest->data;
 | 
			
		||||
    int32_t offset_dest = 0;
 | 
			
		||||
    int32_t offset_src = 0;
 | 
			
		||||
    if (argc > 2)
 | 
			
		||||
    if (argc > 2 && !janet_checktype(argv[2], JANET_NIL))
 | 
			
		||||
        offset_dest = janet_gethalfrange(argv, 2, dest->count, "dest-start");
 | 
			
		||||
    if (argc > 3)
 | 
			
		||||
    if (argc > 3 && !janet_checktype(argv[3], JANET_NIL))
 | 
			
		||||
        offset_src = janet_gethalfrange(argv, 3, src.len, "src-start");
 | 
			
		||||
    int32_t length_src;
 | 
			
		||||
    if (argc > 4) {
 | 
			
		||||
        int32_t src_end = janet_gethalfrange(argv, 4, src.len, "src-end");
 | 
			
		||||
        int32_t src_end = src.len;
 | 
			
		||||
        if (!janet_checktype(argv[4], JANET_NIL))
 | 
			
		||||
            src_end = janet_gethalfrange(argv, 4, src.len, "src-end");
 | 
			
		||||
        length_src = src_end - offset_src;
 | 
			
		||||
        if (length_src < 0) length_src = 0;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -442,7 +637,7 @@ JANET_CORE_FN(cfun_buffer_blit,
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_format,
 | 
			
		||||
              "(buffer/format buffer format & args)",
 | 
			
		||||
              "Snprintf like functionality for printing values into a buffer. Returns "
 | 
			
		||||
              " the modified buffer.") {
 | 
			
		||||
              "the modified buffer.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    const char *strfrmt = (const char *) janet_getstring(argv, 1);
 | 
			
		||||
@@ -450,16 +645,44 @@ JANET_CORE_FN(cfun_buffer_format,
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_format_at,
 | 
			
		||||
              "(buffer/format-at buffer at format & args)",
 | 
			
		||||
              "Snprintf like functionality for printing values into a buffer. Returns "
 | 
			
		||||
              "the modified buffer.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int32_t at = janet_getinteger(argv, 1);
 | 
			
		||||
    if (at < 0) {
 | 
			
		||||
        at += buffer->count + 1;
 | 
			
		||||
    }
 | 
			
		||||
    if (at > buffer->count || at < 0) janet_panicf("expected index at to be in range [0, %d), got %d", buffer->count, at);
 | 
			
		||||
    int32_t oldcount = buffer->count;
 | 
			
		||||
    buffer->count = at;
 | 
			
		||||
    const char *strfrmt = (const char *) janet_getstring(argv, 2);
 | 
			
		||||
    janet_buffer_format(buffer, strfrmt, 2, argc, argv);
 | 
			
		||||
    if (buffer->count < oldcount) {
 | 
			
		||||
        buffer->count = oldcount;
 | 
			
		||||
    }
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_lib_buffer(JanetTable *env) {
 | 
			
		||||
    JanetRegExt buffer_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("buffer/new", cfun_buffer_new),
 | 
			
		||||
        JANET_CORE_REG("buffer/new-filled", cfun_buffer_new_filled),
 | 
			
		||||
        JANET_CORE_REG("buffer/from-bytes", cfun_buffer_frombytes),
 | 
			
		||||
        JANET_CORE_REG("buffer/fill", cfun_buffer_fill),
 | 
			
		||||
        JANET_CORE_REG("buffer/trim", cfun_buffer_trim),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-byte", cfun_buffer_u8),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-word", cfun_buffer_word),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-string", cfun_buffer_chars),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-uint16", cfun_buffer_push_uint16),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-uint32", cfun_buffer_push_uint32),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-uint64", cfun_buffer_push_uint64),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-float32", cfun_buffer_push_float32),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-float64", cfun_buffer_push_float64),
 | 
			
		||||
        JANET_CORE_REG("buffer/push", cfun_buffer_push),
 | 
			
		||||
        JANET_CORE_REG("buffer/push-at", cfun_buffer_push_at),
 | 
			
		||||
        JANET_CORE_REG("buffer/popn", cfun_buffer_popn),
 | 
			
		||||
        JANET_CORE_REG("buffer/clear", cfun_buffer_clear),
 | 
			
		||||
        JANET_CORE_REG("buffer/slice", cfun_buffer_slice),
 | 
			
		||||
@@ -469,6 +692,7 @@ void janet_lib_buffer(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle),
 | 
			
		||||
        JANET_CORE_REG("buffer/blit", cfun_buffer_blit),
 | 
			
		||||
        JANET_CORE_REG("buffer/format", cfun_buffer_format),
 | 
			
		||||
        JANET_CORE_REG("buffer/format-at", cfun_buffer_format_at),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, buffer_cfuns);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "regalloc.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Look up table for instructions */
 | 
			
		||||
@@ -36,11 +37,13 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
 | 
			
		||||
    JINT_0, /* JOP_RETURN_NIL, */
 | 
			
		||||
    JINT_SSI, /* JOP_ADD_IMMEDIATE, */
 | 
			
		||||
    JINT_SSS, /* JOP_ADD, */
 | 
			
		||||
    JINT_SSI, /* JOP_SUBTRACT_IMMEDIATE, */
 | 
			
		||||
    JINT_SSS, /* JOP_SUBTRACT, */
 | 
			
		||||
    JINT_SSI, /* JOP_MULTIPLY_IMMEDIATE, */
 | 
			
		||||
    JINT_SSS, /* JOP_MULTIPLY, */
 | 
			
		||||
    JINT_SSI, /* JOP_DIVIDE_IMMEDIATE, */
 | 
			
		||||
    JINT_SSS, /* JOP_DIVIDE, */
 | 
			
		||||
    JINT_SSS, /* JOP_DIVIDE_FLOOR */
 | 
			
		||||
    JINT_SSS, /* JOP_MODULO, */
 | 
			
		||||
    JINT_SSS, /* JOP_REMAINDER, */
 | 
			
		||||
    JINT_SSS, /* JOP_BAND, */
 | 
			
		||||
@@ -106,6 +109,294 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
 | 
			
		||||
    JINT_SSS /* JOP_CANCEL, */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Remove all noops while preserving jumps and debugging information.
 | 
			
		||||
 * Useful as part of a filtering compiler pass. */
 | 
			
		||||
void janet_bytecode_remove_noops(JanetFuncDef *def) {
 | 
			
		||||
 | 
			
		||||
    /* Get an instruction rewrite map so we can rewrite jumps */
 | 
			
		||||
    uint32_t *pc_map = janet_smalloc(sizeof(uint32_t) * (1 + def->bytecode_length));
 | 
			
		||||
    uint32_t new_bytecode_length = 0;
 | 
			
		||||
    for (int32_t i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
        uint32_t instr = def->bytecode[i];
 | 
			
		||||
        uint32_t opcode = instr & 0x7F;
 | 
			
		||||
        pc_map[i] = new_bytecode_length;
 | 
			
		||||
        if (opcode != JOP_NOOP) {
 | 
			
		||||
            new_bytecode_length++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pc_map[def->bytecode_length] = new_bytecode_length;
 | 
			
		||||
 | 
			
		||||
    /* Linear scan rewrite bytecode and sourcemap. Also fix jumps. */
 | 
			
		||||
    int32_t j = 0;
 | 
			
		||||
    for (int32_t i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
        uint32_t instr = def->bytecode[i];
 | 
			
		||||
        uint32_t opcode = instr & 0x7F;
 | 
			
		||||
        int32_t old_jump_target = 0;
 | 
			
		||||
        int32_t new_jump_target = 0;
 | 
			
		||||
        switch (opcode) {
 | 
			
		||||
            case JOP_NOOP:
 | 
			
		||||
                continue;
 | 
			
		||||
            case JOP_JUMP:
 | 
			
		||||
                /* relative pc is in DS field of instruction */
 | 
			
		||||
                old_jump_target = i + (((int32_t)instr) >> 8);
 | 
			
		||||
                new_jump_target = pc_map[old_jump_target];
 | 
			
		||||
                instr += (uint32_t)(new_jump_target - old_jump_target + (i - j)) << 8;
 | 
			
		||||
                break;
 | 
			
		||||
            case JOP_JUMP_IF:
 | 
			
		||||
            case JOP_JUMP_IF_NIL:
 | 
			
		||||
            case JOP_JUMP_IF_NOT:
 | 
			
		||||
            case JOP_JUMP_IF_NOT_NIL:
 | 
			
		||||
                /* relative pc is in ES field of instruction */
 | 
			
		||||
                old_jump_target = i + (((int32_t)instr) >> 16);
 | 
			
		||||
                new_jump_target = pc_map[old_jump_target];
 | 
			
		||||
                instr += (uint32_t)(new_jump_target - old_jump_target + (i - j)) << 16;
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        def->bytecode[j] = instr;
 | 
			
		||||
        if (def->sourcemap != NULL) {
 | 
			
		||||
            def->sourcemap[j] = def->sourcemap[i];
 | 
			
		||||
        }
 | 
			
		||||
        j++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Rewrite symbolmap */
 | 
			
		||||
    for (int32_t i = 0; i < def->symbolmap_length; i++) {
 | 
			
		||||
        JanetSymbolMap *sm = def->symbolmap + i;
 | 
			
		||||
        /* Don't rewrite upvalue mappings */
 | 
			
		||||
        if (sm->birth_pc < UINT32_MAX) {
 | 
			
		||||
            sm->birth_pc = pc_map[sm->birth_pc];
 | 
			
		||||
            sm->death_pc = pc_map[sm->death_pc];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def->bytecode_length = new_bytecode_length;
 | 
			
		||||
    def->bytecode = janet_realloc(def->bytecode, def->bytecode_length * sizeof(uint32_t));
 | 
			
		||||
    janet_sfree(pc_map);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Remove redundant loads, moves and other instructions if possible and convert them to
 | 
			
		||||
 * noops. Input is assumed valid bytecode. */
 | 
			
		||||
void janet_bytecode_movopt(JanetFuncDef *def) {
 | 
			
		||||
    JanetcRegisterAllocator ra;
 | 
			
		||||
    int recur = 1;
 | 
			
		||||
 | 
			
		||||
    /* Iterate this until no more instructions can be removed. */
 | 
			
		||||
    while (recur) {
 | 
			
		||||
        janetc_regalloc_init(&ra);
 | 
			
		||||
 | 
			
		||||
        /* Look for slots that have writes but no reads (and aren't in the closure bitset). */
 | 
			
		||||
        if (def->closure_bitset != NULL) {
 | 
			
		||||
            for (int32_t i = 0; i < def->slotcount; i++) {
 | 
			
		||||
                int32_t index = i >> 5;
 | 
			
		||||
                uint32_t mask = 1U << (((uint32_t) i) & 31);
 | 
			
		||||
                if (def->closure_bitset[index] & mask) {
 | 
			
		||||
                    janetc_regalloc_touch(&ra, i);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#define AA ((instr >> 8)  & 0xFF)
 | 
			
		||||
#define BB ((instr >> 16) & 0xFF)
 | 
			
		||||
#define CC (instr >> 24)
 | 
			
		||||
#define DD (instr >> 8)
 | 
			
		||||
#define EE (instr >> 16)
 | 
			
		||||
 | 
			
		||||
        /* Check reads and writes */
 | 
			
		||||
        for (int32_t i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
            uint32_t instr = def->bytecode[i];
 | 
			
		||||
            switch (instr & 0x7F) {
 | 
			
		||||
 | 
			
		||||
                /* Group instructions my how they read from slots */
 | 
			
		||||
 | 
			
		||||
                /* No reads or writes */
 | 
			
		||||
                default:
 | 
			
		||||
                    janet_assert(0, "unhandled instruction");
 | 
			
		||||
                case JOP_JUMP:
 | 
			
		||||
                case JOP_NOOP:
 | 
			
		||||
                case JOP_RETURN_NIL:
 | 
			
		||||
                /* Write A */
 | 
			
		||||
                case JOP_LOAD_INTEGER:
 | 
			
		||||
                case JOP_LOAD_CONSTANT:
 | 
			
		||||
                case JOP_LOAD_UPVALUE:
 | 
			
		||||
                case JOP_CLOSURE:
 | 
			
		||||
                /* Write D */
 | 
			
		||||
                case JOP_LOAD_NIL:
 | 
			
		||||
                case JOP_LOAD_TRUE:
 | 
			
		||||
                case JOP_LOAD_FALSE:
 | 
			
		||||
                case JOP_LOAD_SELF:
 | 
			
		||||
                    break;
 | 
			
		||||
                case JOP_MAKE_ARRAY:
 | 
			
		||||
                case JOP_MAKE_BUFFER:
 | 
			
		||||
                case JOP_MAKE_STRING:
 | 
			
		||||
                case JOP_MAKE_STRUCT:
 | 
			
		||||
                case JOP_MAKE_TABLE:
 | 
			
		||||
                case JOP_MAKE_TUPLE:
 | 
			
		||||
                case JOP_MAKE_BRACKET_TUPLE:
 | 
			
		||||
                    /* Reads from the stack, don't remove */
 | 
			
		||||
                    janetc_regalloc_touch(&ra, DD);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Read A */
 | 
			
		||||
                case JOP_ERROR:
 | 
			
		||||
                case JOP_TYPECHECK:
 | 
			
		||||
                case JOP_JUMP_IF:
 | 
			
		||||
                case JOP_JUMP_IF_NOT:
 | 
			
		||||
                case JOP_JUMP_IF_NIL:
 | 
			
		||||
                case JOP_JUMP_IF_NOT_NIL:
 | 
			
		||||
                case JOP_SET_UPVALUE:
 | 
			
		||||
                /* Write E, Read A */
 | 
			
		||||
                case JOP_MOVE_FAR:
 | 
			
		||||
                    janetc_regalloc_touch(&ra, AA);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Read B */
 | 
			
		||||
                case JOP_SIGNAL:
 | 
			
		||||
                /* Write A, Read B */
 | 
			
		||||
                case JOP_ADD_IMMEDIATE:
 | 
			
		||||
                case JOP_SUBTRACT_IMMEDIATE:
 | 
			
		||||
                case JOP_MULTIPLY_IMMEDIATE:
 | 
			
		||||
                case JOP_DIVIDE_IMMEDIATE:
 | 
			
		||||
                case JOP_SHIFT_LEFT_IMMEDIATE:
 | 
			
		||||
                case JOP_SHIFT_RIGHT_IMMEDIATE:
 | 
			
		||||
                case JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE:
 | 
			
		||||
                case JOP_GREATER_THAN_IMMEDIATE:
 | 
			
		||||
                case JOP_LESS_THAN_IMMEDIATE:
 | 
			
		||||
                case JOP_EQUALS_IMMEDIATE:
 | 
			
		||||
                case JOP_NOT_EQUALS_IMMEDIATE:
 | 
			
		||||
                case JOP_GET_INDEX:
 | 
			
		||||
                    janetc_regalloc_touch(&ra, BB);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Read D */
 | 
			
		||||
                case JOP_RETURN:
 | 
			
		||||
                case JOP_PUSH:
 | 
			
		||||
                case JOP_PUSH_ARRAY:
 | 
			
		||||
                case JOP_TAILCALL:
 | 
			
		||||
                    janetc_regalloc_touch(&ra, DD);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Write A, Read E */
 | 
			
		||||
                case JOP_MOVE_NEAR:
 | 
			
		||||
                case JOP_LENGTH:
 | 
			
		||||
                case JOP_BNOT:
 | 
			
		||||
                case JOP_CALL:
 | 
			
		||||
                    janetc_regalloc_touch(&ra, EE);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Read A, B */
 | 
			
		||||
                case JOP_PUT_INDEX:
 | 
			
		||||
                    janetc_regalloc_touch(&ra, AA);
 | 
			
		||||
                    janetc_regalloc_touch(&ra, BB);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Read A, E */
 | 
			
		||||
                case JOP_PUSH_2:
 | 
			
		||||
                    janetc_regalloc_touch(&ra, AA);
 | 
			
		||||
                    janetc_regalloc_touch(&ra, EE);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Read B, C */
 | 
			
		||||
                case JOP_PROPAGATE:
 | 
			
		||||
                /* Write A, Read B and C */
 | 
			
		||||
                case JOP_BAND:
 | 
			
		||||
                case JOP_BOR:
 | 
			
		||||
                case JOP_BXOR:
 | 
			
		||||
                case JOP_ADD:
 | 
			
		||||
                case JOP_SUBTRACT:
 | 
			
		||||
                case JOP_MULTIPLY:
 | 
			
		||||
                case JOP_DIVIDE:
 | 
			
		||||
                case JOP_DIVIDE_FLOOR:
 | 
			
		||||
                case JOP_MODULO:
 | 
			
		||||
                case JOP_REMAINDER:
 | 
			
		||||
                case JOP_SHIFT_LEFT:
 | 
			
		||||
                case JOP_SHIFT_RIGHT:
 | 
			
		||||
                case JOP_SHIFT_RIGHT_UNSIGNED:
 | 
			
		||||
                case JOP_GREATER_THAN:
 | 
			
		||||
                case JOP_LESS_THAN:
 | 
			
		||||
                case JOP_EQUALS:
 | 
			
		||||
                case JOP_COMPARE:
 | 
			
		||||
                case JOP_IN:
 | 
			
		||||
                case JOP_GET:
 | 
			
		||||
                case JOP_GREATER_THAN_EQUAL:
 | 
			
		||||
                case JOP_LESS_THAN_EQUAL:
 | 
			
		||||
                case JOP_NOT_EQUALS:
 | 
			
		||||
                case JOP_CANCEL:
 | 
			
		||||
                case JOP_RESUME:
 | 
			
		||||
                case JOP_NEXT:
 | 
			
		||||
                    janetc_regalloc_touch(&ra, BB);
 | 
			
		||||
                    janetc_regalloc_touch(&ra, CC);
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                /* Read A, B, C */
 | 
			
		||||
                case JOP_PUT:
 | 
			
		||||
                case JOP_PUSH_3:
 | 
			
		||||
                    janetc_regalloc_touch(&ra, AA);
 | 
			
		||||
                    janetc_regalloc_touch(&ra, BB);
 | 
			
		||||
                    janetc_regalloc_touch(&ra, CC);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Iterate and set noops on instructions that make writes that no one ever reads.
 | 
			
		||||
         * Only set noops for instructions with no side effects - moves, loads, etc. that can't
 | 
			
		||||
         * raise errors (outside of systemic errors like oom or stack overflow). */
 | 
			
		||||
        recur = 0;
 | 
			
		||||
        for (int32_t i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
            uint32_t instr = def->bytecode[i];
 | 
			
		||||
            switch (instr & 0x7F) {
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
                /* Write D */
 | 
			
		||||
                case JOP_LOAD_NIL:
 | 
			
		||||
                case JOP_LOAD_TRUE:
 | 
			
		||||
                case JOP_LOAD_FALSE:
 | 
			
		||||
                case JOP_LOAD_SELF:
 | 
			
		||||
                case JOP_MAKE_ARRAY:
 | 
			
		||||
                case JOP_MAKE_TUPLE:
 | 
			
		||||
                case JOP_MAKE_BRACKET_TUPLE: {
 | 
			
		||||
                    if (!janetc_regalloc_check(&ra, DD)) {
 | 
			
		||||
                        def->bytecode[i] = JOP_NOOP;
 | 
			
		||||
                        recur = 1;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
                /* Write E, Read A */
 | 
			
		||||
                case JOP_MOVE_FAR: {
 | 
			
		||||
                    if (!janetc_regalloc_check(&ra, EE)) {
 | 
			
		||||
                        def->bytecode[i] = JOP_NOOP;
 | 
			
		||||
                        recur = 1;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
                /* Write A, Read E */
 | 
			
		||||
                case JOP_MOVE_NEAR:
 | 
			
		||||
                /* Write A, Read B */
 | 
			
		||||
                case JOP_GET_INDEX:
 | 
			
		||||
                /* Write A */
 | 
			
		||||
                case JOP_LOAD_INTEGER:
 | 
			
		||||
                case JOP_LOAD_CONSTANT:
 | 
			
		||||
                case JOP_LOAD_UPVALUE:
 | 
			
		||||
                case JOP_CLOSURE: {
 | 
			
		||||
                    if (!janetc_regalloc_check(&ra, AA)) {
 | 
			
		||||
                        def->bytecode[i] = JOP_NOOP;
 | 
			
		||||
                        recur = 1;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        janetc_regalloc_deinit(&ra);
 | 
			
		||||
#undef AA
 | 
			
		||||
#undef BB
 | 
			
		||||
#undef CC
 | 
			
		||||
#undef DD
 | 
			
		||||
#undef EE
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Verify some bytecode */
 | 
			
		||||
int janet_verify(JanetFuncDef *def) {
 | 
			
		||||
    int vargs = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG);
 | 
			
		||||
@@ -218,6 +509,7 @@ JanetFuncDef *janet_funcdef_alloc(void) {
 | 
			
		||||
    def->closure_bitset = NULL;
 | 
			
		||||
    def->flags = 0;
 | 
			
		||||
    def->slotcount = 0;
 | 
			
		||||
    def->symbolmap = NULL;
 | 
			
		||||
    def->arity = 0;
 | 
			
		||||
    def->min_arity = 0;
 | 
			
		||||
    def->max_arity = INT32_MAX;
 | 
			
		||||
@@ -229,6 +521,7 @@ JanetFuncDef *janet_funcdef_alloc(void) {
 | 
			
		||||
    def->constants_length = 0;
 | 
			
		||||
    def->bytecode_length = 0;
 | 
			
		||||
    def->environments_length = 0;
 | 
			
		||||
    def->symbolmap_length = 0;
 | 
			
		||||
    return def;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										200
									
								
								src/core/capi.c
									
									
									
									
									
								
							
							
						
						
									
										200
									
								
								src/core/capi.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,14 +25,24 @@
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#include "fiber.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_SINGLE_THREADED
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#else
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_USE_STDATOMIC
 | 
			
		||||
#include <stdatomic.h>
 | 
			
		||||
/* We don't need stdatomic on most compilers since we use compiler builtins for atomic operations.
 | 
			
		||||
 * Some (TCC), explicitly require using stdatomic.h and don't have any exposed builtins (that I know of).
 | 
			
		||||
 * For TCC and similar compilers, one would need -std=c11 or similar then to get access. */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
 | 
			
		||||
@@ -52,6 +62,18 @@ JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
 | 
			
		||||
 | 
			
		||||
void janet_signalv(JanetSignal sig, Janet message) {
 | 
			
		||||
    if (janet_vm.return_reg != NULL) {
 | 
			
		||||
        /* Should match logic in janet_call for coercing everything not ok to an error (no awaits, yields, etc.) */
 | 
			
		||||
        if (janet_vm.coerce_error && sig != JANET_SIGNAL_OK) {
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
            if (NULL != janet_vm.root_fiber && sig == JANET_SIGNAL_EVENT) {
 | 
			
		||||
                janet_vm.root_fiber->sched_id++;
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
            if (sig != JANET_SIGNAL_ERROR) {
 | 
			
		||||
                message = janet_wrap_string(janet_formatc("%v coerced from %s to error", message, janet_signal_names[sig]));
 | 
			
		||||
            }
 | 
			
		||||
            sig = JANET_SIGNAL_ERROR;
 | 
			
		||||
        }
 | 
			
		||||
        *janet_vm.return_reg = message;
 | 
			
		||||
        if (NULL != janet_vm.fiber) {
 | 
			
		||||
            janet_vm.fiber->flags |= JANET_FIBER_DID_LONGJUMP;
 | 
			
		||||
@@ -209,12 +231,46 @@ const char *janet_optcstring(const Janet *argv, int32_t argc, int32_t n, const c
 | 
			
		||||
#undef DEFINE_OPTLEN
 | 
			
		||||
 | 
			
		||||
const char *janet_getcstring(const Janet *argv, int32_t n) {
 | 
			
		||||
    const uint8_t *jstr = janet_getstring(argv, n);
 | 
			
		||||
    const char *cstr = (const char *)jstr;
 | 
			
		||||
    if (strlen(cstr) != (size_t) janet_string_length(jstr)) {
 | 
			
		||||
        janet_panic("string contains embedded 0s");
 | 
			
		||||
    if (!janet_checktype(argv[n], JANET_STRING)) {
 | 
			
		||||
        janet_panic_type(argv[n], n, JANET_TFLAG_STRING);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_getcbytes(argv, n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *janet_getcbytes(const Janet *argv, int32_t n) {
 | 
			
		||||
    /* Ensure buffer 0-padded */
 | 
			
		||||
    if (janet_checktype(argv[n], JANET_BUFFER)) {
 | 
			
		||||
        JanetBuffer *b = janet_unwrap_buffer(argv[n]);
 | 
			
		||||
        if ((b->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC) && b->count == b->capacity) {
 | 
			
		||||
            /* Make a copy with janet_smalloc in the rare case we have a buffer that
 | 
			
		||||
             * cannot be realloced and pushing a 0 byte would panic. */
 | 
			
		||||
            char *new_string = janet_smalloc(b->count + 1);
 | 
			
		||||
            memcpy(new_string, b->data, b->count);
 | 
			
		||||
            new_string[b->count] = 0;
 | 
			
		||||
            if (strlen(new_string) != (size_t) b->count) goto badzeros;
 | 
			
		||||
            return new_string;
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Ensure trailing 0 */
 | 
			
		||||
            janet_buffer_push_u8(b, 0);
 | 
			
		||||
            b->count--;
 | 
			
		||||
            if (strlen((char *)b->data) != (size_t) b->count) goto badzeros;
 | 
			
		||||
            return (const char *) b->data;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, n);
 | 
			
		||||
    const char *cstr = (const char *)view.bytes;
 | 
			
		||||
    if (strlen(cstr) != (size_t) view.len) goto badzeros;
 | 
			
		||||
    return cstr;
 | 
			
		||||
 | 
			
		||||
badzeros:
 | 
			
		||||
    janet_panic("bytes contain embedded 0s");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt) {
 | 
			
		||||
    if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
 | 
			
		||||
        return dflt;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_getcbytes(argv, n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t janet_getnat(const Janet *argv, int32_t n) {
 | 
			
		||||
@@ -259,6 +315,31 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
 | 
			
		||||
    return janet_unwrap_integer(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t janet_getuinteger(const Janet *argv, int32_t n) {
 | 
			
		||||
    Janet x = argv[n];
 | 
			
		||||
    if (!janet_checkuint(x)) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected 32 bit unsigned integer, got %v", n, x);
 | 
			
		||||
    }
 | 
			
		||||
    return (uint32_t) janet_unwrap_number(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int16_t janet_getinteger16(const Janet *argv, int32_t n) {
 | 
			
		||||
    Janet x = argv[n];
 | 
			
		||||
    if (!janet_checkint16(x)) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected 16 bit signed integer, got %v", n, x);
 | 
			
		||||
    }
 | 
			
		||||
    return (int16_t) janet_unwrap_number(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t janet_getuinteger16(const Janet *argv, int32_t n) {
 | 
			
		||||
    Janet x = argv[n];
 | 
			
		||||
    if (!janet_checkuint16(x)) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected 16 bit unsigned integer, got %v", n, x);
 | 
			
		||||
    }
 | 
			
		||||
    return (uint16_t) janet_unwrap_number(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int64_t janet_getinteger64(const Janet *argv, int32_t n) {
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
    return janet_unwrap_s64(argv[n]);
 | 
			
		||||
@@ -276,7 +357,7 @@ uint64_t janet_getuinteger64(const Janet *argv, int32_t n) {
 | 
			
		||||
    return janet_unwrap_u64(argv[n]);
 | 
			
		||||
#else
 | 
			
		||||
    Janet x = argv[n];
 | 
			
		||||
    if (!janet_checkint64(x)) {
 | 
			
		||||
    if (!janet_checkuint64(x)) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected 64 bit unsigned integer, got %v", n, x);
 | 
			
		||||
    }
 | 
			
		||||
    return (uint64_t) janet_unwrap_number(x);
 | 
			
		||||
@@ -296,16 +377,30 @@ int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const c
 | 
			
		||||
    int32_t not_raw = raw;
 | 
			
		||||
    if (not_raw < 0) not_raw += length + 1;
 | 
			
		||||
    if (not_raw < 0 || not_raw > length)
 | 
			
		||||
        janet_panicf("%s index %d out of range [%d,%d]", which, raw, -length - 1, length);
 | 
			
		||||
        janet_panicf("%s index %d out of range [%d,%d]", which, (int64_t) raw, -(int64_t)length - 1, (int64_t) length);
 | 
			
		||||
    return not_raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t janet_getstartrange(const Janet *argv, int32_t argc, int32_t n, int32_t length) {
 | 
			
		||||
    if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_gethalfrange(argv, n, length, "start");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t janet_getendrange(const Janet *argv, int32_t argc, int32_t n, int32_t length) {
 | 
			
		||||
    if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
 | 
			
		||||
        return length;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_gethalfrange(argv, n, length, "end");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which) {
 | 
			
		||||
    int32_t raw = janet_getinteger(argv, n);
 | 
			
		||||
    int32_t not_raw = raw;
 | 
			
		||||
    if (not_raw < 0) not_raw += length;
 | 
			
		||||
    if (not_raw < 0 || not_raw > length)
 | 
			
		||||
        janet_panicf("%s index %d out of range [%d,%d)", which, raw, -length, length);
 | 
			
		||||
        janet_panicf("%s index %d out of range [%d,%d)", which, (int64_t)raw, -(int64_t)length, (int64_t)length);
 | 
			
		||||
    return not_raw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -352,24 +447,10 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetRange range;
 | 
			
		||||
    int32_t length = janet_length(argv[0]);
 | 
			
		||||
    if (argc == 1) {
 | 
			
		||||
        range.start = 0;
 | 
			
		||||
        range.end = length;
 | 
			
		||||
    } else if (argc == 2) {
 | 
			
		||||
        range.start = janet_checktype(argv[1], JANET_NIL)
 | 
			
		||||
                      ? 0
 | 
			
		||||
                      : janet_gethalfrange(argv, 1, length, "start");
 | 
			
		||||
        range.end = length;
 | 
			
		||||
    } else {
 | 
			
		||||
        range.start = janet_checktype(argv[1], JANET_NIL)
 | 
			
		||||
                      ? 0
 | 
			
		||||
                      : janet_gethalfrange(argv, 1, length, "start");
 | 
			
		||||
        range.end = janet_checktype(argv[2], JANET_NIL)
 | 
			
		||||
                    ? length
 | 
			
		||||
                    : janet_gethalfrange(argv, 2, length, "end");
 | 
			
		||||
        if (range.end < range.start)
 | 
			
		||||
            range.end = range.start;
 | 
			
		||||
    }
 | 
			
		||||
    range.start = janet_getstartrange(argv, argc, 1, length);
 | 
			
		||||
    range.end = janet_getendrange(argv, argc, 2, length);
 | 
			
		||||
    if (range.end < range.start)
 | 
			
		||||
        range.end = range.start;
 | 
			
		||||
    return range;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -397,6 +478,33 @@ void janet_setdyn(const char *name, Janet value) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Create a function that when called, returns X. Trivial in Janet, a pain in C. */
 | 
			
		||||
JanetFunction *janet_thunk_delay(Janet x) {
 | 
			
		||||
    static const uint32_t bytecode[] = {
 | 
			
		||||
        JOP_LOAD_CONSTANT,
 | 
			
		||||
        JOP_RETURN
 | 
			
		||||
    };
 | 
			
		||||
    JanetFuncDef *def = janet_funcdef_alloc();
 | 
			
		||||
    def->arity = 0;
 | 
			
		||||
    def->min_arity = 0;
 | 
			
		||||
    def->max_arity = INT32_MAX;
 | 
			
		||||
    def->flags = JANET_FUNCDEF_FLAG_VARARG;
 | 
			
		||||
    def->slotcount = 1;
 | 
			
		||||
    def->bytecode = janet_malloc(sizeof(bytecode));
 | 
			
		||||
    def->bytecode_length = (int32_t)(sizeof(bytecode) / sizeof(uint32_t));
 | 
			
		||||
    def->constants = janet_malloc(sizeof(Janet));
 | 
			
		||||
    def->constants_length = 1;
 | 
			
		||||
    def->name = NULL;
 | 
			
		||||
    if (!def->bytecode || !def->constants) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    def->constants[0] = x;
 | 
			
		||||
    memcpy(def->bytecode, bytecode, sizeof(bytecode));
 | 
			
		||||
    janet_def_addflags(def);
 | 
			
		||||
    /* janet_verify(def); */
 | 
			
		||||
    return janet_thunk(def);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags) {
 | 
			
		||||
    uint64_t ret = 0;
 | 
			
		||||
    const uint8_t *keyw = janet_getkeyword(argv, n);
 | 
			
		||||
@@ -449,9 +557,41 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA
 | 
			
		||||
    return janet_getabstract(argv, n, at);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Atomic refcounts */
 | 
			
		||||
 | 
			
		||||
JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
 | 
			
		||||
#ifdef _MSC_VER
 | 
			
		||||
    return _InterlockedIncrement(x);
 | 
			
		||||
#elif defined(JANET_USE_STDATOMIC)
 | 
			
		||||
    return atomic_fetch_add_explicit(x, 1, memory_order_relaxed) + 1;
 | 
			
		||||
#else
 | 
			
		||||
    return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
 | 
			
		||||
#ifdef _MSC_VER
 | 
			
		||||
    return _InterlockedDecrement(x);
 | 
			
		||||
#elif defined(JANET_USE_STDATOMIC)
 | 
			
		||||
    return atomic_fetch_add_explicit(x, -1, memory_order_acq_rel) - 1;
 | 
			
		||||
#else
 | 
			
		||||
    return __atomic_add_fetch(x, -1, __ATOMIC_ACQ_REL);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
 | 
			
		||||
#ifdef _MSC_VER
 | 
			
		||||
    return _InterlockedOr(x, 0);
 | 
			
		||||
#elif defined(JANET_USE_STDATOMIC)
 | 
			
		||||
    return atomic_load_explicit(x, memory_order_acquire);
 | 
			
		||||
#else
 | 
			
		||||
    return __atomic_load_n(x, __ATOMIC_ACQUIRE);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Some definitions for function-like macros */
 | 
			
		||||
 | 
			
		||||
JANET_API JanetStructHead *(janet_struct_head)(const JanetKV *st) {
 | 
			
		||||
JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) {
 | 
			
		||||
    return janet_struct_head(st);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -459,10 +599,10 @@ JANET_API JanetAbstractHead *(janet_abstract_head)(const void *abstract) {
 | 
			
		||||
    return janet_abstract_head(abstract);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_API JanetStringHead *(janet_string_head)(const uint8_t *s) {
 | 
			
		||||
JANET_API JanetStringHead *(janet_string_head)(JanetString s) {
 | 
			
		||||
    return janet_string_head(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_API JanetTupleHead *(janet_tuple_head)(const Janet *tuple) {
 | 
			
		||||
JANET_API JanetTupleHead *(janet_tuple_head)(JanetTuple tuple) {
 | 
			
		||||
    return janet_tuple_head(tuple);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -99,7 +99,7 @@ static JanetSlot opfunction(
 | 
			
		||||
static int can_be_imm(Janet x, int8_t *out) {
 | 
			
		||||
    if (!janet_checkint(x)) return 0;
 | 
			
		||||
    int32_t integer = janet_unwrap_integer(x);
 | 
			
		||||
    if (integer > 127 || integer < -127) return 0;
 | 
			
		||||
    if (integer > INT8_MAX || integer < INT8_MIN) return 0;
 | 
			
		||||
    *out = (int8_t) integer;
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
@@ -116,12 +116,11 @@ static JanetSlot opreduce(
 | 
			
		||||
    JanetSlot *args,
 | 
			
		||||
    int op,
 | 
			
		||||
    int opim,
 | 
			
		||||
    Janet nullary) {
 | 
			
		||||
    Janet nullary,
 | 
			
		||||
    Janet unary) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    int32_t i, len;
 | 
			
		||||
    int8_t imm = 0;
 | 
			
		||||
    int neg = opim < 0;
 | 
			
		||||
    if (opim < 0) opim = -opim;
 | 
			
		||||
    len = janet_v_count(args);
 | 
			
		||||
    JanetSlot t;
 | 
			
		||||
    if (len == 0) {
 | 
			
		||||
@@ -132,19 +131,19 @@ static JanetSlot opreduce(
 | 
			
		||||
        if (op == JOP_SUBTRACT) {
 | 
			
		||||
            janetc_emit_ssi(c, JOP_MULTIPLY_IMMEDIATE, t, args[0], -1, 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            janetc_emit_sss(c, op, t, janetc_cslot(nullary), args[0], 1);
 | 
			
		||||
            janetc_emit_sss(c, op, t, janetc_cslot(unary), args[0], 1);
 | 
			
		||||
        }
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
    t = janetc_gettarget(opts);
 | 
			
		||||
    if (opim && can_slot_be_imm(args[1], &imm)) {
 | 
			
		||||
        janetc_emit_ssi(c, opim, t, args[0], neg ? -imm : imm, 1);
 | 
			
		||||
        janetc_emit_ssi(c, opim, t, args[0], imm, 1);
 | 
			
		||||
    } else {
 | 
			
		||||
        janetc_emit_sss(c, op, t, args[0], args[1], 1);
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 2; i < len; i++) {
 | 
			
		||||
        if (opim && can_slot_be_imm(args[i], &imm)) {
 | 
			
		||||
            janetc_emit_ssi(c, opim, t, t, neg ? -imm : imm, 1);
 | 
			
		||||
            janetc_emit_ssi(c, opim, t, t, imm, 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            janetc_emit_sss(c, op, t, t, args[i], 1);
 | 
			
		||||
        }
 | 
			
		||||
@@ -155,7 +154,7 @@ static JanetSlot opreduce(
 | 
			
		||||
/* Function optimizers */
 | 
			
		||||
 | 
			
		||||
static JanetSlot do_propagate(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_PROPAGATE, 0, janet_wrap_nil());
 | 
			
		||||
    return opreduce(opts, args, JOP_PROPAGATE, 0, janet_wrap_nil(), janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_error(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    janetc_emit_s(opts.compiler, JOP_ERROR, args[0], 0);
 | 
			
		||||
@@ -172,7 +171,7 @@ static JanetSlot do_debug(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return t;
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_in(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil());
 | 
			
		||||
    return opreduce(opts, args, JOP_IN, 0, janet_wrap_nil(), janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    if (janet_v_count(args) == 3) {
 | 
			
		||||
@@ -192,20 +191,14 @@ static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
        c->buffer[label] |= (current - label) << 16;
 | 
			
		||||
        return t;
 | 
			
		||||
    } else {
 | 
			
		||||
        return opreduce(opts, args, JOP_GET, 0, janet_wrap_nil());
 | 
			
		||||
        return opreduce(opts, args, JOP_GET, 0, janet_wrap_nil(), janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_next(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opfunction(opts, args, JOP_NEXT, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_MODULO, 0, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_REMAINDER, 0, janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil());
 | 
			
		||||
    return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil(), janet_wrap_nil());
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    if (opts.flags & JANET_FOPTS_DROP) {
 | 
			
		||||
@@ -262,34 +255,43 @@ static JanetSlot do_apply(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
/* Variadic operators specialization */
 | 
			
		||||
 | 
			
		||||
static JanetSlot do_add(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_ADD, JOP_ADD_IMMEDIATE, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_ADD, JOP_ADD_IMMEDIATE, janet_wrap_integer(0), janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_sub(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_SUBTRACT, -JOP_ADD_IMMEDIATE, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_SUBTRACT, JOP_SUBTRACT_IMMEDIATE, janet_wrap_integer(0), janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_mul(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_MULTIPLY, JOP_MULTIPLY_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_MULTIPLY, JOP_MULTIPLY_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_div(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_DIVIDE, JOP_DIVIDE_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_DIVIDE, JOP_DIVIDE_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_divf(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_DIVIDE_FLOOR, 0, janet_wrap_integer(1), janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_modulo(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_MODULO, 0, janet_wrap_integer(0), janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_remainder(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_REMAINDER, 0, janet_wrap_integer(0), janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_band(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_BAND, 0, janet_wrap_integer(-1));
 | 
			
		||||
    return opreduce(opts, args, JOP_BAND, 0, janet_wrap_integer(-1), janet_wrap_integer(-1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_bor(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_BOR, 0, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_BOR, 0, janet_wrap_integer(0), janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_bxor(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_BXOR, 0, janet_wrap_integer(0));
 | 
			
		||||
    return opreduce(opts, args, JOP_BXOR, 0, janet_wrap_integer(0), janet_wrap_integer(0));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_lshift(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_LEFT, JOP_SHIFT_LEFT_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_LEFT, JOP_SHIFT_LEFT_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_rshift(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_RIGHT, JOP_SHIFT_RIGHT_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_RIGHT, JOP_SHIFT_RIGHT_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_rshiftu(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_RIGHT_UNSIGNED, JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, janet_wrap_integer(1));
 | 
			
		||||
    return opreduce(opts, args, JOP_SHIFT_RIGHT_UNSIGNED, JOP_SHIFT_RIGHT_UNSIGNED_IMMEDIATE, janet_wrap_integer(1), janet_wrap_integer(1));
 | 
			
		||||
}
 | 
			
		||||
static JanetSlot do_bnot(JanetFopts opts, JanetSlot *args) {
 | 
			
		||||
    return genericSS(opts, JOP_BNOT, args[0]);
 | 
			
		||||
@@ -383,10 +385,11 @@ static const JanetFunOptimizer optimizers[] = {
 | 
			
		||||
    {fixarity2, do_propagate},
 | 
			
		||||
    {arity2or3, do_get},
 | 
			
		||||
    {arity1or2, do_next},
 | 
			
		||||
    {fixarity2, do_modulo},
 | 
			
		||||
    {fixarity2, do_remainder},
 | 
			
		||||
    {NULL, do_modulo},
 | 
			
		||||
    {NULL, do_remainder},
 | 
			
		||||
    {fixarity2, do_cmp},
 | 
			
		||||
    {fixarity2, do_cancel},
 | 
			
		||||
    {NULL, do_divf}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const JanetFunOptimizer *janetc_funopt(uint32_t flags) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -93,10 +93,14 @@ void janetc_freeslot(JanetCompiler *c, JanetSlot s) {
 | 
			
		||||
/* Add a slot to a scope with a symbol associated with it (def or var). */
 | 
			
		||||
void janetc_nameslot(JanetCompiler *c, const uint8_t *sym, JanetSlot s) {
 | 
			
		||||
    SymPair sp;
 | 
			
		||||
    int32_t cnt = janet_v_count(c->buffer);
 | 
			
		||||
    sp.sym = sym;
 | 
			
		||||
    sp.sym2 = sym;
 | 
			
		||||
    sp.slot = s;
 | 
			
		||||
    sp.keep = 0;
 | 
			
		||||
    sp.slot.flags |= JANET_SLOT_NAMED;
 | 
			
		||||
    sp.birth_pc = cnt ? cnt - 1 : 0;
 | 
			
		||||
    sp.death_pc = UINT32_MAX;
 | 
			
		||||
    janet_v_push(c->scope->syms, sp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -159,21 +163,27 @@ void janetc_popscope(JanetCompiler *c) {
 | 
			
		||||
        if (oldscope->flags & JANET_SCOPE_CLOSURE) {
 | 
			
		||||
            newscope->flags |= JANET_SCOPE_CLOSURE;
 | 
			
		||||
        }
 | 
			
		||||
        if (newscope->ra.max < oldscope->ra.max)
 | 
			
		||||
        if (newscope->ra.max < oldscope->ra.max) {
 | 
			
		||||
            newscope->ra.max = oldscope->ra.max;
 | 
			
		||||
 | 
			
		||||
        /* Keep upvalue slots */
 | 
			
		||||
        for (int32_t i = 0; i < janet_v_count(oldscope->syms); i++) {
 | 
			
		||||
            SymPair pair = oldscope->syms[i];
 | 
			
		||||
            if (pair.keep) {
 | 
			
		||||
                /* The variable should not be lexically accessible */
 | 
			
		||||
                pair.sym = NULL;
 | 
			
		||||
                janet_v_push(newscope->syms, pair);
 | 
			
		||||
                janetc_regalloc_touch(&newscope->ra, pair.slot.index);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Keep upvalue slots and symbols for debugging. */
 | 
			
		||||
        for (int32_t i = 0; i < janet_v_count(oldscope->syms); i++) {
 | 
			
		||||
            SymPair pair = oldscope->syms[i];
 | 
			
		||||
            /* The variable should not be lexically accessible */
 | 
			
		||||
            pair.sym = NULL;
 | 
			
		||||
            if (pair.death_pc == UINT32_MAX) {
 | 
			
		||||
                pair.death_pc = (uint32_t) janet_v_count(c->buffer);
 | 
			
		||||
            }
 | 
			
		||||
            if (pair.keep) {
 | 
			
		||||
                /* The variable should also not be included in the locals */
 | 
			
		||||
                pair.sym2 = NULL;
 | 
			
		||||
                janetc_regalloc_touch(&newscope->ra, pair.slot.index);
 | 
			
		||||
            }
 | 
			
		||||
            janet_v_push(newscope->syms, pair);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Free the old scope */
 | 
			
		||||
    janet_v_free(oldscope->consts);
 | 
			
		||||
    janet_v_free(oldscope->syms);
 | 
			
		||||
@@ -334,6 +344,7 @@ found:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* non-local scope needs to expose its environment */
 | 
			
		||||
    JanetScope *original_scope = scope;
 | 
			
		||||
    pair->keep = 1;
 | 
			
		||||
    while (scope && !(scope->flags & JANET_SCOPE_FUNCTION))
 | 
			
		||||
        scope = scope->parent;
 | 
			
		||||
@@ -355,7 +366,7 @@ found:
 | 
			
		||||
            /* Check if scope already has env. If so, break */
 | 
			
		||||
            len = janet_v_count(scope->envs);
 | 
			
		||||
            for (j = 0; j < len; j++) {
 | 
			
		||||
                if (scope->envs[j] == envindex) {
 | 
			
		||||
                if (scope->envs[j].envindex == envindex) {
 | 
			
		||||
                    scopefound = 1;
 | 
			
		||||
                    envindex = j;
 | 
			
		||||
                    break;
 | 
			
		||||
@@ -364,7 +375,10 @@ found:
 | 
			
		||||
            /* Add the environment if it is not already referenced */
 | 
			
		||||
            if (!scopefound) {
 | 
			
		||||
                len = janet_v_count(scope->envs);
 | 
			
		||||
                janet_v_push(scope->envs, envindex);
 | 
			
		||||
                JanetEnvRef ref;
 | 
			
		||||
                ref.envindex = envindex;
 | 
			
		||||
                ref.scope = original_scope;
 | 
			
		||||
                janet_v_push(scope->envs, ref);
 | 
			
		||||
                envindex = len;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -408,6 +422,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len) {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    JanetSlot *ret = NULL;
 | 
			
		||||
    JanetFopts subopts = janetc_fopts_default(c);
 | 
			
		||||
    subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
    for (i = 0; i < len; i++) {
 | 
			
		||||
        janet_v_push(ret, janetc_value(subopts, vals[i]));
 | 
			
		||||
    }
 | 
			
		||||
@@ -418,6 +433,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len) {
 | 
			
		||||
JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
 | 
			
		||||
    JanetSlot *ret = NULL;
 | 
			
		||||
    JanetFopts subopts = janetc_fopts_default(c);
 | 
			
		||||
    subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
    const JanetKV *kvs = NULL;
 | 
			
		||||
    int32_t cap = 0, len = 0;
 | 
			
		||||
    janet_dictionary_view(ds, &kvs, &len, &cap);
 | 
			
		||||
@@ -730,12 +746,14 @@ static int macroexpand1(
 | 
			
		||||
    int lock = janet_gclock();
 | 
			
		||||
    Janet mf_kw = janet_ckeywordv("macro-form");
 | 
			
		||||
    janet_table_put(c->env, mf_kw, x);
 | 
			
		||||
    Janet ml_kw = janet_ckeywordv("macro-lints");
 | 
			
		||||
    if (c->lints) {
 | 
			
		||||
        janet_table_put(c->env, ml_kw, janet_wrap_array(c->lints));
 | 
			
		||||
    }
 | 
			
		||||
    Janet tempOut;
 | 
			
		||||
    JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &tempOut);
 | 
			
		||||
    janet_table_put(c->env, mf_kw, janet_wrap_nil());
 | 
			
		||||
    if (c->lints) {
 | 
			
		||||
        janet_table_put(c->env, janet_ckeywordv("macro-lints"), janet_wrap_array(c->lints));
 | 
			
		||||
    }
 | 
			
		||||
    janet_table_put(c->env, ml_kw, janet_wrap_nil());
 | 
			
		||||
    janet_gcunlock(lock);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK) {
 | 
			
		||||
        const uint8_t *es = janet_formatc("(macro) %V", tempOut);
 | 
			
		||||
@@ -868,7 +886,10 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
 | 
			
		||||
    /* Copy envs */
 | 
			
		||||
    def->environments_length = janet_v_count(scope->envs);
 | 
			
		||||
    def->environments = janet_v_flatten(scope->envs);
 | 
			
		||||
    def->environments = janet_malloc(sizeof(int32_t) * def->environments_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->environments_length; i++) {
 | 
			
		||||
        def->environments[i] = scope->envs[i].envindex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def->constants_length = janet_v_count(scope->consts);
 | 
			
		||||
    def->constants = janet_v_flatten(scope->consts);
 | 
			
		||||
@@ -913,7 +934,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        int32_t slotchunks = (def->slotcount + 31) >> 5;
 | 
			
		||||
        /* numchunks is min of slotchunks and scope->ua.count */
 | 
			
		||||
        int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks;
 | 
			
		||||
        uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks);
 | 
			
		||||
        uint32_t *chunks = janet_calloc(slotchunks, sizeof(uint32_t));
 | 
			
		||||
        if (NULL == chunks) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -923,9 +944,66 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        def->closure_bitset = chunks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Capture symbol to local mapping */
 | 
			
		||||
    JanetSymbolMap *locals = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Symbol -> upvalue mapping */
 | 
			
		||||
    JanetScope *top = c->scope;
 | 
			
		||||
    while (top->parent) top = top->parent;
 | 
			
		||||
    for (JanetScope *s = top; s != NULL; s = s->child) {
 | 
			
		||||
        for (int32_t j = 0; j < janet_v_count(scope->envs); j++) {
 | 
			
		||||
            JanetEnvRef ref = scope->envs[j];
 | 
			
		||||
            JanetScope *upscope = ref.scope;
 | 
			
		||||
            if (upscope != s) continue;
 | 
			
		||||
            for (int32_t i = 0; i < janet_v_count(upscope->syms); i++) {
 | 
			
		||||
                SymPair pair = upscope->syms[i];
 | 
			
		||||
                if (pair.sym2) {
 | 
			
		||||
                    JanetSymbolMap jsm;
 | 
			
		||||
                    jsm.birth_pc = UINT32_MAX;
 | 
			
		||||
                    jsm.death_pc = j;
 | 
			
		||||
                    jsm.slot_index = pair.slot.index;
 | 
			
		||||
                    jsm.symbol = pair.sym2;
 | 
			
		||||
                    janet_v_push(locals, jsm);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Symbol -> slot mapping */
 | 
			
		||||
    for (int32_t i = 0; i < janet_v_count(scope->syms); i++) {
 | 
			
		||||
        SymPair pair = scope->syms[i];
 | 
			
		||||
        if (pair.sym2) {
 | 
			
		||||
            JanetSymbolMap jsm;
 | 
			
		||||
            if (pair.death_pc == UINT32_MAX) {
 | 
			
		||||
                jsm.death_pc = def->bytecode_length;
 | 
			
		||||
            } else {
 | 
			
		||||
                jsm.death_pc = pair.death_pc - scope->bytecode_start;
 | 
			
		||||
            }
 | 
			
		||||
            /* Handle birth_pc == 0 correctly */
 | 
			
		||||
            if ((uint32_t) scope->bytecode_start > pair.birth_pc) {
 | 
			
		||||
                jsm.birth_pc = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                jsm.birth_pc = pair.birth_pc - scope->bytecode_start;
 | 
			
		||||
            }
 | 
			
		||||
            janet_assert(jsm.birth_pc <= jsm.death_pc, "birth pc after death pc");
 | 
			
		||||
            janet_assert(jsm.birth_pc < (uint32_t) def->bytecode_length, "bad birth pc");
 | 
			
		||||
            janet_assert(jsm.death_pc <= (uint32_t) def->bytecode_length, "bad death pc");
 | 
			
		||||
            jsm.slot_index = pair.slot.index;
 | 
			
		||||
            jsm.symbol = pair.sym2;
 | 
			
		||||
            janet_v_push(locals, jsm);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    def->symbolmap_length = janet_v_count(locals);
 | 
			
		||||
    def->symbolmap = janet_v_flatten(locals);
 | 
			
		||||
    if (def->symbolmap_length) def->flags |= JANET_FUNCDEF_FLAG_HASSYMBOLMAP;
 | 
			
		||||
 | 
			
		||||
    /* Pop the scope */
 | 
			
		||||
    janetc_popscope(c);
 | 
			
		||||
 | 
			
		||||
    /* Do basic optimization */
 | 
			
		||||
    janet_bytecode_movopt(def);
 | 
			
		||||
    janet_bytecode_remove_noops(def);
 | 
			
		||||
 | 
			
		||||
    return def;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -978,7 +1056,7 @@ JanetCompileResult janet_compile_lint(Janet source,
 | 
			
		||||
 | 
			
		||||
    if (c.result.status == JANET_COMPILE_OK) {
 | 
			
		||||
        JanetFuncDef *def = janetc_pop_funcdef(&c);
 | 
			
		||||
        def->name = janet_cstring("_thunk");
 | 
			
		||||
        def->name = janet_cstring("thunk");
 | 
			
		||||
        janet_def_addflags(def);
 | 
			
		||||
        c.result.funcdef = def;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -1005,7 +1083,8 @@ JANET_CORE_FN(cfun_compile,
 | 
			
		||||
              "If a `lints` array is given, linting messages will be appended to the array. "
 | 
			
		||||
              "Each message will be a tuple of the form `(level line col message)`.") {
 | 
			
		||||
    janet_arity(argc, 1, 4);
 | 
			
		||||
    JanetTable *env = argc > 1 ? janet_gettable(argv, 1) : janet_vm.fiber->env;
 | 
			
		||||
    JanetTable *env = (argc > 1 && !janet_checktype(argv[1], JANET_NIL))
 | 
			
		||||
                      ? janet_gettable(argv, 1) : janet_vm.fiber->env;
 | 
			
		||||
    if (NULL == env) {
 | 
			
		||||
        env = janet_table(0);
 | 
			
		||||
        janet_vm.fiber->env = env;
 | 
			
		||||
@@ -1017,11 +1096,12 @@ JANET_CORE_FN(cfun_compile,
 | 
			
		||||
            source = janet_unwrap_string(x);
 | 
			
		||||
        } else if (janet_checktype(x, JANET_KEYWORD)) {
 | 
			
		||||
            source = janet_unwrap_keyword(x);
 | 
			
		||||
        } else {
 | 
			
		||||
        } else if (!janet_checktype(x, JANET_NIL)) {
 | 
			
		||||
            janet_panic_type(x, 2, JANET_TFLAG_STRING | JANET_TFLAG_KEYWORD);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetArray *lints = (argc >= 4) ? janet_getarray(argv, 3) : NULL;
 | 
			
		||||
    JanetArray *lints = (argc >= 4 && !janet_checktype(argv[3], JANET_NIL))
 | 
			
		||||
                        ? janet_getarray(argv, 3) : NULL;
 | 
			
		||||
    JanetCompileResult res = janet_compile_lint(argv[0], env, source, lints);
 | 
			
		||||
    if (res.status == JANET_COMPILE_OK) {
 | 
			
		||||
        return janet_wrap_function(janet_thunk(res.funcdef));
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -69,6 +69,7 @@ typedef enum {
 | 
			
		||||
#define JANET_FUN_REMAINDER 30
 | 
			
		||||
#define JANET_FUN_CMP 31
 | 
			
		||||
#define JANET_FUN_CANCEL 32
 | 
			
		||||
#define JANET_FUN_DIVIDE_FLOOR 33
 | 
			
		||||
 | 
			
		||||
/* Compiler typedefs */
 | 
			
		||||
typedef struct JanetCompiler JanetCompiler;
 | 
			
		||||
@@ -111,13 +112,21 @@ struct JanetSlot {
 | 
			
		||||
typedef struct SymPair {
 | 
			
		||||
    JanetSlot slot;
 | 
			
		||||
    const uint8_t *sym;
 | 
			
		||||
    const uint8_t *sym2;
 | 
			
		||||
    int keep;
 | 
			
		||||
    uint32_t birth_pc;
 | 
			
		||||
    uint32_t death_pc;
 | 
			
		||||
} SymPair;
 | 
			
		||||
 | 
			
		||||
typedef struct JanetEnvRef {
 | 
			
		||||
    int32_t envindex;
 | 
			
		||||
    JanetScope *scope;
 | 
			
		||||
} JanetEnvRef;
 | 
			
		||||
 | 
			
		||||
/* A lexical scope during compilation */
 | 
			
		||||
struct JanetScope {
 | 
			
		||||
 | 
			
		||||
    /* For debugging */
 | 
			
		||||
    /* For debugging the compiler */
 | 
			
		||||
    const char *name;
 | 
			
		||||
 | 
			
		||||
    /* Scopes are doubly linked list */
 | 
			
		||||
@@ -133,7 +142,7 @@ struct JanetScope {
 | 
			
		||||
    /* FuncDefs */
 | 
			
		||||
    JanetFuncDef **defs;
 | 
			
		||||
 | 
			
		||||
    /* Regsiter allocator */
 | 
			
		||||
    /* Register allocator */
 | 
			
		||||
    JanetcRegisterAllocator ra;
 | 
			
		||||
 | 
			
		||||
    /* Upvalue allocator */
 | 
			
		||||
@@ -142,7 +151,7 @@ struct JanetScope {
 | 
			
		||||
    /* Referenced closure environments. The values at each index correspond
 | 
			
		||||
     * to which index to get the environment from in the parent. The environment
 | 
			
		||||
     * that corresponds to the direct parent's stack will always have value 0. */
 | 
			
		||||
    int32_t *envs;
 | 
			
		||||
    JanetEnvRef *envs;
 | 
			
		||||
 | 
			
		||||
    int32_t bytecode_start;
 | 
			
		||||
    int flags;
 | 
			
		||||
@@ -179,6 +188,7 @@ struct JanetCompiler {
 | 
			
		||||
#define JANET_FOPTS_TAIL 0x10000
 | 
			
		||||
#define JANET_FOPTS_HINT 0x20000
 | 
			
		||||
#define JANET_FOPTS_DROP 0x40000
 | 
			
		||||
#define JANET_FOPTS_ACCEPT_SPLICE 0x80000
 | 
			
		||||
 | 
			
		||||
/* Options for compiling a single form */
 | 
			
		||||
struct JanetFopts {
 | 
			
		||||
@@ -227,7 +237,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len);
 | 
			
		||||
/* Get a bunch of slots for function arguments */
 | 
			
		||||
JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds);
 | 
			
		||||
 | 
			
		||||
/* Push slots load via janetc_toslots. */
 | 
			
		||||
/* Push slots loaded via janetc_toslots. */
 | 
			
		||||
int32_t janetc_pushslots(JanetCompiler *c, JanetSlot *slots);
 | 
			
		||||
 | 
			
		||||
/* Free slots loaded via janetc_toslots */
 | 
			
		||||
@@ -252,10 +262,14 @@ void janetc_popscope(JanetCompiler *c);
 | 
			
		||||
void janetc_popscope_keepslot(JanetCompiler *c, JanetSlot retslot);
 | 
			
		||||
JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c);
 | 
			
		||||
 | 
			
		||||
/* Create a destory slots */
 | 
			
		||||
/* Create a destroy slot */
 | 
			
		||||
JanetSlot janetc_cslot(Janet x);
 | 
			
		||||
 | 
			
		||||
/* Search for a symbol */
 | 
			
		||||
JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym);
 | 
			
		||||
 | 
			
		||||
/* Bytecode optimization */
 | 
			
		||||
void janet_bytecode_movopt(JanetFuncDef *def);
 | 
			
		||||
void janet_bytecode_remove_noops(JanetFuncDef *def);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,6 +43,7 @@ extern size_t janet_core_image_size;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JanetModule janet_native(const char *name, const uint8_t **error) {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_DYNAMIC_MODULES);
 | 
			
		||||
    char *processed_name = get_processed_name(name);
 | 
			
		||||
    Clib lib = load_clib(processed_name);
 | 
			
		||||
    JanetModule init;
 | 
			
		||||
@@ -68,15 +69,15 @@ JanetModule janet_native(const char *name, const uint8_t **error) {
 | 
			
		||||
            host.minor < modconf.minor ||
 | 
			
		||||
            host.bits != modconf.bits) {
 | 
			
		||||
        char errbuf[128];
 | 
			
		||||
        sprintf(errbuf, "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
 | 
			
		||||
                host.major,
 | 
			
		||||
                host.minor,
 | 
			
		||||
                host.patch,
 | 
			
		||||
                host.bits,
 | 
			
		||||
                modconf.major,
 | 
			
		||||
                modconf.minor,
 | 
			
		||||
                modconf.patch,
 | 
			
		||||
                modconf.bits);
 | 
			
		||||
        snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
 | 
			
		||||
                 host.major,
 | 
			
		||||
                 host.minor,
 | 
			
		||||
                 host.patch,
 | 
			
		||||
                 host.bits,
 | 
			
		||||
                 modconf.major,
 | 
			
		||||
                 modconf.minor,
 | 
			
		||||
                 modconf.patch,
 | 
			
		||||
                 modconf.bits);
 | 
			
		||||
        *error = janet_cstring(errbuf);
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
@@ -109,11 +110,14 @@ JANET_CORE_FN(janet_core_expand_path,
 | 
			
		||||
              "(module/expand-path path template)",
 | 
			
		||||
              "Expands a path template as found in `module/paths` for `module/find`. "
 | 
			
		||||
              "This takes in a path (the argument to require) and a template string, "
 | 
			
		||||
              "to expand the path to a path that can be "
 | 
			
		||||
              "used for importing files. The replacements are as follows:\n\n"
 | 
			
		||||
              "* :all: -- the value of path verbatim\n\n"
 | 
			
		||||
              "* :cur: -- the current file, or (dyn :current-file)\n\n"
 | 
			
		||||
              "* :dir: -- the directory containing the current file\n\n"
 | 
			
		||||
              "to expand the path to a path that can be used for importing files. "
 | 
			
		||||
              "The replacements are as follows:\n\n"
 | 
			
		||||
              "* :all: -- the value of path verbatim.\n\n"
 | 
			
		||||
              "* :@all: -- Same as :all:, but if `path` starts with the @ character, "
 | 
			
		||||
              "the first path segment is replaced with a dynamic binding "
 | 
			
		||||
              "`(dyn <first path segment as keyword>)`.\n\n"
 | 
			
		||||
              "* :cur: -- the directory portion, if any, of (dyn :current-file)\n\n"
 | 
			
		||||
              "* :dir: -- the directory portion, if any, of the path argument\n\n"
 | 
			
		||||
              "* :name: -- the name component of path, with extension if given\n\n"
 | 
			
		||||
              "* :native: -- the extension used to load natives, .so or .dll\n\n"
 | 
			
		||||
              "* :sys: -- the system path, or (dyn :syspath)") {
 | 
			
		||||
@@ -157,6 +161,21 @@ JANET_CORE_FN(janet_core_expand_path,
 | 
			
		||||
            if (strncmp(template + i, ":all:", 5) == 0) {
 | 
			
		||||
                janet_buffer_push_cstring(out, input);
 | 
			
		||||
                i += 4;
 | 
			
		||||
            } else if (strncmp(template + i, ":@all:", 6) == 0) {
 | 
			
		||||
                if (input[0] == '@') {
 | 
			
		||||
                    const char *p = input;
 | 
			
		||||
                    while (*p && !is_path_sep(*p)) p++;
 | 
			
		||||
                    size_t len = p - input - 1;
 | 
			
		||||
                    char *str = janet_smalloc(len + 1);
 | 
			
		||||
                    memcpy(str, input + 1, len);
 | 
			
		||||
                    str[len] = '\0';
 | 
			
		||||
                    janet_formatb(out, "%V", janet_dyn(str));
 | 
			
		||||
                    janet_sfree(str);
 | 
			
		||||
                    janet_buffer_push_cstring(out, p);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_buffer_push_cstring(out, input);
 | 
			
		||||
                }
 | 
			
		||||
                i += 5;
 | 
			
		||||
            } else if (strncmp(template + i, ":cur:", 5) == 0) {
 | 
			
		||||
                janet_buffer_push_bytes(out, (const uint8_t *)curdir, curlen);
 | 
			
		||||
                i += 4;
 | 
			
		||||
@@ -407,6 +426,48 @@ JANET_CORE_FN(janet_core_slice,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_range,
 | 
			
		||||
              "(range & args)",
 | 
			
		||||
              "Create an array of values [start, end) with a given step. "
 | 
			
		||||
              "With one argument, returns a range [0, end). With two arguments, returns "
 | 
			
		||||
              "a range [start, end). With three, returns a range with optional step size.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    double start = 0, stop = 0, step = 1, count = 0;
 | 
			
		||||
    if (argc == 3) {
 | 
			
		||||
        start = janet_getnumber(argv, 0);
 | 
			
		||||
        stop = janet_getnumber(argv, 1);
 | 
			
		||||
        step = janet_getnumber(argv, 2);
 | 
			
		||||
        count = (step > 0) ? (stop - start) / step :
 | 
			
		||||
                ((step < 0) ? (stop - start) / step : 0);
 | 
			
		||||
    } else if (argc == 2) {
 | 
			
		||||
        start = janet_getnumber(argv, 0);
 | 
			
		||||
        stop = janet_getnumber(argv, 1);
 | 
			
		||||
        count = stop - start;
 | 
			
		||||
    } else {
 | 
			
		||||
        stop = janet_getnumber(argv, 0);
 | 
			
		||||
        count = stop;
 | 
			
		||||
    }
 | 
			
		||||
    count = (count > 0) ? count : 0;
 | 
			
		||||
    int32_t int_count;
 | 
			
		||||
    janet_assert(count >= 0, "bad range code");
 | 
			
		||||
    if (count > (double) INT32_MAX) {
 | 
			
		||||
        janet_panicf("range is too large, %f elements", count);
 | 
			
		||||
    } else {
 | 
			
		||||
        int_count = (int32_t) ceil(count);
 | 
			
		||||
    }
 | 
			
		||||
    if (step > 0.0) {
 | 
			
		||||
        janet_assert(start + int_count * step >= stop, "bad range code");
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_assert(start + int_count * step <= stop, "bad range code");
 | 
			
		||||
    }
 | 
			
		||||
    JanetArray *array = janet_array(int_count);
 | 
			
		||||
    for (int32_t i = 0; i < int_count; i++) {
 | 
			
		||||
        array->data[i] = janet_wrap_number((double) start + (double) i * step);
 | 
			
		||||
    }
 | 
			
		||||
    array->count = int_count;
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_table,
 | 
			
		||||
              "(table & kvs)",
 | 
			
		||||
              "Creates a new table from a variadic number of keys and values. "
 | 
			
		||||
@@ -439,7 +500,7 @@ JANET_CORE_FN(janet_core_getproto,
 | 
			
		||||
               ? janet_wrap_struct(janet_struct_proto(st))
 | 
			
		||||
               : janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    janet_panicf("expected struct|table, got %v", argv[0]);
 | 
			
		||||
    janet_panicf("expected struct or table, got %v", argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_struct,
 | 
			
		||||
@@ -610,9 +671,45 @@ ret_false:
 | 
			
		||||
    return janet_wrap_false();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_is_bytes,
 | 
			
		||||
              "(bytes? x)",
 | 
			
		||||
              "Check if x is a string, symbol, keyword, or buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_BYTES));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_is_indexed,
 | 
			
		||||
              "(indexed? x)",
 | 
			
		||||
              "Check if x is an array or tuple.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_INDEXED));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_is_dictionary,
 | 
			
		||||
              "(dictionary? x)",
 | 
			
		||||
              "Check if x is a table or struct.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_DICTIONARY));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_is_lengthable,
 | 
			
		||||
              "(lengthable? x)",
 | 
			
		||||
              "Check if x is a bytes, indexed, or dictionary.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_LENGTHABLE));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_signal,
 | 
			
		||||
              "(signal what x)",
 | 
			
		||||
              "Raise a signal with payload x. ") {
 | 
			
		||||
              "Raise a signal with payload x. `what` can be an integer\n"
 | 
			
		||||
              "from 0 through 7 indicating user(0-7), or one of:\n\n"
 | 
			
		||||
              "* :ok\n"
 | 
			
		||||
              "* :error\n"
 | 
			
		||||
              "* :debug\n"
 | 
			
		||||
              "* :yield\n"
 | 
			
		||||
              "* :user(0-7)\n"
 | 
			
		||||
              "* :interrupt\n"
 | 
			
		||||
              "* :await") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
 | 
			
		||||
    if (janet_checkint(argv[0])) {
 | 
			
		||||
@@ -632,6 +729,89 @@ JANET_CORE_FN(janet_core_signal,
 | 
			
		||||
    janet_panicf("unknown signal %v", argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_memcmp,
 | 
			
		||||
              "(memcmp a b &opt len offset-a offset-b)",
 | 
			
		||||
              "Compare memory. Takes two byte sequences `a` and `b`, and "
 | 
			
		||||
              "return 0 if they have identical contents, a negative integer if a is less than b, "
 | 
			
		||||
              "and a positive integer if a is greater than b. Optionally take a length and offsets "
 | 
			
		||||
              "to compare slices of the bytes sequences.") {
 | 
			
		||||
    janet_arity(argc, 2, 5);
 | 
			
		||||
    JanetByteView a = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView b = janet_getbytes(argv, 1);
 | 
			
		||||
    int32_t len = janet_optnat(argv, argc, 2, a.len < b.len ? a.len : b.len);
 | 
			
		||||
    int32_t offset_a = janet_optnat(argv, argc, 3, 0);
 | 
			
		||||
    int32_t offset_b = janet_optnat(argv, argc, 4, 0);
 | 
			
		||||
    if (offset_a + len > a.len) janet_panicf("invalid offset-a: %d", offset_a);
 | 
			
		||||
    if (offset_b + len > b.len) janet_panicf("invalid offset-b: %d", offset_b);
 | 
			
		||||
    return janet_wrap_integer(memcmp(a.bytes + offset_a, b.bytes + offset_b, (size_t) len));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct SandboxOption {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    uint32_t flag;
 | 
			
		||||
} SandboxOption;
 | 
			
		||||
 | 
			
		||||
static const SandboxOption sandbox_options[] = {
 | 
			
		||||
    {"all", JANET_SANDBOX_ALL},
 | 
			
		||||
    {"env", JANET_SANDBOX_ENV},
 | 
			
		||||
    {"ffi", JANET_SANDBOX_FFI},
 | 
			
		||||
    {"ffi-define", JANET_SANDBOX_FFI_DEFINE},
 | 
			
		||||
    {"ffi-jit", JANET_SANDBOX_FFI_JIT},
 | 
			
		||||
    {"ffi-use", JANET_SANDBOX_FFI_USE},
 | 
			
		||||
    {"fs", JANET_SANDBOX_FS},
 | 
			
		||||
    {"fs-read", JANET_SANDBOX_FS_READ},
 | 
			
		||||
    {"fs-temp", JANET_SANDBOX_FS_TEMP},
 | 
			
		||||
    {"fs-write", JANET_SANDBOX_FS_WRITE},
 | 
			
		||||
    {"hrtime", JANET_SANDBOX_HRTIME},
 | 
			
		||||
    {"modules", JANET_SANDBOX_DYNAMIC_MODULES},
 | 
			
		||||
    {"net", JANET_SANDBOX_NET},
 | 
			
		||||
    {"net-connect", JANET_SANDBOX_NET_CONNECT},
 | 
			
		||||
    {"net-listen", JANET_SANDBOX_NET_LISTEN},
 | 
			
		||||
    {"sandbox", JANET_SANDBOX_SANDBOX},
 | 
			
		||||
    {"signal", JANET_SANDBOX_SIGNAL},
 | 
			
		||||
    {"subprocess", JANET_SANDBOX_SUBPROCESS},
 | 
			
		||||
    {NULL, 0}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_sandbox,
 | 
			
		||||
              "(sandbox & forbidden-capabilities)",
 | 
			
		||||
              "Disable feature sets to prevent the interpreter from using certain system resources. "
 | 
			
		||||
              "Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n"
 | 
			
		||||
              "* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
 | 
			
		||||
              "* :env - disallow reading and write env variables\n"
 | 
			
		||||
              "* :ffi - disallow FFI (recommended if disabling anything else)\n"
 | 
			
		||||
              "* :ffi-define - disallow loading new FFI modules and binding new functions\n"
 | 
			
		||||
              "* :ffi-jit - disallow calling `ffi/jitfn`\n"
 | 
			
		||||
              "* :ffi-use - disallow using any previously bound FFI functions and memory-unsafe functions.\n"
 | 
			
		||||
              "* :fs - disallow access to the file system\n"
 | 
			
		||||
              "* :fs-read - disallow read access to the file system\n"
 | 
			
		||||
              "* :fs-temp - disallow creating temporary files\n"
 | 
			
		||||
              "* :fs-write - disallow write access to the file system\n"
 | 
			
		||||
              "* :hrtime - disallow high-resolution timers\n"
 | 
			
		||||
              "* :modules - disallow load dynamic modules (natives)\n"
 | 
			
		||||
              "* :net - disallow network access\n"
 | 
			
		||||
              "* :net-connect - disallow making outbound network connections\n"
 | 
			
		||||
              "* :net-listen - disallow accepting inbound network connections\n"
 | 
			
		||||
              "* :sandbox - disallow calling this function\n"
 | 
			
		||||
              "* :signal - disallow adding or removing signal handlers\n"
 | 
			
		||||
              "* :subprocess - disallow running subprocesses") {
 | 
			
		||||
    uint32_t flags = 0;
 | 
			
		||||
    for (int32_t i = 0; i < argc; i++) {
 | 
			
		||||
        JanetKeyword kw = janet_getkeyword(argv, i);
 | 
			
		||||
        const SandboxOption *opt = sandbox_options;
 | 
			
		||||
        while (opt->name != NULL) {
 | 
			
		||||
            if (janet_cstrcmp(kw, opt->name) == 0) {
 | 
			
		||||
                flags |= opt->flag;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            opt++;
 | 
			
		||||
        }
 | 
			
		||||
        if (opt->name == NULL) janet_panicf("unknown capability %v", argv[i]);
 | 
			
		||||
    }
 | 
			
		||||
    janet_sandbox(flags);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
 | 
			
		||||
/* Utility for inline assembly */
 | 
			
		||||
@@ -816,18 +996,17 @@ static void make_apply(JanetTable *env) {
 | 
			
		||||
        /* Push the array */
 | 
			
		||||
        S(JOP_PUSH_ARRAY, 5),
 | 
			
		||||
 | 
			
		||||
        /* Call the funciton */
 | 
			
		||||
        /* Call the function */
 | 
			
		||||
        S(JOP_TAILCALL, 0)
 | 
			
		||||
    };
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_APPLY | JANET_FUNCDEF_FLAG_VARARG,
 | 
			
		||||
                    "apply", 1, 1, INT32_MAX, 6, apply_asm, sizeof(apply_asm),
 | 
			
		||||
                    JDOC("(apply f & args)\n\n"
 | 
			
		||||
                         "Applies a function to a variable number of arguments. Each element in args "
 | 
			
		||||
                         "is used as an argument to f, except the last element in args, which is expected to "
 | 
			
		||||
                         "be an array-like. Each element in this last argument is then also pushed as an argument to "
 | 
			
		||||
                         "f. For example:\n\n"
 | 
			
		||||
                         "\t(apply + 1000 (range 10))\n\n"
 | 
			
		||||
                         "sums the first 10 integers and 1000."));
 | 
			
		||||
         "Applies a function f to a variable number of arguments. Each "
 | 
			
		||||
         "element in args is used as an argument to f, except the last "
 | 
			
		||||
         "element in args, which is expected to be an array or a tuple. "
 | 
			
		||||
         "Each element in this last argument is then also pushed as an "
 | 
			
		||||
         "argument to f."));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const uint32_t error_asm[] = {
 | 
			
		||||
@@ -885,14 +1064,6 @@ static const uint32_t next_asm[] = {
 | 
			
		||||
    JOP_NEXT | (1 << 24),
 | 
			
		||||
    JOP_RETURN
 | 
			
		||||
};
 | 
			
		||||
static const uint32_t modulo_asm[] = {
 | 
			
		||||
    JOP_MODULO | (1 << 24),
 | 
			
		||||
    JOP_RETURN
 | 
			
		||||
};
 | 
			
		||||
static const uint32_t remainder_asm[] = {
 | 
			
		||||
    JOP_REMAINDER | (1 << 24),
 | 
			
		||||
    JOP_RETURN
 | 
			
		||||
};
 | 
			
		||||
static const uint32_t cmp_asm[] = {
 | 
			
		||||
    JOP_COMPARE | (1 << 24),
 | 
			
		||||
    JOP_RETURN
 | 
			
		||||
@@ -931,9 +1102,16 @@ static void janet_load_libs(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("module/expand-path", janet_core_expand_path),
 | 
			
		||||
        JANET_CORE_REG("int?", janet_core_check_int),
 | 
			
		||||
        JANET_CORE_REG("nat?", janet_core_check_nat),
 | 
			
		||||
        JANET_CORE_REG("bytes?", janet_core_is_bytes),
 | 
			
		||||
        JANET_CORE_REG("indexed?", janet_core_is_indexed),
 | 
			
		||||
        JANET_CORE_REG("dictionary?", janet_core_is_dictionary),
 | 
			
		||||
        JANET_CORE_REG("lengthable?", janet_core_is_lengthable),
 | 
			
		||||
        JANET_CORE_REG("slice", janet_core_slice),
 | 
			
		||||
        JANET_CORE_REG("range", janet_core_range),
 | 
			
		||||
        JANET_CORE_REG("signal", janet_core_signal),
 | 
			
		||||
        JANET_CORE_REG("memcmp", janet_core_memcmp),
 | 
			
		||||
        JANET_CORE_REG("getproto", janet_core_getproto),
 | 
			
		||||
        JANET_CORE_REG("sandbox", janet_core_sandbox),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, corelib_cfuns);
 | 
			
		||||
@@ -962,6 +1140,9 @@ static void janet_load_libs(JanetTable *env) {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_lib_ev(env);
 | 
			
		||||
#ifdef JANET_FILEWATCH
 | 
			
		||||
    janet_lib_filewatch(env);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    janet_lib_net(env);
 | 
			
		||||
@@ -975,90 +1156,85 @@ static void janet_load_libs(JanetTable *env) {
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
    JanetTable *env = (NULL != replacements) ? replacements : janet_table(0);
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_MODULO,
 | 
			
		||||
                    "mod", 2, 2, 2, 2, modulo_asm, sizeof(modulo_asm),
 | 
			
		||||
                    JDOC("(mod dividend divisor)\n\n"
 | 
			
		||||
                         "Returns the modulo of dividend / divisor."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_REMAINDER,
 | 
			
		||||
                    "%", 2, 2, 2, 2, remainder_asm, sizeof(remainder_asm),
 | 
			
		||||
                    JDOC("(% dividend divisor)\n\n"
 | 
			
		||||
                         "Returns the remainder of dividend / divisor."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_CMP,
 | 
			
		||||
                    "cmp", 2, 2, 2, 2, cmp_asm, sizeof(cmp_asm),
 | 
			
		||||
                    JDOC("(cmp x y)\n\n"
 | 
			
		||||
                         "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."));
 | 
			
		||||
         "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"
 | 
			
		||||
                         "Gets the next key in a data structure. Can be used to iterate through "
 | 
			
		||||
                         "the keys of a data structure in an unspecified order. Keys are guaranteed "
 | 
			
		||||
                         "to be seen only once per iteration if they data structure is not mutated "
 | 
			
		||||
                         "during iteration. If key is nil, next returns the first key. If next "
 | 
			
		||||
                         "returns nil, there are no more keys to iterate through."));
 | 
			
		||||
         "Gets the next key in a data structure. Can be used to iterate through "
 | 
			
		||||
         "the keys of a data structure in an unspecified order. Keys are guaranteed "
 | 
			
		||||
         "to be seen only once per iteration if the data structure is not mutated "
 | 
			
		||||
         "during iteration. If key is nil, next returns the first key. If next "
 | 
			
		||||
         "returns nil, there are no more keys to iterate through."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_PROP,
 | 
			
		||||
                    "propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm),
 | 
			
		||||
                    JDOC("(propagate x fiber)\n\n"
 | 
			
		||||
                         "Propagate a signal from a fiber to the current fiber. The resulting "
 | 
			
		||||
                         "stack trace from the current fiber will include frames from fiber. If "
 | 
			
		||||
                         "fiber is in a state that can be resumed, resuming the current fiber will "
 | 
			
		||||
                         "first resume fiber. This function can be used to re-raise an error without "
 | 
			
		||||
                         "losing the original stack trace."));
 | 
			
		||||
         "Propagate a signal from a fiber to the current fiber and "
 | 
			
		||||
         "set the last value of the current fiber to `x`.  The signal "
 | 
			
		||||
         "value is then available as the status of the current fiber. "
 | 
			
		||||
         "The resulting stack trace from the current fiber will include "
 | 
			
		||||
         "frames from fiber. If fiber is in a state that can be resumed, "
 | 
			
		||||
         "resuming the current fiber will first resume `fiber`. "
 | 
			
		||||
         "This function can be used to re-raise an error without losing "
 | 
			
		||||
         "the original stack trace."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_DEBUG,
 | 
			
		||||
                    "debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm),
 | 
			
		||||
                    JDOC("(debug &opt x)\n\n"
 | 
			
		||||
                         "Throws a debug signal that can be caught by a parent fiber and used to inspect "
 | 
			
		||||
                         "the running state of the current fiber. Returns the value passed in by resume."));
 | 
			
		||||
         "Throws a debug signal that can be caught by a parent fiber and used to inspect "
 | 
			
		||||
         "the running state of the current fiber. Returns the value passed in by resume."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_ERROR,
 | 
			
		||||
                    "error", 1, 1, 1, 1, error_asm, sizeof(error_asm),
 | 
			
		||||
                    JDOC("(error e)\n\n"
 | 
			
		||||
                         "Throws an error e that can be caught and handled by a parent fiber."));
 | 
			
		||||
         "Throws an error e that can be caught and handled by a parent fiber."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_YIELD,
 | 
			
		||||
                    "yield", 1, 0, 1, 2, yield_asm, sizeof(yield_asm),
 | 
			
		||||
                    JDOC("(yield &opt x)\n\n"
 | 
			
		||||
                         "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."));
 | 
			
		||||
         "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."));
 | 
			
		||||
         "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"
 | 
			
		||||
                         "Resume a new or suspended fiber and optionally pass in a value to the fiber that "
 | 
			
		||||
                         "will be returned to the last yield in the case of a pending fiber, or the argument to "
 | 
			
		||||
                         "the dispatch function in the case of a new fiber. Returns either the return result of "
 | 
			
		||||
                         "the fiber's dispatch function, or the value from the next yield call in fiber."));
 | 
			
		||||
         "Resume a new or suspended fiber and optionally pass in a value to the fiber that "
 | 
			
		||||
         "will be returned to the last yield in the case of a pending fiber, or the argument to "
 | 
			
		||||
         "the dispatch function in the case of a new fiber. Returns either the return result of "
 | 
			
		||||
         "the fiber's dispatch function, or the value from the next yield call in fiber."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_IN,
 | 
			
		||||
                    "in", 3, 2, 3, 4, in_asm, sizeof(in_asm),
 | 
			
		||||
                    JDOC("(in ds key &opt dflt)\n\n"
 | 
			
		||||
                         "Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
 | 
			
		||||
                         "strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
 | 
			
		||||
                         "and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
 | 
			
		||||
                         "take any value as a key except nil and will return nil or dflt if not found."));
 | 
			
		||||
         "Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
 | 
			
		||||
         "strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
 | 
			
		||||
         "and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
 | 
			
		||||
         "take any value as a key except nil and will return nil or dflt if not found."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_GET,
 | 
			
		||||
                    "get", 3, 2, 3, 4, get_asm, sizeof(in_asm),
 | 
			
		||||
                    JDOC("(get ds key &opt dflt)\n\n"
 | 
			
		||||
                         "Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
 | 
			
		||||
                         "Similar to in, but will not throw an error if the key is invalid for the data structure "
 | 
			
		||||
                         "unless the data structure is an abstract type. In that case, the abstract type getter may throw "
 | 
			
		||||
                         "an error."));
 | 
			
		||||
         "Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
 | 
			
		||||
         "Similar to in, but will not throw an error if the key is invalid for the data structure "
 | 
			
		||||
         "unless the data structure is an abstract type. In that case, the abstract type getter may throw "
 | 
			
		||||
         "an error."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_PUT,
 | 
			
		||||
                    "put", 3, 3, 3, 3, put_asm, sizeof(put_asm),
 | 
			
		||||
                    JDOC("(put ds key value)\n\n"
 | 
			
		||||
                         "Associate a key with a value in any mutable associative data structure. Indexed data structures "
 | 
			
		||||
                         "(arrays and buffers) only accept non-negative integer keys, and will expand if an out of bounds "
 | 
			
		||||
                         "value is provided. In an array, extra space will be filled with nils, and in a buffer, extra "
 | 
			
		||||
                         "space will be filled with 0 bytes. In a table, putting a key that is contained in the table prototype "
 | 
			
		||||
                         "will hide the association defined by the prototype, but will not mutate the prototype table. Putting "
 | 
			
		||||
                         "a value nil into a table will remove the key from the table. Returns the data structure ds."));
 | 
			
		||||
         "Associate a key with a value in any mutable associative data structure. Indexed data structures "
 | 
			
		||||
         "(arrays and buffers) only accept non-negative integer keys, and will expand if an out of bounds "
 | 
			
		||||
         "value is provided. In an array, extra space will be filled with nils, and in a buffer, extra "
 | 
			
		||||
         "space will be filled with 0 bytes. In a table, putting a key that is contained in the table prototype "
 | 
			
		||||
         "will hide the association defined by the prototype, but will not mutate the prototype table. Putting "
 | 
			
		||||
         "a value nil into a table will remove the key from the table. Returns the data structure ds."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_LENGTH,
 | 
			
		||||
                    "length", 1, 1, 1, 1, length_asm, sizeof(length_asm),
 | 
			
		||||
                    JDOC("(length ds)\n\n"
 | 
			
		||||
                         "Returns the length or count of a data structure in constant time as an integer. For "
 | 
			
		||||
                         "structs and tables, returns the number of key-value pairs in the data structure."));
 | 
			
		||||
         "Returns the length or count of a data structure in constant time as an integer. For "
 | 
			
		||||
         "structs and tables, returns the number of key-value pairs in the data structure."));
 | 
			
		||||
    janet_quick_asm(env, JANET_FUN_BNOT,
 | 
			
		||||
                    "bnot", 1, 1, 1, 1, bnot_asm, sizeof(bnot_asm),
 | 
			
		||||
                    JDOC("(bnot x)\n\nReturns the bit-wise inverse of integer x."));
 | 
			
		||||
@@ -1067,62 +1243,74 @@ JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
    /* Variadic ops */
 | 
			
		||||
    templatize_varop(env, JANET_FUN_ADD, "+", 0, 0, JOP_ADD,
 | 
			
		||||
                     JDOC("(+ & xs)\n\n"
 | 
			
		||||
                          "Returns the sum of all xs. xs must be integers or real numbers only. If xs is empty, return 0."));
 | 
			
		||||
         "Returns the sum of all xs. xs must be integers or real numbers only. If xs is empty, return 0."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_SUBTRACT, "-", 0, 0, JOP_SUBTRACT,
 | 
			
		||||
                     JDOC("(- & xs)\n\n"
 | 
			
		||||
                          "Returns the difference of xs. If xs is empty, returns 0. If xs has one element, returns the "
 | 
			
		||||
                          "negative value of that element. Otherwise, returns the first element in xs minus the sum of "
 | 
			
		||||
                          "the rest of the elements."));
 | 
			
		||||
         "Returns the difference of xs. If xs is empty, returns 0. If xs has one element, returns the "
 | 
			
		||||
         "negative value of that element. Otherwise, returns the first element in xs minus the sum of "
 | 
			
		||||
         "the rest of the elements."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_MULTIPLY, "*", 1, 1, JOP_MULTIPLY,
 | 
			
		||||
                     JDOC("(* & xs)\n\n"
 | 
			
		||||
                          "Returns the product of all elements in xs. If xs is empty, returns 1."));
 | 
			
		||||
         "Returns the product of all elements in xs. If xs is empty, returns 1."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_DIVIDE, "/", 1, 1, JOP_DIVIDE,
 | 
			
		||||
                     JDOC("(/ & xs)\n\n"
 | 
			
		||||
                          "Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
 | 
			
		||||
                          "the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
 | 
			
		||||
                          "values."));
 | 
			
		||||
         "Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
 | 
			
		||||
         "the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
 | 
			
		||||
         "values."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_DIVIDE_FLOOR, "div", 1, 1, JOP_DIVIDE_FLOOR,
 | 
			
		||||
                     JDOC("(div & xs)\n\n"
 | 
			
		||||
         "Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns "
 | 
			
		||||
         "the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
 | 
			
		||||
         "values."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_MODULO, "mod", 0, 1, JOP_MODULO,
 | 
			
		||||
                     JDOC("(mod & xs)\n\n"
 | 
			
		||||
         "Returns the result of applying the modulo operator on the first value of xs with each remaining value. "
 | 
			
		||||
         "`(mod x 0)` is defined to be `x`."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_REMAINDER, "%", 0, 1, JOP_REMAINDER,
 | 
			
		||||
                     JDOC("(% & xs)\n\n"
 | 
			
		||||
         "Returns the remainder of dividing the first value of xs by each remaining value."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_BAND, "band", -1, -1, JOP_BAND,
 | 
			
		||||
                     JDOC("(band & xs)\n\n"
 | 
			
		||||
                          "Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
 | 
			
		||||
         "Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_BOR, "bor", 0, 0, JOP_BOR,
 | 
			
		||||
                     JDOC("(bor & xs)\n\n"
 | 
			
		||||
                          "Returns the bit-wise or of all values in xs. Each x in xs must be an integer."));
 | 
			
		||||
         "Returns the bit-wise or of all values in xs. Each x in xs must be an integer."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_BXOR, "bxor", 0, 0, JOP_BXOR,
 | 
			
		||||
                     JDOC("(bxor & xs)\n\n"
 | 
			
		||||
                          "Returns the bit-wise xor of all values in xs. Each in xs must be an integer."));
 | 
			
		||||
         "Returns the bit-wise xor of all values in xs. Each in xs must be an integer."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_LSHIFT, "blshift", 1, 1, JOP_SHIFT_LEFT,
 | 
			
		||||
                     JDOC("(blshift x & shifts)\n\n"
 | 
			
		||||
                          "Returns the value of x bit shifted left by the sum of all values in shifts. x "
 | 
			
		||||
                          "and each element in shift must be an integer."));
 | 
			
		||||
         "Returns the value of x bit shifted left by the sum of all values in shifts. x "
 | 
			
		||||
         "and each element in shift must be an integer."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_RSHIFT, "brshift", 1, 1, JOP_SHIFT_RIGHT,
 | 
			
		||||
                     JDOC("(brshift x & shifts)\n\n"
 | 
			
		||||
                          "Returns the value of x bit shifted right by the sum of all values in shifts. x "
 | 
			
		||||
                          "and each element in shift must be an integer."));
 | 
			
		||||
         "Returns the value of x bit shifted right by the sum of all values in shifts. x "
 | 
			
		||||
         "and each element in shift must be an integer."));
 | 
			
		||||
    templatize_varop(env, JANET_FUN_RSHIFTU, "brushift", 1, 1, JOP_SHIFT_RIGHT_UNSIGNED,
 | 
			
		||||
                     JDOC("(brushift x & shifts)\n\n"
 | 
			
		||||
                          "Returns the value of x bit shifted right by the sum of all values in shifts. x "
 | 
			
		||||
                          "and each element in shift must be an integer. The sign of x is not preserved, so "
 | 
			
		||||
                          "for positive shifts the return value will always be positive."));
 | 
			
		||||
         "Returns the value of x bit shifted right by the sum of all values in shifts. x "
 | 
			
		||||
         "and each element in shift must be an integer. The sign of x is not preserved, so "
 | 
			
		||||
         "for positive shifts the return value will always be positive."));
 | 
			
		||||
 | 
			
		||||
    /* Variadic comparators */
 | 
			
		||||
    templatize_comparator(env, JANET_FUN_GT, ">", 0, JOP_GREATER_THAN,
 | 
			
		||||
                          JDOC("(> & xs)\n\n"
 | 
			
		||||
                               "Check if xs is in descending order. Returns a boolean."));
 | 
			
		||||
         "Check if xs is in descending order. Returns a boolean."));
 | 
			
		||||
    templatize_comparator(env, JANET_FUN_LT, "<", 0, JOP_LESS_THAN,
 | 
			
		||||
                          JDOC("(< & xs)\n\n"
 | 
			
		||||
                               "Check if xs is in ascending order. Returns a boolean."));
 | 
			
		||||
         "Check if xs is in ascending order. Returns a boolean."));
 | 
			
		||||
    templatize_comparator(env, JANET_FUN_GTE, ">=", 0, JOP_GREATER_THAN_EQUAL,
 | 
			
		||||
                          JDOC("(>= & xs)\n\n"
 | 
			
		||||
                               "Check if xs is in non-ascending order. Returns a boolean."));
 | 
			
		||||
         "Check if xs is in non-ascending order. Returns a boolean."));
 | 
			
		||||
    templatize_comparator(env, JANET_FUN_LTE, "<=", 0, JOP_LESS_THAN_EQUAL,
 | 
			
		||||
                          JDOC("(<= & xs)\n\n"
 | 
			
		||||
                               "Check if xs is in non-descending order. Returns a boolean."));
 | 
			
		||||
         "Check if xs is in non-descending order. Returns a boolean."));
 | 
			
		||||
    templatize_comparator(env, JANET_FUN_EQ, "=", 0, JOP_EQUALS,
 | 
			
		||||
                          JDOC("(= & xs)\n\n"
 | 
			
		||||
                               "Check if all values in xs are equal. Returns a boolean."));
 | 
			
		||||
         "Check if all values in xs are equal. Returns a boolean."));
 | 
			
		||||
    templatize_comparator(env, JANET_FUN_NEQ, "not=", 1, JOP_EQUALS,
 | 
			
		||||
                          JDOC("(not= & xs)\n\n"
 | 
			
		||||
                               "Check if any values in xs are not equal. Returns a boolean."));
 | 
			
		||||
         "Check if any values in xs are not equal. Returns a boolean."));
 | 
			
		||||
 | 
			
		||||
    /* Platform detection */
 | 
			
		||||
    janet_def(env, "janet/version", janet_cstringv(JANET_VERSION),
 | 
			
		||||
@@ -1131,7 +1319,7 @@ JanetTable *janet_core_env(JanetTable *replacements) {
 | 
			
		||||
              JDOC("The build identifier of the running janet program."));
 | 
			
		||||
    janet_def(env, "janet/config-bits", janet_wrap_integer(JANET_CURRENT_CONFIG_BITS),
 | 
			
		||||
              JDOC("The flag set of config options from janetconf.h which is used to check "
 | 
			
		||||
                   "if native modules are compatible with the host program."));
 | 
			
		||||
         "if native modules are compatible with the host program."));
 | 
			
		||||
 | 
			
		||||
    /* Allow references to the environment */
 | 
			
		||||
    janet_def(env, "root-env", janet_wrap_table(env),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -102,7 +102,7 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Error reporting. This can be emulated from within Janet, but for
 | 
			
		||||
 * consitency with the top level code it is defined once. */
 | 
			
		||||
 * consistency with the top level code it is defined once. */
 | 
			
		||||
void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
 | 
			
		||||
 | 
			
		||||
    int32_t fi;
 | 
			
		||||
@@ -131,9 +131,9 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
 | 
			
		||||
            if (!wrote_error) {
 | 
			
		||||
                JanetFiberStatus status = janet_fiber_status(fiber);
 | 
			
		||||
                janet_eprintf("%s%s: %s\n",
 | 
			
		||||
                              prefix,
 | 
			
		||||
                              prefix ? prefix : "",
 | 
			
		||||
                              janet_status_names[status],
 | 
			
		||||
                              errstr);
 | 
			
		||||
                              errstr ? errstr : janet_status_names[status]);
 | 
			
		||||
                wrote_error = 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -164,7 +164,7 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (frame->flags & JANET_STACKFRAME_TAILCALL)
 | 
			
		||||
                janet_eprintf(" (tailcall)");
 | 
			
		||||
                janet_eprintf(" (tail call)");
 | 
			
		||||
            if (frame->func && frame->pc) {
 | 
			
		||||
                int32_t off = (int32_t)(frame->pc - def->bytecode);
 | 
			
		||||
                if (def->sourcemap) {
 | 
			
		||||
@@ -180,6 +180,11 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            janet_eprintf("\n");
 | 
			
		||||
            /* Print fiber points optionally. Clutters traces but provides info
 | 
			
		||||
            if (i <= 0 && fi > 0) {
 | 
			
		||||
                janet_eprintf("  in parent fiber\n");
 | 
			
		||||
            }
 | 
			
		||||
            */
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -314,6 +319,7 @@ static Janet doframe(JanetStackFrame *frame) {
 | 
			
		||||
    if (frame->func && frame->pc) {
 | 
			
		||||
        Janet *stack = (Janet *)frame + JANET_FRAME_SIZE;
 | 
			
		||||
        JanetArray *slots;
 | 
			
		||||
        janet_assert(def != NULL, "def != NULL");
 | 
			
		||||
        off = (int32_t)(frame->pc - def->bytecode);
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("pc"), janet_wrap_integer(off));
 | 
			
		||||
        if (def->sourcemap) {
 | 
			
		||||
@@ -329,6 +335,27 @@ static Janet doframe(JanetStackFrame *frame) {
 | 
			
		||||
        safe_memcpy(slots->data, stack, sizeof(Janet) * def->slotcount);
 | 
			
		||||
        slots->count = def->slotcount;
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("slots"), janet_wrap_array(slots));
 | 
			
		||||
        /* Add local bindings */
 | 
			
		||||
        if (def->symbolmap) {
 | 
			
		||||
            JanetTable *local_bindings = janet_table(0);
 | 
			
		||||
            for (int32_t i = def->symbolmap_length - 1; i >= 0; i--) {
 | 
			
		||||
                JanetSymbolMap jsm = def->symbolmap[i];
 | 
			
		||||
                Janet value = janet_wrap_nil();
 | 
			
		||||
                uint32_t pc = (uint32_t)(frame->pc - def->bytecode);
 | 
			
		||||
                if (jsm.birth_pc == UINT32_MAX) {
 | 
			
		||||
                    JanetFuncEnv *env = frame->func->envs[jsm.death_pc];
 | 
			
		||||
                    if (env->offset > 0) {
 | 
			
		||||
                        value = env->as.fiber->data[env->offset + jsm.slot_index];
 | 
			
		||||
                    } else {
 | 
			
		||||
                        value = env->as.values[jsm.slot_index];
 | 
			
		||||
                    }
 | 
			
		||||
                } else if (pc >= jsm.birth_pc && pc < jsm.death_pc) {
 | 
			
		||||
                    value = stack[jsm.slot_index];
 | 
			
		||||
                }
 | 
			
		||||
                janet_table_put(local_bindings, janet_wrap_symbol(jsm.symbol), value);
 | 
			
		||||
            }
 | 
			
		||||
            janet_table_put(t, janet_ckeywordv("locals"), janet_wrap_table(local_bindings));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_table(t);
 | 
			
		||||
}
 | 
			
		||||
@@ -366,8 +393,8 @@ JANET_CORE_FN(cfun_debug_stack,
 | 
			
		||||
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.") {
 | 
			
		||||
              "an error value to print the stack trace with. If `prefix` is nil or not "
 | 
			
		||||
              "provided, will skip the error line. Returns the fiber.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    Janet x = argc == 1 ? janet_wrap_nil() : argv[1];
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -26,6 +26,7 @@
 | 
			
		||||
#include "emit.h"
 | 
			
		||||
#include "vector.h"
 | 
			
		||||
#include "regalloc.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Get a register */
 | 
			
		||||
@@ -128,7 +129,8 @@ static void janetc_movenear(JanetCompiler *c,
 | 
			
		||||
                    ((uint32_t)(src.envindex) << 16) |
 | 
			
		||||
                    ((uint32_t)(dest) << 8) |
 | 
			
		||||
                    JOP_LOAD_UPVALUE);
 | 
			
		||||
    } else if (src.index > 0xFF || src.index != dest) {
 | 
			
		||||
    } else if (src.index != dest) {
 | 
			
		||||
        janet_assert(src.index >= 0, "bad slot");
 | 
			
		||||
        janetc_emit(c,
 | 
			
		||||
                    ((uint32_t)(src.index) << 16) |
 | 
			
		||||
                    ((uint32_t)(dest) << 8) |
 | 
			
		||||
@@ -155,6 +157,7 @@ static void janetc_moveback(JanetCompiler *c,
 | 
			
		||||
                    ((uint32_t)(src) << 8) |
 | 
			
		||||
                    JOP_SET_UPVALUE);
 | 
			
		||||
    } else if (dest.index != src) {
 | 
			
		||||
        janet_assert(dest.index >= 0, "bad slot");
 | 
			
		||||
        janetc_emit(c,
 | 
			
		||||
                    ((uint32_t)(dest.index) << 16) |
 | 
			
		||||
                    ((uint32_t)(src) << 8) |
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1516
									
								
								src/core/ev.c
									
									
									
									
									
								
							
							
						
						
									
										1516
									
								
								src/core/ev.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -26,9 +26,10 @@
 | 
			
		||||
#define JANET_FEATURES_H_defined
 | 
			
		||||
 | 
			
		||||
#if defined(__NetBSD__) || defined(__APPLE__) || defined(__OpenBSD__) \
 | 
			
		||||
    || defined(__bsdi__) || defined(__DragonFly__)
 | 
			
		||||
    || defined(__bsdi__) || defined(__DragonFly__) || defined(__FreeBSD__)
 | 
			
		||||
/* Use BSD source on any BSD systems, include OSX */
 | 
			
		||||
# define _BSD_SOURCE
 | 
			
		||||
# define _POSIX_C_SOURCE 200809L
 | 
			
		||||
#else
 | 
			
		||||
/* Use POSIX feature flags */
 | 
			
		||||
# ifndef _POSIX_C_SOURCE
 | 
			
		||||
@@ -36,6 +37,10 @@
 | 
			
		||||
# endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
#define _DARWIN_C_SOURCE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for sched.h for cpu count */
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
@@ -45,6 +50,11 @@
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* needed for inet_pton and InitializeSRWLock */
 | 
			
		||||
#ifdef __MINGW32__
 | 
			
		||||
#define _WIN32_WINNT _WIN32_WINNT_VISTA
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for realpath on linux, as well as pthread rwlocks. */
 | 
			
		||||
#ifndef _XOPEN_SOURCE
 | 
			
		||||
#define _XOPEN_SOURCE 600
 | 
			
		||||
@@ -61,4 +71,11 @@
 | 
			
		||||
#define _NETBSD_SOURCE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for several things when building with -std=c99. */
 | 
			
		||||
#if !__BSD_VISIBLE && (defined(__DragonFly__) || defined(__FreeBSD__))
 | 
			
		||||
#define __BSD_VISIBLE 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _FILE_OFFSET_BITS 64
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										557
									
								
								src/core/ffi.c
									
									
									
									
									
								
							
							
						
						
									
										557
									
								
								src/core/ffi.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,7 @@
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_FFI
 | 
			
		||||
@@ -37,6 +38,13 @@
 | 
			
		||||
#define alloca __builtin_alloca
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* FFI jit includes */
 | 
			
		||||
#ifdef JANET_FFI_JIT
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define JANET_FFI_MAX_RECUR 64
 | 
			
		||||
 | 
			
		||||
/* Compiler, OS, and arch detection. Used
 | 
			
		||||
@@ -48,6 +56,9 @@
 | 
			
		||||
#if (defined(__x86_64__) || defined(_M_X64)) && !defined(JANET_WINDOWS)
 | 
			
		||||
#define JANET_FFI_SYSV64_ENABLED
 | 
			
		||||
#endif
 | 
			
		||||
#if (defined(__aarch64__) || defined(_M_ARM64)) && !defined(JANET_WINDOWS)
 | 
			
		||||
#define JANET_FFI_AAPCS64_ENABLED
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct JanetFFIType JanetFFIType;
 | 
			
		||||
typedef struct JanetFFIStruct JanetFFIStruct;
 | 
			
		||||
@@ -132,7 +143,13 @@ typedef enum {
 | 
			
		||||
    JANET_WIN64_REGISTER,
 | 
			
		||||
    JANET_WIN64_STACK,
 | 
			
		||||
    JANET_WIN64_REGISTER_REF,
 | 
			
		||||
    JANET_WIN64_STACK_REF
 | 
			
		||||
    JANET_WIN64_STACK_REF,
 | 
			
		||||
    JANET_AAPCS64_GENERAL,
 | 
			
		||||
    JANET_AAPCS64_SSE,
 | 
			
		||||
    JANET_AAPCS64_GENERAL_REF,
 | 
			
		||||
    JANET_AAPCS64_STACK,
 | 
			
		||||
    JANET_AAPCS64_STACK_REF,
 | 
			
		||||
    JANET_AAPCS64_NONE
 | 
			
		||||
} JanetFFIWordSpec;
 | 
			
		||||
 | 
			
		||||
/* Describe how each Janet argument is interpreted in terms of machine words
 | 
			
		||||
@@ -147,13 +164,16 @@ typedef struct {
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_FFI_CC_NONE,
 | 
			
		||||
    JANET_FFI_CC_SYSV_64,
 | 
			
		||||
    JANET_FFI_CC_WIN_64
 | 
			
		||||
    JANET_FFI_CC_WIN_64,
 | 
			
		||||
    JANET_FFI_CC_AAPCS64
 | 
			
		||||
} JanetFFICallingConvention;
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_FFI_WIN64_ENABLED
 | 
			
		||||
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_WIN_64
 | 
			
		||||
#elif defined(JANET_FFI_SYSV64_ENABLED)
 | 
			
		||||
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_SYSV_64
 | 
			
		||||
#elif defined(JANET_FFI_AAPCS64_ENABLED)
 | 
			
		||||
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_AAPCS64
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_NONE
 | 
			
		||||
#endif
 | 
			
		||||
@@ -202,6 +222,11 @@ int struct_mark(void *p, size_t s) {
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    void *function_pointer;
 | 
			
		||||
    size_t size;
 | 
			
		||||
} JanetFFIJittedFn;
 | 
			
		||||
 | 
			
		||||
static const JanetAbstractType janet_struct_type = {
 | 
			
		||||
    "core/ffi-struct",
 | 
			
		||||
    NULL,
 | 
			
		||||
@@ -209,6 +234,42 @@ static const JanetAbstractType janet_struct_type = {
 | 
			
		||||
    JANET_ATEND_GCMARK
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int janet_ffijit_gc(void *p, size_t s) {
 | 
			
		||||
    (void) s;
 | 
			
		||||
    JanetFFIJittedFn *fn = p;
 | 
			
		||||
    if (fn->function_pointer == NULL) return 0;
 | 
			
		||||
#ifdef JANET_FFI_JIT
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    VirtualFree(fn->function_pointer, fn->size, MEM_RELEASE);
 | 
			
		||||
#else
 | 
			
		||||
    munmap(fn->function_pointer, fn->size);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetByteView janet_ffijit_getbytes(void *p, size_t s) {
 | 
			
		||||
    (void) s;
 | 
			
		||||
    JanetFFIJittedFn *fn = p;
 | 
			
		||||
    JanetByteView bytes;
 | 
			
		||||
    bytes.bytes = fn->function_pointer;
 | 
			
		||||
    bytes.len = (int32_t) fn->size;
 | 
			
		||||
    return bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static size_t janet_ffijit_length(void *p, size_t s) {
 | 
			
		||||
    (void) s;
 | 
			
		||||
    JanetFFIJittedFn *fn = p;
 | 
			
		||||
    return fn->size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_type_ffijit = {
 | 
			
		||||
    .name = "ffi/jitfn",
 | 
			
		||||
    .gc = janet_ffijit_gc,
 | 
			
		||||
    .bytes = janet_ffijit_getbytes,
 | 
			
		||||
    .length = janet_ffijit_length
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    Clib clib;
 | 
			
		||||
    int closed;
 | 
			
		||||
@@ -252,6 +313,9 @@ static JanetFFICallingConvention decode_ffi_cc(const uint8_t *name) {
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI_SYSV64_ENABLED
 | 
			
		||||
    if (!janet_cstrcmp(name, "sysv64")) return JANET_FFI_CC_SYSV_64;
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI_AAPCS64_ENABLED
 | 
			
		||||
    if (!janet_cstrcmp(name, "aapcs64")) return JANET_FFI_CC_AAPCS64;
 | 
			
		||||
#endif
 | 
			
		||||
    if (!janet_cstrcmp(name, "default")) return JANET_FFI_CC_DEFAULT;
 | 
			
		||||
    janet_panicf("unknown calling convention %s", name);
 | 
			
		||||
@@ -261,6 +325,7 @@ static JanetFFIPrimType decode_ffi_prim(const uint8_t *name) {
 | 
			
		||||
    if (!janet_cstrcmp(name, "void")) return JANET_FFI_TYPE_VOID;
 | 
			
		||||
    if (!janet_cstrcmp(name, "bool")) return JANET_FFI_TYPE_BOOL;
 | 
			
		||||
    if (!janet_cstrcmp(name, "ptr")) return JANET_FFI_TYPE_PTR;
 | 
			
		||||
    if (!janet_cstrcmp(name, "pointer")) return JANET_FFI_TYPE_PTR;
 | 
			
		||||
    if (!janet_cstrcmp(name, "string")) return JANET_FFI_TYPE_STRING;
 | 
			
		||||
    if (!janet_cstrcmp(name, "float")) return JANET_FFI_TYPE_FLOAT;
 | 
			
		||||
    if (!janet_cstrcmp(name, "double")) return JANET_FFI_TYPE_DOUBLE;
 | 
			
		||||
@@ -335,7 +400,7 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
 | 
			
		||||
 | 
			
		||||
    JanetFFIStruct *st = janet_abstract(&janet_struct_type,
 | 
			
		||||
                                        sizeof(JanetFFIStruct) + argc * sizeof(JanetFFIStructMember));
 | 
			
		||||
    st->field_count = member_count;
 | 
			
		||||
    st->field_count = 0;
 | 
			
		||||
    st->size = 0;
 | 
			
		||||
    st->align = 1;
 | 
			
		||||
    if (argc == 0) {
 | 
			
		||||
@@ -353,6 +418,7 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
 | 
			
		||||
        st->fields[i].type = decode_ffi_type(argv[j]);
 | 
			
		||||
        size_t el_size = type_size(st->fields[i].type);
 | 
			
		||||
        size_t el_align = type_align(st->fields[i].type);
 | 
			
		||||
        if (el_align <= 0) janet_panicf("bad field type %V", argv[j]);
 | 
			
		||||
        if (all_packed || pack_one) {
 | 
			
		||||
            if (st->size % el_align != 0) is_aligned = 0;
 | 
			
		||||
            st->fields[i].offset = st->size;
 | 
			
		||||
@@ -368,6 +434,7 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
 | 
			
		||||
    st->size += (st->align - 1);
 | 
			
		||||
    st->size /= st->align;
 | 
			
		||||
    st->size *= st->align;
 | 
			
		||||
    st->field_count = member_count;
 | 
			
		||||
    return st;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -425,13 +492,15 @@ JANET_CORE_FN(cfun_ffi_align,
 | 
			
		||||
static void *janet_ffi_getpointer(const Janet *argv, int32_t n) {
 | 
			
		||||
    switch (janet_type(argv[n])) {
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panicf("bad slot #%d, expected ffi pointer convertable type, got %v", argv[n]);
 | 
			
		||||
            janet_panicf("bad slot #%d, expected ffi pointer convertible type, got %v", n, argv[n]);
 | 
			
		||||
        case JANET_POINTER:
 | 
			
		||||
        case JANET_STRING:
 | 
			
		||||
        case JANET_KEYWORD:
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
        case JANET_ABSTRACT:
 | 
			
		||||
        case JANET_CFUNCTION:
 | 
			
		||||
            return janet_unwrap_pointer(argv[n]);
 | 
			
		||||
        case JANET_ABSTRACT:
 | 
			
		||||
            return (void *) janet_getbytes(argv, n).bytes;
 | 
			
		||||
        case JANET_BUFFER:
 | 
			
		||||
            return janet_unwrap_buffer(argv[n])->data;
 | 
			
		||||
        case JANET_FUNCTION:
 | 
			
		||||
@@ -444,6 +513,19 @@ static void *janet_ffi_getpointer(const Janet *argv, int32_t n) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *janet_ffi_get_callable_pointer(const Janet *argv, int32_t n) {
 | 
			
		||||
    switch (janet_type(argv[n])) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_POINTER:
 | 
			
		||||
            return janet_unwrap_pointer(argv[n]);
 | 
			
		||||
        case JANET_ABSTRACT:
 | 
			
		||||
            if (!janet_checkabstract(argv[n], &janet_type_ffijit)) break;
 | 
			
		||||
            return ((JanetFFIJittedFn *)janet_unwrap_abstract(argv[n]))->function_pointer;
 | 
			
		||||
    }
 | 
			
		||||
    janet_panicf("bad slot #%d, expected ffi callable pointer type, got %v", n, argv[n]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write a value given by some Janet values and an FFI type as it would appear in memory.
 | 
			
		||||
 * The alignment and space available is assumed to already be sufficient */
 | 
			
		||||
static void janet_ffi_write_one(void *to, const Janet *argv, int32_t n, JanetFFIType type, int recur) {
 | 
			
		||||
@@ -698,6 +780,101 @@ static JanetFFIWordSpec sysv64_classify(JanetFFIType type) {
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_FFI_AAPCS64_ENABLED
 | 
			
		||||
/* Procedure Call Standard for the Arm® 64-bit Architecture (AArch64) 2023Q3 – October 6, 2023
 | 
			
		||||
 * See section 6.8.2 Parameter passing rules.
 | 
			
		||||
 * https://github.com/ARM-software/abi-aa/releases/download/2023Q3/aapcs64.pdf
 | 
			
		||||
 *
 | 
			
		||||
 * Additional documentation needed for Apple platforms.
 | 
			
		||||
 * https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms */
 | 
			
		||||
 | 
			
		||||
#define JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, alignment) (ptr = ((ptr) + ((alignment) - 1)) & ~((alignment) - 1))
 | 
			
		||||
#if !defined(JANET_APPLE)
 | 
			
		||||
#define JANET_FFI_AAPCS64_STACK_ALIGN(ptr, alignment) ((void) alignment, JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, 8))
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_FFI_AAPCS64_STACK_ALIGN(ptr, alignment) JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, alignment)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint64_t a;
 | 
			
		||||
    uint64_t b;
 | 
			
		||||
} Aapcs64Variant1ReturnGeneral;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    double a;
 | 
			
		||||
    double b;
 | 
			
		||||
    double c;
 | 
			
		||||
    double d;
 | 
			
		||||
} Aapcs64Variant2ReturnSse;
 | 
			
		||||
 | 
			
		||||
/* Workaround for passing a return value pointer through x8.
 | 
			
		||||
 * Limits struct returns to 128 bytes. */
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint64_t a;
 | 
			
		||||
    uint64_t b;
 | 
			
		||||
    uint64_t c;
 | 
			
		||||
    uint64_t d;
 | 
			
		||||
    uint64_t e;
 | 
			
		||||
    uint64_t f;
 | 
			
		||||
    uint64_t g;
 | 
			
		||||
    uint64_t h;
 | 
			
		||||
    uint64_t i;
 | 
			
		||||
    uint64_t j;
 | 
			
		||||
    uint64_t k;
 | 
			
		||||
    uint64_t l;
 | 
			
		||||
    uint64_t m;
 | 
			
		||||
    uint64_t n;
 | 
			
		||||
    uint64_t o;
 | 
			
		||||
    uint64_t p;
 | 
			
		||||
} Aapcs64Variant3ReturnPointer;
 | 
			
		||||
 | 
			
		||||
static JanetFFIWordSpec aapcs64_classify(JanetFFIType type) {
 | 
			
		||||
    switch (type.prim) {
 | 
			
		||||
        case JANET_FFI_TYPE_PTR:
 | 
			
		||||
        case JANET_FFI_TYPE_STRING:
 | 
			
		||||
        case JANET_FFI_TYPE_BOOL:
 | 
			
		||||
        case JANET_FFI_TYPE_INT8:
 | 
			
		||||
        case JANET_FFI_TYPE_INT16:
 | 
			
		||||
        case JANET_FFI_TYPE_INT32:
 | 
			
		||||
        case JANET_FFI_TYPE_INT64:
 | 
			
		||||
        case JANET_FFI_TYPE_UINT8:
 | 
			
		||||
        case JANET_FFI_TYPE_UINT16:
 | 
			
		||||
        case JANET_FFI_TYPE_UINT32:
 | 
			
		||||
        case JANET_FFI_TYPE_UINT64:
 | 
			
		||||
            return JANET_AAPCS64_GENERAL;
 | 
			
		||||
        case JANET_FFI_TYPE_DOUBLE:
 | 
			
		||||
        case JANET_FFI_TYPE_FLOAT:
 | 
			
		||||
            return JANET_AAPCS64_SSE;
 | 
			
		||||
        case JANET_FFI_TYPE_STRUCT: {
 | 
			
		||||
            JanetFFIStruct *st = type.st;
 | 
			
		||||
            if (st->field_count <= 4 && aapcs64_classify(st->fields[0].type) == JANET_AAPCS64_SSE) {
 | 
			
		||||
                bool is_hfa = true;
 | 
			
		||||
                for (uint32_t i = 1; i < st->field_count; i++) {
 | 
			
		||||
                    if (st->fields[0].type.prim != st->fields[i].type.prim) {
 | 
			
		||||
                        is_hfa = false;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (is_hfa) {
 | 
			
		||||
                    return JANET_AAPCS64_SSE;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (type_size(type) > 16) {
 | 
			
		||||
                return JANET_AAPCS64_GENERAL_REF;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return JANET_AAPCS64_GENERAL;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FFI_TYPE_VOID:
 | 
			
		||||
            return JANET_AAPCS64_NONE;
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panic("nyi");
 | 
			
		||||
            return JANET_AAPCS64_NONE;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_signature,
 | 
			
		||||
              "(ffi/signature calling-convention ret-type & arg-types)",
 | 
			
		||||
              "Create a function signature object that can be used to make calls "
 | 
			
		||||
@@ -775,7 +952,6 @@ JANET_CORE_FN(cfun_ffi_signature,
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Add reference items */
 | 
			
		||||
            size_t old_stack_count = stack_count;
 | 
			
		||||
            stack_count += 2 * ref_stack_count;
 | 
			
		||||
            if (stack_count & 0x1) {
 | 
			
		||||
                stack_count++;
 | 
			
		||||
@@ -896,6 +1072,96 @@ JANET_CORE_FN(cfun_ffi_signature,
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_FFI_AAPCS64_ENABLED
 | 
			
		||||
        case JANET_FFI_CC_AAPCS64: {
 | 
			
		||||
            uint32_t next_general_reg = 0;
 | 
			
		||||
            uint32_t next_fp_reg = 0;
 | 
			
		||||
            uint32_t stack_offset = 0;
 | 
			
		||||
            uint32_t ref_stack_offset = 0;
 | 
			
		||||
 | 
			
		||||
            JanetFFIWordSpec ret_spec = aapcs64_classify(ret_type);
 | 
			
		||||
            ret.spec = ret_spec;
 | 
			
		||||
            if (ret_spec == JANET_AAPCS64_SSE) {
 | 
			
		||||
                variant = 1;
 | 
			
		||||
            } else if (ret_spec == JANET_AAPCS64_GENERAL_REF) {
 | 
			
		||||
                if (type_size(ret_type) > sizeof(Aapcs64Variant3ReturnPointer)) {
 | 
			
		||||
                    janet_panic("return value bigger than supported");
 | 
			
		||||
                }
 | 
			
		||||
                variant = 2;
 | 
			
		||||
            } else {
 | 
			
		||||
                variant = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for (uint32_t i = 0; i < arg_count; i++) {
 | 
			
		||||
                mappings[i].type = decode_ffi_type(argv[i + 2]);
 | 
			
		||||
                mappings[i].spec = aapcs64_classify(mappings[i].type);
 | 
			
		||||
                size_t arg_size = type_size(mappings[i].type);
 | 
			
		||||
 | 
			
		||||
                switch (mappings[i].spec) {
 | 
			
		||||
                    case JANET_AAPCS64_GENERAL: {
 | 
			
		||||
                        bool arg_is_struct = mappings[i].type.prim == JANET_FFI_TYPE_STRUCT;
 | 
			
		||||
                        uint32_t needed_registers = (arg_size + 7) / 8;
 | 
			
		||||
                        if (next_general_reg + needed_registers <= 8) {
 | 
			
		||||
                            mappings[i].offset = next_general_reg;
 | 
			
		||||
                            next_general_reg += needed_registers;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            size_t arg_align = arg_is_struct ? 8 : type_align(mappings[i].type);
 | 
			
		||||
                            mappings[i].spec = JANET_AAPCS64_STACK;
 | 
			
		||||
                            mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, arg_align);
 | 
			
		||||
#if !defined(JANET_APPLE)
 | 
			
		||||
                            stack_offset += arg_size > 8 ? arg_size : 8;
 | 
			
		||||
#else
 | 
			
		||||
                            stack_offset += arg_size;
 | 
			
		||||
#endif
 | 
			
		||||
                            next_general_reg = 8;
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    case JANET_AAPCS64_GENERAL_REF:
 | 
			
		||||
                        if (next_general_reg < 8) {
 | 
			
		||||
                            mappings[i].offset = next_general_reg++;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            mappings[i].spec = JANET_AAPCS64_STACK_REF;
 | 
			
		||||
                            mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, 8);
 | 
			
		||||
                            stack_offset += 8;
 | 
			
		||||
                        }
 | 
			
		||||
                        mappings[i].offset2 = JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ref_stack_offset, 8);
 | 
			
		||||
                        ref_stack_offset += arg_size;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case JANET_AAPCS64_SSE: {
 | 
			
		||||
                        uint32_t needed_registers = (arg_size + 7) / 8;
 | 
			
		||||
                        if (next_fp_reg + needed_registers <= 8) {
 | 
			
		||||
                            mappings[i].offset = next_fp_reg;
 | 
			
		||||
                            next_fp_reg += needed_registers;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            mappings[i].spec = JANET_AAPCS64_STACK;
 | 
			
		||||
                            mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, 8);
 | 
			
		||||
#if !defined(JANET_APPLE)
 | 
			
		||||
                            stack_offset += 8;
 | 
			
		||||
#else
 | 
			
		||||
                            stack_offset += arg_size;
 | 
			
		||||
#endif
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    default:
 | 
			
		||||
                        janet_panic("nyi");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            stack_offset = (stack_offset + 15) & ~0xFUL;
 | 
			
		||||
            ref_stack_offset = (ref_stack_offset + 15) & ~0xFUL;
 | 
			
		||||
            stack_count = stack_offset + ref_stack_offset;
 | 
			
		||||
 | 
			
		||||
            for (uint32_t i = 0; i < arg_count; i++) {
 | 
			
		||||
                if (mappings[i].spec == JANET_AAPCS64_GENERAL_REF || mappings[i].spec == JANET_AAPCS64_STACK_REF) {
 | 
			
		||||
                    mappings[i].offset2 = stack_offset + mappings[i].offset2;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Create signature abstract value */
 | 
			
		||||
@@ -1116,7 +1382,13 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
 | 
			
		||||
 | 
			
		||||
    /* hack to get proper stack placement and avoid clobbering from logic above - shift stack down, otherwise we have issues.
 | 
			
		||||
     * Technically, this writes into 16 bytes of unallocated stack memory */
 | 
			
		||||
#ifdef JANET_MINGW
 | 
			
		||||
#pragma GCC diagnostic ignored "-Wstringop-overflow"
 | 
			
		||||
#endif
 | 
			
		||||
    if (stack_size) memmove(stack - stack_shift, stack, stack_size);
 | 
			
		||||
#ifdef JANET_MINGW
 | 
			
		||||
#pragma GCC diagnostic pop
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    switch (signature->variant) {
 | 
			
		||||
        default:
 | 
			
		||||
@@ -1224,17 +1496,170 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_FFI_AAPCS64_ENABLED
 | 
			
		||||
 | 
			
		||||
static void janet_ffi_aapcs64_standard_callback(void *ctx, void *userdata) {
 | 
			
		||||
    janet_ffi_trampoline(ctx, userdata);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
typedef Aapcs64Variant1ReturnGeneral janet_aapcs64_variant_1(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
 | 
			
		||||
        double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
 | 
			
		||||
typedef Aapcs64Variant2ReturnSse janet_aapcs64_variant_2(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
 | 
			
		||||
        double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
 | 
			
		||||
typedef Aapcs64Variant3ReturnPointer janet_aapcs64_variant_3(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
 | 
			
		||||
        double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static Janet janet_ffi_aapcs64(JanetFFISignature *signature, void *function_pointer, const Janet *argv) {
 | 
			
		||||
    union {
 | 
			
		||||
        Aapcs64Variant1ReturnGeneral general_return;
 | 
			
		||||
        Aapcs64Variant2ReturnSse sse_return;
 | 
			
		||||
        Aapcs64Variant3ReturnPointer pointer_return;
 | 
			
		||||
    } retu;
 | 
			
		||||
    uint64_t regs[8];
 | 
			
		||||
    double fp_regs[8];
 | 
			
		||||
    void *ret_mem = &retu.general_return;
 | 
			
		||||
 | 
			
		||||
    /* Apple's stack values do not need to be 8-byte aligned,
 | 
			
		||||
     * thus all stack offsets refer to actual byte positions. */
 | 
			
		||||
    uint8_t *stack = alloca(signature->stack_count);
 | 
			
		||||
#if defined(JANET_APPLE)
 | 
			
		||||
    /* Values must be zero-extended by the caller instead of the callee. */
 | 
			
		||||
    memset(stack, 0, signature->stack_count);
 | 
			
		||||
#endif
 | 
			
		||||
    for (uint32_t i = 0; i < signature->arg_count; i++) {
 | 
			
		||||
        int32_t n = i + 2;
 | 
			
		||||
        JanetFFIMapping arg = signature->args[i];
 | 
			
		||||
        void *to = NULL;
 | 
			
		||||
 | 
			
		||||
        switch (arg.spec) {
 | 
			
		||||
            case JANET_AAPCS64_GENERAL:
 | 
			
		||||
                to = regs + arg.offset;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_AAPCS64_GENERAL_REF:
 | 
			
		||||
                to = stack + arg.offset2;
 | 
			
		||||
                regs[arg.offset] = (uint64_t) to;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_AAPCS64_SSE:
 | 
			
		||||
                to = fp_regs + arg.offset;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_AAPCS64_STACK:
 | 
			
		||||
                to = stack + arg.offset;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_AAPCS64_STACK_REF:
 | 
			
		||||
                to = stack + arg.offset2;
 | 
			
		||||
                uint64_t *ptr = (uint64_t *) stack + arg.offset;
 | 
			
		||||
                *ptr = (uint64_t) to;
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                janet_panic("nyi");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (to) {
 | 
			
		||||
            janet_ffi_write_one(to, argv, n, arg.type, JANET_FFI_MAX_RECUR);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (signature->variant) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            retu.general_return = ((janet_aapcs64_variant_1 *)(function_pointer))(
 | 
			
		||||
                                      regs[0], regs[1], regs[2], regs[3],
 | 
			
		||||
                                      regs[4], regs[5], regs[6], regs[7],
 | 
			
		||||
                                      fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
 | 
			
		||||
                                      fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
 | 
			
		||||
            break;
 | 
			
		||||
        case 1:
 | 
			
		||||
            retu.sse_return = ((janet_aapcs64_variant_2 *)(function_pointer))(
 | 
			
		||||
                                  regs[0], regs[1], regs[2], regs[3],
 | 
			
		||||
                                  regs[4], regs[5], regs[6], regs[7],
 | 
			
		||||
                                  fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
 | 
			
		||||
                                  fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
 | 
			
		||||
            break;
 | 
			
		||||
        case 2: {
 | 
			
		||||
            retu.pointer_return = ((janet_aapcs64_variant_3 *)(function_pointer))(
 | 
			
		||||
                                      regs[0], regs[1], regs[2], regs[3],
 | 
			
		||||
                                      regs[4], regs[5], regs[6], regs[7],
 | 
			
		||||
                                      fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
 | 
			
		||||
                                      fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return janet_ffi_read_one(ret_mem, signature->ret.type, JANET_FFI_MAX_RECUR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Allocate executable memory chunks in sizes of a page. Ideally we would keep
 | 
			
		||||
 * an allocator around so that multiple JIT allocations would point to the same
 | 
			
		||||
 * region but it isn't really worth it. */
 | 
			
		||||
#define FFI_PAGE_MASK 0xFFF
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_jitfn,
 | 
			
		||||
              "(ffi/jitfn bytes)",
 | 
			
		||||
              "Create an abstract type that can be used as the pointer argument to `ffi/call`. The content "
 | 
			
		||||
              "of `bytes` is architecture specific machine code that will be copied into executable memory.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_JIT);
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView bytes = janet_getbytes(argv, 0);
 | 
			
		||||
 | 
			
		||||
    /* Quick hack to align to page boundary, we should query OS. FIXME */
 | 
			
		||||
    size_t alloc_size = ((size_t) bytes.len + FFI_PAGE_MASK) & ~FFI_PAGE_MASK;
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_FFI_JIT
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    JanetFFIJittedFn *fn = janet_abstract_threaded(&janet_type_ffijit, sizeof(JanetFFIJittedFn));
 | 
			
		||||
#else
 | 
			
		||||
    JanetFFIJittedFn *fn = janet_abstract(&janet_type_ffijit, sizeof(JanetFFIJittedFn));
 | 
			
		||||
#endif
 | 
			
		||||
    fn->function_pointer = NULL;
 | 
			
		||||
    fn->size = 0;
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    void *ptr = VirtualAlloc(NULL, alloc_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
 | 
			
		||||
#elif defined(MAP_ANONYMOUS)
 | 
			
		||||
    void *ptr = mmap(0, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 | 
			
		||||
#elif defined(MAP_ANON)
 | 
			
		||||
    /* macos doesn't have MAP_ANONYMOUS */
 | 
			
		||||
    void *ptr = mmap(0, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
 | 
			
		||||
#else
 | 
			
		||||
    /* -std=c99 gets in the way */
 | 
			
		||||
    /* #define MAP_ANONYMOUS 0x20 should work, though. */
 | 
			
		||||
    void *ptr = mmap(0, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, -1, 0);
 | 
			
		||||
#endif
 | 
			
		||||
    if (!ptr) {
 | 
			
		||||
        janet_panic("failed to memory map writable memory");
 | 
			
		||||
    }
 | 
			
		||||
    memcpy(ptr, bytes.bytes, bytes.len);
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    DWORD old = 0;
 | 
			
		||||
    if (!VirtualProtect(ptr, alloc_size, PAGE_EXECUTE_READ, &old)) {
 | 
			
		||||
        janet_panic("failed to make mapped memory executable");
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    if (mprotect(ptr, alloc_size, PROT_READ | PROT_EXEC) == -1) {
 | 
			
		||||
        janet_panic("failed to make mapped memory executable");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    fn->size = alloc_size;
 | 
			
		||||
    fn->function_pointer = ptr;
 | 
			
		||||
    return janet_wrap_abstract(fn);
 | 
			
		||||
#else
 | 
			
		||||
    janet_panic("ffi/jitfn not available on this platform");
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_call,
 | 
			
		||||
              "(ffi/call pointer signature & args)",
 | 
			
		||||
              "Call a raw pointer as a function pointer. The function signature specifies "
 | 
			
		||||
              "how Janet values in `args` are converted to native machine types.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    void *function_pointer = janet_getpointer(argv, 0);
 | 
			
		||||
    void *function_pointer = janet_ffi_get_callable_pointer(argv, 0);
 | 
			
		||||
    JanetFFISignature *signature = janet_getabstract(argv, 1, &janet_signature_type);
 | 
			
		||||
    janet_fixarity(argc - 2, signature->arg_count);
 | 
			
		||||
    switch (signature->cc) {
 | 
			
		||||
        default:
 | 
			
		||||
        case JANET_FFI_CC_NONE:
 | 
			
		||||
            (void) function_pointer;
 | 
			
		||||
            janet_panic("calling convention not supported");
 | 
			
		||||
#ifdef JANET_FFI_WIN64_ENABLED
 | 
			
		||||
        case JANET_FFI_CC_WIN_64:
 | 
			
		||||
@@ -1243,23 +1668,34 @@ JANET_CORE_FN(cfun_ffi_call,
 | 
			
		||||
#ifdef JANET_FFI_SYSV64_ENABLED
 | 
			
		||||
        case JANET_FFI_CC_SYSV_64:
 | 
			
		||||
            return janet_ffi_sysv64(signature, function_pointer, argv);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI_AAPCS64_ENABLED
 | 
			
		||||
        case JANET_FFI_CC_AAPCS64:
 | 
			
		||||
            return janet_ffi_aapcs64(signature, function_pointer, argv);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_buffer_write,
 | 
			
		||||
              "(ffi/write ffi-type data &opt buffer)",
 | 
			
		||||
              "Append a native tyep to a buffer such as it would appear in memory. This can be used "
 | 
			
		||||
              "(ffi/write ffi-type data &opt buffer index)",
 | 
			
		||||
              "Append a native type to a buffer such as it would appear in memory. This can be used "
 | 
			
		||||
              "to pass pointers to structs in the ffi, or send C/C++/native structs over the network "
 | 
			
		||||
              "or to files. Returns a modifed buffer or a new buffer if one is not supplied.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
              "or to files. Returns a modified buffer or a new buffer if one is not supplied.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
 | 
			
		||||
    janet_arity(argc, 2, 4);
 | 
			
		||||
    JanetFFIType type = decode_ffi_type(argv[0]);
 | 
			
		||||
    uint32_t el_size = (uint32_t) type_size(type);
 | 
			
		||||
    JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
 | 
			
		||||
    int32_t index = janet_optnat(argv, argc, 3, 0);
 | 
			
		||||
    int32_t old_count = buffer->count;
 | 
			
		||||
    if (index > old_count) janet_panic("index out of bounds");
 | 
			
		||||
    buffer->count = index;
 | 
			
		||||
    janet_buffer_extra(buffer, el_size);
 | 
			
		||||
    memset(buffer->data, 0, el_size);
 | 
			
		||||
    janet_ffi_write_one(buffer->data, argv, 1, type, JANET_FFI_MAX_RECUR);
 | 
			
		||||
    buffer->count += el_size;
 | 
			
		||||
    buffer->count = old_count;
 | 
			
		||||
    memset(buffer->data + index, 0, el_size);
 | 
			
		||||
    janet_ffi_write_one(buffer->data + index, argv, 1, type, JANET_FFI_MAX_RECUR);
 | 
			
		||||
    index += el_size;
 | 
			
		||||
    if (buffer->count < index) buffer->count = index;
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1268,6 +1704,7 @@ JANET_CORE_FN(cfun_ffi_buffer_read,
 | 
			
		||||
              "Parse a native struct out of a buffer and convert it to normal Janet data structures. "
 | 
			
		||||
              "This function is the inverse of `ffi/write`. `bytes` can also be a raw pointer, although "
 | 
			
		||||
              "this is unsafe.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetFFIType type = decode_ffi_type(argv[0]);
 | 
			
		||||
    size_t offset = (size_t) janet_optnat(argv, argc, 2, 0);
 | 
			
		||||
@@ -1304,6 +1741,10 @@ JANET_CORE_FN(cfun_ffi_get_callback_trampoline,
 | 
			
		||||
#ifdef JANET_FFI_SYSV64_ENABLED
 | 
			
		||||
        case JANET_FFI_CC_SYSV_64:
 | 
			
		||||
            return janet_wrap_pointer(janet_ffi_sysv64_standard_callback);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI_AAPCS64_ENABLED
 | 
			
		||||
        case JANET_FFI_CC_AAPCS64:
 | 
			
		||||
            return janet_wrap_pointer(janet_ffi_aapcs64_standard_callback);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1314,6 +1755,7 @@ JANET_CORE_FN(janet_core_raw_native,
 | 
			
		||||
              " or run any code from it. This is different than `native`, which will "
 | 
			
		||||
              "run initialization code to get a module table. If `path` is nil, opens the current running binary. "
 | 
			
		||||
              "Returns a `core/native`.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE);
 | 
			
		||||
    janet_arity(argc, 0, 1);
 | 
			
		||||
    const char *path = janet_optcstring(argv, argc, 0, NULL);
 | 
			
		||||
    Clib lib = load_clib(path);
 | 
			
		||||
@@ -1329,6 +1771,7 @@ JANET_CORE_FN(janet_core_native_lookup,
 | 
			
		||||
              "(ffi/lookup native symbol-name)",
 | 
			
		||||
              "Lookup a symbol from a native object. All symbol lookups will return a raw pointer "
 | 
			
		||||
              "if the symbol is found, else nil.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE);
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type);
 | 
			
		||||
    const char *sym = janet_getcstring(argv, 1);
 | 
			
		||||
@@ -1342,6 +1785,7 @@ JANET_CORE_FN(janet_core_native_close,
 | 
			
		||||
              "(ffi/close native)",
 | 
			
		||||
              "Free a native object. Dereferencing pointers to symbols in the object will have undefined "
 | 
			
		||||
              "behavior after freeing.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_DEFINE);
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetAbstractNative *anative = janet_getabstract(argv, 0, &janet_native_type);
 | 
			
		||||
    if (anative->closed) janet_panic("native object already closed");
 | 
			
		||||
@@ -1351,6 +1795,83 @@ JANET_CORE_FN(janet_core_native_close,
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_malloc,
 | 
			
		||||
              "(ffi/malloc size)",
 | 
			
		||||
              "Allocates memory directly using the janet memory allocator. Memory allocated in this way must be freed manually! Returns a raw pointer, or nil if size = 0.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    size_t size = janet_getsize(argv, 0);
 | 
			
		||||
    if (size == 0) return janet_wrap_nil();
 | 
			
		||||
    return janet_wrap_pointer(janet_malloc(size));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_free,
 | 
			
		||||
              "(ffi/free pointer)",
 | 
			
		||||
              "Free memory allocated with `ffi/malloc`. Returns nil.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (janet_checktype(argv[0], JANET_NIL)) return janet_wrap_nil();
 | 
			
		||||
    void *pointer = janet_getpointer(argv, 0);
 | 
			
		||||
    janet_free(pointer);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_pointer_buffer,
 | 
			
		||||
              "(ffi/pointer-buffer pointer capacity &opt count offset)",
 | 
			
		||||
              "Create a buffer from a pointer. The underlying memory of the buffer will not be "
 | 
			
		||||
              "reallocated or freed by the garbage collector, allowing unmanaged, mutable memory "
 | 
			
		||||
              "to be manipulated with buffer functions. Attempts to resize or extend the buffer "
 | 
			
		||||
              "beyond its initial capacity will raise an error. As with many FFI functions, this is memory "
 | 
			
		||||
              "unsafe and can potentially allow out of bounds memory access. Returns a new buffer.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
 | 
			
		||||
    janet_arity(argc, 2, 4);
 | 
			
		||||
    void *pointer = janet_getpointer(argv, 0);
 | 
			
		||||
    int32_t capacity = janet_getnat(argv, 1);
 | 
			
		||||
    int32_t count = janet_optnat(argv, argc, 2, 0);
 | 
			
		||||
    int64_t offset = janet_optinteger64(argv, argc, 3, 0);
 | 
			
		||||
    uint8_t *offset_pointer = ((uint8_t *) pointer) + offset;
 | 
			
		||||
    return janet_wrap_buffer(janet_pointer_buffer_unsafe(offset_pointer, capacity, count));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_pointer_cfunction,
 | 
			
		||||
              "(ffi/pointer-cfunction pointer &opt name source-file source-line)",
 | 
			
		||||
              "Create a C Function from a raw pointer. Optionally give the cfunction a name and "
 | 
			
		||||
              "source location for stack traces and debugging.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
 | 
			
		||||
    janet_arity(argc, 1, 4);
 | 
			
		||||
    void *pointer = janet_getpointer(argv, 0);
 | 
			
		||||
    const char *name = janet_optcstring(argv, argc, 1, NULL);
 | 
			
		||||
    const char *source = janet_optcstring(argv, argc, 2, NULL);
 | 
			
		||||
    int32_t line = janet_optinteger(argv, argc, 3, -1);
 | 
			
		||||
    if ((name != NULL) || (source != NULL) || (line != -1)) {
 | 
			
		||||
        janet_registry_put((JanetCFunction) pointer, name, NULL, source, line);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_cfunction((JanetCFunction) pointer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ffi_supported_calling_conventions,
 | 
			
		||||
              "(ffi/calling-conventions)",
 | 
			
		||||
              "Get an array of all supported calling conventions on the current architecture. Some architectures may have some FFI "
 | 
			
		||||
              "functionality (ffi/malloc, ffi/free, ffi/read, ffi/write, etc.) but not support "
 | 
			
		||||
              "any calling conventions. This function can be used to get all supported calling conventions "
 | 
			
		||||
              "that can be used on this architecture. All architectures support the :none calling "
 | 
			
		||||
              "convention which is a placeholder that cannot be used at runtime.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    JanetArray *array = janet_array(4);
 | 
			
		||||
#ifdef JANET_FFI_WIN64_ENABLED
 | 
			
		||||
    janet_array_push(array, janet_ckeywordv("win64"));
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI_SYSV64_ENABLED
 | 
			
		||||
    janet_array_push(array, janet_ckeywordv("sysv64"));
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI_AAPCS64_ENABLED
 | 
			
		||||
    janet_array_push(array, janet_ckeywordv("aapcs64"));
 | 
			
		||||
#endif
 | 
			
		||||
    janet_array_push(array, janet_ckeywordv("none"));
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_lib_ffi(JanetTable *env) {
 | 
			
		||||
    JanetRegExt ffi_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("ffi/native", janet_core_raw_native),
 | 
			
		||||
@@ -1364,6 +1885,12 @@ void janet_lib_ffi(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("ffi/size", cfun_ffi_size),
 | 
			
		||||
        JANET_CORE_REG("ffi/align", cfun_ffi_align),
 | 
			
		||||
        JANET_CORE_REG("ffi/trampoline", cfun_ffi_get_callback_trampoline),
 | 
			
		||||
        JANET_CORE_REG("ffi/jitfn", cfun_ffi_jitfn),
 | 
			
		||||
        JANET_CORE_REG("ffi/malloc", cfun_ffi_malloc),
 | 
			
		||||
        JANET_CORE_REG("ffi/free", cfun_ffi_free),
 | 
			
		||||
        JANET_CORE_REG("ffi/pointer-buffer", cfun_ffi_pointer_buffer),
 | 
			
		||||
        JANET_CORE_REG("ffi/pointer-cfunction", cfun_ffi_pointer_cfunction),
 | 
			
		||||
        JANET_CORE_REG("ffi/calling-conventions", cfun_ffi_supported_calling_conventions),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, ffi_cfuns);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -39,8 +39,10 @@ static void fiber_reset(JanetFiber *fiber) {
 | 
			
		||||
    fiber->env = NULL;
 | 
			
		||||
    fiber->last_value = janet_wrap_nil();
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    fiber->waiting = NULL;
 | 
			
		||||
    fiber->sched_id = 0;
 | 
			
		||||
    fiber->ev_callback = NULL;
 | 
			
		||||
    fiber->ev_state = NULL;
 | 
			
		||||
    fiber->ev_stream = NULL;
 | 
			
		||||
    fiber->supervisor_channel = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
    janet_fiber_set_status(fiber, JANET_STATUS_NEW);
 | 
			
		||||
@@ -81,10 +83,10 @@ JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t
 | 
			
		||||
        }
 | 
			
		||||
        fiber->stacktop = newstacktop;
 | 
			
		||||
    }
 | 
			
		||||
    /* Don't panic on failure since we use this to implement janet_pcall */
 | 
			
		||||
    if (janet_fiber_funcframe(fiber, callee)) return NULL;
 | 
			
		||||
    janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    fiber->waiting = NULL;
 | 
			
		||||
    fiber->supervisor_channel = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
    return fiber;
 | 
			
		||||
@@ -237,8 +239,8 @@ int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) {
 | 
			
		||||
                                         fiber->data + tuplehead,
 | 
			
		||||
                                         oldtop - tuplehead)
 | 
			
		||||
                                     : janet_wrap_tuple(janet_tuple_n(
 | 
			
		||||
                                                 fiber->data + tuplehead,
 | 
			
		||||
                                                 oldtop - tuplehead));
 | 
			
		||||
                                             fiber->data + tuplehead,
 | 
			
		||||
                                             oldtop - tuplehead));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -368,8 +370,8 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
 | 
			
		||||
                                         fiber->data + tuplehead,
 | 
			
		||||
                                         fiber->stacktop - tuplehead)
 | 
			
		||||
                                     : janet_wrap_tuple(janet_tuple_n(
 | 
			
		||||
                                                 fiber->data + tuplehead,
 | 
			
		||||
                                                 fiber->stacktop - tuplehead));
 | 
			
		||||
                                             fiber->data + tuplehead,
 | 
			
		||||
                                             fiber->stacktop - tuplehead));
 | 
			
		||||
        }
 | 
			
		||||
        stacksize = tuplehead - fiber->stackstart + 1;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -477,10 +479,10 @@ JANET_CORE_FN(cfun_fiber_setenv,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_new,
 | 
			
		||||
              "(fiber/new func &opt sigmask)",
 | 
			
		||||
              "(fiber/new func &opt sigmask env)",
 | 
			
		||||
              "Create a new fiber with function body func. Can optionally "
 | 
			
		||||
              "take a set of signals to block from the current parent fiber "
 | 
			
		||||
              "when called. The mask is specified as a keyword where each character "
 | 
			
		||||
              "take a set of signals `sigmask` to capture from child fibers, "
 | 
			
		||||
              "and an environment table `env`. The mask is specified as a keyword where each character "
 | 
			
		||||
              "is used to indicate a signal to block. If the ev module is enabled, and "
 | 
			
		||||
              "this fiber is used as an argument to `ev/go`, these \"blocked\" signals "
 | 
			
		||||
              "will result in messages being sent to the supervisor channel. "
 | 
			
		||||
@@ -495,19 +497,25 @@ JANET_CORE_FN(cfun_fiber_new,
 | 
			
		||||
              "* :t - block termination signals: error + user[0-4]\n"
 | 
			
		||||
              "* :u - block user signals\n"
 | 
			
		||||
              "* :y - block yield signals\n"
 | 
			
		||||
              "* :w - block await signals (user9)\n"
 | 
			
		||||
              "* :r - block interrupt signals (user8)\n"
 | 
			
		||||
              "* :0-9 - block a specific user signal\n\n"
 | 
			
		||||
              "The sigmask argument also can take environment flags. If any mutually "
 | 
			
		||||
              "exclusive flags are present, the last flag takes precedence.\n\n"
 | 
			
		||||
              "* :i - inherit the environment from the current fiber\n"
 | 
			
		||||
              "* :p - the environment table's prototype is the current environment table") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetFunction *func = janet_getfunction(argv, 0);
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
    if (func->def->min_arity > 1) {
 | 
			
		||||
        janet_panicf("fiber function must accept 0 or 1 arguments");
 | 
			
		||||
    }
 | 
			
		||||
    fiber = janet_fiber(func, 64, func->def->min_arity, NULL);
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
    janet_assert(fiber != NULL, "bad fiber arity check");
 | 
			
		||||
    if (argc == 3 && !janet_checktype(argv[2], JANET_NIL)) {
 | 
			
		||||
        fiber->env = janet_gettable(argv, 2);
 | 
			
		||||
    }
 | 
			
		||||
    if (argc >= 2) {
 | 
			
		||||
        int32_t i;
 | 
			
		||||
        JanetByteView view = janet_getbytes(argv, 1);
 | 
			
		||||
        fiber->flags = JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
 | 
			
		||||
@@ -518,7 +526,7 @@ JANET_CORE_FN(cfun_fiber_new,
 | 
			
		||||
            } else {
 | 
			
		||||
                switch (view.bytes[i]) {
 | 
			
		||||
                    default:
 | 
			
		||||
                        janet_panicf("invalid flag %c, expected a, t, d, e, u, y, i, or p", view.bytes[i]);
 | 
			
		||||
                        janet_panicf("invalid flag %c, expected a, t, d, e, u, y, w, r, i, or p", view.bytes[i]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'a':
 | 
			
		||||
                        fiber->flags |=
 | 
			
		||||
@@ -548,6 +556,12 @@ JANET_CORE_FN(cfun_fiber_new,
 | 
			
		||||
                    case 'y':
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_MASK_YIELD;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'w':
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_MASK_USER9;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'r':
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_MASK_USER8;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'i':
 | 
			
		||||
                        if (!janet_vm.fiber->env) {
 | 
			
		||||
                            janet_vm.fiber->env = janet_table(0);
 | 
			
		||||
@@ -575,7 +589,9 @@ JANET_CORE_FN(cfun_fiber_status,
 | 
			
		||||
              "* :error - the fiber has errored out\n"
 | 
			
		||||
              "* :debug - the fiber is suspended in debug mode\n"
 | 
			
		||||
              "* :pending - the fiber has been yielded\n"
 | 
			
		||||
              "* :user(0-9) - the fiber is suspended by a user signal\n"
 | 
			
		||||
              "* :user(0-7) - the fiber is suspended by a user signal\n"
 | 
			
		||||
              "* :interrupted - the fiber was interrupted\n"
 | 
			
		||||
              "* :suspended - the fiber is waiting to be resumed by the scheduler\n"
 | 
			
		||||
              "* :alive - the fiber is currently running and cannot be resumed\n"
 | 
			
		||||
              "* :new - the fiber has just been created and not yet run") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
@@ -625,11 +641,7 @@ JANET_CORE_FN(cfun_fiber_setmaxstack,
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_can_resume,
 | 
			
		||||
              "(fiber/can-resume? fiber)",
 | 
			
		||||
              "Check if a fiber is finished and cannot be resumed.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
int janet_fiber_can_resume(JanetFiber *fiber) {
 | 
			
		||||
    JanetFiberStatus s = janet_fiber_status(fiber);
 | 
			
		||||
    int isFinished = s == JANET_STATUS_DEAD ||
 | 
			
		||||
                     s == JANET_STATUS_ERROR ||
 | 
			
		||||
@@ -638,11 +650,19 @@ JANET_CORE_FN(cfun_fiber_can_resume,
 | 
			
		||||
                     s == JANET_STATUS_USER2 ||
 | 
			
		||||
                     s == JANET_STATUS_USER3 ||
 | 
			
		||||
                     s == JANET_STATUS_USER4;
 | 
			
		||||
    return janet_wrap_boolean(!isFinished);
 | 
			
		||||
    return !isFinished;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_can_resume,
 | 
			
		||||
              "(fiber/can-resume? fiber)",
 | 
			
		||||
              "Check if a fiber is finished and cannot be resumed.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    return janet_wrap_boolean(janet_fiber_can_resume(fiber));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_last_value,
 | 
			
		||||
              "(fiber/last-value)",
 | 
			
		||||
              "(fiber/last-value fiber)",
 | 
			
		||||
              "Get the last value returned or signaled from the fiber.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -59,6 +59,9 @@
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_CANCELED 0x10000
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000
 | 
			
		||||
#define JANET_FIBER_FLAG_ROOT 0x40000
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_IN_FLIGHT 0x1
 | 
			
		||||
 | 
			
		||||
/* used only on windows, should otherwise be unset */
 | 
			
		||||
 | 
			
		||||
#define janet_fiber_set_status(f, s) do {\
 | 
			
		||||
    (f)->flags &= ~JANET_FIBER_STATUS_MASK;\
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										688
									
								
								src/core/filewatch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										688
									
								
								src/core/filewatch.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,688 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2025 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 "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifdef JANET_FILEWATCH
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_LINUX
 | 
			
		||||
#include <sys/inotify.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    uint32_t flag;
 | 
			
		||||
} JanetWatchFlagName;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
    JanetStream *stream;
 | 
			
		||||
#endif
 | 
			
		||||
    JanetTable *watch_descriptors;
 | 
			
		||||
    JanetChannel *channel;
 | 
			
		||||
    uint32_t default_flags;
 | 
			
		||||
    int is_watching;
 | 
			
		||||
} JanetWatcher;
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_LINUX
 | 
			
		||||
 | 
			
		||||
#include <sys/inotify.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
static const JanetWatchFlagName watcher_flags_linux[] = {
 | 
			
		||||
    {"access", IN_ACCESS},
 | 
			
		||||
    {"all", IN_ALL_EVENTS},
 | 
			
		||||
    {"attrib", IN_ATTRIB},
 | 
			
		||||
    {"close-nowrite", IN_CLOSE_NOWRITE},
 | 
			
		||||
    {"close-write", IN_CLOSE_WRITE},
 | 
			
		||||
    {"create", IN_CREATE},
 | 
			
		||||
    {"delete", IN_DELETE},
 | 
			
		||||
    {"delete-self", IN_DELETE_SELF},
 | 
			
		||||
    {"ignored", IN_IGNORED},
 | 
			
		||||
    {"modify", IN_MODIFY},
 | 
			
		||||
    {"move-self", IN_MOVE_SELF},
 | 
			
		||||
    {"moved-from", IN_MOVED_FROM},
 | 
			
		||||
    {"moved-to", IN_MOVED_TO},
 | 
			
		||||
    {"open", IN_OPEN},
 | 
			
		||||
    {"q-overflow", IN_Q_OVERFLOW},
 | 
			
		||||
    {"unmount", IN_UNMOUNT},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
 | 
			
		||||
    uint32_t flags = 0;
 | 
			
		||||
    for (int32_t i = 0; i < n; i++) {
 | 
			
		||||
        if (!(janet_checktype(options[i], JANET_KEYWORD))) {
 | 
			
		||||
            janet_panicf("expected keyword, got %v", options[i]);
 | 
			
		||||
        }
 | 
			
		||||
        JanetKeyword keyw = janet_unwrap_keyword(options[i]);
 | 
			
		||||
        const JanetWatchFlagName *result = janet_strbinsearch(watcher_flags_linux,
 | 
			
		||||
                                           sizeof(watcher_flags_linux) / sizeof(JanetWatchFlagName),
 | 
			
		||||
                                           sizeof(JanetWatchFlagName),
 | 
			
		||||
                                           keyw);
 | 
			
		||||
        if (!result) {
 | 
			
		||||
            janet_panicf("unknown inotify flag %v", options[i]);
 | 
			
		||||
        }
 | 
			
		||||
        flags |= result->flag;
 | 
			
		||||
    }
 | 
			
		||||
    return flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
 | 
			
		||||
    int fd;
 | 
			
		||||
    do {
 | 
			
		||||
        fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
 | 
			
		||||
    } while (fd == -1 && errno == EINTR);
 | 
			
		||||
    if (fd == -1) {
 | 
			
		||||
        janet_panicv(janet_ev_lasterr());
 | 
			
		||||
    }
 | 
			
		||||
    watcher->watch_descriptors = janet_table(0);
 | 
			
		||||
    watcher->channel = channel;
 | 
			
		||||
    watcher->default_flags = default_flags;
 | 
			
		||||
    watcher->is_watching = 0;
 | 
			
		||||
    watcher->stream = janet_stream(fd, JANET_STREAM_READABLE, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
 | 
			
		||||
    if (watcher->stream == NULL) janet_panic("watcher closed");
 | 
			
		||||
    int result;
 | 
			
		||||
    do {
 | 
			
		||||
        result = inotify_add_watch(watcher->stream->handle, path, flags);
 | 
			
		||||
    } while (result == -1 && errno == EINTR);
 | 
			
		||||
    if (result == -1) {
 | 
			
		||||
        janet_panicv(janet_ev_lasterr());
 | 
			
		||||
    }
 | 
			
		||||
    Janet name = janet_cstringv(path);
 | 
			
		||||
    Janet wd = janet_wrap_integer(result);
 | 
			
		||||
    janet_table_put(watcher->watch_descriptors, name, wd);
 | 
			
		||||
    janet_table_put(watcher->watch_descriptors, wd, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
 | 
			
		||||
    if (watcher->stream == NULL) janet_panic("watcher closed");
 | 
			
		||||
    Janet check = janet_table_get(watcher->watch_descriptors, janet_cstringv(path));
 | 
			
		||||
    janet_assert(janet_checktype(check, JANET_NUMBER), "bad watch descriptor");
 | 
			
		||||
    int watch_handle = janet_unwrap_integer(check);
 | 
			
		||||
    int result;
 | 
			
		||||
    do {
 | 
			
		||||
        result = inotify_rm_watch(watcher->stream->handle, watch_handle);
 | 
			
		||||
    } while (result != -1 && errno == EINTR);
 | 
			
		||||
    if (result == -1) {
 | 
			
		||||
        janet_panicv(janet_ev_lasterr());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    JanetStream *stream = fiber->ev_stream;
 | 
			
		||||
    JanetWatcher *watcher = *((JanetWatcher **) fiber->ev_state);
 | 
			
		||||
    char buf[1024];
 | 
			
		||||
    switch (event) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_MARK:
 | 
			
		||||
            janet_mark(janet_wrap_abstract(watcher));
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_CLOSE:
 | 
			
		||||
            janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
            janet_async_end(fiber);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_ERR: {
 | 
			
		||||
            janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
            janet_async_end(fiber);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    read_more:
 | 
			
		||||
        case JANET_ASYNC_EVENT_HUP:
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT:
 | 
			
		||||
        case JANET_ASYNC_EVENT_READ: {
 | 
			
		||||
            Janet name = janet_wrap_nil();
 | 
			
		||||
 | 
			
		||||
            /* Assumption - read will never return partial events *
 | 
			
		||||
             * From documentation:
 | 
			
		||||
             *
 | 
			
		||||
             * The behavior when the buffer given to read(2) is too small to
 | 
			
		||||
             * return information about the next event depends on the kernel
 | 
			
		||||
             * version: before Linux 2.6.21, read(2) returns 0; since Linux
 | 
			
		||||
             * 2.6.21, read(2) fails with the error EINVAL.  Specifying a buffer
 | 
			
		||||
             * of size
 | 
			
		||||
             *
 | 
			
		||||
             *     sizeof(struct inotify_event) + NAME_MAX + 1
 | 
			
		||||
             *
 | 
			
		||||
             * will be sufficient to read at least one event. */
 | 
			
		||||
            ssize_t nread;
 | 
			
		||||
            do {
 | 
			
		||||
                nread = read(stream->handle, buf, sizeof(buf));
 | 
			
		||||
            } while (nread == -1 && errno == EINTR);
 | 
			
		||||
 | 
			
		||||
            /* Check for errors - special case errors that can just be waited on to fix */
 | 
			
		||||
            if (nread == -1) {
 | 
			
		||||
                if (errno == EAGAIN || errno == EWOULDBLOCK) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                janet_cancel(fiber, janet_ev_lasterr());
 | 
			
		||||
                fiber->ev_state = NULL;
 | 
			
		||||
                janet_async_end(fiber);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (nread < (ssize_t) sizeof(struct inotify_event)) break;
 | 
			
		||||
 | 
			
		||||
            /* Iterate through all events read from the buffer */
 | 
			
		||||
            char *cursor = buf;
 | 
			
		||||
            while (cursor < buf + nread) {
 | 
			
		||||
                struct inotify_event inevent;
 | 
			
		||||
                memcpy(&inevent, cursor, sizeof(inevent));
 | 
			
		||||
                cursor += sizeof(inevent);
 | 
			
		||||
                /* Read path of inevent */
 | 
			
		||||
                if (inevent.len) {
 | 
			
		||||
                    name = janet_cstringv(cursor);
 | 
			
		||||
                    cursor += inevent.len;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                /* Got an event */
 | 
			
		||||
                Janet path = janet_table_get(watcher->watch_descriptors, janet_wrap_integer(inevent.wd));
 | 
			
		||||
                JanetKV *event = janet_struct_begin(6);
 | 
			
		||||
                janet_struct_put(event, janet_ckeywordv("wd"), janet_wrap_integer(inevent.wd));
 | 
			
		||||
                janet_struct_put(event, janet_ckeywordv("wd-path"), path);
 | 
			
		||||
                if (janet_checktype(name, JANET_NIL)) {
 | 
			
		||||
                    /* We were watching a file directly, so path is the full path. Split into dirname / basename */
 | 
			
		||||
                    JanetString spath = janet_unwrap_string(path);
 | 
			
		||||
                    const uint8_t *cursor = spath + janet_string_length(spath);
 | 
			
		||||
                    const uint8_t *cursor_end = cursor;
 | 
			
		||||
                    while (cursor > spath && cursor[0] != '/') {
 | 
			
		||||
                        cursor--;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (cursor == spath) {
 | 
			
		||||
                        janet_struct_put(event, janet_ckeywordv("dir-name"), path);
 | 
			
		||||
                        janet_struct_put(event, janet_ckeywordv("file-name"), name);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        janet_struct_put(event, janet_ckeywordv("dir-name"), janet_wrap_string(janet_string(spath, (cursor - spath))));
 | 
			
		||||
                        janet_struct_put(event, janet_ckeywordv("file-name"), janet_wrap_string(janet_string(cursor + 1, (cursor_end - cursor - 1))));
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_struct_put(event, janet_ckeywordv("dir-name"), path);
 | 
			
		||||
                    janet_struct_put(event, janet_ckeywordv("file-name"), name);
 | 
			
		||||
                }
 | 
			
		||||
                janet_struct_put(event, janet_ckeywordv("cookie"), janet_wrap_integer(inevent.cookie));
 | 
			
		||||
                Janet etype = janet_ckeywordv("type");
 | 
			
		||||
                const JanetWatchFlagName *wfn_end = watcher_flags_linux + sizeof(watcher_flags_linux) / sizeof(watcher_flags_linux[0]);
 | 
			
		||||
                for (const JanetWatchFlagName *wfn = watcher_flags_linux; wfn < wfn_end; wfn++) {
 | 
			
		||||
                    if ((inevent.mask & wfn->flag) == wfn->flag) janet_struct_put(event, etype, janet_ckeywordv(wfn->name));
 | 
			
		||||
                }
 | 
			
		||||
                Janet eventv = janet_wrap_struct(janet_struct_end(event));
 | 
			
		||||
 | 
			
		||||
                janet_channel_give(watcher->channel, eventv);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Read some more if possible */
 | 
			
		||||
            goto read_more;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_listen(JanetWatcher *watcher) {
 | 
			
		||||
    if (watcher->is_watching) janet_panic("already watching");
 | 
			
		||||
    watcher->is_watching = 1;
 | 
			
		||||
    JanetFunction *thunk = janet_thunk_delay(janet_wrap_nil());
 | 
			
		||||
    JanetFiber *fiber = janet_fiber(thunk, 64, 0, NULL);
 | 
			
		||||
    JanetWatcher **state = janet_malloc(sizeof(JanetWatcher *)); /* Gross */
 | 
			
		||||
    *state = watcher;
 | 
			
		||||
    janet_async_start_fiber(fiber, watcher->stream, JANET_ASYNC_LISTEN_READ, watcher_callback_read, state);
 | 
			
		||||
    janet_gcroot(janet_wrap_abstract(watcher));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_unlisten(JanetWatcher *watcher) {
 | 
			
		||||
    if (!watcher->is_watching) return;
 | 
			
		||||
    watcher->is_watching = 0;
 | 
			
		||||
    janet_stream_close(watcher->stream);
 | 
			
		||||
    janet_gcunroot(janet_wrap_abstract(watcher));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#elif JANET_WINDOWS
 | 
			
		||||
 | 
			
		||||
#define WATCHFLAG_RECURSIVE 0x100000u
 | 
			
		||||
 | 
			
		||||
static const JanetWatchFlagName watcher_flags_windows[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "all",
 | 
			
		||||
        FILE_NOTIFY_CHANGE_ATTRIBUTES |
 | 
			
		||||
        FILE_NOTIFY_CHANGE_CREATION |
 | 
			
		||||
        FILE_NOTIFY_CHANGE_DIR_NAME |
 | 
			
		||||
        FILE_NOTIFY_CHANGE_FILE_NAME |
 | 
			
		||||
        FILE_NOTIFY_CHANGE_LAST_ACCESS |
 | 
			
		||||
        FILE_NOTIFY_CHANGE_LAST_WRITE |
 | 
			
		||||
        FILE_NOTIFY_CHANGE_SECURITY |
 | 
			
		||||
        FILE_NOTIFY_CHANGE_SIZE |
 | 
			
		||||
        WATCHFLAG_RECURSIVE
 | 
			
		||||
    },
 | 
			
		||||
    {"attributes", FILE_NOTIFY_CHANGE_ATTRIBUTES},
 | 
			
		||||
    {"creation", FILE_NOTIFY_CHANGE_CREATION},
 | 
			
		||||
    {"dir-name", FILE_NOTIFY_CHANGE_DIR_NAME},
 | 
			
		||||
    {"file-name", FILE_NOTIFY_CHANGE_FILE_NAME},
 | 
			
		||||
    {"last-access", FILE_NOTIFY_CHANGE_LAST_ACCESS},
 | 
			
		||||
    {"last-write", FILE_NOTIFY_CHANGE_LAST_WRITE},
 | 
			
		||||
    {"recursive", WATCHFLAG_RECURSIVE},
 | 
			
		||||
    {"security", FILE_NOTIFY_CHANGE_SECURITY},
 | 
			
		||||
    {"size", FILE_NOTIFY_CHANGE_SIZE},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
 | 
			
		||||
    uint32_t flags = 0;
 | 
			
		||||
    for (int32_t i = 0; i < n; i++) {
 | 
			
		||||
        if (!(janet_checktype(options[i], JANET_KEYWORD))) {
 | 
			
		||||
            janet_panicf("expected keyword, got %v", options[i]);
 | 
			
		||||
        }
 | 
			
		||||
        JanetKeyword keyw = janet_unwrap_keyword(options[i]);
 | 
			
		||||
        const JanetWatchFlagName *result = janet_strbinsearch(watcher_flags_windows,
 | 
			
		||||
                                           sizeof(watcher_flags_windows) / sizeof(JanetWatchFlagName),
 | 
			
		||||
                                           sizeof(JanetWatchFlagName),
 | 
			
		||||
                                           keyw);
 | 
			
		||||
        if (!result) {
 | 
			
		||||
            janet_panicf("unknown windows filewatch flag %v", options[i]);
 | 
			
		||||
        }
 | 
			
		||||
        flags |= result->flag;
 | 
			
		||||
    }
 | 
			
		||||
    return flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
 | 
			
		||||
    watcher->watch_descriptors = janet_table(0);
 | 
			
		||||
    watcher->channel = channel;
 | 
			
		||||
    watcher->default_flags = default_flags;
 | 
			
		||||
    watcher->is_watching = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Since the file info padding includes embedded file names, we want to include more space for data.
 | 
			
		||||
 * We also need to handle manually calculating changes if path names are too long, but ideally just avoid
 | 
			
		||||
 * that scenario as much as possible */
 | 
			
		||||
#define FILE_INFO_PADDING (4096 * 4)
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    OVERLAPPED overlapped;
 | 
			
		||||
    JanetStream *stream;
 | 
			
		||||
    JanetWatcher *watcher;
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
    JanetString dir_path;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    uint64_t buf[FILE_INFO_PADDING / sizeof(uint64_t)]; /* Ensure alignment */
 | 
			
		||||
} OverlappedWatch;
 | 
			
		||||
 | 
			
		||||
#define NotifyChange FILE_NOTIFY_INFORMATION
 | 
			
		||||
 | 
			
		||||
static void read_dir_changes(OverlappedWatch *ow) {
 | 
			
		||||
    BOOL result = ReadDirectoryChangesW(ow->stream->handle,
 | 
			
		||||
                                        (NotifyChange *) ow->buf,
 | 
			
		||||
                                        FILE_INFO_PADDING,
 | 
			
		||||
                                        (ow->flags & WATCHFLAG_RECURSIVE) ? TRUE : FALSE,
 | 
			
		||||
                                        ow->flags & ~WATCHFLAG_RECURSIVE,
 | 
			
		||||
                                        NULL,
 | 
			
		||||
                                        (OVERLAPPED *) ow,
 | 
			
		||||
                                        NULL);
 | 
			
		||||
    if (!result) {
 | 
			
		||||
        janet_panicv(janet_ev_lasterr());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *watcher_actions_windows[] = {
 | 
			
		||||
    "unknown",
 | 
			
		||||
    "added",
 | 
			
		||||
    "removed",
 | 
			
		||||
    "modified",
 | 
			
		||||
    "renamed-old",
 | 
			
		||||
    "renamed-new",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    OverlappedWatch *ow = (OverlappedWatch *) fiber->ev_state;
 | 
			
		||||
    JanetWatcher *watcher = ow->watcher;
 | 
			
		||||
    switch (event) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT:
 | 
			
		||||
            janet_async_in_flight(fiber);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_MARK:
 | 
			
		||||
            janet_mark(janet_wrap_abstract(ow->stream));
 | 
			
		||||
            janet_mark(janet_wrap_fiber(ow->fiber));
 | 
			
		||||
            janet_mark(janet_wrap_abstract(watcher));
 | 
			
		||||
            janet_mark(janet_wrap_string(ow->dir_path));
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_CLOSE:
 | 
			
		||||
            janet_table_remove(ow->watcher->watch_descriptors, janet_wrap_string(ow->dir_path));
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_ERR:
 | 
			
		||||
        case JANET_ASYNC_EVENT_FAILED:
 | 
			
		||||
            janet_stream_close(ow->stream);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ASYNC_EVENT_COMPLETE: {
 | 
			
		||||
            if (!watcher->is_watching) {
 | 
			
		||||
                janet_stream_close(ow->stream);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            NotifyChange *fni = (NotifyChange *) ow->buf;
 | 
			
		||||
 | 
			
		||||
            while (1) {
 | 
			
		||||
                /* Got an event */
 | 
			
		||||
 | 
			
		||||
                /* Extract name */
 | 
			
		||||
                Janet filename;
 | 
			
		||||
                if (fni->FileNameLength) {
 | 
			
		||||
                    int32_t nbytes = (int32_t) WideCharToMultiByte(CP_UTF8, 0, fni->FileName, fni->FileNameLength / sizeof(wchar_t), NULL, 0, NULL, NULL);
 | 
			
		||||
                    janet_assert(nbytes, "bad utf8 path");
 | 
			
		||||
                    uint8_t *into = janet_string_begin(nbytes);
 | 
			
		||||
                    WideCharToMultiByte(CP_UTF8, 0, fni->FileName, fni->FileNameLength / sizeof(wchar_t), (char *) into, nbytes, NULL, NULL);
 | 
			
		||||
                    filename = janet_wrap_string(janet_string_end(into));
 | 
			
		||||
                } else {
 | 
			
		||||
                    filename = janet_cstringv("");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                JanetKV *event = janet_struct_begin(3);
 | 
			
		||||
                janet_struct_put(event, janet_ckeywordv("type"), janet_ckeywordv(watcher_actions_windows[fni->Action]));
 | 
			
		||||
                janet_struct_put(event, janet_ckeywordv("file-name"), filename);
 | 
			
		||||
                janet_struct_put(event, janet_ckeywordv("dir-name"), janet_wrap_string(ow->dir_path));
 | 
			
		||||
                Janet eventv = janet_wrap_struct(janet_struct_end(event));
 | 
			
		||||
 | 
			
		||||
                janet_channel_give(watcher->channel, eventv);
 | 
			
		||||
 | 
			
		||||
                /* Next event */
 | 
			
		||||
                if (!fni->NextEntryOffset) break;
 | 
			
		||||
                fni = (NotifyChange *)((char *)fni + fni->NextEntryOffset);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Make another call to read directory changes */
 | 
			
		||||
            read_dir_changes(ow);
 | 
			
		||||
            janet_async_in_flight(fiber);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void start_listening_ow(OverlappedWatch *ow) {
 | 
			
		||||
    read_dir_changes(ow);
 | 
			
		||||
    JanetStream *stream = ow->stream;
 | 
			
		||||
    JanetFunction *thunk = janet_thunk_delay(janet_wrap_nil());
 | 
			
		||||
    JanetFiber *fiber = janet_fiber(thunk, 64, 0, NULL);
 | 
			
		||||
    fiber->supervisor_channel = janet_root_fiber()->supervisor_channel;
 | 
			
		||||
    ow->fiber = fiber;
 | 
			
		||||
    janet_async_start_fiber(fiber, stream, JANET_ASYNC_LISTEN_READ, watcher_callback_read, ow);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
 | 
			
		||||
    HANDLE handle = CreateFileA(path,
 | 
			
		||||
                                FILE_LIST_DIRECTORY | GENERIC_READ,
 | 
			
		||||
                                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
 | 
			
		||||
                                NULL,
 | 
			
		||||
                                OPEN_EXISTING,
 | 
			
		||||
                                FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,
 | 
			
		||||
                                NULL);
 | 
			
		||||
    if (handle == INVALID_HANDLE_VALUE) {
 | 
			
		||||
        janet_panicv(janet_ev_lasterr());
 | 
			
		||||
    }
 | 
			
		||||
    JanetStream *stream = janet_stream(handle, JANET_STREAM_READABLE, NULL);
 | 
			
		||||
    OverlappedWatch *ow = janet_malloc(sizeof(OverlappedWatch));
 | 
			
		||||
    memset(ow, 0, sizeof(OverlappedWatch));
 | 
			
		||||
    ow->stream = stream;
 | 
			
		||||
    ow->dir_path = janet_cstring(path);
 | 
			
		||||
    ow->fiber = NULL;
 | 
			
		||||
    Janet pathv = janet_wrap_string(ow->dir_path);
 | 
			
		||||
    ow->flags = flags | watcher->default_flags;
 | 
			
		||||
    ow->watcher = watcher;
 | 
			
		||||
    ow->overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL); /* Do we need this */
 | 
			
		||||
    Janet streamv = janet_wrap_pointer(ow);
 | 
			
		||||
    janet_table_put(watcher->watch_descriptors, pathv, streamv);
 | 
			
		||||
    if (watcher->is_watching) {
 | 
			
		||||
        start_listening_ow(ow);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
 | 
			
		||||
    Janet pathv = janet_cstringv(path);
 | 
			
		||||
    Janet streamv = janet_table_get(watcher->watch_descriptors, pathv);
 | 
			
		||||
    if (janet_checktype(streamv, JANET_NIL)) {
 | 
			
		||||
        janet_panicf("path %v is not being watched", pathv);
 | 
			
		||||
    }
 | 
			
		||||
    janet_table_remove(watcher->watch_descriptors, pathv);
 | 
			
		||||
    OverlappedWatch *ow = janet_unwrap_pointer(streamv);
 | 
			
		||||
    janet_stream_close(ow->stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_listen(JanetWatcher *watcher) {
 | 
			
		||||
    if (watcher->is_watching) janet_panic("already watching");
 | 
			
		||||
    watcher->is_watching = 1;
 | 
			
		||||
    for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
 | 
			
		||||
        const JanetKV *kv = watcher->watch_descriptors->data + i;
 | 
			
		||||
        if (!janet_checktype(kv->value, JANET_POINTER)) continue;
 | 
			
		||||
        OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
 | 
			
		||||
        start_listening_ow(ow);
 | 
			
		||||
    }
 | 
			
		||||
    janet_gcroot(janet_wrap_abstract(watcher));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_unlisten(JanetWatcher *watcher) {
 | 
			
		||||
    if (!watcher->is_watching) return;
 | 
			
		||||
    watcher->is_watching = 0;
 | 
			
		||||
    for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
 | 
			
		||||
        const JanetKV *kv = watcher->watch_descriptors->data + i;
 | 
			
		||||
        if (!janet_checktype(kv->value, JANET_POINTER)) continue;
 | 
			
		||||
        OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
 | 
			
		||||
        janet_stream_close(ow->stream);
 | 
			
		||||
    }
 | 
			
		||||
    janet_table_clear(watcher->watch_descriptors);
 | 
			
		||||
    janet_gcunroot(janet_wrap_abstract(watcher));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
/* Default implementation */
 | 
			
		||||
 | 
			
		||||
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
 | 
			
		||||
    (void) options;
 | 
			
		||||
    (void) n;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
 | 
			
		||||
    (void) watcher;
 | 
			
		||||
    (void) channel;
 | 
			
		||||
    (void) default_flags;
 | 
			
		||||
    janet_panic("filewatch not supported on this platform");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
 | 
			
		||||
    (void) watcher;
 | 
			
		||||
    (void) flags;
 | 
			
		||||
    (void) path;
 | 
			
		||||
    janet_panic("nyi");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
 | 
			
		||||
    (void) watcher;
 | 
			
		||||
    (void) path;
 | 
			
		||||
    janet_panic("nyi");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_listen(JanetWatcher *watcher) {
 | 
			
		||||
    (void) watcher;
 | 
			
		||||
    janet_panic("nyi");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_watcher_unlisten(JanetWatcher *watcher) {
 | 
			
		||||
    (void) watcher;
 | 
			
		||||
    janet_panic("nyi");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* C Functions */
 | 
			
		||||
 | 
			
		||||
static int janet_filewatch_mark(void *p, size_t s) {
 | 
			
		||||
    JanetWatcher *watcher = (JanetWatcher *) p;
 | 
			
		||||
    (void) s;
 | 
			
		||||
    if (watcher->channel == NULL) return 0; /* Incomplete initialization */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
 | 
			
		||||
        const JanetKV *kv = watcher->watch_descriptors->data + i;
 | 
			
		||||
        if (!janet_checktype(kv->value, JANET_POINTER)) continue;
 | 
			
		||||
        OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
 | 
			
		||||
        janet_mark(janet_wrap_fiber(ow->fiber));
 | 
			
		||||
        janet_mark(janet_wrap_abstract(ow->stream));
 | 
			
		||||
        janet_mark(janet_wrap_string(ow->dir_path));
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    janet_mark(janet_wrap_abstract(watcher->stream));
 | 
			
		||||
#endif
 | 
			
		||||
    janet_mark(janet_wrap_abstract(watcher->channel));
 | 
			
		||||
    janet_mark(janet_wrap_table(watcher->watch_descriptors));
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetAbstractType janet_filewatch_at = {
 | 
			
		||||
    "filewatch/watcher",
 | 
			
		||||
    NULL,
 | 
			
		||||
    janet_filewatch_mark,
 | 
			
		||||
    JANET_ATEND_GCMARK
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_filewatch_make,
 | 
			
		||||
              "(filewatch/new channel &opt default-flags)",
 | 
			
		||||
              "Create a new filewatcher that will give events to a channel channel. See `filewatch/add` for available flags.\n\n"
 | 
			
		||||
              "When an event is triggered by the filewatcher, a struct containing information will be given to channel as with `ev/give`. "
 | 
			
		||||
              "The contents of the channel depend on the OS, but will contain some common keys:\n\n"
 | 
			
		||||
              "* `:type` -- the type of the event that was raised.\n\n"
 | 
			
		||||
              "* `:file-name` -- the base file name of the file that triggered the event.\n\n"
 | 
			
		||||
              "* `:dir-name` -- the directory name of the file that triggered the event.\n\n"
 | 
			
		||||
              "Events also will contain keys specific to the host OS.\n\n"
 | 
			
		||||
              "Windows has no extra properties on events.\n\n"
 | 
			
		||||
              "Linux has the following extra properties on events:\n\n"
 | 
			
		||||
              "* `:wd` -- the integer key returned by `filewatch/add` for the path that triggered this.\n\n"
 | 
			
		||||
              "* `:wd-path` -- the string path for watched directory of file. For files, will be the same as `:file-name`, and for directories, will be the same as `:dir-name`.\n\n"
 | 
			
		||||
              "* `:cookie` -- a randomized integer used to associate related events, such as :moved-from and :moved-to events.\n\n"
 | 
			
		||||
              "") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FS_READ);
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetChannel *channel = janet_getchannel(argv, 0);
 | 
			
		||||
    JanetWatcher *watcher = janet_abstract(&janet_filewatch_at, sizeof(JanetWatcher));
 | 
			
		||||
    uint32_t default_flags = decode_watch_flags(argv + 1, argc - 1);
 | 
			
		||||
    janet_watcher_init(watcher, channel, default_flags);
 | 
			
		||||
    return janet_wrap_abstract(watcher);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_filewatch_add,
 | 
			
		||||
              "(filewatch/add watcher path &opt flags)",
 | 
			
		||||
              "Add a path to the watcher. Available flags depend on the current OS, and are as follows:\n\n"
 | 
			
		||||
              "Windows/MINGW (flags correspond to FILE_NOTIFY_CHANGE_* flags in win32 documentation):\n\n"
 | 
			
		||||
              "* `:all` - trigger an event for all of the below triggers.\n\n"
 | 
			
		||||
              "* `:attributes` - FILE_NOTIFY_CHANGE_ATTRIBUTES\n\n"
 | 
			
		||||
              "* `:creation` - FILE_NOTIFY_CHANGE_CREATION\n\n"
 | 
			
		||||
              "* `:dir-name` - FILE_NOTIFY_CHANGE_DIR_NAME\n\n"
 | 
			
		||||
              "* `:last-access` - FILE_NOTIFY_CHANGE_LAST_ACCESS\n\n"
 | 
			
		||||
              "* `:last-write` - FILE_NOTIFY_CHANGE_LAST_WRITE\n\n"
 | 
			
		||||
              "* `:security` - FILE_NOTIFY_CHANGE_SECURITY\n\n"
 | 
			
		||||
              "* `:size` - FILE_NOTIFY_CHANGE_SIZE\n\n"
 | 
			
		||||
              "* `:recursive` - watch subdirectories recursively\n\n"
 | 
			
		||||
              "Linux (flags correspond to IN_* flags from <sys/inotify.h>):\n\n"
 | 
			
		||||
              "* `:access` - IN_ACCESS\n\n"
 | 
			
		||||
              "* `:all` - IN_ALL_EVENTS\n\n"
 | 
			
		||||
              "* `:attrib` - IN_ATTRIB\n\n"
 | 
			
		||||
              "* `:close-nowrite` - IN_CLOSE_NOWRITE\n\n"
 | 
			
		||||
              "* `:close-write` - IN_CLOSE_WRITE\n\n"
 | 
			
		||||
              "* `:create` - IN_CREATE\n\n"
 | 
			
		||||
              "* `:delete` - IN_DELETE\n\n"
 | 
			
		||||
              "* `:delete-self` - IN_DELETE_SELF\n\n"
 | 
			
		||||
              "* `:ignored` - IN_IGNORED\n\n"
 | 
			
		||||
              "* `:modify` - IN_MODIFY\n\n"
 | 
			
		||||
              "* `:move-self` - IN_MOVE_SELF\n\n"
 | 
			
		||||
              "* `:moved-from` - IN_MOVED_FROM\n\n"
 | 
			
		||||
              "* `:moved-to` - IN_MOVED_TO\n\n"
 | 
			
		||||
              "* `:open` - IN_OPEN\n\n"
 | 
			
		||||
              "* `:q-overflow` - IN_Q_OVERFLOW\n\n"
 | 
			
		||||
              "* `:unmount` - IN_UNMOUNT\n\n\n"
 | 
			
		||||
              "On Windows, events will have the following possible types:\n\n"
 | 
			
		||||
              "* `:unknown`\n\n"
 | 
			
		||||
              "* `:added`\n\n"
 | 
			
		||||
              "* `:removed`\n\n"
 | 
			
		||||
              "* `:modified`\n\n"
 | 
			
		||||
              "* `:renamed-old`\n\n"
 | 
			
		||||
              "* `:renamed-new`\n\n"
 | 
			
		||||
              "On Linux, events will a `:type` corresponding to the possible flags, excluding `:all`.\n"
 | 
			
		||||
              "") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
 | 
			
		||||
    const char *path = janet_getcstring(argv, 1);
 | 
			
		||||
    uint32_t flags = watcher->default_flags | decode_watch_flags(argv + 2, argc - 2);
 | 
			
		||||
    janet_watcher_add(watcher, path, flags);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_filewatch_remove,
 | 
			
		||||
              "(filewatch/remove watcher path)",
 | 
			
		||||
              "Remove a path from the watcher.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
 | 
			
		||||
    const char *path = janet_getcstring(argv, 1);
 | 
			
		||||
    janet_watcher_remove(watcher, path);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_filewatch_listen,
 | 
			
		||||
              "(filewatch/listen watcher)",
 | 
			
		||||
              "Listen for changes in the watcher.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
 | 
			
		||||
    janet_watcher_listen(watcher);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_filewatch_unlisten,
 | 
			
		||||
              "(filewatch/unlisten watcher)",
 | 
			
		||||
              "Stop listening for changes on a given watcher.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
 | 
			
		||||
    janet_watcher_unlisten(watcher);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_filewatch(JanetTable *env) {
 | 
			
		||||
    JanetRegExt cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("filewatch/new", cfun_filewatch_make),
 | 
			
		||||
        JANET_CORE_REG("filewatch/add", cfun_filewatch_add),
 | 
			
		||||
        JANET_CORE_REG("filewatch/remove", cfun_filewatch_remove),
 | 
			
		||||
        JANET_CORE_REG("filewatch/listen", cfun_filewatch_listen),
 | 
			
		||||
        JANET_CORE_REG("filewatch/unlisten", cfun_filewatch_unlisten),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, cfuns);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										184
									
								
								src/core/gc.c
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								src/core/gc.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -132,6 +132,24 @@ static void janet_mark_many(const Janet *values, int32_t n) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark a bunch of key values items in memory */
 | 
			
		||||
static void janet_mark_keys(const JanetKV *kvs, int32_t n) {
 | 
			
		||||
    const JanetKV *end = kvs + n;
 | 
			
		||||
    while (kvs < end) {
 | 
			
		||||
        janet_mark(kvs->key);
 | 
			
		||||
        kvs++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark a bunch of key values items in memory */
 | 
			
		||||
static void janet_mark_values(const JanetKV *kvs, int32_t n) {
 | 
			
		||||
    const JanetKV *end = kvs + n;
 | 
			
		||||
    while (kvs < end) {
 | 
			
		||||
        janet_mark(kvs->value);
 | 
			
		||||
        kvs++;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark a bunch of key values items in memory */
 | 
			
		||||
static void janet_mark_kvs(const JanetKV *kvs, int32_t n) {
 | 
			
		||||
    const JanetKV *end = kvs + n;
 | 
			
		||||
@@ -146,7 +164,9 @@ static void janet_mark_array(JanetArray *array) {
 | 
			
		||||
    if (janet_gc_reachable(array))
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(array);
 | 
			
		||||
    janet_mark_many(array->data, array->count);
 | 
			
		||||
    if (janet_gc_type((JanetGCObject *) array) == JANET_MEMORY_ARRAY) {
 | 
			
		||||
        janet_mark_many(array->data, array->count);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_table(JanetTable *table) {
 | 
			
		||||
@@ -154,7 +174,15 @@ recur: /* Manual tail recursion */
 | 
			
		||||
    if (janet_gc_reachable(table))
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(table);
 | 
			
		||||
    janet_mark_kvs(table->data, table->capacity);
 | 
			
		||||
    enum JanetMemoryType memtype = janet_gc_type(table);
 | 
			
		||||
    if (memtype == JANET_MEMORY_TABLE_WEAKK) {
 | 
			
		||||
        janet_mark_values(table->data, table->capacity);
 | 
			
		||||
    } else if (memtype == JANET_MEMORY_TABLE_WEAKV) {
 | 
			
		||||
        janet_mark_keys(table->data, table->capacity);
 | 
			
		||||
    } else if (memtype == JANET_MEMORY_TABLE) {
 | 
			
		||||
        janet_mark_kvs(table->data, table->capacity);
 | 
			
		||||
    }
 | 
			
		||||
    /* do nothing for JANET_MEMORY_TABLE_WEAKKV */
 | 
			
		||||
    if (table->proto) {
 | 
			
		||||
        table = table->proto;
 | 
			
		||||
        goto recur;
 | 
			
		||||
@@ -209,6 +237,12 @@ static void janet_mark_funcdef(JanetFuncDef *def) {
 | 
			
		||||
        janet_mark_string(def->source);
 | 
			
		||||
    if (def->name)
 | 
			
		||||
        janet_mark_string(def->name);
 | 
			
		||||
    if (def->symbolmap) {
 | 
			
		||||
        for (int i = 0; i < def->symbolmap_length; i++) {
 | 
			
		||||
            janet_mark_string(def->symbolmap[i].symbol);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_function(JanetFunction *func) {
 | 
			
		||||
@@ -262,6 +296,12 @@ recur:
 | 
			
		||||
    if (fiber->supervisor_channel) {
 | 
			
		||||
        janet_mark_abstract(fiber->supervisor_channel);
 | 
			
		||||
    }
 | 
			
		||||
    if (fiber->ev_stream) {
 | 
			
		||||
        janet_mark_abstract(fiber->ev_stream);
 | 
			
		||||
    }
 | 
			
		||||
    if (fiber->ev_callback) {
 | 
			
		||||
        fiber->ev_callback(fiber, JANET_ASYNC_EVENT_MARK);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Explicit tail recursion */
 | 
			
		||||
@@ -281,14 +321,26 @@ static void janet_deinit_block(JanetGCObject *mem) {
 | 
			
		||||
            janet_symbol_deinit(((JanetStringHead *) mem)->data);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_MEMORY_ARRAY:
 | 
			
		||||
        case JANET_MEMORY_ARRAY_WEAK:
 | 
			
		||||
            janet_free(((JanetArray *) mem)->data);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_MEMORY_TABLE:
 | 
			
		||||
        case JANET_MEMORY_TABLE_WEAKK:
 | 
			
		||||
        case JANET_MEMORY_TABLE_WEAKV:
 | 
			
		||||
        case JANET_MEMORY_TABLE_WEAKKV:
 | 
			
		||||
            janet_free(((JanetTable *) mem)->data);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_MEMORY_FIBER:
 | 
			
		||||
            janet_free(((JanetFiber *)mem)->data);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_MEMORY_FIBER: {
 | 
			
		||||
            JanetFiber *f = (JanetFiber *)mem;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
            if (f->ev_state && !(f->flags & JANET_FIBER_EV_FLAG_IN_FLIGHT)) {
 | 
			
		||||
                janet_ev_dec_refcount();
 | 
			
		||||
                janet_free(f->ev_state);
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
            janet_free(f->data);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
        case JANET_MEMORY_BUFFER:
 | 
			
		||||
            janet_buffer_deinit((JanetBuffer *) mem);
 | 
			
		||||
            break;
 | 
			
		||||
@@ -314,17 +366,104 @@ static void janet_deinit_block(JanetGCObject *mem) {
 | 
			
		||||
            janet_free(def->bytecode);
 | 
			
		||||
            janet_free(def->sourcemap);
 | 
			
		||||
            janet_free(def->closure_bitset);
 | 
			
		||||
            janet_free(def->symbolmap);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check that a value x has been visited in the mark phase */
 | 
			
		||||
static int janet_check_liveref(Janet x) {
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        default:
 | 
			
		||||
            return 1;
 | 
			
		||||
        case JANET_ARRAY:
 | 
			
		||||
        case JANET_TABLE:
 | 
			
		||||
        case JANET_FUNCTION:
 | 
			
		||||
        case JANET_BUFFER:
 | 
			
		||||
        case JANET_FIBER:
 | 
			
		||||
            return janet_gc_reachable(janet_unwrap_pointer(x));
 | 
			
		||||
        case JANET_STRING:
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
        case JANET_KEYWORD:
 | 
			
		||||
            return janet_gc_reachable(janet_string_head(janet_unwrap_string(x)));
 | 
			
		||||
        case JANET_ABSTRACT:
 | 
			
		||||
            return janet_gc_reachable(janet_abstract_head(janet_unwrap_abstract(x)));
 | 
			
		||||
        case JANET_TUPLE:
 | 
			
		||||
            return janet_gc_reachable(janet_tuple_head(janet_unwrap_tuple(x)));
 | 
			
		||||
        case JANET_STRUCT:
 | 
			
		||||
            return janet_gc_reachable(janet_struct_head(janet_unwrap_struct(x)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Iterate over all allocated memory, and free memory that is not
 | 
			
		||||
 * marked as reachable. Flip the gc color flag for next sweep. */
 | 
			
		||||
void janet_sweep() {
 | 
			
		||||
    JanetGCObject *previous = NULL;
 | 
			
		||||
    JanetGCObject *current = janet_vm.blocks;
 | 
			
		||||
    JanetGCObject *current = janet_vm.weak_blocks;
 | 
			
		||||
    JanetGCObject *next;
 | 
			
		||||
 | 
			
		||||
    /* Sweep weak heap to drop weak refs */
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        next = current->data.next;
 | 
			
		||||
        if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
 | 
			
		||||
            /* Check for dead references */
 | 
			
		||||
            enum JanetMemoryType type = janet_gc_type(current);
 | 
			
		||||
            if (type == JANET_MEMORY_ARRAY_WEAK) {
 | 
			
		||||
                JanetArray *array = (JanetArray *) current;
 | 
			
		||||
                for (uint32_t i = 0; i < (uint32_t) array->count; i++) {
 | 
			
		||||
                    if (!janet_check_liveref(array->data[i])) {
 | 
			
		||||
                        array->data[i] = janet_wrap_nil();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                JanetTable *table = (JanetTable *) current;
 | 
			
		||||
                int check_values = (type == JANET_MEMORY_TABLE_WEAKV) || (type == JANET_MEMORY_TABLE_WEAKKV);
 | 
			
		||||
                int check_keys = (type == JANET_MEMORY_TABLE_WEAKK) || (type == JANET_MEMORY_TABLE_WEAKKV);
 | 
			
		||||
                JanetKV *end = table->data + table->capacity;
 | 
			
		||||
                JanetKV *kvs = table->data;
 | 
			
		||||
                while (kvs < end) {
 | 
			
		||||
                    int drop = 0;
 | 
			
		||||
                    if (check_keys && !janet_check_liveref(kvs->key)) drop = 1;
 | 
			
		||||
                    if (check_values && !janet_check_liveref(kvs->value)) drop = 1;
 | 
			
		||||
                    if (drop) {
 | 
			
		||||
                        /* Inlined from janet_table_remove without search */
 | 
			
		||||
                        table->count--;
 | 
			
		||||
                        table->deleted++;
 | 
			
		||||
                        kvs->key = janet_wrap_nil();
 | 
			
		||||
                        kvs->value = janet_wrap_false();
 | 
			
		||||
                    }
 | 
			
		||||
                    kvs++;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Sweep weak heap to free blocks */
 | 
			
		||||
    previous = NULL;
 | 
			
		||||
    current = janet_vm.weak_blocks;
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        next = current->data.next;
 | 
			
		||||
        if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
 | 
			
		||||
            previous = current;
 | 
			
		||||
            current->flags &= ~JANET_MEM_REACHABLE;
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_vm.block_count--;
 | 
			
		||||
            janet_deinit_block(current);
 | 
			
		||||
            if (NULL != previous) {
 | 
			
		||||
                previous->data.next = next;
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_vm.weak_blocks = next;
 | 
			
		||||
            }
 | 
			
		||||
            janet_free(current);
 | 
			
		||||
        }
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Sweep main heap to free blocks */
 | 
			
		||||
    previous = NULL;
 | 
			
		||||
    current = janet_vm.blocks;
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        next = current->data.next;
 | 
			
		||||
        if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
 | 
			
		||||
@@ -342,6 +481,7 @@ void janet_sweep() {
 | 
			
		||||
        }
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Sweep threaded abstract types for references to decrement */
 | 
			
		||||
    JanetKV *items = janet_vm.threaded_abstracts.data;
 | 
			
		||||
@@ -363,14 +503,15 @@ void janet_sweep() {
 | 
			
		||||
                    if (head->type->gc) {
 | 
			
		||||
                        janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
 | 
			
		||||
                    }
 | 
			
		||||
                    /* Mark as tombstone in place */
 | 
			
		||||
                    items[i].key = janet_wrap_nil();
 | 
			
		||||
                    items[i].value = janet_wrap_false();
 | 
			
		||||
                    janet_vm.threaded_abstracts.deleted++;
 | 
			
		||||
                    janet_vm.threaded_abstracts.count--;
 | 
			
		||||
                    /* Free memory */
 | 
			
		||||
                    janet_free(janet_abstract_head(abst));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                /* Mark as tombstone in place */
 | 
			
		||||
                items[i].key = janet_wrap_nil();
 | 
			
		||||
                items[i].value = janet_wrap_false();
 | 
			
		||||
                janet_vm.threaded_abstracts.deleted++;
 | 
			
		||||
                janet_vm.threaded_abstracts.count--;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /* Reset for next sweep */
 | 
			
		||||
@@ -398,8 +539,15 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
 | 
			
		||||
 | 
			
		||||
    /* Prepend block to heap list */
 | 
			
		||||
    janet_vm.next_collection += size;
 | 
			
		||||
    mem->data.next = janet_vm.blocks;
 | 
			
		||||
    janet_vm.blocks = mem;
 | 
			
		||||
    if (type < JANET_MEMORY_TABLE_WEAKK) {
 | 
			
		||||
        /* normal heap */
 | 
			
		||||
        mem->data.next = janet_vm.blocks;
 | 
			
		||||
        janet_vm.blocks = mem;
 | 
			
		||||
    } else {
 | 
			
		||||
        /* weak heap */
 | 
			
		||||
        mem->data.next = janet_vm.weak_blocks;
 | 
			
		||||
        janet_vm.weak_blocks = mem;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm.block_count++;
 | 
			
		||||
 | 
			
		||||
    return (void *)mem;
 | 
			
		||||
@@ -430,7 +578,8 @@ void janet_collect(void) {
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
    if (janet_vm.gc_suspend) return;
 | 
			
		||||
    depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    /* Try and prevent many major collections back to back.
 | 
			
		||||
    janet_vm.gc_mark_phase = 1;
 | 
			
		||||
    /* Try to prevent many major collections back to back.
 | 
			
		||||
     * A full collection will take O(janet_vm.block_count) time.
 | 
			
		||||
     * If we have a large heap, make sure our interval is not too
 | 
			
		||||
     * small so we won't make many collections over it. This is just a
 | 
			
		||||
@@ -449,6 +598,7 @@ void janet_collect(void) {
 | 
			
		||||
        Janet x = janet_vm.roots[--janet_vm.root_count];
 | 
			
		||||
        janet_mark(x);
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm.gc_mark_phase = 0;
 | 
			
		||||
    janet_sweep();
 | 
			
		||||
    janet_vm.next_collection = 0;
 | 
			
		||||
    janet_free_all_scratch();
 | 
			
		||||
@@ -552,7 +702,9 @@ void janet_gcunlock(int handle) {
 | 
			
		||||
    janet_vm.gc_suspend = handle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Scratch memory API */
 | 
			
		||||
/* Scratch memory API
 | 
			
		||||
 * Scratch memory allocations do not need to be free (but optionally can be), and will be automatically cleaned
 | 
			
		||||
 * up in the next call to janet_collect. */
 | 
			
		||||
 | 
			
		||||
void *janet_smalloc(size_t size) {
 | 
			
		||||
    JanetScratch *s = janet_malloc(sizeof(JanetScratch) + size);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -57,10 +57,14 @@ enum JanetMemoryType {
 | 
			
		||||
    JANET_MEMORY_FUNCENV,
 | 
			
		||||
    JANET_MEMORY_FUNCDEF,
 | 
			
		||||
    JANET_MEMORY_THREADED_ABSTRACT,
 | 
			
		||||
    JANET_MEMORY_TABLE_WEAKK,
 | 
			
		||||
    JANET_MEMORY_TABLE_WEAKV,
 | 
			
		||||
    JANET_MEMORY_TABLE_WEAKKV,
 | 
			
		||||
    JANET_MEMORY_ARRAY_WEAK
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* To allocate collectable memory, one must call janet_alloc, initialize the memory,
 | 
			
		||||
 * and then call when janet_enablegc when it is initailize and reachable by the gc (on the JANET stack) */
 | 
			
		||||
 * and then call when janet_enablegc when it is initialized and reachable by the gc (on the JANET stack) */
 | 
			
		||||
void *janet_gcalloc(enum JanetMemoryType type, size_t size);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose & contributors
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose & contributors
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -73,13 +73,13 @@ static void *int64_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
 | 
			
		||||
static void it_s64_tostring(void *p, JanetBuffer *buffer) {
 | 
			
		||||
    char str[32];
 | 
			
		||||
    sprintf(str, "%" PRId64, *((int64_t *)p));
 | 
			
		||||
    snprintf(str, sizeof(str), "%" PRId64, *((int64_t *)p));
 | 
			
		||||
    janet_buffer_push_cstring(buffer, str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void it_u64_tostring(void *p, JanetBuffer *buffer) {
 | 
			
		||||
    char str[32];
 | 
			
		||||
    sprintf(str, "%" PRIu64, *((uint64_t *)p));
 | 
			
		||||
    snprintf(str, sizeof(str), "%" PRIu64, *((uint64_t *)p));
 | 
			
		||||
    janet_buffer_push_cstring(buffer, str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -118,10 +118,9 @@ int64_t janet_unwrap_s64(Janet x) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_NUMBER : {
 | 
			
		||||
            double dbl = janet_unwrap_number(x);
 | 
			
		||||
            if (fabs(dbl) <=  MAX_INT_IN_DBL)
 | 
			
		||||
                return (int64_t)dbl;
 | 
			
		||||
            break;
 | 
			
		||||
            double d = janet_unwrap_number(x);
 | 
			
		||||
            if (!janet_checkint64range(d)) break;
 | 
			
		||||
            return (int64_t) d;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_STRING: {
 | 
			
		||||
            int64_t value;
 | 
			
		||||
@@ -138,7 +137,7 @@ int64_t janet_unwrap_s64(Janet x) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_panicf("bad s64 initializer: %t", x);
 | 
			
		||||
    janet_panicf("can not convert %t %q to 64 bit signed integer", x, x);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -147,12 +146,9 @@ uint64_t janet_unwrap_u64(Janet x) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_NUMBER : {
 | 
			
		||||
            double dbl = janet_unwrap_number(x);
 | 
			
		||||
            /* Allow negative values to be cast to "wrap around".
 | 
			
		||||
             * This let's addition and subtraction work as expected. */
 | 
			
		||||
            if (fabs(dbl) <=  MAX_INT_IN_DBL)
 | 
			
		||||
                return (uint64_t)dbl;
 | 
			
		||||
            break;
 | 
			
		||||
            double d = janet_unwrap_number(x);
 | 
			
		||||
            if (!janet_checkuint64range(d)) break;
 | 
			
		||||
            return (uint64_t) d;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_STRING: {
 | 
			
		||||
            uint64_t value;
 | 
			
		||||
@@ -169,7 +165,7 @@ uint64_t janet_unwrap_u64(Janet x) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_panicf("bad u64 initializer: %t", x);
 | 
			
		||||
    janet_panicf("can not convert %t %q to a 64 bit unsigned integer", x, x);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -195,21 +191,21 @@ Janet janet_wrap_u64(uint64_t x) {
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_it_s64_new,
 | 
			
		||||
              "(int/s64 value)",
 | 
			
		||||
              "Create a boxed signed 64 bit integer from a string value.") {
 | 
			
		||||
              "Create a boxed signed 64 bit integer from a string value or a number.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_s64(janet_unwrap_s64(argv[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_it_u64_new,
 | 
			
		||||
              "(int/u64 value)",
 | 
			
		||||
              "Create a boxed unsigned 64 bit integer from a string value.") {
 | 
			
		||||
              "Create a boxed unsigned 64 bit integer from a string value or a number.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_u64(janet_unwrap_u64(argv[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_to_number,
 | 
			
		||||
              "(int/to-number value)",
 | 
			
		||||
              "Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int32.") {
 | 
			
		||||
              "Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int64.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (janet_type(argv[0]) == JANET_ABSTRACT) {
 | 
			
		||||
        void *abst = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
@@ -243,7 +239,7 @@ JANET_CORE_FN(cfun_to_bytes,
 | 
			
		||||
              "Write the bytes of an `int/s64` or `int/u64` into a buffer.\n"
 | 
			
		||||
              "The `buffer` parameter specifies an existing buffer to write to, if unset a new buffer will be created.\n"
 | 
			
		||||
              "Returns the modified buffer.\n"
 | 
			
		||||
              "The `endianness` paramater indicates the byte order:\n"
 | 
			
		||||
              "The `endianness` parameter indicates the byte order:\n"
 | 
			
		||||
              "- `nil` (unset): system byte order\n"
 | 
			
		||||
              "- `:le`: little-endian, least significant byte first\n"
 | 
			
		||||
              "- `:be`: big-endian, most significant byte first\n") {
 | 
			
		||||
@@ -307,8 +303,8 @@ static int compare_double_double(double x, double y) {
 | 
			
		||||
 | 
			
		||||
static int compare_int64_double(int64_t x, double y) {
 | 
			
		||||
    if (isnan(y)) {
 | 
			
		||||
        return 0; // clojure and python do this
 | 
			
		||||
    } else if ((y > (- ((double) MAX_INT_IN_DBL))) && (y < ((double) MAX_INT_IN_DBL))) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    } else if ((y > JANET_INTMIN_DOUBLE) && (y < JANET_INTMAX_DOUBLE)) {
 | 
			
		||||
        double dx = (double) x;
 | 
			
		||||
        return compare_double_double(dx, y);
 | 
			
		||||
    } else if (y > ((double) INT64_MAX)) {
 | 
			
		||||
@@ -323,10 +319,10 @@ static int compare_int64_double(int64_t x, double y) {
 | 
			
		||||
 | 
			
		||||
static int compare_uint64_double(uint64_t x, double y) {
 | 
			
		||||
    if (isnan(y)) {
 | 
			
		||||
        return 0; // clojure and python do this
 | 
			
		||||
        return 0;
 | 
			
		||||
    } else if (y < 0) {
 | 
			
		||||
        return 1;
 | 
			
		||||
    } else if ((y >= 0) && (y < ((double) MAX_INT_IN_DBL))) {
 | 
			
		||||
    } else if ((y >= 0) && (y < JANET_INTMAX_DOUBLE)) {
 | 
			
		||||
        double dx = (double) x;
 | 
			
		||||
        return compare_double_double(dx, y);
 | 
			
		||||
    } else if (y > ((double) UINT64_MAX)) {
 | 
			
		||||
@@ -339,8 +335,9 @@ static int compare_uint64_double(uint64_t x, double y) {
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    if (janet_is_int(argv[0]) != JANET_INT_S64)
 | 
			
		||||
    if (janet_is_int(argv[0]) != JANET_INT_S64) {
 | 
			
		||||
        janet_panic("compare method requires int/s64 as first argument");
 | 
			
		||||
    }
 | 
			
		||||
    int64_t x = janet_unwrap_s64(argv[0]);
 | 
			
		||||
    switch (janet_type(argv[1])) {
 | 
			
		||||
        default:
 | 
			
		||||
@@ -355,7 +352,6 @@ static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
 | 
			
		||||
                int64_t y = *(int64_t *)abst;
 | 
			
		||||
                return janet_wrap_number((x < y) ? -1 : (x > y ? 1 : 0));
 | 
			
		||||
            } else if (janet_abstract_type(abst) == &janet_u64_type) {
 | 
			
		||||
                // comparing signed to unsigned -- be careful!
 | 
			
		||||
                uint64_t y = *(uint64_t *)abst;
 | 
			
		||||
                if (x < 0) {
 | 
			
		||||
                    return janet_wrap_number(-1);
 | 
			
		||||
@@ -374,8 +370,9 @@ static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    if (janet_is_int(argv[0]) != JANET_INT_U64)  // is this needed?
 | 
			
		||||
    if (janet_is_int(argv[0]) != JANET_INT_U64) {
 | 
			
		||||
        janet_panic("compare method requires int/u64 as first argument");
 | 
			
		||||
    }
 | 
			
		||||
    uint64_t x = janet_unwrap_u64(argv[0]);
 | 
			
		||||
    switch (janet_type(argv[1])) {
 | 
			
		||||
        default:
 | 
			
		||||
@@ -390,7 +387,6 @@ static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
 | 
			
		||||
                uint64_t y = *(uint64_t *)abst;
 | 
			
		||||
                return janet_wrap_number((x < y) ? -1 : (x > y ? 1 : 0));
 | 
			
		||||
            } else if (janet_abstract_type(abst) == &janet_s64_type) {
 | 
			
		||||
                // comparing unsigned to signed -- be careful!
 | 
			
		||||
                int64_t y = *(int64_t *)abst;
 | 
			
		||||
                if (y < 0) {
 | 
			
		||||
                    return janet_wrap_number(1);
 | 
			
		||||
@@ -431,7 +427,7 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define OPMETHODINVERT(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_fixarity(argc, 2); \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[1]); \
 | 
			
		||||
@@ -440,6 +436,19 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define UNARYMETHOD(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_fixarity(argc, 1); \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = oper(janet_unwrap_##type(argv[0])); \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define DIVZERO(name) DIVZERO_##name
 | 
			
		||||
#define DIVZERO_div janet_panic("division by zero")
 | 
			
		||||
#define DIVZERO_rem janet_panic("division by zero")
 | 
			
		||||
#define DIVZERO_mod return janet_wrap_abstract(box)
 | 
			
		||||
 | 
			
		||||
#define DIVMETHOD(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_arity(argc, 2, -1);                       \
 | 
			
		||||
@@ -447,19 +456,19 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[0]); \
 | 
			
		||||
    for (int32_t i = 1; i < argc; i++) { \
 | 
			
		||||
      T value = janet_unwrap_##type(argv[i]); \
 | 
			
		||||
      if (value == 0) janet_panic("division by zero"); \
 | 
			
		||||
      if (value == 0) DIVZERO(name); \
 | 
			
		||||
      *box oper##= value; \
 | 
			
		||||
    } \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define DIVMETHODINVERT(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_fixarity(argc, 2);                       \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[1]); \
 | 
			
		||||
    T value = janet_unwrap_##type(argv[0]); \
 | 
			
		||||
    if (value == 0) janet_panic("division by zero"); \
 | 
			
		||||
    if (value == 0) DIVZERO(name); \
 | 
			
		||||
    *box oper##= value; \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
@@ -471,7 +480,7 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[0]); \
 | 
			
		||||
    for (int32_t i = 1; i < argc; i++) { \
 | 
			
		||||
      T value = janet_unwrap_##type(argv[i]); \
 | 
			
		||||
      if (value == 0) janet_panic("division by zero"); \
 | 
			
		||||
      if (value == 0) DIVZERO(name); \
 | 
			
		||||
      if ((value == -1) && (*box == INT64_MIN)) janet_panic("INT64_MIN divided by -1"); \
 | 
			
		||||
      *box oper##= value; \
 | 
			
		||||
    } \
 | 
			
		||||
@@ -479,51 +488,95 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define DIVMETHODINVERT_SIGNED(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_fixarity(argc, 2);                       \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[1]); \
 | 
			
		||||
    T value = janet_unwrap_##type(argv[0]); \
 | 
			
		||||
    if (value == 0) janet_panic("division by zero"); \
 | 
			
		||||
    if (value == 0) DIVZERO(name); \
 | 
			
		||||
    if ((value == -1) && (*box == INT64_MIN)) janet_panic("INT64_MIN divided by -1"); \
 | 
			
		||||
    *box oper##= value; \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_divf(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
 | 
			
		||||
    int64_t op1 = janet_unwrap_s64(argv[0]);
 | 
			
		||||
    int64_t op2 = janet_unwrap_s64(argv[1]);
 | 
			
		||||
    if (op2 == 0) janet_panic("division by zero");
 | 
			
		||||
    int64_t x = op1 / op2;
 | 
			
		||||
    *box = x - (((op1 ^ op2) < 0) && (x * op2 != op1));
 | 
			
		||||
    return janet_wrap_abstract(box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_divfi(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
 | 
			
		||||
    int64_t op2 = janet_unwrap_s64(argv[0]);
 | 
			
		||||
    int64_t op1 = janet_unwrap_s64(argv[1]);
 | 
			
		||||
    if (op2 == 0) janet_panic("division by zero");
 | 
			
		||||
    int64_t x = op1 / op2;
 | 
			
		||||
    *box = x - (((op1 ^ op2) < 0) && (x * op2 != op1));
 | 
			
		||||
    return janet_wrap_abstract(box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_mod(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
 | 
			
		||||
    int64_t op1 = janet_unwrap_s64(argv[0]);
 | 
			
		||||
    int64_t op2 = janet_unwrap_s64(argv[1]);
 | 
			
		||||
    int64_t x = op1 % op2;
 | 
			
		||||
    *box = (op1 > 0)
 | 
			
		||||
           ? ((op2 > 0) ? x : (0 == x ? x : x + op2))
 | 
			
		||||
           : ((op2 > 0) ? (0 == x ? x : x + op2) : x);
 | 
			
		||||
    if (op2 == 0) {
 | 
			
		||||
        *box = op1;
 | 
			
		||||
    } else {
 | 
			
		||||
        int64_t x = op1 % op2;
 | 
			
		||||
        *box = (((op1 ^ op2) < 0) && (x != 0)) ? x + op2 : x;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_abstract(box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_modi(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    int64_t *box = janet_abstract(&janet_s64_type, sizeof(int64_t));
 | 
			
		||||
    int64_t op2 = janet_unwrap_s64(argv[0]);
 | 
			
		||||
    int64_t op1 = janet_unwrap_s64(argv[1]);
 | 
			
		||||
    if (op2 == 0) {
 | 
			
		||||
        *box = op1;
 | 
			
		||||
    } else {
 | 
			
		||||
        int64_t x = op1 % op2;
 | 
			
		||||
        *box = (((op1 ^ op2) < 0) && (x != 0)) ? x + op2 : x;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_abstract(box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OPMETHOD(int64_t, s64, add, +)
 | 
			
		||||
OPMETHOD(int64_t, s64, sub, -)
 | 
			
		||||
OPMETHODINVERT(int64_t, s64, subi, -)
 | 
			
		||||
OPMETHODINVERT(int64_t, s64, sub, -)
 | 
			
		||||
OPMETHOD(int64_t, s64, mul, *)
 | 
			
		||||
DIVMETHOD_SIGNED(int64_t, s64, div, /)
 | 
			
		||||
DIVMETHOD_SIGNED(int64_t, s64, rem, %)
 | 
			
		||||
DIVMETHODINVERT_SIGNED(int64_t, s64, divi, /)
 | 
			
		||||
DIVMETHODINVERT_SIGNED(int64_t, s64, div, /)
 | 
			
		||||
DIVMETHODINVERT_SIGNED(int64_t, s64, rem, %)
 | 
			
		||||
OPMETHOD(int64_t, s64, and, &)
 | 
			
		||||
OPMETHOD(int64_t, s64, or, |)
 | 
			
		||||
OPMETHOD(int64_t, s64, xor, ^)
 | 
			
		||||
UNARYMETHOD(int64_t, s64, not, ~)
 | 
			
		||||
OPMETHOD(int64_t, s64, lshift, <<)
 | 
			
		||||
OPMETHOD(int64_t, s64, rshift, >>)
 | 
			
		||||
OPMETHOD(uint64_t, u64, add, +)
 | 
			
		||||
OPMETHOD(uint64_t, u64, sub, -)
 | 
			
		||||
OPMETHODINVERT(uint64_t, u64, subi, -)
 | 
			
		||||
OPMETHODINVERT(uint64_t, u64, sub, -)
 | 
			
		||||
OPMETHOD(uint64_t, u64, mul, *)
 | 
			
		||||
DIVMETHOD(uint64_t, u64, div, /)
 | 
			
		||||
DIVMETHOD(uint64_t, u64, rem, %)
 | 
			
		||||
DIVMETHOD(uint64_t, u64, mod, %)
 | 
			
		||||
DIVMETHODINVERT(uint64_t, u64, divi, /)
 | 
			
		||||
DIVMETHODINVERT(uint64_t, u64, div, /)
 | 
			
		||||
DIVMETHODINVERT(uint64_t, u64, rem, %)
 | 
			
		||||
DIVMETHODINVERT(uint64_t, u64, mod, %)
 | 
			
		||||
OPMETHOD(uint64_t, u64, and, &)
 | 
			
		||||
OPMETHOD(uint64_t, u64, or, |)
 | 
			
		||||
OPMETHOD(uint64_t, u64, xor, ^)
 | 
			
		||||
UNARYMETHOD(uint64_t, u64, not, ~)
 | 
			
		||||
OPMETHOD(uint64_t, u64, lshift, <<)
 | 
			
		||||
OPMETHOD(uint64_t, u64, rshift, >>)
 | 
			
		||||
 | 
			
		||||
@@ -532,7 +585,6 @@ OPMETHOD(uint64_t, u64, rshift, >>)
 | 
			
		||||
#undef DIVMETHOD_SIGNED
 | 
			
		||||
#undef COMPMETHOD
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static JanetMethod it_s64_methods[] = {
 | 
			
		||||
    {"+", cfun_it_s64_add},
 | 
			
		||||
    {"r+", cfun_it_s64_add},
 | 
			
		||||
@@ -542,20 +594,22 @@ static JanetMethod it_s64_methods[] = {
 | 
			
		||||
    {"r*", cfun_it_s64_mul},
 | 
			
		||||
    {"/", cfun_it_s64_div},
 | 
			
		||||
    {"r/", cfun_it_s64_divi},
 | 
			
		||||
    {"div", cfun_it_s64_divf},
 | 
			
		||||
    {"rdiv", cfun_it_s64_divfi},
 | 
			
		||||
    {"mod", cfun_it_s64_mod},
 | 
			
		||||
    {"rmod", cfun_it_s64_mod},
 | 
			
		||||
    {"rmod", cfun_it_s64_modi},
 | 
			
		||||
    {"%", cfun_it_s64_rem},
 | 
			
		||||
    {"r%", cfun_it_s64_rem},
 | 
			
		||||
    {"r%", cfun_it_s64_remi},
 | 
			
		||||
    {"&", cfun_it_s64_and},
 | 
			
		||||
    {"r&", cfun_it_s64_and},
 | 
			
		||||
    {"|", cfun_it_s64_or},
 | 
			
		||||
    {"r|", cfun_it_s64_or},
 | 
			
		||||
    {"^", cfun_it_s64_xor},
 | 
			
		||||
    {"r^", cfun_it_s64_xor},
 | 
			
		||||
    {"~", cfun_it_s64_not},
 | 
			
		||||
    {"<<", cfun_it_s64_lshift},
 | 
			
		||||
    {">>", cfun_it_s64_rshift},
 | 
			
		||||
    {"compare", cfun_it_s64_compare},
 | 
			
		||||
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -568,20 +622,22 @@ static JanetMethod it_u64_methods[] = {
 | 
			
		||||
    {"r*", cfun_it_u64_mul},
 | 
			
		||||
    {"/", cfun_it_u64_div},
 | 
			
		||||
    {"r/", cfun_it_u64_divi},
 | 
			
		||||
    {"div", cfun_it_u64_div},
 | 
			
		||||
    {"rdiv", cfun_it_u64_divi},
 | 
			
		||||
    {"mod", cfun_it_u64_mod},
 | 
			
		||||
    {"rmod", cfun_it_u64_mod},
 | 
			
		||||
    {"%", cfun_it_u64_mod},
 | 
			
		||||
    {"r%", cfun_it_u64_mod},
 | 
			
		||||
    {"rmod", cfun_it_u64_modi},
 | 
			
		||||
    {"%", cfun_it_u64_rem},
 | 
			
		||||
    {"r%", cfun_it_u64_remi},
 | 
			
		||||
    {"&", cfun_it_u64_and},
 | 
			
		||||
    {"r&", cfun_it_u64_and},
 | 
			
		||||
    {"|", cfun_it_u64_or},
 | 
			
		||||
    {"r|", cfun_it_u64_or},
 | 
			
		||||
    {"^", cfun_it_u64_xor},
 | 
			
		||||
    {"r^", cfun_it_u64_xor},
 | 
			
		||||
    {"~", cfun_it_u64_not},
 | 
			
		||||
    {"<<", cfun_it_u64_lshift},
 | 
			
		||||
    {">>", cfun_it_u64_rshift},
 | 
			
		||||
    {"compare", cfun_it_u64_compare},
 | 
			
		||||
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#endif
 | 
			
		||||
@@ -41,6 +42,11 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx);
 | 
			
		||||
static void *io_file_unmarshal(JanetMarshalContext *ctx);
 | 
			
		||||
static Janet io_file_next(void *p, Janet key);
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#define ftell _ftelli64
 | 
			
		||||
#define fseek _fseeki64
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_file_type = {
 | 
			
		||||
    "core/file",
 | 
			
		||||
    cfun_io_gc,
 | 
			
		||||
@@ -69,12 +75,15 @@ static int32_t checkflags(const uint8_t *str) {
 | 
			
		||||
            break;
 | 
			
		||||
        case 'w':
 | 
			
		||||
            flags |= JANET_FILE_WRITE;
 | 
			
		||||
            janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'a':
 | 
			
		||||
            flags |= JANET_FILE_APPEND;
 | 
			
		||||
            janet_sandbox_assert(JANET_SANDBOX_FS);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'r':
 | 
			
		||||
            flags |= JANET_FILE_READ;
 | 
			
		||||
            janet_sandbox_assert(JANET_SANDBOX_FS_READ);
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 1; i < len; i++) {
 | 
			
		||||
@@ -84,6 +93,7 @@ static int32_t checkflags(const uint8_t *str) {
 | 
			
		||||
                break;
 | 
			
		||||
            case '+':
 | 
			
		||||
                if (flags & JANET_FILE_UPDATE) return -1;
 | 
			
		||||
                janet_sandbox_assert(JANET_SANDBOX_FS_WRITE);
 | 
			
		||||
                flags |= JANET_FILE_UPDATE;
 | 
			
		||||
                break;
 | 
			
		||||
            case 'b':
 | 
			
		||||
@@ -116,17 +126,18 @@ JANET_CORE_FN(cfun_io_temp,
 | 
			
		||||
              "(file/temp)",
 | 
			
		||||
              "Open an anonymous temporary file that is removed on close. "
 | 
			
		||||
              "Raises an error on failure.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FS_TEMP);
 | 
			
		||||
    (void)argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    // XXX use mkostemp when we can to avoid CLOEXEC race.
 | 
			
		||||
    FILE *tmp = tmpfile();
 | 
			
		||||
    if (!tmp)
 | 
			
		||||
        janet_panicf("unable to create temporary file - %s", strerror(errno));
 | 
			
		||||
        janet_panicf("unable to create temporary file - %s", janet_strerror(errno));
 | 
			
		||||
    return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_fopen,
 | 
			
		||||
              "(file/open path &opt mode)",
 | 
			
		||||
              "(file/open path &opt mode buffer-size)",
 | 
			
		||||
              "Open a file. `path` is an absolute or relative path, and "
 | 
			
		||||
              "`mode` is a set of flags indicating the mode to open the file in. "
 | 
			
		||||
              "`mode` is a keyword where each character represents a flag. If the file "
 | 
			
		||||
@@ -138,8 +149,9 @@ JANET_CORE_FN(cfun_io_fopen,
 | 
			
		||||
              "Following one of the initial flags, 0 or more of the following flags can be appended:\n\n"
 | 
			
		||||
              "* b - open the file in binary mode (rather than text mode)\n\n"
 | 
			
		||||
              "* + - append to the file instead of overwriting it\n\n"
 | 
			
		||||
              "* n - error if the file cannot be opened instead of returning nil") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
              "* n - error if the file cannot be opened instead of returning nil\n\n"
 | 
			
		||||
              "See fopen (<stdio.h>, C99) for further details.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    const uint8_t *fname = janet_getstring(argv, 0);
 | 
			
		||||
    const uint8_t *fmode;
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
@@ -148,11 +160,29 @@ JANET_CORE_FN(cfun_io_fopen,
 | 
			
		||||
        flags = checkflags(fmode);
 | 
			
		||||
    } else {
 | 
			
		||||
        fmode = (const uint8_t *)"r";
 | 
			
		||||
        janet_sandbox_assert(JANET_SANDBOX_FS_READ);
 | 
			
		||||
        flags = JANET_FILE_READ;
 | 
			
		||||
    }
 | 
			
		||||
    FILE *f = fopen((const char *)fname, (const char *)fmode);
 | 
			
		||||
    if (f != NULL) {
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
        struct stat st;
 | 
			
		||||
        fstat(fileno(f), &st);
 | 
			
		||||
        if (S_ISDIR(st.st_mode)) {
 | 
			
		||||
            fclose(f);
 | 
			
		||||
            janet_panicf("cannot open directory: %s", fname);
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
 | 
			
		||||
        if (bufsize != BUFSIZ) {
 | 
			
		||||
            int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
 | 
			
		||||
            if (result) {
 | 
			
		||||
                janet_panic("failed to set buffer size for file");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return f ? janet_makefile(f, flags)
 | 
			
		||||
           : (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil())
 | 
			
		||||
           : (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
 | 
			
		||||
           : janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -243,6 +273,13 @@ JANET_CORE_FN(cfun_io_fwrite,
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void io_assert_writeable(JanetFile *iof) {
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
        janet_panic("file is closed");
 | 
			
		||||
    if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
 | 
			
		||||
        janet_panic("file is not writeable");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Flush the bytes in the file */
 | 
			
		||||
JANET_CORE_FN(cfun_io_fflush,
 | 
			
		||||
              "(file/flush f)",
 | 
			
		||||
@@ -250,10 +287,7 @@ JANET_CORE_FN(cfun_io_fflush,
 | 
			
		||||
              "buffered for efficiency reasons. Returns the file handle.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
        janet_panic("file is closed");
 | 
			
		||||
    if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
 | 
			
		||||
        janet_panic("file is not writeable");
 | 
			
		||||
    io_assert_writeable(iof);
 | 
			
		||||
    if (fflush(iof->file))
 | 
			
		||||
        janet_panic("could not flush file");
 | 
			
		||||
    return argv[0];
 | 
			
		||||
@@ -269,6 +303,7 @@ int janet_file_close(JanetFile *file) {
 | 
			
		||||
    if (!(file->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
 | 
			
		||||
        ret = fclose(file->file);
 | 
			
		||||
        file->flags |= JANET_FILE_CLOSED;
 | 
			
		||||
        file->file = NULL; /* NULL dereference is easier to debug then other problems */
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -316,7 +351,7 @@ JANET_CORE_FN(cfun_io_fseek,
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
        janet_panic("file is closed");
 | 
			
		||||
    long int offset = 0;
 | 
			
		||||
    int64_t offset = 0;
 | 
			
		||||
    int whence = SEEK_CUR;
 | 
			
		||||
    if (argc >= 2) {
 | 
			
		||||
        const uint8_t *whence_sym = janet_getkeyword(argv, 1);
 | 
			
		||||
@@ -330,18 +365,31 @@ JANET_CORE_FN(cfun_io_fseek,
 | 
			
		||||
            janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]);
 | 
			
		||||
        }
 | 
			
		||||
        if (argc == 3) {
 | 
			
		||||
            offset = (long) janet_getinteger64(argv, 2);
 | 
			
		||||
            offset = (int64_t) janet_getinteger64(argv, 2);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (fseek(iof->file, offset, whence)) janet_panic("error seeking file");
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_ftell,
 | 
			
		||||
              "(file/tell f)",
 | 
			
		||||
              "Get the current value of the file position for file `f`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
        janet_panic("file is closed");
 | 
			
		||||
    int64_t pos = ftell(iof->file);
 | 
			
		||||
    if (pos == -1) janet_panic("error getting position in file");
 | 
			
		||||
    return janet_wrap_number((double)pos);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetMethod io_file_methods[] = {
 | 
			
		||||
    {"close", cfun_io_fclose},
 | 
			
		||||
    {"flush", cfun_io_fflush},
 | 
			
		||||
    {"read", cfun_io_fread},
 | 
			
		||||
    {"seek", cfun_io_fseek},
 | 
			
		||||
    {"tell", cfun_io_ftell},
 | 
			
		||||
    {"write", cfun_io_fwrite},
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
@@ -449,6 +497,7 @@ static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
            if (janet_abstract_type(abstract) != &janet_file_type)
 | 
			
		||||
                return janet_wrap_nil();
 | 
			
		||||
            JanetFile *iofile = abstract;
 | 
			
		||||
            io_assert_writeable(iofile);
 | 
			
		||||
            f = iofile->file;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -479,7 +528,6 @@ static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_print_impl(int32_t argc, Janet *argv,
 | 
			
		||||
                                int newline, const char *name, FILE *dflt_file) {
 | 
			
		||||
    Janet x = janet_dyn(name);
 | 
			
		||||
@@ -564,6 +612,10 @@ static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
            if (janet_abstract_type(abstract) != &janet_file_type)
 | 
			
		||||
                return janet_wrap_nil();
 | 
			
		||||
            JanetFile *iofile = abstract;
 | 
			
		||||
            if (iofile->flags & JANET_FILE_CLOSED) {
 | 
			
		||||
                janet_panic("cannot print to closed file");
 | 
			
		||||
            }
 | 
			
		||||
            io_assert_writeable(iofile);
 | 
			
		||||
            f = iofile->file;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -688,6 +740,7 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
 | 
			
		||||
                if (janet_abstract_type(abstract) != &janet_file_type)
 | 
			
		||||
                    break;
 | 
			
		||||
                JanetFile *iofile = abstract;
 | 
			
		||||
                io_assert_writeable(iofile);
 | 
			
		||||
                f = iofile->file;
 | 
			
		||||
            }
 | 
			
		||||
            fwrite(buffer.data, buffer.count, 1, f);
 | 
			
		||||
@@ -718,17 +771,17 @@ JanetFile *janet_getjfile(const Janet *argv, int32_t n) {
 | 
			
		||||
    return janet_getabstract(argv, n, &janet_file_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) {
 | 
			
		||||
FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags) {
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, n, &janet_file_type);
 | 
			
		||||
    if (NULL != flags) *flags = iof->flags;
 | 
			
		||||
    return iof->file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetFile *janet_makejfile(FILE *f, int flags) {
 | 
			
		||||
JanetFile *janet_makejfile(FILE *f, int32_t flags) {
 | 
			
		||||
    return makef(f, flags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_makefile(FILE *f, int flags) {
 | 
			
		||||
Janet janet_makefile(FILE *f, int32_t flags) {
 | 
			
		||||
    return janet_wrap_abstract(makef(f, flags));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -736,7 +789,7 @@ JanetAbstract janet_checkfile(Janet j) {
 | 
			
		||||
    return janet_checkabstract(j, &janet_file_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FILE *janet_unwrapfile(Janet j, int *flags) {
 | 
			
		||||
FILE *janet_unwrapfile(Janet j, int32_t *flags) {
 | 
			
		||||
    JanetFile *iof = janet_unwrap_abstract(j);
 | 
			
		||||
    if (NULL != flags) *flags = iof->flags;
 | 
			
		||||
    return iof->file;
 | 
			
		||||
@@ -766,6 +819,7 @@ void janet_lib_io(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("file/write", cfun_io_fwrite),
 | 
			
		||||
        JANET_CORE_REG("file/flush", cfun_io_fflush),
 | 
			
		||||
        JANET_CORE_REG("file/seek", cfun_io_fseek),
 | 
			
		||||
        JANET_CORE_REG("file/tell", cfun_io_ftell),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, io_cfuns);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										254
									
								
								src/core/marsh.c
									
									
									
									
									
								
							
							
						
						
									
										254
									
								
								src/core/marsh.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,8 +67,16 @@ enum {
 | 
			
		||||
    LB_UNSAFE_POINTER, /* 222 */
 | 
			
		||||
    LB_STRUCT_PROTO, /* 223 */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    LB_THREADED_ABSTRACT/* 224 */
 | 
			
		||||
    LB_THREADED_ABSTRACT, /* 224 */
 | 
			
		||||
    LB_POINTER_BUFFER, /* 225 */
 | 
			
		||||
#endif
 | 
			
		||||
    LB_TABLE_WEAKK, /* 226 */
 | 
			
		||||
    LB_TABLE_WEAKV, /* 227 */
 | 
			
		||||
    LB_TABLE_WEAKKV, /* 228 */
 | 
			
		||||
    LB_TABLE_WEAKK_PROTO, /* 229 */
 | 
			
		||||
    LB_TABLE_WEAKV_PROTO, /* 230 */
 | 
			
		||||
    LB_TABLE_WEAKKV_PROTO, /* 231 */
 | 
			
		||||
    LB_ARRAY_WEAK, /* 232 */
 | 
			
		||||
} LeadBytes;
 | 
			
		||||
 | 
			
		||||
/* Helper to look inside an entry in an environment */
 | 
			
		||||
@@ -153,6 +161,10 @@ static void pushbytes(MarshalState *st, const uint8_t *bytes, int32_t len) {
 | 
			
		||||
    janet_buffer_push_bytes(st->buf, bytes, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pushpointer(MarshalState *st, const void *ptr) {
 | 
			
		||||
    janet_buffer_push_bytes(st->buf, (const uint8_t *) &ptr, sizeof(ptr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Marshal a size_t onto the buffer */
 | 
			
		||||
static void push64(MarshalState *st, uint64_t x) {
 | 
			
		||||
    if (x <= 0xF0) {
 | 
			
		||||
@@ -180,6 +192,19 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags);
 | 
			
		||||
/* Prevent stack overflows */
 | 
			
		||||
#define MARSH_STACKCHECK if ((flags & 0xFFFF) > JANET_RECURSION_GUARD) janet_panic("stack overflow")
 | 
			
		||||
 | 
			
		||||
/* Quick check if a fiber cannot be marshalled. This is will
 | 
			
		||||
 * have no false positives, but may have false negatives. */
 | 
			
		||||
static int fiber_cannot_be_marshalled(JanetFiber *fiber) {
 | 
			
		||||
    if (janet_fiber_status(fiber) == JANET_STATUS_ALIVE) return 1;
 | 
			
		||||
    int32_t i = fiber->frame;
 | 
			
		||||
    while (i > 0) {
 | 
			
		||||
        JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
 | 
			
		||||
        if (!frame->func) return 1; /* has cfunction on stack */
 | 
			
		||||
        i = frame->prevframe;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Marshal a function env */
 | 
			
		||||
static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
 | 
			
		||||
    MARSH_STACKCHECK;
 | 
			
		||||
@@ -192,7 +217,9 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
 | 
			
		||||
    }
 | 
			
		||||
    janet_env_valid(env);
 | 
			
		||||
    janet_v_push(st->seen_envs, env);
 | 
			
		||||
    if (env->offset > 0 && (JANET_STATUS_ALIVE == janet_fiber_status(env->as.fiber))) {
 | 
			
		||||
 | 
			
		||||
    /* Special case for early detachment */
 | 
			
		||||
    if (env->offset > 0 && fiber_cannot_be_marshalled(env->as.fiber)) {
 | 
			
		||||
        pushint(st, 0);
 | 
			
		||||
        pushint(st, env->length);
 | 
			
		||||
        Janet *values = env->as.fiber->data + env->offset;
 | 
			
		||||
@@ -241,6 +268,7 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
 | 
			
		||||
    }
 | 
			
		||||
    /* Add to lookup */
 | 
			
		||||
    janet_v_push(st->seen_defs, def);
 | 
			
		||||
 | 
			
		||||
    pushint(st, def->flags);
 | 
			
		||||
    pushint(st, def->slotcount);
 | 
			
		||||
    pushint(st, def->arity);
 | 
			
		||||
@@ -252,6 +280,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
 | 
			
		||||
        pushint(st, def->environments_length);
 | 
			
		||||
    if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
 | 
			
		||||
        pushint(st, def->defs_length);
 | 
			
		||||
    if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP)
 | 
			
		||||
        pushint(st, def->symbolmap_length);
 | 
			
		||||
    if (def->flags & JANET_FUNCDEF_FLAG_HASNAME)
 | 
			
		||||
        marshal_one(st, janet_wrap_string(def->name), flags);
 | 
			
		||||
    if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCE)
 | 
			
		||||
@@ -259,7 +289,15 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
 | 
			
		||||
 | 
			
		||||
    /* marshal constants */
 | 
			
		||||
    for (int32_t i = 0; i < def->constants_length; i++)
 | 
			
		||||
        marshal_one(st, def->constants[i], flags);
 | 
			
		||||
        marshal_one(st, def->constants[i], flags + 1);
 | 
			
		||||
 | 
			
		||||
    /* Marshal symbol map, if needed */
 | 
			
		||||
    for (int32_t i = 0; i < def->symbolmap_length; i++) {
 | 
			
		||||
        pushint(st, (int32_t) def->symbolmap[i].birth_pc);
 | 
			
		||||
        pushint(st, (int32_t) def->symbolmap[i].death_pc);
 | 
			
		||||
        pushint(st, (int32_t) def->symbolmap[i].slot_index);
 | 
			
		||||
        marshal_one(st, janet_wrap_symbol(def->symbolmap[i].symbol), flags + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* marshal the bytecode */
 | 
			
		||||
    janet_marshal_u32s(st, def->bytecode, def->bytecode_length);
 | 
			
		||||
@@ -270,7 +308,7 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
 | 
			
		||||
 | 
			
		||||
    /* marshal the sub funcdefs if needed */
 | 
			
		||||
    for (int32_t i = 0; i < def->defs_length; i++)
 | 
			
		||||
        marshal_one_def(st, def->defs[i], flags);
 | 
			
		||||
        marshal_one_def(st, def->defs[i], flags + 1);
 | 
			
		||||
 | 
			
		||||
    /* marshal source maps if needed */
 | 
			
		||||
    if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCEMAP) {
 | 
			
		||||
@@ -312,7 +350,7 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
 | 
			
		||||
    while (i > 0) {
 | 
			
		||||
        JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
 | 
			
		||||
        if (frame->env) frame->flags |= JANET_STACKFRAME_HASENV;
 | 
			
		||||
        if (!frame->func) janet_panic("cannot marshal fiber with c stackframe");
 | 
			
		||||
        if (!frame->func) janet_panicf("cannot marshal fiber with c stackframe (%v)", janet_wrap_cfunction((JanetCFunction) frame->pc));
 | 
			
		||||
        pushint(st, frame->flags);
 | 
			
		||||
        pushint(st, frame->prevframe);
 | 
			
		||||
        int32_t pcdiff = (int32_t)(frame->pc - frame->func->def->bytecode);
 | 
			
		||||
@@ -347,6 +385,15 @@ void janet_marshal_int(JanetMarshalContext *ctx, int32_t value) {
 | 
			
		||||
    pushint(st, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Only use in unsafe - don't marshal pointers otherwise */
 | 
			
		||||
void janet_marshal_ptr(JanetMarshalContext *ctx, const void *ptr) {
 | 
			
		||||
    if (!(ctx->flags & JANET_MARSHAL_UNSAFE)) {
 | 
			
		||||
        janet_panic("can only marshal pointers in unsafe mode");
 | 
			
		||||
    }
 | 
			
		||||
    MarshalState *st = (MarshalState *)(ctx->m_state);
 | 
			
		||||
    pushpointer(st, ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value) {
 | 
			
		||||
    MarshalState *st = (MarshalState *)(ctx->m_state);
 | 
			
		||||
    pushbyte(st, value);
 | 
			
		||||
@@ -363,18 +410,27 @@ void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) {
 | 
			
		||||
    marshal_one(st, x, ctx->flags + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_MARSHAL_DEBUG
 | 
			
		||||
#define MARK_SEEN() \
 | 
			
		||||
    do { if (st->maybe_cycles) { \
 | 
			
		||||
        Janet _check = janet_table_get(&st->seen, x); \
 | 
			
		||||
        if (!janet_checktype(_check, JANET_NIL)) janet_eprintf("double MARK_SEEN on %v\n", x); \
 | 
			
		||||
        janet_eprintf("made reference %d (%t) to %v\n", st->nextid, x, x); \
 | 
			
		||||
        janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); \
 | 
			
		||||
    } } while (0)
 | 
			
		||||
#else
 | 
			
		||||
#define MARK_SEEN() \
 | 
			
		||||
    do { if (st->maybe_cycles) { \
 | 
			
		||||
        janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); \
 | 
			
		||||
    } } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void janet_marshal_abstract(JanetMarshalContext *ctx, void *abstract) {
 | 
			
		||||
    MarshalState *st = (MarshalState *)(ctx->m_state);
 | 
			
		||||
    if (st->maybe_cycles) {
 | 
			
		||||
        janet_table_put(&st->seen,
 | 
			
		||||
                        janet_wrap_abstract(abstract),
 | 
			
		||||
                        janet_wrap_integer(st->nextid++));
 | 
			
		||||
    }
 | 
			
		||||
    Janet x = janet_wrap_abstract(abstract);
 | 
			
		||||
    MARK_SEEN();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MARK_SEEN() \
 | 
			
		||||
    do { if (st->maybe_cycles) janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); } while (0)
 | 
			
		||||
 | 
			
		||||
static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
    void *abstract = janet_unwrap_abstract(x);
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
@@ -396,7 +452,7 @@ static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
    if (at->marshal) {
 | 
			
		||||
        pushbyte(st, LB_ABSTRACT);
 | 
			
		||||
        marshal_one(st, janet_csymbolv(at->name), flags + 1);
 | 
			
		||||
        JanetMarshalContext context = {st, NULL, flags, NULL, at};
 | 
			
		||||
        JanetMarshalContext context = {st, NULL, flags + 1, NULL, at};
 | 
			
		||||
        at->marshal(abstract, &context);
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panicf("cannot marshal %p", x);
 | 
			
		||||
@@ -501,6 +557,16 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
            JanetBuffer *buffer = janet_unwrap_buffer(x);
 | 
			
		||||
            /* Record reference */
 | 
			
		||||
            MARK_SEEN();
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
            if ((flags & JANET_MARSHAL_UNSAFE) &&
 | 
			
		||||
                    (buffer->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC)) {
 | 
			
		||||
                pushbyte(st, LB_POINTER_BUFFER);
 | 
			
		||||
                pushint(st, buffer->count);
 | 
			
		||||
                pushint(st, buffer->capacity);
 | 
			
		||||
                pushpointer(st, buffer->data);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
#endif
 | 
			
		||||
            pushbyte(st, LB_BUFFER);
 | 
			
		||||
            pushint(st, buffer->count);
 | 
			
		||||
            pushbytes(st, buffer->data, buffer->count);
 | 
			
		||||
@@ -510,7 +576,8 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
            int32_t i;
 | 
			
		||||
            JanetArray *a = janet_unwrap_array(x);
 | 
			
		||||
            MARK_SEEN();
 | 
			
		||||
            pushbyte(st, LB_ARRAY);
 | 
			
		||||
            enum JanetMemoryType memtype = janet_gc_type(a);
 | 
			
		||||
            pushbyte(st, memtype == JANET_MEMORY_ARRAY_WEAK ? LB_ARRAY_WEAK : LB_ARRAY);
 | 
			
		||||
            pushint(st, a->count);
 | 
			
		||||
            for (i = 0; i < a->count; i++)
 | 
			
		||||
                marshal_one(st, a->data[i], flags + 1);
 | 
			
		||||
@@ -533,7 +600,16 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
        case JANET_TABLE: {
 | 
			
		||||
            JanetTable *t = janet_unwrap_table(x);
 | 
			
		||||
            MARK_SEEN();
 | 
			
		||||
            pushbyte(st, t->proto ? LB_TABLE_PROTO : LB_TABLE);
 | 
			
		||||
            enum JanetMemoryType memtype = janet_gc_type(t);
 | 
			
		||||
            if (memtype == JANET_MEMORY_TABLE_WEAKK) {
 | 
			
		||||
                pushbyte(st, t->proto ? LB_TABLE_WEAKK_PROTO : LB_TABLE_WEAKK);
 | 
			
		||||
            } else if (memtype == JANET_MEMORY_TABLE_WEAKV) {
 | 
			
		||||
                pushbyte(st, t->proto ? LB_TABLE_WEAKV_PROTO : LB_TABLE_WEAKV);
 | 
			
		||||
            } else if (memtype == JANET_MEMORY_TABLE_WEAKKV) {
 | 
			
		||||
                pushbyte(st, t->proto ? LB_TABLE_WEAKKV_PROTO : LB_TABLE_WEAKKV);
 | 
			
		||||
            } else {
 | 
			
		||||
                pushbyte(st, t->proto ? LB_TABLE_PROTO : LB_TABLE);
 | 
			
		||||
            }
 | 
			
		||||
            pushint(st, t->count);
 | 
			
		||||
            if (t->proto)
 | 
			
		||||
                marshal_one(st, janet_wrap_table(t->proto), flags + 1);
 | 
			
		||||
@@ -596,8 +672,7 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
            if (!(flags & JANET_MARSHAL_UNSAFE)) goto no_registry;
 | 
			
		||||
            MARK_SEEN();
 | 
			
		||||
            pushbyte(st, LB_UNSAFE_POINTER);
 | 
			
		||||
            void *ptr = janet_unwrap_pointer(x);
 | 
			
		||||
            pushbytes(st, (uint8_t *) &ptr, sizeof(void *));
 | 
			
		||||
            pushpointer(st, janet_unwrap_pointer(x));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    no_registry:
 | 
			
		||||
@@ -704,9 +779,22 @@ static uint64_t read64(UnmarshalState *st, const uint8_t **atdata) {
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_MARSHAL_DEBUG
 | 
			
		||||
static void dump_reference_table(UnmarshalState *st) {
 | 
			
		||||
    for (int32_t i = 0; i < janet_v_count(st->lookup); i++) {
 | 
			
		||||
        janet_eprintf("  reference %d (%t) = %v\n", i, st->lookup[i], st->lookup[i]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Assert a janet type */
 | 
			
		||||
static void janet_asserttype(Janet x, JanetType t) {
 | 
			
		||||
static void janet_asserttype(Janet x, JanetType t, UnmarshalState *st) {
 | 
			
		||||
    if (!janet_checktype(x, t)) {
 | 
			
		||||
#ifdef JANET_MARSHAL_DEBUG
 | 
			
		||||
        dump_reference_table(st);
 | 
			
		||||
#else
 | 
			
		||||
        (void) st;
 | 
			
		||||
#endif
 | 
			
		||||
        janet_panicf("expected type %T, got %v", 1 << t, x);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -758,7 +846,7 @@ static const uint8_t *unmarshal_one_env(
 | 
			
		||||
            Janet fiberv;
 | 
			
		||||
            /* On stack variant */
 | 
			
		||||
            data = unmarshal_one(st, data, &fiberv, flags);
 | 
			
		||||
            janet_asserttype(fiberv, JANET_FIBER);
 | 
			
		||||
            janet_asserttype(fiberv, JANET_FIBER, st);
 | 
			
		||||
            env->as.fiber = janet_unwrap_fiber(fiberv);
 | 
			
		||||
            /* Negative offset indicates untrusted input */
 | 
			
		||||
            env->offset = -offset;
 | 
			
		||||
@@ -824,6 +912,8 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
        def->constants = NULL;
 | 
			
		||||
        def->bytecode = NULL;
 | 
			
		||||
        def->sourcemap = NULL;
 | 
			
		||||
        def->symbolmap = NULL;
 | 
			
		||||
        def->symbolmap_length = 0;
 | 
			
		||||
        janet_v_push(st->lookup_defs, def);
 | 
			
		||||
 | 
			
		||||
        /* Set default lengths to zero */
 | 
			
		||||
@@ -831,6 +921,7 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
        int32_t constants_length = 0;
 | 
			
		||||
        int32_t environments_length = 0;
 | 
			
		||||
        int32_t defs_length = 0;
 | 
			
		||||
        int32_t symbolmap_length = 0;
 | 
			
		||||
 | 
			
		||||
        /* Read flags and other fixed values */
 | 
			
		||||
        def->flags = readint(st, &data);
 | 
			
		||||
@@ -846,18 +937,20 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
            environments_length = readnat(st, &data);
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASDEFS)
 | 
			
		||||
            defs_length = readnat(st, &data);
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP)
 | 
			
		||||
            symbolmap_length = readnat(st, &data);
 | 
			
		||||
 | 
			
		||||
        /* Check name and source (optional) */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASNAME) {
 | 
			
		||||
            Janet x;
 | 
			
		||||
            data = unmarshal_one(st, data, &x, flags + 1);
 | 
			
		||||
            janet_asserttype(x, JANET_STRING);
 | 
			
		||||
            janet_asserttype(x, JANET_STRING, st);
 | 
			
		||||
            def->name = janet_unwrap_string(x);
 | 
			
		||||
        }
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASSOURCE) {
 | 
			
		||||
            Janet x;
 | 
			
		||||
            data = unmarshal_one(st, data, &x, flags + 1);
 | 
			
		||||
            janet_asserttype(x, JANET_STRING);
 | 
			
		||||
            janet_asserttype(x, JANET_STRING, st);
 | 
			
		||||
            def->source = janet_unwrap_string(x);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -874,6 +967,27 @@ static const uint8_t *unmarshal_one_def(
 | 
			
		||||
        }
 | 
			
		||||
        def->constants_length = constants_length;
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal symbol map, if needed */
 | 
			
		||||
        if (def->flags & JANET_FUNCDEF_FLAG_HASSYMBOLMAP) {
 | 
			
		||||
            size_t size = sizeof(JanetSymbolMap) * symbolmap_length;
 | 
			
		||||
            def->symbolmap = janet_malloc(size);
 | 
			
		||||
            if (def->symbolmap == NULL) {
 | 
			
		||||
                JANET_OUT_OF_MEMORY;
 | 
			
		||||
            }
 | 
			
		||||
            for (int32_t i = 0; i < symbolmap_length; i++) {
 | 
			
		||||
                def->symbolmap[i].birth_pc = (uint32_t) readint(st, &data);
 | 
			
		||||
                def->symbolmap[i].death_pc = (uint32_t) readint(st, &data);
 | 
			
		||||
                def->symbolmap[i].slot_index = (uint32_t) readint(st, &data);
 | 
			
		||||
                Janet value;
 | 
			
		||||
                data = unmarshal_one(st, data, &value, flags + 1);
 | 
			
		||||
                if (!janet_checktype(value, JANET_SYMBOL)) {
 | 
			
		||||
                    janet_panicf("corrupted symbolmap when unmarshalling debug info, got %v", value);
 | 
			
		||||
                }
 | 
			
		||||
                def->symbolmap[i].symbol = janet_unwrap_symbol(value);
 | 
			
		||||
            }
 | 
			
		||||
            def->symbolmap_length = (uint32_t) symbolmap_length;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Unmarshal bytecode */
 | 
			
		||||
        def->bytecode = janet_malloc(sizeof(uint32_t) * bytecode_length);
 | 
			
		||||
        if (!def->bytecode) {
 | 
			
		||||
@@ -966,9 +1080,11 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
    fiber->env = NULL;
 | 
			
		||||
    fiber->last_value = janet_wrap_nil();
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    fiber->waiting = NULL;
 | 
			
		||||
    fiber->sched_id = 0;
 | 
			
		||||
    fiber->supervisor_channel = NULL;
 | 
			
		||||
    fiber->ev_state = NULL;
 | 
			
		||||
    fiber->ev_callback = NULL;
 | 
			
		||||
    fiber->ev_stream = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Push fiber to seen stack */
 | 
			
		||||
@@ -1017,7 +1133,7 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
        /* Get function */
 | 
			
		||||
        Janet funcv;
 | 
			
		||||
        data = unmarshal_one(st, data, &funcv, flags + 1);
 | 
			
		||||
        janet_asserttype(funcv, JANET_FUNCTION);
 | 
			
		||||
        janet_asserttype(funcv, JANET_FUNCTION, st);
 | 
			
		||||
        func = janet_unwrap_function(funcv);
 | 
			
		||||
        def = func->def;
 | 
			
		||||
 | 
			
		||||
@@ -1063,7 +1179,7 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
        Janet envv;
 | 
			
		||||
        fiber_flags &= ~JANET_FIBER_FLAG_HASENV;
 | 
			
		||||
        data = unmarshal_one(st, data, &envv, flags + 1);
 | 
			
		||||
        janet_asserttype(envv, JANET_TABLE);
 | 
			
		||||
        janet_asserttype(envv, JANET_TABLE, st);
 | 
			
		||||
        fiber_env = janet_unwrap_table(envv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1072,7 +1188,7 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
        Janet fiberv;
 | 
			
		||||
        fiber_flags &= ~JANET_FIBER_FLAG_HASCHILD;
 | 
			
		||||
        data = unmarshal_one(st, data, &fiberv, flags + 1);
 | 
			
		||||
        janet_asserttype(fiberv, JANET_FIBER);
 | 
			
		||||
        janet_asserttype(fiberv, JANET_FIBER, st);
 | 
			
		||||
        fiber->child = janet_unwrap_fiber(fiberv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1116,6 +1232,18 @@ int64_t janet_unmarshal_int64(JanetMarshalContext *ctx) {
 | 
			
		||||
    return read64(st, &(ctx->data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_unmarshal_ptr(JanetMarshalContext *ctx) {
 | 
			
		||||
    if (!(ctx->flags & JANET_MARSHAL_UNSAFE)) {
 | 
			
		||||
        janet_panic("can only unmarshal pointers in unsafe mode");
 | 
			
		||||
    }
 | 
			
		||||
    UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
 | 
			
		||||
    void *ptr;
 | 
			
		||||
    MARSH_EOS(st, ctx->data + sizeof(void *) - 1);
 | 
			
		||||
    memcpy((char *) &ptr, ctx->data, sizeof(void *));
 | 
			
		||||
    ctx->data += sizeof(void *);
 | 
			
		||||
    return ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx) {
 | 
			
		||||
    UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
 | 
			
		||||
    MARSH_EOS(st, ctx->data);
 | 
			
		||||
@@ -1151,6 +1279,18 @@ void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size) {
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
    void *p = janet_abstract_threaded(ctx->at, size);
 | 
			
		||||
    janet_unmarshal_abstract_reuse(ctx, p);
 | 
			
		||||
    return p;
 | 
			
		||||
#else
 | 
			
		||||
    (void) ctx;
 | 
			
		||||
    (void) size;
 | 
			
		||||
    janet_panic("threaded abstracts not supported");
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *data, Janet *out, int flags) {
 | 
			
		||||
    Janet key;
 | 
			
		||||
    data = unmarshal_one(st, data, &key, flags + 1);
 | 
			
		||||
@@ -1158,7 +1298,9 @@ static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *
 | 
			
		||||
    if (at == NULL) janet_panic("unknown abstract type");
 | 
			
		||||
    if (at->unmarshal) {
 | 
			
		||||
        JanetMarshalContext context = {NULL, st, flags, data, at};
 | 
			
		||||
        *out = janet_wrap_abstract(at->unmarshal(&context));
 | 
			
		||||
        void *abst = at->unmarshal(&context);
 | 
			
		||||
        janet_assert(abst != NULL, "null pointer abstract");
 | 
			
		||||
        *out = janet_wrap_abstract(abst);
 | 
			
		||||
        if (context.at != NULL) {
 | 
			
		||||
            janet_panic("janet_unmarshal_abstract not called");
 | 
			
		||||
        }
 | 
			
		||||
@@ -1259,7 +1401,7 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
        }
 | 
			
		||||
        case LB_FIBER: {
 | 
			
		||||
            JanetFiber *fiber;
 | 
			
		||||
            data = unmarshal_one_fiber(st, data + 1, &fiber, flags);
 | 
			
		||||
            data = unmarshal_one_fiber(st, data + 1, &fiber, flags + 1);
 | 
			
		||||
            *out = janet_wrap_fiber(fiber);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1274,6 +1416,9 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            func = janet_gcalloc(JANET_MEMORY_FUNCTION, sizeof(JanetFunction) +
 | 
			
		||||
                                 len * sizeof(JanetFuncEnv));
 | 
			
		||||
            func->def = NULL;
 | 
			
		||||
            for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
                func->envs[i] = NULL;
 | 
			
		||||
            }
 | 
			
		||||
            *out = janet_wrap_function(func);
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            data = unmarshal_one_def(st, data, &def, flags + 1);
 | 
			
		||||
@@ -1289,11 +1434,18 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
        }
 | 
			
		||||
        case LB_REFERENCE:
 | 
			
		||||
        case LB_ARRAY:
 | 
			
		||||
        case LB_ARRAY_WEAK:
 | 
			
		||||
        case LB_TUPLE:
 | 
			
		||||
        case LB_STRUCT:
 | 
			
		||||
        case LB_STRUCT_PROTO:
 | 
			
		||||
        case LB_TABLE:
 | 
			
		||||
        case LB_TABLE_PROTO:
 | 
			
		||||
        case LB_TABLE_WEAKK:
 | 
			
		||||
        case LB_TABLE_WEAKV:
 | 
			
		||||
        case LB_TABLE_WEAKKV:
 | 
			
		||||
        case LB_TABLE_WEAKK_PROTO:
 | 
			
		||||
        case LB_TABLE_WEAKV_PROTO:
 | 
			
		||||
        case LB_TABLE_WEAKKV_PROTO:
 | 
			
		||||
            /* Things that open with integers */
 | 
			
		||||
        {
 | 
			
		||||
            data++;
 | 
			
		||||
@@ -1302,9 +1454,9 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            if (lead != LB_REFERENCE) {
 | 
			
		||||
                MARSH_EOS(st, data - 1 + len);
 | 
			
		||||
            }
 | 
			
		||||
            if (lead == LB_ARRAY) {
 | 
			
		||||
            if (lead == LB_ARRAY || lead == LB_ARRAY_WEAK) {
 | 
			
		||||
                /* Array */
 | 
			
		||||
                JanetArray *array = janet_array(len);
 | 
			
		||||
                JanetArray *array = (lead == LB_ARRAY_WEAK) ? janet_array_weak(len) : janet_array(len);
 | 
			
		||||
                array->count = len;
 | 
			
		||||
                *out = janet_wrap_array(array);
 | 
			
		||||
                janet_v_push(st->lookup, *out);
 | 
			
		||||
@@ -1327,7 +1479,7 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
                if (lead == LB_STRUCT_PROTO) {
 | 
			
		||||
                    Janet proto;
 | 
			
		||||
                    data = unmarshal_one(st, data, &proto, flags + 1);
 | 
			
		||||
                    janet_asserttype(proto, JANET_STRUCT);
 | 
			
		||||
                    janet_asserttype(proto, JANET_STRUCT, st);
 | 
			
		||||
                    janet_struct_proto(struct_) = janet_unwrap_struct(proto);
 | 
			
		||||
                }
 | 
			
		||||
                for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
@@ -1344,13 +1496,22 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
                *out = st->lookup[len];
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Table */
 | 
			
		||||
                JanetTable *t = janet_table(len);
 | 
			
		||||
                JanetTable *t;
 | 
			
		||||
                if (lead == LB_TABLE_WEAKK_PROTO || lead == LB_TABLE_WEAKK) {
 | 
			
		||||
                    t = janet_table_weakk(len);
 | 
			
		||||
                } else if (lead == LB_TABLE_WEAKV_PROTO || lead == LB_TABLE_WEAKV) {
 | 
			
		||||
                    t = janet_table_weakv(len);
 | 
			
		||||
                } else if (lead == LB_TABLE_WEAKKV_PROTO || lead == LB_TABLE_WEAKKV) {
 | 
			
		||||
                    t = janet_table_weakkv(len);
 | 
			
		||||
                } else {
 | 
			
		||||
                    t = janet_table(len);
 | 
			
		||||
                }
 | 
			
		||||
                *out = janet_wrap_table(t);
 | 
			
		||||
                janet_v_push(st->lookup, *out);
 | 
			
		||||
                if (lead == LB_TABLE_PROTO) {
 | 
			
		||||
                if (lead == LB_TABLE_PROTO || lead == LB_TABLE_WEAKK_PROTO || lead == LB_TABLE_WEAKV_PROTO || lead == LB_TABLE_WEAKKV_PROTO) {
 | 
			
		||||
                    Janet proto;
 | 
			
		||||
                    data = unmarshal_one(st, data, &proto, flags + 1);
 | 
			
		||||
                    janet_asserttype(proto, JANET_TABLE);
 | 
			
		||||
                    janet_asserttype(proto, JANET_TABLE, st);
 | 
			
		||||
                    t->proto = janet_unwrap_table(proto);
 | 
			
		||||
                }
 | 
			
		||||
                for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
@@ -1380,6 +1541,29 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
        case LB_POINTER_BUFFER: {
 | 
			
		||||
            data++;
 | 
			
		||||
            int32_t count = readnat(st, &data);
 | 
			
		||||
            int32_t capacity = readnat(st, &data);
 | 
			
		||||
            MARSH_EOS(st, data + sizeof(void *));
 | 
			
		||||
            union {
 | 
			
		||||
                void *ptr;
 | 
			
		||||
                uint8_t bytes[sizeof(void *)];
 | 
			
		||||
            } u;
 | 
			
		||||
            if (!(flags & JANET_MARSHAL_UNSAFE)) {
 | 
			
		||||
                janet_panicf("unsafe flag not given, "
 | 
			
		||||
                             "will not unmarshal raw pointer at index %d",
 | 
			
		||||
                             (int)(data - st->start));
 | 
			
		||||
            }
 | 
			
		||||
            memcpy(u.bytes, data, sizeof(void *));
 | 
			
		||||
            data += sizeof(void *);
 | 
			
		||||
            JanetBuffer *buffer = janet_pointer_buffer_unsafe(u.ptr, capacity, count);
 | 
			
		||||
            *out = janet_wrap_buffer(buffer);
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        case LB_UNSAFE_CFUNCTION: {
 | 
			
		||||
            MARSH_EOS(st, data + sizeof(JanetCFunction));
 | 
			
		||||
            data++;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								src/core/math.c
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								src/core/math.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -85,10 +85,10 @@ void janet_rng_longseed(JanetRNG *rng, const uint8_t *bytes, int32_t len) {
 | 
			
		||||
    uint8_t state[16] = {0};
 | 
			
		||||
    for (int32_t i = 0; i < len; i++)
 | 
			
		||||
        state[i & 0xF] ^= bytes[i];
 | 
			
		||||
    rng->a = state[0] + (state[1] << 8) + (state[2] << 16) + (state[3] << 24);
 | 
			
		||||
    rng->b = state[4] + (state[5] << 8) + (state[6] << 16) + (state[7] << 24);
 | 
			
		||||
    rng->c = state[8] + (state[9] << 8) + (state[10] << 16) + (state[11] << 24);
 | 
			
		||||
    rng->d = state[12] + (state[13] << 8) + (state[14] << 16) + (state[15] << 24);
 | 
			
		||||
    rng->a = state[0] + ((uint32_t) state[1] << 8) + ((uint32_t) state[2] << 16) + ((uint32_t) state[3] << 24);
 | 
			
		||||
    rng->b = state[4] + ((uint32_t) state[5] << 8) + ((uint32_t) state[6] << 16) + ((uint32_t) state[7] << 24);
 | 
			
		||||
    rng->c = state[8] + ((uint32_t) state[9] << 8) + ((uint32_t) state[10] << 16) + ((uint32_t) state[11] << 24);
 | 
			
		||||
    rng->d = state[12] + ((uint32_t) state[13] << 8) + ((uint32_t) state[14] << 16) + ((uint32_t) state[15] << 24);
 | 
			
		||||
    rng->counter = 0u;
 | 
			
		||||
    /* a, b, c, d can't all be 0 */
 | 
			
		||||
    if (rng->a == 0) rng->a = 1u;
 | 
			
		||||
@@ -119,7 +119,7 @@ double janet_rng_double(JanetRNG *rng) {
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_rng_make,
 | 
			
		||||
              "(math/rng &opt seed)",
 | 
			
		||||
              "Creates a Psuedo-Random number generator, with an optional seed. "
 | 
			
		||||
              "Creates a Pseudo-Random number generator, with an optional seed. "
 | 
			
		||||
              "The seed should be an unsigned 32 bit integer or a buffer. "
 | 
			
		||||
              "Do not use this for cryptography. Returns a core/rng abstract type."
 | 
			
		||||
             ) {
 | 
			
		||||
@@ -150,8 +150,8 @@ JANET_CORE_FN(cfun_rng_uniform,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_rng_int,
 | 
			
		||||
              "(math/rng-int rng &opt max)",
 | 
			
		||||
              "Extract a random random integer in the range [0, max] from the RNG. If "
 | 
			
		||||
              "no max is given, the default is 2^31 - 1."
 | 
			
		||||
              "Extract a random integer in the range [0, max) for max > 0 from the RNG.  "
 | 
			
		||||
              "If max is 0, return 0.  If no max is given, the default is 2^31 - 1."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type);
 | 
			
		||||
@@ -254,43 +254,45 @@ JANET_CORE_FN(janet_srand,
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define JANET_DEFINE_MATHOP(name, fop, doc)\
 | 
			
		||||
JANET_CORE_FN(janet_##name, "(math/" #name " x)", doc) {\
 | 
			
		||||
#define JANET_DEFINE_NAMED_MATHOP(janet_name, fop, doc)\
 | 
			
		||||
JANET_CORE_FN(janet_##fop, "(math/" janet_name " x)", doc) {\
 | 
			
		||||
    janet_fixarity(argc, 1); \
 | 
			
		||||
    double x = janet_getnumber(argv, 0); \
 | 
			
		||||
    return janet_wrap_number(fop(x)); \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_DEFINE_MATHOP(acos, acos, "Returns the arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asin, asin, "Returns the arcsin of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atan, atan, "Returns the arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cos, cos, "Returns the cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cosh, cosh, "Returns the hyperbolic cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(acosh, acosh, "Returns the hyperbolic arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sin, sin, "Returns the sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sinh, sinh, "Returns the hyperbolic sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asinh, asinh, "Returns the hypberbolic arcsine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tan, tan, "Returns the tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tanh, tanh, "Returns the hyperbolic tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atanh, atanh, "Returns the hyperbolic arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp, exp, "Returns e to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp2, exp2, "Returns 2 to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(expm1, expm1, "Returns e to the power of x minus 1.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log, log, "Returns the natural logarithm of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log10, log10, "Returns the log base 10 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log2, log2, "Returns the log base 2 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sqrt, sqrt, "Returns the square root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cbrt, cbrt, "Returns the cube root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(ceil, ceil, "Returns the smallest integer value number that is not less than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(fabs, fabs, "Return the absolute value of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(floor, floor, "Returns the largest integer value number that is not greater than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(trunc, trunc, "Returns the integer between x and 0 nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(round, round, "Returns the integer nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(gamma, tgamma, "Returns gamma(x).")
 | 
			
		||||
JANET_DEFINE_MATHOP(lgamma, lgamma, "Returns log-gamma(x).")
 | 
			
		||||
JANET_DEFINE_MATHOP(log1p, log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
 | 
			
		||||
JANET_DEFINE_MATHOP(erf, erf, "Returns the error function of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(erfc, erfc, "Returns the complementary error function of x.")
 | 
			
		||||
#define JANET_DEFINE_MATHOP(fop, doc) JANET_DEFINE_NAMED_MATHOP(#fop, fop, doc)
 | 
			
		||||
 | 
			
		||||
JANET_DEFINE_MATHOP(acos, "Returns the arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asin, "Returns the arcsin of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atan, "Returns the arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cos, "Returns the cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cosh, "Returns the hyperbolic cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(acosh, "Returns the hyperbolic arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sin, "Returns the sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sinh, "Returns the hyperbolic sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asinh, "Returns the hyperbolic arcsine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tan, "Returns the tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tanh, "Returns the hyperbolic tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atanh, "Returns the hyperbolic arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp, "Returns e to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp2, "Returns 2 to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(expm1, "Returns e to the power of x minus 1.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log, "Returns the natural logarithm of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log10, "Returns the log base 10 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log2, "Returns the log base 2 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sqrt, "Returns the square root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cbrt, "Returns the cube root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(ceil, "Returns the smallest integer value number that is not less than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(floor, "Returns the largest integer value number that is not greater than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(trunc, "Returns the integer between x and 0 nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(round, "Returns the integer nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
 | 
			
		||||
JANET_DEFINE_MATHOP(erf, "Returns the error function of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(erfc, "Returns the complementary error function of x.")
 | 
			
		||||
JANET_DEFINE_NAMED_MATHOP("log-gamma", lgamma, "Returns log-gamma(x).")
 | 
			
		||||
JANET_DEFINE_NAMED_MATHOP("abs", fabs, "Return the absolute value of x.")
 | 
			
		||||
JANET_DEFINE_NAMED_MATHOP("gamma", tgamma, "Returns gamma(x).")
 | 
			
		||||
 | 
			
		||||
#define JANET_DEFINE_MATH2OP(name, fop, signature, doc)\
 | 
			
		||||
JANET_CORE_FN(janet_##name, signature, doc) {\
 | 
			
		||||
@@ -303,7 +305,7 @@ JANET_CORE_FN(janet_##name, signature, doc) {\
 | 
			
		||||
JANET_DEFINE_MATH2OP(atan2, atan2, "(math/atan2 y x)", "Returns the arctangent of y/x. Works even when x is 0.")
 | 
			
		||||
JANET_DEFINE_MATH2OP(pow, pow, "(math/pow a x)", "Returns a to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATH2OP(hypot, hypot, "(math/hypot a b)", "Returns c from the equation c^2 = a^2 + b^2.")
 | 
			
		||||
JANET_DEFINE_MATH2OP(nextafter, nextafter,  "(math/next x y)", "Returns the next representable floating point vaue after x in the direction of y.")
 | 
			
		||||
JANET_DEFINE_MATH2OP(nextafter, nextafter,  "(math/next x y)", "Returns the next representable floating point value after x in the direction of y.")
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_not, "(not x)", "Returns the boolean inverse of x.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
@@ -315,7 +317,7 @@ static double janet_gcd(double x, double y) {
 | 
			
		||||
#ifdef NAN
 | 
			
		||||
        return NAN;
 | 
			
		||||
#else
 | 
			
		||||
        return 0.0 \ 0.0;
 | 
			
		||||
        return 0.0 / 0.0;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    if (isinf(x) || isinf(y)) return INFINITY;
 | 
			
		||||
@@ -347,6 +349,26 @@ JANET_CORE_FN(janet_cfun_lcm, "(math/lcm x y)",
 | 
			
		||||
    return janet_wrap_number(janet_lcm(x, y));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_frexp, "(math/frexp x)",
 | 
			
		||||
              "Returns a tuple of (mantissa, exponent) from number.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    double x = janet_getnumber(argv, 0);
 | 
			
		||||
    int exp;
 | 
			
		||||
    x = frexp(x, &exp);
 | 
			
		||||
    Janet *result = janet_tuple_begin(2);
 | 
			
		||||
    result[0] = janet_wrap_number(x);
 | 
			
		||||
    result[1] = janet_wrap_number((double) exp);
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_end(result));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_ldexp, "(math/ldexp m e)",
 | 
			
		||||
              "Creates a new number from a mantissa and an exponent.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    double x = janet_getnumber(argv, 0);
 | 
			
		||||
    int32_t y = janet_getinteger(argv, 1);
 | 
			
		||||
    return janet_wrap_number(ldexp(x, y));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_math(JanetTable *env) {
 | 
			
		||||
    JanetRegExt math_cfuns[] = {
 | 
			
		||||
@@ -383,7 +405,7 @@ void janet_lib_math(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("math/hypot", janet_hypot),
 | 
			
		||||
        JANET_CORE_REG("math/exp2", janet_exp2),
 | 
			
		||||
        JANET_CORE_REG("math/log1p", janet_log1p),
 | 
			
		||||
        JANET_CORE_REG("math/gamma", janet_gamma),
 | 
			
		||||
        JANET_CORE_REG("math/gamma", janet_tgamma),
 | 
			
		||||
        JANET_CORE_REG("math/log-gamma", janet_lgamma),
 | 
			
		||||
        JANET_CORE_REG("math/erfc", janet_erfc),
 | 
			
		||||
        JANET_CORE_REG("math/erf", janet_erf),
 | 
			
		||||
@@ -393,6 +415,8 @@ void janet_lib_math(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("math/next", janet_nextafter),
 | 
			
		||||
        JANET_CORE_REG("math/gcd", janet_cfun_gcd),
 | 
			
		||||
        JANET_CORE_REG("math/lcm", janet_cfun_lcm),
 | 
			
		||||
        JANET_CORE_REG("math/frexp", janet_cfun_frexp),
 | 
			
		||||
        JANET_CORE_REG("math/ldexp", janet_cfun_ldexp),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, math_cfuns);
 | 
			
		||||
@@ -409,11 +433,11 @@ void janet_lib_math(JanetTable *env) {
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int32-min", janet_wrap_number(INT32_MIN),
 | 
			
		||||
                   "The minimum contiguous integer representable by a 32 bit signed integer");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX),
 | 
			
		||||
                   "The maximum contiguous integer represtenable by a 32 bit signed integer");
 | 
			
		||||
                   "The maximum contiguous integer representable by a 32 bit signed integer");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
 | 
			
		||||
                   "The minimum contiguous integer representable by a double (2^53)");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
 | 
			
		||||
                   "The maximum contiguous integer represtenable by a double (-(2^53))");
 | 
			
		||||
                   "The maximum contiguous integer representable by a double (-(2^53))");
 | 
			
		||||
#ifdef NAN
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#else
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										356
									
								
								src/core/net.c
									
									
									
									
									
								
							
							
						
						
									
										356
									
								
								src/core/net.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose and contributors.
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose and contributors.
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -24,6 +24,7 @@
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "fiber.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
@@ -34,9 +35,11 @@
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#include <ws2tcpip.h>
 | 
			
		||||
#include <mswsock.h>
 | 
			
		||||
#ifdef JANET_MSVC
 | 
			
		||||
#pragma comment (lib, "Ws2_32.lib")
 | 
			
		||||
#pragma comment (lib, "Mswsock.lib")
 | 
			
		||||
#pragma comment (lib, "Advapi32.lib")
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
@@ -76,12 +79,20 @@ const JanetAbstractType janet_address_type = {
 | 
			
		||||
 | 
			
		||||
/* maximum number of bytes in a socket address host (post name resolution) */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#ifdef JANET_NO_IPV6
 | 
			
		||||
#define SA_ADDRSTRLEN (INET_ADDRSTRLEN + 1)
 | 
			
		||||
#else
 | 
			
		||||
#define SA_ADDRSTRLEN (INET6_ADDRSTRLEN + 1)
 | 
			
		||||
#endif
 | 
			
		||||
typedef unsigned short in_port_t;
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_SA_MAX(a, b) (((a) > (b))? (a) : (b))
 | 
			
		||||
#ifdef JANET_NO_IPV6
 | 
			
		||||
#define SA_ADDRSTRLEN JANET_SA_MAX(INET_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
 | 
			
		||||
#else
 | 
			
		||||
#define SA_ADDRSTRLEN JANET_SA_MAX(INET6_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static JanetStream *make_stream(JSock handle, uint32_t flags);
 | 
			
		||||
 | 
			
		||||
@@ -109,12 +120,57 @@ static void janet_net_socknoblock(JSock s) {
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* State machine for async connect */
 | 
			
		||||
 | 
			
		||||
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    JanetStream *stream = fiber->ev_stream;
 | 
			
		||||
    switch (event) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
        /* Wait until we have an actual event before checking.
 | 
			
		||||
         * Windows doesn't support async connect with this, just try immediately.*/
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT:
 | 
			
		||||
#endif
 | 
			
		||||
        case JANET_ASYNC_EVENT_DEINIT:
 | 
			
		||||
            return;
 | 
			
		||||
        case JANET_ASYNC_EVENT_CLOSE:
 | 
			
		||||
            janet_cancel(fiber, janet_cstringv("stream closed"));
 | 
			
		||||
            janet_async_end(fiber);
 | 
			
		||||
            return;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    int res = 0;
 | 
			
		||||
    int size = sizeof(res);
 | 
			
		||||
    int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size);
 | 
			
		||||
#else
 | 
			
		||||
    int res = 0;
 | 
			
		||||
    socklen_t size = sizeof res;
 | 
			
		||||
    int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
 | 
			
		||||
#endif
 | 
			
		||||
    if (r == 0) {
 | 
			
		||||
        if (res == 0) {
 | 
			
		||||
            janet_schedule(fiber, janet_wrap_abstract(stream));
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_cancel(fiber, janet_cstringv(janet_strerror(res)));
 | 
			
		||||
            stream->flags |= JANET_STREAM_TOCLOSE;
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_cancel(fiber, janet_ev_lasterr());
 | 
			
		||||
        stream->flags |= JANET_STREAM_TOCLOSE;
 | 
			
		||||
    }
 | 
			
		||||
    janet_async_end(fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
 | 
			
		||||
    janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* State machine for accepting connections. */
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    JanetListenerState head;
 | 
			
		||||
    WSAOVERLAPPED overlapped;
 | 
			
		||||
    JanetFunction *function;
 | 
			
		||||
    JanetStream *lstream;
 | 
			
		||||
@@ -122,10 +178,10 @@ typedef struct {
 | 
			
		||||
    char buf[1024];
 | 
			
		||||
} NetStateAccept;
 | 
			
		||||
 | 
			
		||||
static int net_sched_accept_impl(NetStateAccept *state, Janet *err);
 | 
			
		||||
static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err);
 | 
			
		||||
 | 
			
		||||
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) {
 | 
			
		||||
    NetStateAccept *state = (NetStateAccept *)s;
 | 
			
		||||
void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
 | 
			
		||||
    switch (event) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
@@ -136,56 +192,60 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_ASYNC_EVENT_CLOSE:
 | 
			
		||||
            janet_schedule(s->fiber, janet_wrap_nil());
 | 
			
		||||
            return JANET_ASYNC_STATUS_DONE;
 | 
			
		||||
            janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
            janet_async_end(fiber);
 | 
			
		||||
            return;
 | 
			
		||||
        case JANET_ASYNC_EVENT_COMPLETE: {
 | 
			
		||||
            if (state->astream->flags & JANET_STREAM_CLOSED) {
 | 
			
		||||
                janet_cancel(s->fiber, janet_cstringv("failed to accept connection"));
 | 
			
		||||
                return JANET_ASYNC_STATUS_DONE;
 | 
			
		||||
                janet_cancel(fiber, janet_cstringv("failed to accept connection"));
 | 
			
		||||
                janet_async_end(fiber);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            SOCKET lsock = (SOCKET) state->lstream->handle;
 | 
			
		||||
            if (NO_ERROR != setsockopt((SOCKET) state->astream->handle, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
 | 
			
		||||
                                       (char *) &lsock, sizeof(lsock))) {
 | 
			
		||||
                janet_cancel(s->fiber, janet_cstringv("failed to accept connection"));
 | 
			
		||||
                return JANET_ASYNC_STATUS_DONE;
 | 
			
		||||
                janet_cancel(fiber, janet_cstringv("failed to accept connection"));
 | 
			
		||||
                janet_async_end(fiber);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Janet streamv = janet_wrap_abstract(state->astream);
 | 
			
		||||
            if (state->function) {
 | 
			
		||||
                /* Schedule worker */
 | 
			
		||||
                JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv);
 | 
			
		||||
                fiber->supervisor_channel = s->fiber->supervisor_channel;
 | 
			
		||||
                janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
                JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
 | 
			
		||||
                sub_fiber->supervisor_channel = fiber->supervisor_channel;
 | 
			
		||||
                janet_schedule(sub_fiber, janet_wrap_nil());
 | 
			
		||||
                /* Now listen again for next connection */
 | 
			
		||||
                Janet err;
 | 
			
		||||
                if (net_sched_accept_impl(state, &err)) {
 | 
			
		||||
                    janet_cancel(s->fiber, err);
 | 
			
		||||
                    return JANET_ASYNC_STATUS_DONE;
 | 
			
		||||
                if (net_sched_accept_impl(state, fiber, &err)) {
 | 
			
		||||
                    janet_cancel(fiber, err);
 | 
			
		||||
                    janet_async_end(fiber);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_schedule(s->fiber, streamv);
 | 
			
		||||
                return JANET_ASYNC_STATUS_DONE;
 | 
			
		||||
                janet_schedule(fiber, streamv);
 | 
			
		||||
                janet_async_end(fiber);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return JANET_ASYNC_STATUS_NOT_DONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
 | 
			
		||||
    Janet err;
 | 
			
		||||
    SOCKET lsock = (SOCKET) stream->handle;
 | 
			
		||||
    JanetListenerState *s = janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL);
 | 
			
		||||
    NetStateAccept *state = (NetStateAccept *)s;
 | 
			
		||||
    NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
 | 
			
		||||
    memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
 | 
			
		||||
    memset(&state->buf, 0, 1024);
 | 
			
		||||
    state->function = fun;
 | 
			
		||||
    state->lstream = stream;
 | 
			
		||||
    s->tag = &state->overlapped;
 | 
			
		||||
    if (net_sched_accept_impl(state, &err)) janet_panicv(err);
 | 
			
		||||
    janet_await();
 | 
			
		||||
    if (net_sched_accept_impl(state, janet_root_fiber(), &err)) {
 | 
			
		||||
        janet_free(state);
 | 
			
		||||
        janet_panicv(err);
 | 
			
		||||
    }
 | 
			
		||||
    janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
 | 
			
		||||
static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err) {
 | 
			
		||||
    SOCKET lsock = (SOCKET) state->lstream->handle;
 | 
			
		||||
    SOCKET asock = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
 | 
			
		||||
    if (asock == INVALID_SOCKET) {
 | 
			
		||||
@@ -197,7 +257,11 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
 | 
			
		||||
    int socksize = sizeof(SOCKADDR_STORAGE) + 16;
 | 
			
		||||
    if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) {
 | 
			
		||||
        int code = WSAGetLastError();
 | 
			
		||||
        if (code == WSA_IO_PENDING) return 0; /* indicates io is happening async */
 | 
			
		||||
        if (code == WSA_IO_PENDING) {
 | 
			
		||||
            /* indicates io is happening async */
 | 
			
		||||
            janet_async_in_flight(fiber);
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        *err = janet_ev_lasterr();
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
@@ -207,12 +271,12 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    JanetListenerState head;
 | 
			
		||||
    JanetFunction *function;
 | 
			
		||||
} NetStateAccept;
 | 
			
		||||
 | 
			
		||||
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) {
 | 
			
		||||
    NetStateAccept *state = (NetStateAccept *)s;
 | 
			
		||||
void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
 | 
			
		||||
    JanetStream *stream = fiber->ev_stream;
 | 
			
		||||
    NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
 | 
			
		||||
    switch (event) {
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
@@ -221,44 +285,47 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_ASYNC_EVENT_CLOSE:
 | 
			
		||||
            janet_schedule(s->fiber, janet_wrap_nil());
 | 
			
		||||
            return JANET_ASYNC_STATUS_DONE;
 | 
			
		||||
            janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
            janet_async_end(fiber);
 | 
			
		||||
            return;
 | 
			
		||||
        case JANET_ASYNC_EVENT_INIT:
 | 
			
		||||
        case JANET_ASYNC_EVENT_READ: {
 | 
			
		||||
#if defined(JANET_LINUX)
 | 
			
		||||
            JSock connfd = accept4(s->stream->handle, NULL, NULL, SOCK_CLOEXEC);
 | 
			
		||||
            JSock connfd = accept4(stream->handle, NULL, NULL, SOCK_CLOEXEC);
 | 
			
		||||
#else
 | 
			
		||||
            /* On BSDs, CLOEXEC should be inherited from server socket */
 | 
			
		||||
            JSock connfd = accept(s->stream->handle, NULL, NULL);
 | 
			
		||||
            JSock connfd = accept(stream->handle, NULL, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
            if (JSOCKVALID(connfd)) {
 | 
			
		||||
                janet_net_socknoblock(connfd);
 | 
			
		||||
                JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
 | 
			
		||||
                Janet streamv = janet_wrap_abstract(stream);
 | 
			
		||||
                if (state->function) {
 | 
			
		||||
                    JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv);
 | 
			
		||||
                    fiber->supervisor_channel = s->fiber->supervisor_channel;
 | 
			
		||||
                    janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
                    JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
 | 
			
		||||
                    sub_fiber->supervisor_channel = fiber->supervisor_channel;
 | 
			
		||||
                    janet_schedule(sub_fiber, janet_wrap_nil());
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_schedule(s->fiber, streamv);
 | 
			
		||||
                    return JANET_ASYNC_STATUS_DONE;
 | 
			
		||||
                    janet_schedule(fiber, streamv);
 | 
			
		||||
                    janet_async_end(fiber);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return JANET_ASYNC_STATUS_NOT_DONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
 | 
			
		||||
    NetStateAccept *state = (NetStateAccept *) janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL);
 | 
			
		||||
    NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
 | 
			
		||||
    memset(state, 0, sizeof(NetStateAccept));
 | 
			
		||||
    state->function = fun;
 | 
			
		||||
    janet_await();
 | 
			
		||||
    if (fun) janet_stream_level_triggered(stream);
 | 
			
		||||
    janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Adress info */
 | 
			
		||||
/* Address info */
 | 
			
		||||
 | 
			
		||||
static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
 | 
			
		||||
    JanetKeyword stype = janet_optkeyword(argv, argc, n, NULL);
 | 
			
		||||
@@ -333,6 +400,7 @@ JANET_CORE_FN(cfun_net_sockaddr,
 | 
			
		||||
              "given in the port argument. On Linux, abstract "
 | 
			
		||||
              "unix domain sockets are specified with a leading '@' character in port. If `multi` is truthy, will "
 | 
			
		||||
              "return all address that match in an array instead of just the first.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_NET_CONNECT); /* connect OR listen */
 | 
			
		||||
    janet_arity(argc, 2, 4);
 | 
			
		||||
    int socktype = janet_get_sockettype(argv, argc, 2);
 | 
			
		||||
    int is_unix = 0;
 | 
			
		||||
@@ -378,6 +446,7 @@ JANET_CORE_FN(cfun_net_connect,
 | 
			
		||||
              "to specify a connection type, either :stream or :datagram. The default is :stream. "
 | 
			
		||||
              "Bindhost is an optional string to select from what address to make the outgoing "
 | 
			
		||||
              "connection, with the default being the same as using the OS's preferred address. ") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_NET_CONNECT);
 | 
			
		||||
    janet_arity(argc, 2, 5);
 | 
			
		||||
 | 
			
		||||
    /* Check arguments */
 | 
			
		||||
@@ -414,7 +483,6 @@ JANET_CORE_FN(cfun_net_connect,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /* Create socket */
 | 
			
		||||
    JSock sock = JSOCKDEFAULT;
 | 
			
		||||
    void *addr = NULL;
 | 
			
		||||
@@ -457,7 +525,7 @@ JANET_CORE_FN(cfun_net_connect,
 | 
			
		||||
    if (binding) {
 | 
			
		||||
        struct addrinfo *rp = NULL;
 | 
			
		||||
        int did_bind = 0;
 | 
			
		||||
        for (rp = ai; rp != NULL; rp = rp->ai_next) {
 | 
			
		||||
        for (rp = binding; rp != NULL; rp = rp->ai_next) {
 | 
			
		||||
            if (bind(sock, rp->ai_addr, (int) rp->ai_addrlen) == 0) {
 | 
			
		||||
                did_bind = 1;
 | 
			
		||||
                break;
 | 
			
		||||
@@ -474,14 +542,23 @@ JANET_CORE_FN(cfun_net_connect,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Wrap socket in abstract type JanetStream */
 | 
			
		||||
    JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
 | 
			
		||||
 | 
			
		||||
    /* Set up the socket for non-blocking IO before connecting */
 | 
			
		||||
    janet_net_socknoblock(sock);
 | 
			
		||||
 | 
			
		||||
    /* Connect to socket */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
 | 
			
		||||
    Janet lasterr = janet_ev_lasterr();
 | 
			
		||||
    int err = WSAGetLastError();
 | 
			
		||||
    freeaddrinfo(ai);
 | 
			
		||||
#else
 | 
			
		||||
    int status = connect(sock, addr, addrlen);
 | 
			
		||||
    Janet lasterr = janet_ev_lasterr();
 | 
			
		||||
    int status;
 | 
			
		||||
    do {
 | 
			
		||||
        status = connect(sock, addr, addrlen);
 | 
			
		||||
    } while (status == -1 && errno == EINTR);
 | 
			
		||||
    int err = errno;
 | 
			
		||||
    if (is_unix) {
 | 
			
		||||
        janet_free(ai);
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -489,30 +566,38 @@ JANET_CORE_FN(cfun_net_connect,
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    if (status == -1) {
 | 
			
		||||
        JSOCKCLOSE(sock);
 | 
			
		||||
        janet_panicf("could not connect socket: %V", lasterr);
 | 
			
		||||
    if (status) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
        if (err != WSAEWOULDBLOCK) {
 | 
			
		||||
#else
 | 
			
		||||
        if (err != EINPROGRESS) {
 | 
			
		||||
#endif
 | 
			
		||||
            JSOCKCLOSE(sock);
 | 
			
		||||
            Janet lasterr = janet_ev_lasterr();
 | 
			
		||||
            janet_panicf("could not connect socket: %V", lasterr);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set up the socket for non-blocking IO after connect - TODO - non-blocking connect? */
 | 
			
		||||
    janet_net_socknoblock(sock);
 | 
			
		||||
 | 
			
		||||
    /* Wrap socket in abstract type JanetStream */
 | 
			
		||||
    JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
 | 
			
		||||
    return janet_wrap_abstract(stream);
 | 
			
		||||
    net_sched_connect(stream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *serverify_socket(JSock sfd) {
 | 
			
		||||
static const char *serverify_socket(JSock sfd, int reuse_addr, int reuse_port) {
 | 
			
		||||
    /* Set various socket options */
 | 
			
		||||
    int enable = 1;
 | 
			
		||||
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
 | 
			
		||||
        return "setsockopt(SO_REUSEADDR) failed";
 | 
			
		||||
    if (reuse_addr) {
 | 
			
		||||
        if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
 | 
			
		||||
            return "setsockopt(SO_REUSEADDR) failed";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (reuse_port) {
 | 
			
		||||
#ifdef SO_REUSEPORT
 | 
			
		||||
    if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
 | 
			
		||||
        return "setsockopt(SO_REUSEPORT) failed";
 | 
			
		||||
    }
 | 
			
		||||
        if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
 | 
			
		||||
            return "setsockopt(SO_REUSEPORT) failed";
 | 
			
		||||
        }
 | 
			
		||||
#else
 | 
			
		||||
        (void) reuse_port;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    janet_net_socknoblock(sfd);
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -566,18 +651,21 @@ JANET_CORE_FN(cfun_net_shutdown,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_net_listen,
 | 
			
		||||
              "(net/listen host port &opt type)",
 | 
			
		||||
              "(net/listen host port &opt type no-reuse)",
 | 
			
		||||
              "Creates a server. Returns a new stream that is neither readable nor "
 | 
			
		||||
              "writeable. Use net/accept or net/accept-loop be to handle connections and start the server. "
 | 
			
		||||
              "The type parameter specifies the type of network connection, either "
 | 
			
		||||
              "a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is "
 | 
			
		||||
              ":stream. The host and port arguments are the same as in net/address.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
              ":stream. The host and port arguments are the same as in net/address. The last boolean parameter `no-reuse` will "
 | 
			
		||||
              "disable the use of SO_REUSEADDR and SO_REUSEPORT when creating a server on some operating systems.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_NET_LISTEN);
 | 
			
		||||
    janet_arity(argc, 2, 4);
 | 
			
		||||
 | 
			
		||||
    /* Get host, port, and handler*/
 | 
			
		||||
    int socktype = janet_get_sockettype(argv, argc, 2);
 | 
			
		||||
    int is_unix = 0;
 | 
			
		||||
    struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix);
 | 
			
		||||
    int reuse = !(argc >= 4 && janet_truthy(argv[3]));
 | 
			
		||||
 | 
			
		||||
    JSock sfd = JSOCKDEFAULT;
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
@@ -587,7 +675,7 @@ JANET_CORE_FN(cfun_net_listen,
 | 
			
		||||
            janet_free(ai);
 | 
			
		||||
            janet_panicf("could not create socket: %V", janet_ev_lasterr());
 | 
			
		||||
        }
 | 
			
		||||
        const char *err = serverify_socket(sfd);
 | 
			
		||||
        const char *err = serverify_socket(sfd, reuse, 0);
 | 
			
		||||
        if (NULL != err || bind(sfd, (struct sockaddr *)ai, sizeof(struct sockaddr_un))) {
 | 
			
		||||
            JSOCKCLOSE(sfd);
 | 
			
		||||
            janet_free(ai);
 | 
			
		||||
@@ -610,7 +698,7 @@ JANET_CORE_FN(cfun_net_listen,
 | 
			
		||||
            sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
 | 
			
		||||
#endif
 | 
			
		||||
            if (!JSOCKVALID(sfd)) continue;
 | 
			
		||||
            const char *err = serverify_socket(sfd);
 | 
			
		||||
            const char *err = serverify_socket(sfd, reuse, reuse);
 | 
			
		||||
            if (NULL != err) {
 | 
			
		||||
                JSOCKCLOSE(sfd);
 | 
			
		||||
                continue;
 | 
			
		||||
@@ -669,6 +757,7 @@ static Janet janet_so_getname(const void *sa_any) {
 | 
			
		||||
            Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai->sin_port))};
 | 
			
		||||
            return janet_wrap_tuple(janet_tuple_n(pair, 2));
 | 
			
		||||
        }
 | 
			
		||||
#ifndef JANET_NO_IPV6
 | 
			
		||||
        case AF_INET6: {
 | 
			
		||||
            const struct sockaddr_in6 *sai6 = sa_any;
 | 
			
		||||
            if (!inet_ntop(AF_INET6, &(sai6->sin6_addr), buffer, sizeof(buffer))) {
 | 
			
		||||
@@ -677,6 +766,7 @@ static Janet janet_so_getname(const void *sa_any) {
 | 
			
		||||
            Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai6->sin6_port))};
 | 
			
		||||
            return janet_wrap_tuple(janet_tuple_n(pair, 2));
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
        case AF_UNIX: {
 | 
			
		||||
            const struct sockaddr_un *sun = sa_any;
 | 
			
		||||
@@ -706,7 +796,7 @@ JANET_CORE_FN(cfun_net_getsockname,
 | 
			
		||||
    if (getsockname((JSock)js->handle, (struct sockaddr *) &ss, &slen)) {
 | 
			
		||||
        janet_panicf("Failed to get localname on %v: %V", argv[0], janet_ev_lasterr());
 | 
			
		||||
    }
 | 
			
		||||
    janet_assert(slen <= sizeof(ss), "socket address truncated");
 | 
			
		||||
    janet_assert(slen <= (socklen_t) sizeof(ss), "socket address truncated");
 | 
			
		||||
    return janet_so_getname(&ss);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -722,13 +812,13 @@ JANET_CORE_FN(cfun_net_getpeername,
 | 
			
		||||
    if (getpeername((JSock)js->handle, (struct sockaddr *)&ss, &slen)) {
 | 
			
		||||
        janet_panicf("Failed to get peername on %v: %V", argv[0], janet_ev_lasterr());
 | 
			
		||||
    }
 | 
			
		||||
    janet_assert(slen <= sizeof(ss), "socket address truncated");
 | 
			
		||||
    janet_assert(slen <= (socklen_t) sizeof(ss), "socket address truncated");
 | 
			
		||||
    return janet_so_getname(&ss);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_net_address_unpack,
 | 
			
		||||
              "(net/address-unpack address)",
 | 
			
		||||
              "Given an address returned by net/adress, return a host, port pair. Unix domain sockets "
 | 
			
		||||
              "Given an address returned by net/address, return a host, port pair. Unix domain sockets "
 | 
			
		||||
              "will have only the path in the returned tuple.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    struct sockaddr *sa = janet_getabstract(argv, 0, &janet_address_type);
 | 
			
		||||
@@ -743,13 +833,14 @@ JANET_CORE_FN(cfun_stream_accept_loop,
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
    janet_stream_flags(stream, JANET_STREAM_ACCEPTABLE | JANET_STREAM_SOCKET);
 | 
			
		||||
    JanetFunction *fun = janet_getfunction(argv, 1);
 | 
			
		||||
    if (fun->def->min_arity < 1) janet_panic("handler function must take at least 1 argument");
 | 
			
		||||
    janet_sched_accept(stream, fun);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_accept,
 | 
			
		||||
              "(net/accept stream &opt timeout)",
 | 
			
		||||
              "Get the next connection on a server stream. This would usually be called in a loop in a dedicated fiber. "
 | 
			
		||||
              "Takes an optional timeout in seconds, after which will return nil. "
 | 
			
		||||
              "Takes an optional timeout in seconds, after which will raise an error. "
 | 
			
		||||
              "Returns a new duplex stream which represents a connection to the client.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
@@ -764,7 +855,7 @@ JANET_CORE_FN(cfun_stream_read,
 | 
			
		||||
              "Read up to n bytes from a stream, suspending the current fiber until the bytes are available. "
 | 
			
		||||
              "`n` can also be the keyword `:all` to read into the buffer until end of stream. "
 | 
			
		||||
              "If less than n bytes are available (and more than 0), will push those bytes and return early. "
 | 
			
		||||
              "Takes an optional timeout in seconds, after which will return nil. "
 | 
			
		||||
              "Takes an optional timeout in seconds, after which will raise an error. "
 | 
			
		||||
              "Returns a buffer with up to n more bytes in it, or raises an error if the read failed.") {
 | 
			
		||||
    janet_arity(argc, 2, 4);
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
@@ -779,13 +870,12 @@ JANET_CORE_FN(cfun_stream_read,
 | 
			
		||||
        if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
        janet_ev_recv(stream, buffer, n, MSG_NOSIGNAL);
 | 
			
		||||
    }
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_chunk,
 | 
			
		||||
              "(net/chunk stream nbytes &opt buf timeout)",
 | 
			
		||||
              "Same a net/read, but will wait for all n bytes to arrive rather than return early. "
 | 
			
		||||
              "Takes an optional timeout in seconds, after which will return nil.") {
 | 
			
		||||
              "Takes an optional timeout in seconds, after which will raise an error.") {
 | 
			
		||||
    janet_arity(argc, 2, 4);
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
    janet_stream_flags(stream, JANET_STREAM_READABLE | JANET_STREAM_SOCKET);
 | 
			
		||||
@@ -794,13 +884,12 @@ JANET_CORE_FN(cfun_stream_chunk,
 | 
			
		||||
    double to = janet_optnumber(argv, argc, 3, INFINITY);
 | 
			
		||||
    if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
    janet_ev_recvchunk(stream, buffer, n, MSG_NOSIGNAL);
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_recv_from,
 | 
			
		||||
              "(net/recv-from stream nbytes buf &opt timoeut)",
 | 
			
		||||
              "(net/recv-from stream nbytes buf &opt timeout)",
 | 
			
		||||
              "Receives data from a server stream and puts it into a buffer. Returns the socket-address the "
 | 
			
		||||
              "packet came from. Takes an optional timeout in seconds, after which will return nil.") {
 | 
			
		||||
              "packet came from. Takes an optional timeout in seconds, after which will raise an error.") {
 | 
			
		||||
    janet_arity(argc, 3, 4);
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
    janet_stream_flags(stream, JANET_STREAM_UDPSERVER | JANET_STREAM_SOCKET);
 | 
			
		||||
@@ -809,13 +898,12 @@ JANET_CORE_FN(cfun_stream_recv_from,
 | 
			
		||||
    double to = janet_optnumber(argv, argc, 3, INFINITY);
 | 
			
		||||
    if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
    janet_ev_recvfrom(stream, buffer, n, MSG_NOSIGNAL);
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_write,
 | 
			
		||||
              "(net/write stream data &opt timeout)",
 | 
			
		||||
              "Write data to a stream, suspending the current fiber until the write "
 | 
			
		||||
              "completes. Takes an optional timeout in seconds, after which will return nil. "
 | 
			
		||||
              "completes. Takes an optional timeout in seconds, after which will raise an error. "
 | 
			
		||||
              "Returns nil, or raises an error if the write failed.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
@@ -829,13 +917,12 @@ JANET_CORE_FN(cfun_stream_write,
 | 
			
		||||
        if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
        janet_ev_send_string(stream, bytes.bytes, MSG_NOSIGNAL);
 | 
			
		||||
    }
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_send_to,
 | 
			
		||||
              "(net/send-to stream dest data &opt timeout)",
 | 
			
		||||
              "Writes a datagram to a server stream. dest is a the destination address of the packet. "
 | 
			
		||||
              "Takes an optional timeout in seconds, after which will return nil. "
 | 
			
		||||
              "Takes an optional timeout in seconds, after which will raise an error. "
 | 
			
		||||
              "Returns stream.") {
 | 
			
		||||
    janet_arity(argc, 3, 4);
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
@@ -850,7 +937,6 @@ JANET_CORE_FN(cfun_stream_send_to,
 | 
			
		||||
        if (to != INFINITY) janet_addtimeout(to);
 | 
			
		||||
        janet_ev_sendto_string(stream, bytes.bytes, dest, MSG_NOSIGNAL);
 | 
			
		||||
    }
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_stream_flush,
 | 
			
		||||
@@ -868,6 +954,104 @@ JANET_CORE_FN(cfun_stream_flush,
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct sockopt_type {
 | 
			
		||||
    const char *name;
 | 
			
		||||
    int level;
 | 
			
		||||
    int optname;
 | 
			
		||||
    enum JanetType type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* List of supported socket options; The type JANET_POINTER is used
 | 
			
		||||
 * for options that require special handling depending on the type. */
 | 
			
		||||
static const struct sockopt_type sockopt_type_list[] = {
 | 
			
		||||
    { "so-broadcast", SOL_SOCKET, SO_BROADCAST, JANET_BOOLEAN },
 | 
			
		||||
    { "so-reuseaddr", SOL_SOCKET, SO_REUSEADDR, JANET_BOOLEAN },
 | 
			
		||||
    { "so-keepalive", SOL_SOCKET, SO_KEEPALIVE, JANET_BOOLEAN },
 | 
			
		||||
    { "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER },
 | 
			
		||||
    { "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER },
 | 
			
		||||
    { "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER },
 | 
			
		||||
#ifndef JANET_NO_IPV6
 | 
			
		||||
    { "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
 | 
			
		||||
    { "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
 | 
			
		||||
#endif
 | 
			
		||||
    { NULL, 0, 0, JANET_POINTER }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_net_setsockopt,
 | 
			
		||||
              "(net/setsockopt stream option value)",
 | 
			
		||||
              "set socket options.\n"
 | 
			
		||||
              "\n"
 | 
			
		||||
              "supported options and associated value types:\n"
 | 
			
		||||
              "- :so-broadcast boolean\n"
 | 
			
		||||
              "- :so-reuseaddr boolean\n"
 | 
			
		||||
              "- :so-keepalive boolean\n"
 | 
			
		||||
              "- :ip-multicast-ttl number\n"
 | 
			
		||||
              "- :ip-add-membership string\n"
 | 
			
		||||
              "- :ip-drop-membership string\n"
 | 
			
		||||
              "- :ipv6-join-group string\n"
 | 
			
		||||
              "- :ipv6-leave-group string\n") {
 | 
			
		||||
    janet_arity(argc, 3, 3);
 | 
			
		||||
    JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
 | 
			
		||||
    janet_stream_flags(stream, JANET_STREAM_SOCKET);
 | 
			
		||||
    JanetKeyword optstr = janet_getkeyword(argv, 1);
 | 
			
		||||
 | 
			
		||||
    const struct sockopt_type *st = sockopt_type_list;
 | 
			
		||||
    while (st->name) {
 | 
			
		||||
        if (janet_cstrcmp(optstr, st->name) == 0) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        st++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (st->name == NULL) {
 | 
			
		||||
        janet_panicf("unknown socket option %q", argv[1]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    union {
 | 
			
		||||
        int v_int;
 | 
			
		||||
        struct ip_mreq v_mreq;
 | 
			
		||||
#ifndef JANET_NO_IPV6
 | 
			
		||||
        struct ipv6_mreq v_mreq6;
 | 
			
		||||
#endif
 | 
			
		||||
    } val;
 | 
			
		||||
 | 
			
		||||
    void *optval = (void *)&val;
 | 
			
		||||
    socklen_t optlen = 0;
 | 
			
		||||
 | 
			
		||||
    if (st->type == JANET_BOOLEAN) {
 | 
			
		||||
        val.v_int = janet_getboolean(argv, 2);
 | 
			
		||||
        optlen = sizeof(val.v_int);
 | 
			
		||||
    } else if (st->type == JANET_NUMBER) {
 | 
			
		||||
        val.v_int = janet_getinteger(argv, 2);
 | 
			
		||||
        optlen = sizeof(val.v_int);
 | 
			
		||||
    } else if (st->optname == IP_ADD_MEMBERSHIP || st->optname == IP_DROP_MEMBERSHIP) {
 | 
			
		||||
        const char *addr = janet_getcstring(argv, 2);
 | 
			
		||||
        memset(&val.v_mreq, 0, sizeof val.v_mreq);
 | 
			
		||||
        val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY);
 | 
			
		||||
        inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr);
 | 
			
		||||
        optlen = sizeof(val.v_mreq);
 | 
			
		||||
#ifndef JANET_NO_IPV6
 | 
			
		||||
    } else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) {
 | 
			
		||||
        const char *addr = janet_getcstring(argv, 2);
 | 
			
		||||
        memset(&val.v_mreq6, 0, sizeof val.v_mreq6);
 | 
			
		||||
        val.v_mreq6.ipv6mr_interface = 0;
 | 
			
		||||
        inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr);
 | 
			
		||||
        optlen = sizeof(val.v_mreq6);
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panicf("invalid socket option type");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    janet_assert(optlen != 0, "invalid socket option value");
 | 
			
		||||
 | 
			
		||||
    int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen);
 | 
			
		||||
    if (r == -1) {
 | 
			
		||||
        janet_panicf("setsockopt(%q): %s", argv[1], janet_strerror(errno));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetMethod net_stream_methods[] = {
 | 
			
		||||
    {"chunk", cfun_stream_chunk},
 | 
			
		||||
    {"close", janet_cfun_stream_close},
 | 
			
		||||
@@ -882,6 +1066,7 @@ static const JanetMethod net_stream_methods[] = {
 | 
			
		||||
    {"evchunk", janet_cfun_stream_chunk},
 | 
			
		||||
    {"evwrite", janet_cfun_stream_write},
 | 
			
		||||
    {"shutdown", cfun_net_shutdown},
 | 
			
		||||
    {"setsockopt", cfun_net_setsockopt},
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -906,6 +1091,7 @@ void janet_lib_net(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("net/peername", cfun_net_getpeername),
 | 
			
		||||
        JANET_CORE_REG("net/localname", cfun_net_getsockname),
 | 
			
		||||
        JANET_CORE_REG("net/address-unpack", cfun_net_address_unpack),
 | 
			
		||||
        JANET_CORE_REG("net/setsockopt", cfun_net_setsockopt),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, net_cfuns);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										848
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										848
									
								
								src/core/os.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -231,7 +231,7 @@ static void delim_error(JanetParser *parser, size_t stack_index, char c, const c
 | 
			
		||||
                janet_buffer_push_u8(buffer, '`');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
 | 
			
		||||
        janet_formatb(buffer, " opened at line %d, column %d", (int32_t) s->line, (int32_t) s->column);
 | 
			
		||||
    }
 | 
			
		||||
    parser->error = (const char *) janet_string(buffer->data, buffer->count);
 | 
			
		||||
    parser->flag |= JANET_PARSER_GENERATED_ERROR;
 | 
			
		||||
@@ -259,6 +259,14 @@ static int checkescape(uint8_t c) {
 | 
			
		||||
            return '\f';
 | 
			
		||||
        case 'v':
 | 
			
		||||
            return '\v';
 | 
			
		||||
        case 'a':
 | 
			
		||||
            return '\a';
 | 
			
		||||
        case 'b':
 | 
			
		||||
            return '\b';
 | 
			
		||||
        case '\'':
 | 
			
		||||
            return '\'';
 | 
			
		||||
        case '?':
 | 
			
		||||
            return '?';
 | 
			
		||||
        case 'e':
 | 
			
		||||
            return 27;
 | 
			
		||||
        case '"':
 | 
			
		||||
@@ -355,8 +363,7 @@ static int stringend(JanetParser *p, JanetParseState *state) {
 | 
			
		||||
        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. */
 | 
			
		||||
        /* Unless there are only spaces before EOLs, disable reindenting */
 | 
			
		||||
        int reindent = 1;
 | 
			
		||||
        while (reindent && (r < end)) {
 | 
			
		||||
            if (*r++ == '\n') {
 | 
			
		||||
@@ -366,34 +373,36 @@ static int stringend(JanetParser *p, JanetParseState *state) {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if ((r + 1) < end && *r == '\r' && *(r + 1) == '\n') reindent = 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        /* Now reindent if able to, otherwise just drop leading newline. */
 | 
			
		||||
        if (!reindent) {
 | 
			
		||||
            if (buflen > 0 && bufstart[0] == '\n') {
 | 
			
		||||
                buflen--;
 | 
			
		||||
                bufstart++;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
        /* Now reindent if able */
 | 
			
		||||
        if (reindent) {
 | 
			
		||||
            uint8_t *w = bufstart;
 | 
			
		||||
            r = bufstart;
 | 
			
		||||
            while (r < end) {
 | 
			
		||||
                if (*r == '\n') {
 | 
			
		||||
                    if (r == bufstart) {
 | 
			
		||||
                        /* Skip leading newline */
 | 
			
		||||
                        r++;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        *w++ = *r++;
 | 
			
		||||
                    }
 | 
			
		||||
                    *w++ = *r++;
 | 
			
		||||
                    for (int32_t j = 0; (r < end) && (*r != '\n') && (j < indent_col); j++, r++);
 | 
			
		||||
                    if ((r + 1) < end && *r == '\r' && *(r + 1) == '\n') *w++ = *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') {
 | 
			
		||||
        /* Check for leading EOL so we can remove it */
 | 
			
		||||
        if (buflen > 1 && bufstart[0] == '\r' && bufstart[1] == '\n') { /* Windows EOL */
 | 
			
		||||
            buflen = buflen - 2;
 | 
			
		||||
            bufstart = bufstart + 2;
 | 
			
		||||
        } else if (buflen > 0 && bufstart[0] == '\n') { /* Unix EOL */
 | 
			
		||||
            buflen--;
 | 
			
		||||
            bufstart++;
 | 
			
		||||
        }
 | 
			
		||||
        /* Check for trailing EOL so we can remove it */
 | 
			
		||||
        if (buflen > 1 && bufstart[buflen - 2] == '\r' && bufstart[buflen - 1] == '\n') { /* Windows EOL */
 | 
			
		||||
            buflen = buflen - 2;
 | 
			
		||||
        } else if (buflen > 0 && bufstart[buflen - 1] == '\n') { /* Unix EOL */
 | 
			
		||||
            buflen--;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -459,8 +468,13 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        ret = janet_keywordv(p->buf + 1, blen - 1);
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
    } else if (start_num && !janet_scan_numeric(p->buf, blen, &ret)) {
 | 
			
		||||
        (void) numval;
 | 
			
		||||
#else
 | 
			
		||||
    } else if (start_num && !janet_scan_number(p->buf, blen, &numval)) {
 | 
			
		||||
        ret = janet_wrap_number(numval);
 | 
			
		||||
#endif
 | 
			
		||||
    } else if (!check_str_const("nil", p->buf, blen)) {
 | 
			
		||||
        ret = janet_wrap_nil();
 | 
			
		||||
    } else if (!check_str_const("false", p->buf, blen)) {
 | 
			
		||||
@@ -1194,7 +1208,8 @@ static Janet parser_state_delimiters(const JanetParser *_p) {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    str = janet_string(p->buf + oldcount, (int32_t)(p->bufcount - oldcount));
 | 
			
		||||
    /* avoid ptr arithmetic on NULL */
 | 
			
		||||
    str = janet_string(oldcount ? p->buf + oldcount : p->buf, (int32_t)(p->bufcount - oldcount));
 | 
			
		||||
    p->bufcount = oldcount;
 | 
			
		||||
    return janet_wrap_string(str);
 | 
			
		||||
}
 | 
			
		||||
@@ -1205,10 +1220,11 @@ static Janet parser_state_frames(const JanetParser *p) {
 | 
			
		||||
    states->count = count;
 | 
			
		||||
    uint8_t *buf = p->buf;
 | 
			
		||||
    /* Iterate arg stack backwards */
 | 
			
		||||
    Janet *args = p->args + p->argcount;
 | 
			
		||||
    Janet *args = p->argcount ? p->args + p->argcount : p->args; /* avoid ptr arithmetic on NULL */
 | 
			
		||||
    for (int32_t i = count - 1; i >= 0; --i) {
 | 
			
		||||
        JanetParseState *s = p->states + i;
 | 
			
		||||
        if (s->flags & PFLAG_CONTAINER) {
 | 
			
		||||
        /* avoid ptr arithmetic on args if NULL */
 | 
			
		||||
        if ((s->flags & PFLAG_CONTAINER) && s->argn) {
 | 
			
		||||
            args -= s->argn;
 | 
			
		||||
        }
 | 
			
		||||
        states->data[i] = janet_wrap_parse_state(s, args, buf, (uint32_t) p->bufcount);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										261
									
								
								src/core/peg.c
									
									
									
									
									
								
							
							
						
						
									
										261
									
								
								src/core/peg.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -39,6 +39,10 @@
 | 
			
		||||
typedef struct {
 | 
			
		||||
    const uint8_t *text_start;
 | 
			
		||||
    const uint8_t *text_end;
 | 
			
		||||
    /* text_end can be restricted by some rules, but
 | 
			
		||||
       outer_text_end will always contain the real end of
 | 
			
		||||
       input, which we need to generate a line mapping */
 | 
			
		||||
    const uint8_t *outer_text_end;
 | 
			
		||||
    const uint32_t *bytecode;
 | 
			
		||||
    const Janet *constants;
 | 
			
		||||
    JanetArray *captures;
 | 
			
		||||
@@ -114,12 +118,12 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) {
 | 
			
		||||
    /* Generate if not made yet */
 | 
			
		||||
    if (s->linemaplen < 0) {
 | 
			
		||||
        int32_t newline_count = 0;
 | 
			
		||||
        for (const uint8_t *c = s->text_start; c < s->text_end; c++) {
 | 
			
		||||
        for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
 | 
			
		||||
            if (*c == '\n') newline_count++;
 | 
			
		||||
        }
 | 
			
		||||
        int32_t *mem = janet_smalloc(sizeof(int32_t) * newline_count);
 | 
			
		||||
        size_t index = 0;
 | 
			
		||||
        for (const uint8_t *c = s->text_start; c < s->text_end; c++) {
 | 
			
		||||
        for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
 | 
			
		||||
            if (*c == '\n') mem[index++] = (int32_t)(c - s->text_start);
 | 
			
		||||
        }
 | 
			
		||||
        s->linemaplen = newline_count;
 | 
			
		||||
@@ -130,7 +134,7 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) {
 | 
			
		||||
     *   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.
 | 
			
		||||
     *   is before position. we use that to calculate 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 */
 | 
			
		||||
@@ -179,7 +183,7 @@ static const uint8_t *peg_rule(
 | 
			
		||||
    const uint32_t *rule,
 | 
			
		||||
    const uint8_t *text) {
 | 
			
		||||
tail:
 | 
			
		||||
    switch (*rule & 0x1F) {
 | 
			
		||||
    switch (*rule) {
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panic("unexpected opcode");
 | 
			
		||||
            return NULL;
 | 
			
		||||
@@ -306,7 +310,7 @@ tail:
 | 
			
		||||
        case RULE_THRU:
 | 
			
		||||
        case RULE_TO: {
 | 
			
		||||
            const uint32_t *rule_a = s->bytecode + rule[1];
 | 
			
		||||
            const uint8_t *next_text;
 | 
			
		||||
            const uint8_t *next_text = NULL;
 | 
			
		||||
            CapState cs = cap_save(s);
 | 
			
		||||
            down1(s);
 | 
			
		||||
            while (text <= s->text_end) {
 | 
			
		||||
@@ -338,7 +342,7 @@ tail:
 | 
			
		||||
            while (captured < hi) {
 | 
			
		||||
                CapState cs2 = cap_save(s);
 | 
			
		||||
                next_text = peg_rule(s, rule_a, text);
 | 
			
		||||
                if (!next_text || next_text == text) {
 | 
			
		||||
                if (!next_text || ((next_text == text) && (hi == UINT32_MAX))) {
 | 
			
		||||
                    cap_load(s, cs2);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -461,6 +465,16 @@ tail:
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_ONLY_TAGS: {
 | 
			
		||||
            CapState cs = cap_save(s);
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            cap_load_keept(s, cs);
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_GROUP: {
 | 
			
		||||
            uint32_t tag = rule[2];
 | 
			
		||||
            int oldmode = s->mode;
 | 
			
		||||
@@ -482,6 +496,131 @@ tail:
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_NTH: {
 | 
			
		||||
            uint32_t nth = rule[1];
 | 
			
		||||
            if (nth > INT32_MAX) nth = INT32_MAX;
 | 
			
		||||
            uint32_t tag = rule[3];
 | 
			
		||||
            int oldmode = s->mode;
 | 
			
		||||
            CapState cs = cap_save(s);
 | 
			
		||||
            s->mode = PEG_MODE_NORMAL;
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, s->bytecode + rule[2], text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            s->mode = oldmode;
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            int32_t num_sub_captures = s->captures->count - cs.cap;
 | 
			
		||||
            Janet cap;
 | 
			
		||||
            if (num_sub_captures > (int32_t) nth) {
 | 
			
		||||
                cap = s->captures->data[cs.cap + nth];
 | 
			
		||||
            } else {
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
            cap_load_keept(s, cs);
 | 
			
		||||
            pushcap(s, cap, tag);
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_SUB: {
 | 
			
		||||
            const uint8_t *text_start = text;
 | 
			
		||||
            const uint32_t *rule_window = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_subpattern = s->bytecode + rule[2];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *window_end = peg_rule(s, rule_window, text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (!window_end) {
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
            const uint8_t *saved_end = s->text_end;
 | 
			
		||||
            s->text_end = window_end;
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *next_text = peg_rule(s, rule_subpattern, text_start);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            s->text_end = saved_end;
 | 
			
		||||
 | 
			
		||||
            if (!next_text) {
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return window_end;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_TIL: {
 | 
			
		||||
            const uint32_t *rule_terminus = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_subpattern = s->bytecode + rule[2];
 | 
			
		||||
 | 
			
		||||
            const uint8_t *terminus_start = text;
 | 
			
		||||
            const uint8_t *terminus_end = NULL;
 | 
			
		||||
            down1(s);
 | 
			
		||||
            while (terminus_start <= s->text_end) {
 | 
			
		||||
                CapState cs2 = cap_save(s);
 | 
			
		||||
                terminus_end = peg_rule(s, rule_terminus, terminus_start);
 | 
			
		||||
                cap_load(s, cs2);
 | 
			
		||||
                if (terminus_end) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                terminus_start++;
 | 
			
		||||
            }
 | 
			
		||||
            up1(s);
 | 
			
		||||
 | 
			
		||||
            if (!terminus_end) {
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const uint8_t *saved_end = s->text_end;
 | 
			
		||||
            s->text_end = terminus_start;
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *matched = peg_rule(s, rule_subpattern, text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            s->text_end = saved_end;
 | 
			
		||||
 | 
			
		||||
            if (!matched) {
 | 
			
		||||
                return NULL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return terminus_end;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_SPLIT: {
 | 
			
		||||
            const uint8_t *saved_end = s->text_end;
 | 
			
		||||
            const uint32_t *rule_separator = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_subpattern = s->bytecode + rule[2];
 | 
			
		||||
 | 
			
		||||
            const uint8_t *chunk_start = text;
 | 
			
		||||
            const uint8_t *chunk_end = NULL;
 | 
			
		||||
 | 
			
		||||
            while (text <= saved_end) {
 | 
			
		||||
                /* Find next split (or end of text) */
 | 
			
		||||
                CapState cs = cap_save(s);
 | 
			
		||||
                down1(s);
 | 
			
		||||
                while (text <= saved_end) {
 | 
			
		||||
                    chunk_end = text;
 | 
			
		||||
                    const uint8_t *check = peg_rule(s, rule_separator, text);
 | 
			
		||||
                    cap_load(s, cs);
 | 
			
		||||
                    if (check) {
 | 
			
		||||
                        text = check;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    text++;
 | 
			
		||||
                }
 | 
			
		||||
                up1(s);
 | 
			
		||||
 | 
			
		||||
                /* Match between splits */
 | 
			
		||||
                s->text_end = chunk_end;
 | 
			
		||||
                down1(s);
 | 
			
		||||
                const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, chunk_start);
 | 
			
		||||
                up1(s);
 | 
			
		||||
                s->text_end = saved_end;
 | 
			
		||||
                if (!subpattern_end) return NULL; /* Don't match anything */
 | 
			
		||||
 | 
			
		||||
                /* Ensure forward progress */
 | 
			
		||||
                if (text == chunk_start) return NULL;
 | 
			
		||||
                chunk_start = text;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            s->text_end = saved_end;
 | 
			
		||||
            return s->text_end;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_REPLACE:
 | 
			
		||||
        case RULE_MATCHTIME: {
 | 
			
		||||
            uint32_t tag = rule[3];
 | 
			
		||||
@@ -601,11 +740,11 @@ tail:
 | 
			
		||||
        case RULE_READINT: {
 | 
			
		||||
            uint32_t tag = rule[2];
 | 
			
		||||
            uint32_t signedness = rule[1] & 0x10;
 | 
			
		||||
            uint32_t endianess = rule[1] & 0x20;
 | 
			
		||||
            uint32_t endianness = rule[1] & 0x20;
 | 
			
		||||
            int width = (int)(rule[1] & 0xF);
 | 
			
		||||
            if (text + width > s->text_end) return NULL;
 | 
			
		||||
            uint64_t accum = 0;
 | 
			
		||||
            if (endianess) {
 | 
			
		||||
            if (endianness) {
 | 
			
		||||
                /* BE */
 | 
			
		||||
                for (int i = 0; i < width; i++) accum = (accum << 8) | text[i];
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -995,6 +1134,9 @@ static void spec_thru(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
static void spec_drop(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_onerule(b, argc, argv, RULE_DROP);
 | 
			
		||||
}
 | 
			
		||||
static void spec_only_tags(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_onerule(b, argc, argv, RULE_ONLY_TAGS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Rule of the form [rule, tag] */
 | 
			
		||||
static void spec_cap1(Builder *b, int32_t argc, const Janet *argv, uint32_t op) {
 | 
			
		||||
@@ -1018,6 +1160,15 @@ static void spec_unref(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    spec_cap1(b, argc, argv, RULE_UNREF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_nth(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_arity(b, argc, 2, 3);
 | 
			
		||||
    Reserve r = reserve(b, 4);
 | 
			
		||||
    uint32_t nth = peg_getnat(b, argv[0]);
 | 
			
		||||
    uint32_t rule = peg_compile1(b, argv[1]);
 | 
			
		||||
    uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
 | 
			
		||||
    emit_3(r, RULE_NTH, nth, rule, tag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_arity(b, argc, 1, 3);
 | 
			
		||||
    Reserve r = reserve(b, 4);
 | 
			
		||||
@@ -1034,7 +1185,7 @@ static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    emit_3(r, RULE_CAPTURE_NUM, rule, base, tag);
 | 
			
		||||
    return;
 | 
			
		||||
error:
 | 
			
		||||
    peg_panicf(b, "expected integer between 2 and 36, got %v", argv[2]);
 | 
			
		||||
    peg_panicf(b, "expected integer between 2 and 36, got %v", argv[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_reference(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
@@ -1100,13 +1251,37 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    Janet fun = argv[1];
 | 
			
		||||
    if (!janet_checktype(fun, JANET_FUNCTION) &&
 | 
			
		||||
            !janet_checktype(fun, JANET_CFUNCTION)) {
 | 
			
		||||
        peg_panicf(b, "expected function|cfunction, got %v", fun);
 | 
			
		||||
        peg_panicf(b, "expected function or cfunction, got %v", fun);
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
 | 
			
		||||
    uint32_t cindex = emit_constant(b, fun);
 | 
			
		||||
    emit_3(r, RULE_MATCHTIME, subrule, cindex, tag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_fixarity(b, argc, 2);
 | 
			
		||||
    Reserve r = reserve(b, 3);
 | 
			
		||||
    uint32_t subrule1 = peg_compile1(b, argv[0]);
 | 
			
		||||
    uint32_t subrule2 = peg_compile1(b, argv[1]);
 | 
			
		||||
    emit_2(r, RULE_SUB, subrule1, subrule2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_til(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_fixarity(b, argc, 2);
 | 
			
		||||
    Reserve r = reserve(b, 3);
 | 
			
		||||
    uint32_t subrule1 = peg_compile1(b, argv[0]);
 | 
			
		||||
    uint32_t subrule2 = peg_compile1(b, argv[1]);
 | 
			
		||||
    emit_2(r, RULE_TIL, subrule1, subrule2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_split(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_fixarity(b, argc, 2);
 | 
			
		||||
    Reserve r = reserve(b, 3);
 | 
			
		||||
    uint32_t subrule1 = peg_compile1(b, argv[0]);
 | 
			
		||||
    uint32_t subrule2 = peg_compile1(b, argv[1]);
 | 
			
		||||
    emit_2(r, RULE_SPLIT, subrule1, subrule2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
#define JANET_MAX_READINT_WIDTH 8
 | 
			
		||||
#else
 | 
			
		||||
@@ -1180,7 +1355,9 @@ static const SpecialPair peg_specials[] = {
 | 
			
		||||
    {"line", spec_line},
 | 
			
		||||
    {"look", spec_look},
 | 
			
		||||
    {"not", spec_not},
 | 
			
		||||
    {"nth", spec_nth},
 | 
			
		||||
    {"number", spec_capture_number},
 | 
			
		||||
    {"only-tags", spec_only_tags},
 | 
			
		||||
    {"opt", spec_opt},
 | 
			
		||||
    {"position", spec_position},
 | 
			
		||||
    {"quote", spec_capture},
 | 
			
		||||
@@ -1190,7 +1367,10 @@ static const SpecialPair peg_specials[] = {
 | 
			
		||||
    {"sequence", spec_sequence},
 | 
			
		||||
    {"set", spec_set},
 | 
			
		||||
    {"some", spec_some},
 | 
			
		||||
    {"split", spec_split},
 | 
			
		||||
    {"sub", spec_sub},
 | 
			
		||||
    {"thru", spec_thru},
 | 
			
		||||
    {"til", spec_til},
 | 
			
		||||
    {"to", spec_to},
 | 
			
		||||
    {"uint", spec_uint_le},
 | 
			
		||||
    {"uint-be", spec_uint_be},
 | 
			
		||||
@@ -1261,6 +1441,13 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
 | 
			
		||||
        default:
 | 
			
		||||
            peg_panic(b, "unexpected peg source");
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        case JANET_BOOLEAN: {
 | 
			
		||||
            int n = janet_unwrap_boolean(peg);
 | 
			
		||||
            Reserve r = reserve(b, 2);
 | 
			
		||||
            emit_1(r, n ? RULE_NCHAR : RULE_NOTNCHAR, 0);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_NUMBER: {
 | 
			
		||||
            int32_t n = peg_getinteger(b, peg);
 | 
			
		||||
            Reserve r = reserve(b, 2);
 | 
			
		||||
@@ -1277,6 +1464,11 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
 | 
			
		||||
            emit_bytes(b, RULE_LITERAL, len, str);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_BUFFER: {
 | 
			
		||||
            const JanetBuffer *buf = janet_unwrap_buffer(peg);
 | 
			
		||||
            emit_bytes(b, RULE_LITERAL, buf->count, buf->data);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_TABLE: {
 | 
			
		||||
            /* Build grammar table */
 | 
			
		||||
            JanetTable *new_grammar = janet_table_clone(janet_unwrap_table(peg));
 | 
			
		||||
@@ -1424,7 +1616,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
        uint32_t instr = bytecode[i];
 | 
			
		||||
        uint32_t *rule = bytecode + i;
 | 
			
		||||
        op_flags[i] |= 0x02;
 | 
			
		||||
        switch (instr & 0x1F) {
 | 
			
		||||
        switch (instr) {
 | 
			
		||||
            case RULE_LITERAL:
 | 
			
		||||
                i += 2 + ((rule[1] + 3) >> 2);
 | 
			
		||||
                break;
 | 
			
		||||
@@ -1517,8 +1709,19 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
                op_flags[rule[1]] |= 0x01;
 | 
			
		||||
                i += 4;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_SUB:
 | 
			
		||||
            case RULE_TIL:
 | 
			
		||||
            case RULE_SPLIT:
 | 
			
		||||
                /* [rule, rule] */
 | 
			
		||||
                if (rule[1] >= blen) goto bad;
 | 
			
		||||
                if (rule[2] >= blen) goto bad;
 | 
			
		||||
                op_flags[rule[1]] |= 0x01;
 | 
			
		||||
                op_flags[rule[2]] |= 0x01;
 | 
			
		||||
                i += 3;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_ERROR:
 | 
			
		||||
            case RULE_DROP:
 | 
			
		||||
            case RULE_ONLY_TAGS:
 | 
			
		||||
            case RULE_NOT:
 | 
			
		||||
            case RULE_TO:
 | 
			
		||||
            case RULE_THRU:
 | 
			
		||||
@@ -1528,10 +1731,16 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
                i += 2;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_READINT:
 | 
			
		||||
                /* [ width | (endianess << 5) | (signedness << 6), tag ] */
 | 
			
		||||
                /* [ width | (endianness << 5) | (signedness << 6), tag ] */
 | 
			
		||||
                if (rule[1] > JANET_MAX_READINT_WIDTH) goto bad;
 | 
			
		||||
                i += 3;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_NTH:
 | 
			
		||||
                /* [nth, rule, tag] */
 | 
			
		||||
                if (rule[2] >= blen) goto bad;
 | 
			
		||||
                op_flags[rule[2]] |= 0x01;
 | 
			
		||||
                i += 4;
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                goto bad;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1625,7 +1834,7 @@ static JanetPeg *compile_peg(Janet x) {
 | 
			
		||||
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 "
 | 
			
		||||
              "if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to supplement "
 | 
			
		||||
              "the grammar of the peg for otherwise undefined peg keywords.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetPeg *peg = compile_peg(argv[0]);
 | 
			
		||||
@@ -1637,7 +1846,7 @@ typedef struct {
 | 
			
		||||
    JanetPeg *peg;
 | 
			
		||||
    PegState s;
 | 
			
		||||
    JanetByteView bytes;
 | 
			
		||||
    JanetByteView repl;
 | 
			
		||||
    Janet subst;
 | 
			
		||||
    int32_t start;
 | 
			
		||||
} PegCall;
 | 
			
		||||
 | 
			
		||||
@@ -1645,7 +1854,7 @@ typedef struct {
 | 
			
		||||
static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
 | 
			
		||||
    PegCall ret;
 | 
			
		||||
    int32_t min = get_replace ? 3 : 2;
 | 
			
		||||
    janet_arity(argc, get_replace, -1);
 | 
			
		||||
    janet_arity(argc, min, -1);
 | 
			
		||||
    if (janet_checktype(argv[0], JANET_ABSTRACT) &&
 | 
			
		||||
            janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) {
 | 
			
		||||
        ret.peg = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
@@ -1653,7 +1862,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
 | 
			
		||||
        ret.peg = compile_peg(argv[0]);
 | 
			
		||||
    }
 | 
			
		||||
    if (get_replace) {
 | 
			
		||||
        ret.repl = janet_getbytes(argv, 1);
 | 
			
		||||
        ret.subst = argv[1];
 | 
			
		||||
        ret.bytes = janet_getbytes(argv, 2);
 | 
			
		||||
    } else {
 | 
			
		||||
        ret.bytes = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -1670,6 +1879,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
 | 
			
		||||
    ret.s.mode = PEG_MODE_NORMAL;
 | 
			
		||||
    ret.s.text_start = ret.bytes.bytes;
 | 
			
		||||
    ret.s.text_end = ret.bytes.bytes + ret.bytes.len;
 | 
			
		||||
    ret.s.outer_text_end = ret.s.text_end;
 | 
			
		||||
    ret.s.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    ret.s.captures = janet_array(0);
 | 
			
		||||
    ret.s.tagged_captures = janet_array(0);
 | 
			
		||||
@@ -1738,7 +1948,8 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
 | 
			
		||||
                trail = i;
 | 
			
		||||
            }
 | 
			
		||||
            int32_t nexti = (int32_t)(result - c.bytes.bytes);
 | 
			
		||||
            janet_buffer_push_bytes(ret, c.repl.bytes, c.repl.len);
 | 
			
		||||
            JanetByteView subst = janet_text_substitution(&c.subst, c.bytes.bytes + i, nexti - i, c.s.captures);
 | 
			
		||||
            janet_buffer_push_bytes(ret, subst.bytes, subst.len);
 | 
			
		||||
            trail = nexti;
 | 
			
		||||
            if (nexti == i) nexti++;
 | 
			
		||||
            i = nexti;
 | 
			
		||||
@@ -1754,14 +1965,20 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace_all,
 | 
			
		||||
              "(peg/replace-all peg repl text &opt start & args)",
 | 
			
		||||
              "Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.") {
 | 
			
		||||
              "(peg/replace-all peg subst text &opt start & args)",
 | 
			
		||||
              "Replace all matches of `peg` in `text` with `subst`, returning a new buffer. "
 | 
			
		||||
              "The peg does not need to make captures to do replacement. "
 | 
			
		||||
              "If `subst` is a function, it will be called with the "
 | 
			
		||||
              "matching text followed by any captures.") {
 | 
			
		||||
    return cfun_peg_replace_generic(argc, argv, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace,
 | 
			
		||||
              "(peg/replace peg repl text &opt start & args)",
 | 
			
		||||
              "Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. "
 | 
			
		||||
              "(peg/replace peg subst text &opt start & args)",
 | 
			
		||||
              "Replace first match of `peg` in `text` with `subst`, returning a new buffer. "
 | 
			
		||||
              "The peg does not need to make captures to do replacement. "
 | 
			
		||||
              "If `subst` is a function, it will be called with the "
 | 
			
		||||
              "matching text followed by any captures. "
 | 
			
		||||
              "If no matches are found, returns the input string in a new buffer.") {
 | 
			
		||||
    return cfun_peg_replace_generic(argc, argv, 1);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										116
									
								
								src/core/pp.c
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								src/core/pp.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -30,6 +30,8 @@
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <float.h>
 | 
			
		||||
 | 
			
		||||
/* Implements a pretty printer for Janet. The pretty printer
 | 
			
		||||
 * is simple and not that flexible, but fast. */
 | 
			
		||||
@@ -37,11 +39,15 @@
 | 
			
		||||
/* Temporary buffer size */
 | 
			
		||||
#define BUFSIZE 64
 | 
			
		||||
 | 
			
		||||
/* Preprocessor hacks */
 | 
			
		||||
#define STR_HELPER(x) #x
 | 
			
		||||
#define STR(x) STR_HELPER(x)
 | 
			
		||||
 | 
			
		||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
 | 
			
		||||
    janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
 | 
			
		||||
    const char *fmt = (x == floor(x) &&
 | 
			
		||||
                       x <= JANET_INTMAX_DOUBLE &&
 | 
			
		||||
                       x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g";
 | 
			
		||||
                       x >= JANET_INTMIN_DOUBLE) ? "%.0f" : ("%." STR(DBL_DIG) "g");
 | 
			
		||||
    int count;
 | 
			
		||||
    if (x == 0.0) {
 | 
			
		||||
        /* Prevent printing of '-0' */
 | 
			
		||||
@@ -108,7 +114,7 @@ static void string_description_b(JanetBuffer *buffer, const char *title, void *p
 | 
			
		||||
    pbuf.p = pointer;
 | 
			
		||||
    *c++ = '<';
 | 
			
		||||
    /* Maximum of 32 bytes for abstract type name */
 | 
			
		||||
    for (i = 0; title[i] && i < 32; ++i)
 | 
			
		||||
    for (i = 0; i < 32 && title[i]; ++i)
 | 
			
		||||
        *c++ = ((uint8_t *)title) [i];
 | 
			
		||||
    *c++ = ' ';
 | 
			
		||||
    *c++ = '0';
 | 
			
		||||
@@ -151,6 +157,12 @@ static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, in
 | 
			
		||||
            case '\v':
 | 
			
		||||
                janet_buffer_push_bytes(buffer, (const uint8_t *)"\\v", 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case '\a':
 | 
			
		||||
                janet_buffer_push_bytes(buffer, (const uint8_t *)"\\a", 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case '\b':
 | 
			
		||||
                janet_buffer_push_bytes(buffer, (const uint8_t *)"\\b", 2);
 | 
			
		||||
                break;
 | 
			
		||||
            case 27:
 | 
			
		||||
                janet_buffer_push_bytes(buffer, (const uint8_t *)"\\e", 2);
 | 
			
		||||
                break;
 | 
			
		||||
@@ -243,6 +255,10 @@ void janet_to_string_b(JanetBuffer *buffer, Janet x) {
 | 
			
		||||
        case JANET_FUNCTION: {
 | 
			
		||||
            JanetFunction *fun = janet_unwrap_function(x);
 | 
			
		||||
            JanetFuncDef *def = fun->def;
 | 
			
		||||
            if (def == NULL) {
 | 
			
		||||
                janet_buffer_push_cstring(buffer, "<incomplete function>");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (def->name) {
 | 
			
		||||
                const uint8_t *n = def->name;
 | 
			
		||||
                janet_buffer_push_cstring(buffer, "<function ");
 | 
			
		||||
@@ -363,8 +379,10 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_NUMBER:
 | 
			
		||||
            janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2);
 | 
			
		||||
            int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", janet_unwrap_number(x));
 | 
			
		||||
            S->buffer->count += count;
 | 
			
		||||
            double num = janet_unwrap_number(x);
 | 
			
		||||
            if (isnan(num)) return 1;
 | 
			
		||||
            if (isinf(num)) return 1;
 | 
			
		||||
            janet_buffer_dtostr(S->buffer, num);
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
        case JANET_KEYWORD:
 | 
			
		||||
@@ -636,7 +654,7 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                janet_sorted_keys(kvs, cap, S->keysort_buffer + ks_start);
 | 
			
		||||
                janet_sorted_keys(kvs, cap, S->keysort_buffer == NULL ? NULL : S->keysort_buffer + ks_start);
 | 
			
		||||
                S->keysort_start += len;
 | 
			
		||||
                if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
 | 
			
		||||
                    len = JANET_PRETTY_DICT_LIMIT;
 | 
			
		||||
@@ -735,7 +753,7 @@ static void pushtypes(JanetBuffer *buffer, int types) {
 | 
			
		||||
            if (first) {
 | 
			
		||||
                first = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_buffer_push_u8(buffer, '|');
 | 
			
		||||
                janet_buffer_push_cstring(buffer, (types == 1) ? " or " : ", ");
 | 
			
		||||
            }
 | 
			
		||||
            janet_buffer_push_cstring(buffer, janet_type_names[i]);
 | 
			
		||||
        }
 | 
			
		||||
@@ -750,14 +768,43 @@ static void pushtypes(JanetBuffer *buffer, int types) {
 | 
			
		||||
 | 
			
		||||
#define MAX_ITEM  256
 | 
			
		||||
#define FMT_FLAGS "-+ #0"
 | 
			
		||||
#define FMT_REPLACE_INTTYPES "diouxX"
 | 
			
		||||
#define MAX_FORMAT 32
 | 
			
		||||
 | 
			
		||||
struct FmtMapping {
 | 
			
		||||
    char c;
 | 
			
		||||
    const char *mapping;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Janet uses fixed width integer types for most things, so map
 | 
			
		||||
 * format specifiers to these fixed sizes */
 | 
			
		||||
static const struct FmtMapping format_mappings[] = {
 | 
			
		||||
    {'D', PRId64},
 | 
			
		||||
    {'I', PRIi64},
 | 
			
		||||
    {'d', PRId64},
 | 
			
		||||
    {'i', PRIi64},
 | 
			
		||||
    {'o', PRIo64},
 | 
			
		||||
    {'u', PRIu64},
 | 
			
		||||
    {'x', PRIx64},
 | 
			
		||||
    {'X', PRIX64},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *get_fmt_mapping(char c) {
 | 
			
		||||
    for (size_t i = 0; i < (sizeof(format_mappings) / sizeof(struct FmtMapping)); i++) {
 | 
			
		||||
        if (format_mappings[i].c == c)
 | 
			
		||||
            return format_mappings[i].mapping;
 | 
			
		||||
    }
 | 
			
		||||
    janet_assert(0, "bad format mapping");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *scanformat(
 | 
			
		||||
    const char *strfrmt,
 | 
			
		||||
    char *form,
 | 
			
		||||
    char width[3],
 | 
			
		||||
    char precision[3]) {
 | 
			
		||||
    const char *p = strfrmt;
 | 
			
		||||
 | 
			
		||||
    /* Parse strfrmt */
 | 
			
		||||
    memset(width, '\0', 3);
 | 
			
		||||
    memset(precision, '\0', 3);
 | 
			
		||||
    while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL)
 | 
			
		||||
@@ -776,10 +823,23 @@ static const char *scanformat(
 | 
			
		||||
    }
 | 
			
		||||
    if (isdigit((int)(*p)))
 | 
			
		||||
        janet_panic("invalid format (width or precision too long)");
 | 
			
		||||
 | 
			
		||||
    /* Write to form - replace characters with fixed size stuff */
 | 
			
		||||
    *(form++) = '%';
 | 
			
		||||
    memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char));
 | 
			
		||||
    form += (p - strfrmt) + 1;
 | 
			
		||||
    const char *p2 = strfrmt;
 | 
			
		||||
    while (p2 <= p) {
 | 
			
		||||
        char *loc = strchr(FMT_REPLACE_INTTYPES, *p2);
 | 
			
		||||
        if (loc != NULL && *loc != '\0') {
 | 
			
		||||
            const char *mapping = get_fmt_mapping(*p2++);
 | 
			
		||||
            size_t len = strlen(mapping);
 | 
			
		||||
            memcpy(form, mapping, len);
 | 
			
		||||
            form += len;
 | 
			
		||||
        } else {
 | 
			
		||||
            *(form++) = *(p2++);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    *form = '\0';
 | 
			
		||||
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -799,16 +859,27 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
            c = scanformat(c, form, width, precision);
 | 
			
		||||
            switch (*c++) {
 | 
			
		||||
                case 'c': {
 | 
			
		||||
                    int n = va_arg(args, long);
 | 
			
		||||
                    int n = va_arg(args, int);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'd':
 | 
			
		||||
                case 'i':
 | 
			
		||||
                case 'o':
 | 
			
		||||
                case 'i': {
 | 
			
		||||
                    int64_t n = (int64_t) va_arg(args, int32_t);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'D':
 | 
			
		||||
                case 'I': {
 | 
			
		||||
                    int64_t n = va_arg(args, int64_t);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'x':
 | 
			
		||||
                case 'X': {
 | 
			
		||||
                    int32_t n = va_arg(args, long);
 | 
			
		||||
                case 'X':
 | 
			
		||||
                case 'o':
 | 
			
		||||
                case 'u': {
 | 
			
		||||
                    uint64_t n = va_arg(args, uint64_t);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -852,7 +923,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
                    janet_buffer_push_cstring(b, typestr(va_arg(args, Janet)));
 | 
			
		||||
                    break;
 | 
			
		||||
                case 'T': {
 | 
			
		||||
                    int types = va_arg(args, long);
 | 
			
		||||
                    int types = va_arg(args, int);
 | 
			
		||||
                    pushtypes(b, types);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -961,12 +1032,19 @@ void janet_buffer_format(
 | 
			
		||||
                                  janet_getinteger(argv, arg));
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'D':
 | 
			
		||||
                case 'I':
 | 
			
		||||
                case 'd':
 | 
			
		||||
                case 'i':
 | 
			
		||||
                case 'o':
 | 
			
		||||
                case 'i': {
 | 
			
		||||
                    int64_t n = janet_getinteger64(argv, arg);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 'x':
 | 
			
		||||
                case 'X': {
 | 
			
		||||
                    int32_t n = janet_getinteger(argv, arg);
 | 
			
		||||
                case 'X':
 | 
			
		||||
                case 'o':
 | 
			
		||||
                case 'u': {
 | 
			
		||||
                    uint64_t n = janet_getuinteger64(argv, arg);
 | 
			
		||||
                    nb = snprintf(item, MAX_ITEM, form, n);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -27,6 +27,8 @@
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* The JanetRegisterAllocator is really just a bitset. */
 | 
			
		||||
 | 
			
		||||
void janetc_regalloc_init(JanetcRegisterAllocator *ra) {
 | 
			
		||||
    ra->chunks = NULL;
 | 
			
		||||
    ra->count = 0;
 | 
			
		||||
@@ -139,6 +141,14 @@ void janetc_regalloc_free(JanetcRegisterAllocator *ra, int32_t reg) {
 | 
			
		||||
    ra->chunks[chunk] &= ~ithbit(bit);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a register is set. */
 | 
			
		||||
int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg) {
 | 
			
		||||
    int32_t chunk = reg >> 5;
 | 
			
		||||
    int32_t bit = reg & 0x1F;
 | 
			
		||||
    while (chunk >= ra->count) pushchunk(ra);
 | 
			
		||||
    return !!(ra->chunks[chunk] & ithbit(bit));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a register that will fit in 8 bits (< 256). Do not call this
 | 
			
		||||
 * twice with the same value of nth without calling janetc_regalloc_free
 | 
			
		||||
 * on the returned register before. */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -56,5 +56,6 @@ int32_t janetc_regalloc_temp(JanetcRegisterAllocator *ra, JanetcRegisterTemp nth
 | 
			
		||||
void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRegisterTemp nth);
 | 
			
		||||
void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src);
 | 
			
		||||
void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg);
 | 
			
		||||
int janetc_regalloc_check(JanetcRegisterAllocator *ra, int32_t reg);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,26 +28,29 @@
 | 
			
		||||
 | 
			
		||||
/* Run a string */
 | 
			
		||||
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out) {
 | 
			
		||||
    JanetParser parser;
 | 
			
		||||
    JanetParser *parser;
 | 
			
		||||
    int errflags = 0, done = 0;
 | 
			
		||||
    int32_t index = 0;
 | 
			
		||||
    Janet ret = janet_wrap_nil();
 | 
			
		||||
    JanetFiber *fiber = NULL;
 | 
			
		||||
    const uint8_t *where = sourcePath ? janet_cstring(sourcePath) : NULL;
 | 
			
		||||
 | 
			
		||||
    if (where) janet_gcroot(janet_wrap_string(where));
 | 
			
		||||
    if (NULL == sourcePath) sourcePath = "<unknown>";
 | 
			
		||||
    janet_parser_init(&parser);
 | 
			
		||||
    parser = janet_abstract(&janet_parser_type, sizeof(JanetParser));
 | 
			
		||||
    janet_parser_init(parser);
 | 
			
		||||
    janet_gcroot(janet_wrap_abstract(parser));
 | 
			
		||||
 | 
			
		||||
    /* While we haven't seen an error */
 | 
			
		||||
    while (!done) {
 | 
			
		||||
 | 
			
		||||
        /* Evaluate parsed values */
 | 
			
		||||
        while (janet_parser_has_more(&parser)) {
 | 
			
		||||
            Janet form = janet_parser_produce(&parser);
 | 
			
		||||
        while (janet_parser_has_more(parser)) {
 | 
			
		||||
            Janet form = janet_parser_produce(parser);
 | 
			
		||||
            JanetCompileResult cres = janet_compile(form, env, where);
 | 
			
		||||
            if (cres.status == JANET_COMPILE_OK) {
 | 
			
		||||
                JanetFunction *f = janet_thunk(cres.funcdef);
 | 
			
		||||
                JanetFiber *fiber = janet_fiber(f, 64, 0, NULL);
 | 
			
		||||
                fiber = janet_fiber(f, 64, 0, NULL);
 | 
			
		||||
                fiber->env = env;
 | 
			
		||||
                JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
 | 
			
		||||
                if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
 | 
			
		||||
@@ -57,12 +60,20 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                ret = janet_wrap_string(cres.error);
 | 
			
		||||
                int32_t line = (int32_t) parser->line;
 | 
			
		||||
                int32_t col = (int32_t) parser->column;
 | 
			
		||||
                if ((cres.error_mapping.line > 0) &&
 | 
			
		||||
                        (cres.error_mapping.column > 0)) {
 | 
			
		||||
                    line = cres.error_mapping.line;
 | 
			
		||||
                    col = cres.error_mapping.column;
 | 
			
		||||
                }
 | 
			
		||||
                if (cres.macrofiber) {
 | 
			
		||||
                    janet_eprintf("compile error in %s: ", sourcePath);
 | 
			
		||||
                    janet_eprintf("%s:%d:%d: compile error", sourcePath,
 | 
			
		||||
                                  line, col);
 | 
			
		||||
                    janet_stacktrace_ext(cres.macrofiber, ret, "");
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf("compile error in %s: %s\n", sourcePath,
 | 
			
		||||
                                  (const char *)cres.error);
 | 
			
		||||
                    janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
 | 
			
		||||
                                  line, col, (const char *)cres.error);
 | 
			
		||||
                }
 | 
			
		||||
                errflags |= 0x02;
 | 
			
		||||
                done = 1;
 | 
			
		||||
@@ -72,16 +83,16 @@ 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)) {
 | 
			
		||||
        switch (janet_parser_status(parser)) {
 | 
			
		||||
            case JANET_PARSE_DEAD:
 | 
			
		||||
                done = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_PARSE_ERROR: {
 | 
			
		||||
                const char *e = janet_parser_error(&parser);
 | 
			
		||||
                const char *e = janet_parser_error(parser);
 | 
			
		||||
                errflags |= 0x04;
 | 
			
		||||
                ret = janet_cstringv(e);
 | 
			
		||||
                int32_t line = (int32_t) parser.line;
 | 
			
		||||
                int32_t col = (int32_t) parser.column;
 | 
			
		||||
                int32_t line = (int32_t) parser->line;
 | 
			
		||||
                int32_t col = (int32_t) parser->column;
 | 
			
		||||
                janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
 | 
			
		||||
                done = 1;
 | 
			
		||||
                break;
 | 
			
		||||
@@ -89,9 +100,9 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
            case JANET_PARSE_ROOT:
 | 
			
		||||
            case JANET_PARSE_PENDING:
 | 
			
		||||
                if (index >= len) {
 | 
			
		||||
                    janet_parser_eof(&parser);
 | 
			
		||||
                    janet_parser_eof(parser);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_parser_consume(&parser, bytes[index++]);
 | 
			
		||||
                    janet_parser_consume(parser, bytes[index++]);
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -99,14 +110,19 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Clean up and return errors */
 | 
			
		||||
    janet_parser_deinit(&parser);
 | 
			
		||||
    janet_gcunroot(janet_wrap_abstract(parser));
 | 
			
		||||
    if (where) janet_gcunroot(janet_wrap_string(where));
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Enter the event loop if we are not already in it */
 | 
			
		||||
    if (janet_vm.stackn == 0) {
 | 
			
		||||
        janet_gcroot(ret);
 | 
			
		||||
        if (fiber) {
 | 
			
		||||
            janet_gcroot(janet_wrap_fiber(fiber));
 | 
			
		||||
        }
 | 
			
		||||
        janet_loop();
 | 
			
		||||
        janet_gcunroot(ret);
 | 
			
		||||
        if (fiber) {
 | 
			
		||||
            janet_gcunroot(janet_wrap_fiber(fiber));
 | 
			
		||||
            ret = fiber->last_value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (out) *out = ret;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -39,6 +39,10 @@ static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
 | 
			
		||||
static JanetSlot janetc_splice(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetSlot ret;
 | 
			
		||||
    if (!(opts.flags & JANET_FOPTS_ACCEPT_SPLICE)) {
 | 
			
		||||
        janetc_cerror(opts.compiler, "splice can only be used in function parameters and data constructors, it has no effect here");
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    if (argn != 1) {
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 1 argument to splice");
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
@@ -75,7 +79,9 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
 | 
			
		||||
                const uint8_t *head = janet_unwrap_symbol(tup[0]);
 | 
			
		||||
                if (!janet_cstrcmp(head, "unquote")) {
 | 
			
		||||
                    if (level == 0) {
 | 
			
		||||
                        return janetc_value(janetc_fopts_default(opts.compiler), tup[1]);
 | 
			
		||||
                        JanetFopts subopts = janetc_fopts_default(opts.compiler);
 | 
			
		||||
                        subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
                        return janetc_value(subopts, tup[1]);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        level--;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -143,7 +149,7 @@ static int destructure(JanetCompiler *c,
 | 
			
		||||
                       JanetTable *attr) {
 | 
			
		||||
    switch (janet_type(left)) {
 | 
			
		||||
        default:
 | 
			
		||||
            janetc_error(c, janet_formatc("unexpected type in destruction, got %v", left));
 | 
			
		||||
            janetc_error(c, janet_formatc("unexpected type in destructuring, got %v", left));
 | 
			
		||||
            return 1;
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
            /* Leaf, assign right to left */
 | 
			
		||||
@@ -176,7 +182,6 @@ static int destructure(JanetCompiler *c,
 | 
			
		||||
                        return 1;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    if (!janet_checktype(values[i + 1], JANET_SYMBOL)) {
 | 
			
		||||
                        janetc_error(c, janet_formatc("expected symbol following '& in destructuring pattern, found %q", values[i + 1]));
 | 
			
		||||
                        return 1;
 | 
			
		||||
@@ -203,8 +208,9 @@ static int destructure(JanetCompiler *c,
 | 
			
		||||
                    janetc_emit(c, JOP_JUMP);
 | 
			
		||||
                    int32_t label_loop_exit = janet_v_count(c->buffer);
 | 
			
		||||
 | 
			
		||||
                    c->buffer[label_loop_cond_jump] |= (label_loop_exit - label_loop_cond_jump) << 16;
 | 
			
		||||
                    c->buffer[label_loop_loop] |= (label_loop_start - label_loop_loop) << 8;
 | 
			
		||||
                    /* avoid shifting negative numbers */
 | 
			
		||||
                    c->buffer[label_loop_cond_jump] |= (uint32_t)(label_loop_exit - label_loop_cond_jump) << 16;
 | 
			
		||||
                    c->buffer[label_loop_loop] |= (uint32_t)(label_loop_start - label_loop_loop) << 8;
 | 
			
		||||
 | 
			
		||||
                    janetc_freeslot(c, argi);
 | 
			
		||||
                    janetc_freeslot(c, arg);
 | 
			
		||||
@@ -257,7 +263,7 @@ static const Janet *janetc_make_sourcemap(JanetCompiler *c) {
 | 
			
		||||
 | 
			
		||||
static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    if (argn != 2) {
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 2 arguments");
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 2 arguments to set");
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    JanetFopts subopts = janetc_fopts_default(opts.compiler);
 | 
			
		||||
@@ -299,12 +305,16 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add attributes to a global def or var table */
 | 
			
		||||
static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv) {
 | 
			
		||||
static JanetTable *handleattr(JanetCompiler *c, const char *kind, int32_t argn, const Janet *argv) {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    JanetTable *tab = janet_table(2);
 | 
			
		||||
    const char *binding_name = janet_type(argv[0]) == JANET_SYMBOL
 | 
			
		||||
                               ? ((const char *)janet_unwrap_symbol(argv[0]))
 | 
			
		||||
                               : "<multiple bindings>";
 | 
			
		||||
    if (argn < 2) {
 | 
			
		||||
        janetc_error(c, janet_formatc("expected at least 2 arguments to %s", kind));
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    for (i = 1; i < argn - 1; i++) {
 | 
			
		||||
        Janet attr = argv[i];
 | 
			
		||||
        switch (janet_type(attr)) {
 | 
			
		||||
@@ -328,18 +338,52 @@ static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv)
 | 
			
		||||
    return tab;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetSlot dohead(JanetCompiler *c, JanetFopts opts, Janet *head, int32_t argn, const Janet *argv) {
 | 
			
		||||
typedef struct SlotHeadPair {
 | 
			
		||||
    Janet lhs;
 | 
			
		||||
    JanetSlot rhs;
 | 
			
		||||
} SlotHeadPair;
 | 
			
		||||
 | 
			
		||||
SlotHeadPair *dohead_destructure(JanetCompiler *c, SlotHeadPair *into, JanetFopts opts, Janet lhs, Janet rhs) {
 | 
			
		||||
 | 
			
		||||
    /* Detect if we can do an optimization to avoid some allocations */
 | 
			
		||||
    int can_destructure_lhs = janet_checktype(lhs, JANET_TUPLE)
 | 
			
		||||
                              || janet_checktype(lhs, JANET_ARRAY);
 | 
			
		||||
    int rhs_is_indexed = janet_checktype(rhs, JANET_ARRAY)
 | 
			
		||||
                         || (janet_checktype(rhs, JANET_TUPLE) && (janet_tuple_flag(janet_unwrap_tuple(rhs)) & JANET_TUPLE_FLAG_BRACKETCTOR));
 | 
			
		||||
    uint32_t has_drop = opts.flags & JANET_FOPTS_DROP;
 | 
			
		||||
 | 
			
		||||
    JanetFopts subopts = janetc_fopts_default(c);
 | 
			
		||||
    JanetSlot ret;
 | 
			
		||||
    if (argn < 2) {
 | 
			
		||||
        janetc_cerror(c, "expected at least 2 arguments");
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    *head = argv[0];
 | 
			
		||||
    subopts.flags = opts.flags & ~(JANET_FOPTS_TAIL | JANET_FOPTS_DROP);
 | 
			
		||||
 | 
			
		||||
    if (has_drop && can_destructure_lhs && rhs_is_indexed) {
 | 
			
		||||
        /* Code is of the form (def [a b] [1 2]), avoid the allocation of two tuples */
 | 
			
		||||
        JanetView view_lhs = {0};
 | 
			
		||||
        JanetView view_rhs = {0};
 | 
			
		||||
        janet_indexed_view(lhs, &view_lhs.items, &view_lhs.len);
 | 
			
		||||
        janet_indexed_view(rhs, &view_rhs.items, &view_rhs.len);
 | 
			
		||||
        int found_amp = 0;
 | 
			
		||||
        for (int32_t i = 0; i < view_lhs.len; i++) {
 | 
			
		||||
            if (janet_symeq(view_lhs.items[i], "&")) {
 | 
			
		||||
                found_amp = 1;
 | 
			
		||||
                /* Good error will be generated later. */
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!found_amp) {
 | 
			
		||||
            for (int32_t i = 0; i < view_lhs.len; i++) {
 | 
			
		||||
                Janet sub_rhs = view_rhs.len <= i ? janet_wrap_nil() : view_rhs.items[i];
 | 
			
		||||
                into = dohead_destructure(c, into, subopts, view_lhs.items[i], sub_rhs);
 | 
			
		||||
            }
 | 
			
		||||
            return into;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* No optimization, do the simple way */
 | 
			
		||||
    subopts.hint = opts.hint;
 | 
			
		||||
    ret = janetc_value(subopts, argv[argn - 1]);
 | 
			
		||||
    return ret;
 | 
			
		||||
    JanetSlot ret = janetc_value(subopts, rhs);
 | 
			
		||||
    SlotHeadPair shp = {lhs, ret};
 | 
			
		||||
    janet_v_push(into, shp);
 | 
			
		||||
    return into;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Def or var a symbol in a local scope */
 | 
			
		||||
@@ -347,7 +391,17 @@ static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, Janet
 | 
			
		||||
    int isUnnamedRegister = !(ret.flags & JANET_SLOT_NAMED) &&
 | 
			
		||||
                            ret.index > 0 &&
 | 
			
		||||
                            ret.envindex >= 0;
 | 
			
		||||
    if (!isUnnamedRegister) {
 | 
			
		||||
    /* optimization for `(def x my-def)` - don't emit a movn/movf instruction, we can just alias my-def */
 | 
			
		||||
    /* TODO - implement optimization for `(def x my-var)` correctly as well w/ de-aliasing */
 | 
			
		||||
    int canAlias = !(flags & JANET_SLOT_MUTABLE) &&
 | 
			
		||||
                   !(ret.flags & JANET_SLOT_MUTABLE) &&
 | 
			
		||||
                   (ret.flags & JANET_SLOT_NAMED) &&
 | 
			
		||||
                   (ret.index >= 0) &&
 | 
			
		||||
                   (ret.envindex == -1);
 | 
			
		||||
    if (canAlias) {
 | 
			
		||||
        ret.flags &= ~JANET_SLOT_MUTABLE;
 | 
			
		||||
        isUnnamedRegister = 1; /* don't free slot after use - is an alias for another slot */
 | 
			
		||||
    } else if (!isUnnamedRegister) {
 | 
			
		||||
        /* Slot is not able to be named */
 | 
			
		||||
        JanetSlot localslot = janetc_farslot(c);
 | 
			
		||||
        janetc_copy(c, localslot, ret);
 | 
			
		||||
@@ -395,12 +449,23 @@ static int varleaf(
 | 
			
		||||
 | 
			
		||||
static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    Janet head;
 | 
			
		||||
    JanetTable *attr_table = handleattr(c, argn, argv);
 | 
			
		||||
    JanetSlot ret = dohead(c, opts, &head, argn, argv);
 | 
			
		||||
    if (c->result.status == JANET_COMPILE_ERROR)
 | 
			
		||||
    JanetTable *attr_table = handleattr(c, "var", argn, argv);
 | 
			
		||||
    if (c->result.status == JANET_COMPILE_ERROR) {
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    destructure(c, argv[0], ret, varleaf, attr_table);
 | 
			
		||||
    }
 | 
			
		||||
    SlotHeadPair *into = NULL;
 | 
			
		||||
    into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]);
 | 
			
		||||
    if (c->result.status == JANET_COMPILE_ERROR) {
 | 
			
		||||
        janet_v_free(into);
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    JanetSlot ret;
 | 
			
		||||
    janet_assert(janet_v_count(into) > 0, "bad destructure");
 | 
			
		||||
    for (int32_t i = 0; i < janet_v_count(into); i++) {
 | 
			
		||||
        destructure(c, into[i].lhs, into[i].rhs, varleaf, attr_table);
 | 
			
		||||
        ret = into[i].rhs;
 | 
			
		||||
    }
 | 
			
		||||
    janet_v_free(into);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -444,16 +509,47 @@ static int defleaf(
 | 
			
		||||
 | 
			
		||||
static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    Janet head;
 | 
			
		||||
    opts.flags &= ~JANET_FOPTS_HINT;
 | 
			
		||||
    JanetTable *attr_table = handleattr(c, argn, argv);
 | 
			
		||||
    JanetSlot ret = dohead(c, opts, &head, argn, argv);
 | 
			
		||||
    if (c->result.status == JANET_COMPILE_ERROR)
 | 
			
		||||
    JanetTable *attr_table = handleattr(c, "def", argn, argv);
 | 
			
		||||
    if (c->result.status == JANET_COMPILE_ERROR) {
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    destructure(c, argv[0], ret, defleaf, attr_table);
 | 
			
		||||
    }
 | 
			
		||||
    opts.flags &= ~JANET_FOPTS_HINT;
 | 
			
		||||
    SlotHeadPair *into = NULL;
 | 
			
		||||
    into = dohead_destructure(c, into, opts, argv[0], argv[argn - 1]);
 | 
			
		||||
    if (c->result.status == JANET_COMPILE_ERROR) {
 | 
			
		||||
        janet_v_free(into);
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    JanetSlot ret;
 | 
			
		||||
    janet_assert(janet_v_count(into) > 0, "bad destructure");
 | 
			
		||||
    for (int32_t i = 0; i < janet_v_count(into); i++) {
 | 
			
		||||
        destructure(c, into[i].lhs, into[i].rhs, defleaf, attr_table);
 | 
			
		||||
        ret = into[i].rhs;
 | 
			
		||||
    }
 | 
			
		||||
    janet_v_free(into);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a form matches the pattern (= nil _) or (not= nil _) */
 | 
			
		||||
static int janetc_check_nil_form(Janet x, Janet *capture, uint32_t fun_tag) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_TUPLE)) return 0;
 | 
			
		||||
    JanetTuple tup = janet_unwrap_tuple(x);
 | 
			
		||||
    if (3 != janet_tuple_length(tup)) return 0;
 | 
			
		||||
    Janet op1 = tup[0];
 | 
			
		||||
    if (!janet_checktype(op1, JANET_FUNCTION)) return 0;
 | 
			
		||||
    JanetFunction *fun = janet_unwrap_function(op1);
 | 
			
		||||
    uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
 | 
			
		||||
    if (tag != fun_tag) return 0;
 | 
			
		||||
    if (janet_checktype(tup[1], JANET_NIL)) {
 | 
			
		||||
        *capture = tup[2];
 | 
			
		||||
        return 1;
 | 
			
		||||
    } else if (janet_checktype(tup[2], JANET_NIL)) {
 | 
			
		||||
        *capture = tup[1];
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * :condition
 | 
			
		||||
 * ...
 | 
			
		||||
@@ -474,6 +570,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetScope condscope, tempscope;
 | 
			
		||||
    const int tail = opts.flags & JANET_FOPTS_TAIL;
 | 
			
		||||
    const int drop = opts.flags & JANET_FOPTS_DROP;
 | 
			
		||||
    uint8_t ifnjmp = JOP_JUMP_IF_NOT;
 | 
			
		||||
 | 
			
		||||
    if (argn < 2 || argn > 3) {
 | 
			
		||||
        janetc_cerror(c, "expected 2 or 3 arguments to if");
 | 
			
		||||
@@ -487,6 +584,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    /* Get options */
 | 
			
		||||
    condopts = janetc_fopts_default(c);
 | 
			
		||||
    bodyopts = opts;
 | 
			
		||||
    bodyopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
 | 
			
		||||
    /* Set target for compilation */
 | 
			
		||||
    target = (drop || tail)
 | 
			
		||||
@@ -495,12 +593,24 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
 | 
			
		||||
    /* Compile condition */
 | 
			
		||||
    janetc_scope(&condscope, c, 0, "if");
 | 
			
		||||
    cond = janetc_value(condopts, argv[0]);
 | 
			
		||||
 | 
			
		||||
    Janet condform = argv[0];
 | 
			
		||||
    if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
 | 
			
		||||
        ifnjmp = JOP_JUMP_IF_NOT_NIL;
 | 
			
		||||
    } else if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
 | 
			
		||||
        ifnjmp = JOP_JUMP_IF_NIL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cond = janetc_value(condopts, condform);
 | 
			
		||||
 | 
			
		||||
    /* Check constant condition. */
 | 
			
		||||
    /* TODO: Use type info for more short circuits */
 | 
			
		||||
    if (cond.flags & JANET_SLOT_CONSTANT) {
 | 
			
		||||
        if (!janet_truthy(cond.constant)) {
 | 
			
		||||
        int swap_condition = 0;
 | 
			
		||||
        if (ifnjmp == JOP_JUMP_IF_NOT && !janet_truthy(cond.constant)) swap_condition = 1;
 | 
			
		||||
        if (ifnjmp == JOP_JUMP_IF_NIL && janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
 | 
			
		||||
        if (ifnjmp == JOP_JUMP_IF_NOT_NIL && !janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
 | 
			
		||||
        if (swap_condition) {
 | 
			
		||||
            /* Swap the true and false bodies */
 | 
			
		||||
            Janet temp = falsebody;
 | 
			
		||||
            falsebody = truebody;
 | 
			
		||||
@@ -518,7 +628,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Compile jump to right */
 | 
			
		||||
    labeljr = janetc_emit_si(c, JOP_JUMP_IF_NOT, cond, 0, 0);
 | 
			
		||||
    labeljr = janetc_emit_si(c, ifnjmp, cond, 0, 0);
 | 
			
		||||
 | 
			
		||||
    /* Condition left body */
 | 
			
		||||
    janetc_scope(&tempscope, c, 0, "if-true");
 | 
			
		||||
@@ -528,7 +638,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
 | 
			
		||||
    /* Compile jump to done */
 | 
			
		||||
    labeljd = janet_v_count(c->buffer);
 | 
			
		||||
    if (!tail) janetc_emit(c, JOP_JUMP);
 | 
			
		||||
    if (!tail && !(drop && janet_checktype(falsebody, JANET_NIL))) janetc_emit(c, JOP_JUMP);
 | 
			
		||||
 | 
			
		||||
    /* Compile right body */
 | 
			
		||||
    labelr = janet_v_count(c->buffer);
 | 
			
		||||
@@ -563,6 +673,7 @@ static JanetSlot janetc_do(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
            subopts.flags = JANET_FOPTS_DROP;
 | 
			
		||||
        } else {
 | 
			
		||||
            subopts = opts;
 | 
			
		||||
            subopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
        }
 | 
			
		||||
        ret = janetc_value(subopts, argv[i]);
 | 
			
		||||
        if (i != argn - 1) {
 | 
			
		||||
@@ -573,7 +684,6 @@ static JanetSlot janetc_do(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Compile an upscope form. Upscope forms execute their body sequentially and
 | 
			
		||||
 * evaluate to the last expression in the body, but without lexical scope. */
 | 
			
		||||
static JanetSlot janetc_upscope(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
@@ -586,6 +696,7 @@ static JanetSlot janetc_upscope(JanetFopts opts, int32_t argn, const Janet *argv
 | 
			
		||||
            subopts.flags = JANET_FOPTS_DROP;
 | 
			
		||||
        } else {
 | 
			
		||||
            subopts = opts;
 | 
			
		||||
            subopts.flags &= ~JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
        }
 | 
			
		||||
        ret = janetc_value(subopts, argv[i]);
 | 
			
		||||
        if (i != argn - 1) {
 | 
			
		||||
@@ -638,9 +749,8 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
        if (!(scope->flags & JANET_SCOPE_WHILE) && argn) {
 | 
			
		||||
            /* Closure body with return argument */
 | 
			
		||||
            subopts.flags |= JANET_FOPTS_TAIL;
 | 
			
		||||
            JanetSlot ret = janetc_value(subopts, argv[0]);
 | 
			
		||||
            ret.flags |= JANET_SLOT_RETURNED;
 | 
			
		||||
            return ret;
 | 
			
		||||
            janetc_value(subopts, argv[0]);
 | 
			
		||||
            return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
        } else {
 | 
			
		||||
            /* while loop IIFE or no argument */
 | 
			
		||||
            if (argn) {
 | 
			
		||||
@@ -648,9 +758,7 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
                janetc_value(subopts, argv[0]);
 | 
			
		||||
            }
 | 
			
		||||
            janetc_emit(c, JOP_RETURN_NIL);
 | 
			
		||||
            JanetSlot s = janetc_cslot(janet_wrap_nil());
 | 
			
		||||
            s.flags |= JANET_SLOT_RETURNED;
 | 
			
		||||
            return s;
 | 
			
		||||
            return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (argn) {
 | 
			
		||||
@@ -663,20 +771,6 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a form matches the pattern (not= nil _) */
 | 
			
		||||
static int janetc_check_notnil_form(Janet x, Janet *capture) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_TUPLE)) return 0;
 | 
			
		||||
    JanetTuple tup = janet_unwrap_tuple(x);
 | 
			
		||||
    if (!janet_checktype(tup[0], JANET_FUNCTION)) return 0;
 | 
			
		||||
    if (3 != janet_tuple_length(tup)) return 0;
 | 
			
		||||
    JanetFunction *fun = janet_unwrap_function(tup[0]);
 | 
			
		||||
    uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
 | 
			
		||||
    if (tag != JANET_FUN_NEQ) return 0;
 | 
			
		||||
    if (!janet_checktype(tup[1], JANET_NIL)) return 0;
 | 
			
		||||
    *capture = tup[2];
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * :whiletop
 | 
			
		||||
 * ...
 | 
			
		||||
@@ -693,12 +787,13 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
    JanetScope tempscope;
 | 
			
		||||
    int32_t labelwt, labeld, labeljt, labelc, i;
 | 
			
		||||
    int infinite = 0;
 | 
			
		||||
    int is_nil_form = 0;
 | 
			
		||||
    int is_notnil_form = 0;
 | 
			
		||||
    uint8_t ifjmp = JOP_JUMP_IF;
 | 
			
		||||
    uint8_t ifnjmp = JOP_JUMP_IF_NOT;
 | 
			
		||||
 | 
			
		||||
    if (argn < 2) {
 | 
			
		||||
        janetc_cerror(c, "expected at least 2 arguments");
 | 
			
		||||
    if (argn < 1) {
 | 
			
		||||
        janetc_cerror(c, "expected at least 1 argument to while");
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -706,11 +801,16 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
 | 
			
		||||
    janetc_scope(&tempscope, c, JANET_SCOPE_WHILE, "while");
 | 
			
		||||
 | 
			
		||||
    /* Check for `(not= nil _)` in condition, and if so, use the
 | 
			
		||||
    /* Check for `(= nil _)` or `(not= nil _)` in condition, and if so, use the
 | 
			
		||||
     * jmpnl or jmpnn instructions. This let's us implement `(each ...)`
 | 
			
		||||
     * more efficiently. */
 | 
			
		||||
    Janet condform = argv[0];
 | 
			
		||||
    if (janetc_check_notnil_form(condform, &condform)) {
 | 
			
		||||
    if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
 | 
			
		||||
        is_nil_form = 1;
 | 
			
		||||
        ifjmp = JOP_JUMP_IF_NIL;
 | 
			
		||||
        ifnjmp = JOP_JUMP_IF_NOT_NIL;
 | 
			
		||||
    }
 | 
			
		||||
    if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
 | 
			
		||||
        is_notnil_form = 1;
 | 
			
		||||
        ifjmp = JOP_JUMP_IF_NOT_NIL;
 | 
			
		||||
        ifnjmp = JOP_JUMP_IF_NIL;
 | 
			
		||||
@@ -722,7 +822,9 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
    /* Check for constant condition */
 | 
			
		||||
    if (cond.flags & JANET_SLOT_CONSTANT) {
 | 
			
		||||
        /* Loop never executes */
 | 
			
		||||
        int never_executes = is_notnil_form
 | 
			
		||||
        int never_executes = is_nil_form
 | 
			
		||||
                             ? !janet_checktype(cond.constant, JANET_NIL)
 | 
			
		||||
                             : is_notnil_form
 | 
			
		||||
                             ? janet_checktype(cond.constant, JANET_NIL)
 | 
			
		||||
                             : !janet_truthy(cond.constant);
 | 
			
		||||
        if (never_executes) {
 | 
			
		||||
@@ -813,7 +915,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetSlot ret;
 | 
			
		||||
    Janet head;
 | 
			
		||||
    JanetScope fnscope;
 | 
			
		||||
    int32_t paramcount, argi, parami, arity, min_arity, max_arity, defindex, i;
 | 
			
		||||
    int32_t paramcount, argi, parami, arity, min_arity = 0, max_arity, defindex, i;
 | 
			
		||||
    JanetFopts subopts = janetc_fopts_default(c);
 | 
			
		||||
    const Janet *params;
 | 
			
		||||
    const char *errmsg = NULL;
 | 
			
		||||
@@ -823,6 +925,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    int structarg = 0;
 | 
			
		||||
    int allow_extra = 0;
 | 
			
		||||
    int selfref = 0;
 | 
			
		||||
    int hasname = 0;
 | 
			
		||||
    int seenamp = 0;
 | 
			
		||||
    int seenopt = 0;
 | 
			
		||||
    int namedargs = 0;
 | 
			
		||||
@@ -841,6 +944,10 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    head = argv[0];
 | 
			
		||||
    if (janet_checktype(head, JANET_SYMBOL)) {
 | 
			
		||||
        selfref = 1;
 | 
			
		||||
        hasname = 1;
 | 
			
		||||
        parami = 1;
 | 
			
		||||
    } else if (janet_checktype(head, JANET_KEYWORD)) {
 | 
			
		||||
        hasname = 1;
 | 
			
		||||
        parami = 1;
 | 
			
		||||
    }
 | 
			
		||||
    if (parami >= argn || !janet_checktype(argv[parami], JANET_TUPLE)) {
 | 
			
		||||
@@ -940,6 +1047,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    for (i = 0; i < paramcount; i++) {
 | 
			
		||||
        Janet param = params[i];
 | 
			
		||||
        if (!janet_checktype(param, JANET_SYMBOL)) {
 | 
			
		||||
            janet_assert(janet_v_count(destructed_params) > j, "out of bounds");
 | 
			
		||||
            JanetSlot reg = destructed_params[j++];
 | 
			
		||||
            destructure(c, param, reg, defleaf, NULL);
 | 
			
		||||
            janetc_freeslot(c, reg);
 | 
			
		||||
@@ -958,12 +1066,26 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    max_arity = (vararg || allow_extra) ? INT32_MAX : arity;
 | 
			
		||||
    if (!seenopt) min_arity = arity;
 | 
			
		||||
 | 
			
		||||
    /* Check for self ref */
 | 
			
		||||
    /* Check for self ref (also avoid if arguments shadow own name) */
 | 
			
		||||
    if (selfref) {
 | 
			
		||||
        JanetSlot slot = janetc_farslot(c);
 | 
			
		||||
        slot.flags = JANET_SLOT_NAMED | JANET_FUNCTION;
 | 
			
		||||
        janetc_emit_s(c, JOP_LOAD_SELF, slot, 1);
 | 
			
		||||
        janetc_nameslot(c, janet_unwrap_symbol(head), slot);
 | 
			
		||||
        /* Check if the parameters shadow the function name. If so, don't
 | 
			
		||||
         * emit JOP_LOAD_SELF and add a binding since that most users
 | 
			
		||||
         * seem to expect that function parameters take precedence over the
 | 
			
		||||
         * function name */
 | 
			
		||||
        const uint8_t *sym = janet_unwrap_symbol(head);
 | 
			
		||||
        int32_t len = janet_v_count(c->scope->syms);
 | 
			
		||||
        int found = 0;
 | 
			
		||||
        for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
            if (c->scope->syms[i].sym == sym) {
 | 
			
		||||
                found = 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!found) {
 | 
			
		||||
            JanetSlot slot = janetc_farslot(c);
 | 
			
		||||
            slot.flags = JANET_SLOT_NAMED | JANET_FUNCTION;
 | 
			
		||||
            janetc_emit_s(c, JOP_LOAD_SELF, slot, 1);
 | 
			
		||||
            janetc_nameslot(c, sym, slot);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Compile function body */
 | 
			
		||||
@@ -986,7 +1108,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
 | 
			
		||||
    if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
 | 
			
		||||
 | 
			
		||||
    if (selfref) def->name = janet_unwrap_symbol(head);
 | 
			
		||||
    if (hasname) def->name = janet_unwrap_symbol(head); /* Also correctly unwraps keyword */
 | 
			
		||||
    janet_def_addflags(def);
 | 
			
		||||
    defindex = janetc_addfuncdef(c, def);
 | 
			
		||||
 | 
			
		||||
@@ -1030,4 +1152,3 @@ const JanetSpecial *janetc_special(const uint8_t *name) {
 | 
			
		||||
               sizeof(JanetSpecial),
 | 
			
		||||
               name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -24,6 +24,11 @@
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_THREAD_LOCAL JanetVM janet_vm;
 | 
			
		||||
@@ -53,9 +58,14 @@ void janet_vm_load(JanetVM *from) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Trigger suspension of the Janet vm by trying to
 | 
			
		||||
 * exit the interpeter loop when convenient. You can optionally
 | 
			
		||||
 * exit the interpreter 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;
 | 
			
		||||
    janet_atomic_inc(&vm->auto_suspend);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_interpreter_interrupt_handled(JanetVM *vm) {
 | 
			
		||||
    vm = vm ? vm : &janet_vm;
 | 
			
		||||
    janet_atomic_dec(&vm->auto_suspend);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -23,8 +23,15 @@
 | 
			
		||||
#ifndef JANET_STATE_H_defined
 | 
			
		||||
#define JANET_STATE_H_defined
 | 
			
		||||
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
typedef int64_t JanetTimestamp;
 | 
			
		||||
 | 
			
		||||
typedef struct JanetScratch {
 | 
			
		||||
@@ -54,7 +61,7 @@ typedef struct {
 | 
			
		||||
    int is_error;
 | 
			
		||||
} JanetTimeout;
 | 
			
		||||
 | 
			
		||||
/* Registry table for C functions - containts metadata that can
 | 
			
		||||
/* Registry table for C functions - contains metadata that can
 | 
			
		||||
 * be looked up by cfunction pointer. All strings here are pointing to
 | 
			
		||||
 * static memory not managed by Janet. */
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -82,10 +89,10 @@ struct JanetVM {
 | 
			
		||||
 | 
			
		||||
    /* If this flag is true, suspend on function calls and backwards jumps.
 | 
			
		||||
     * When this occurs, this flag will be reset to 0. */
 | 
			
		||||
    int auto_suspend;
 | 
			
		||||
    volatile JanetAtomicInt auto_suspend;
 | 
			
		||||
 | 
			
		||||
    /* The current running fiber on the current thread.
 | 
			
		||||
     * Set and unset by janet_run. */
 | 
			
		||||
     * Set and unset by functions in vm.c */
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
    JanetFiber *root_fiber;
 | 
			
		||||
 | 
			
		||||
@@ -93,6 +100,7 @@ struct JanetVM {
 | 
			
		||||
     * return point for panics. */
 | 
			
		||||
    jmp_buf *signal_buf;
 | 
			
		||||
    Janet *return_reg;
 | 
			
		||||
    int coerce_error;
 | 
			
		||||
 | 
			
		||||
    /* The global registry for c functions. Used to store meta-data
 | 
			
		||||
     * along with otherwise bare c function pointers. */
 | 
			
		||||
@@ -101,7 +109,7 @@ struct JanetVM {
 | 
			
		||||
    size_t registry_count;
 | 
			
		||||
    int registry_dirty;
 | 
			
		||||
 | 
			
		||||
    /* Registry for abstract abstract types that can be marshalled.
 | 
			
		||||
    /* Registry for abstract types that can be marshalled.
 | 
			
		||||
     * We need this to look up the constructors when unmarshalling. */
 | 
			
		||||
    JanetTable *abstract_registry;
 | 
			
		||||
 | 
			
		||||
@@ -114,10 +122,12 @@ struct JanetVM {
 | 
			
		||||
 | 
			
		||||
    /* Garbage collection */
 | 
			
		||||
    void *blocks;
 | 
			
		||||
    void *weak_blocks;
 | 
			
		||||
    size_t gc_interval;
 | 
			
		||||
    size_t next_collection;
 | 
			
		||||
    size_t block_count;
 | 
			
		||||
    int gc_suspend;
 | 
			
		||||
    int gc_mark_phase;
 | 
			
		||||
 | 
			
		||||
    /* GC roots */
 | 
			
		||||
    Janet *roots;
 | 
			
		||||
@@ -129,6 +139,9 @@ struct JanetVM {
 | 
			
		||||
    size_t scratch_cap;
 | 
			
		||||
    size_t scratch_len;
 | 
			
		||||
 | 
			
		||||
    /* Sandbox flags */
 | 
			
		||||
    uint32_t sandbox_flags;
 | 
			
		||||
 | 
			
		||||
    /* Random number generator */
 | 
			
		||||
    JanetRNG rng;
 | 
			
		||||
 | 
			
		||||
@@ -137,6 +150,11 @@ struct JanetVM {
 | 
			
		||||
    JanetTraversalNode *traversal_top;
 | 
			
		||||
    JanetTraversalNode *traversal_base;
 | 
			
		||||
 | 
			
		||||
    /* Thread safe strerror error buffer - for janet_strerror */
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
    char strerror_buf[256];
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Event loop and scheduler globals */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    size_t tq_count;
 | 
			
		||||
@@ -144,24 +162,29 @@ struct JanetVM {
 | 
			
		||||
    JanetQueue spawn;
 | 
			
		||||
    JanetTimeout *tq;
 | 
			
		||||
    JanetRNG ev_rng;
 | 
			
		||||
    JanetListenerState **listeners;
 | 
			
		||||
    size_t listener_count;
 | 
			
		||||
    size_t listener_cap;
 | 
			
		||||
    size_t extra_listeners;
 | 
			
		||||
    volatile JanetAtomicInt listener_count; /* used in signal handler, must be volatile */
 | 
			
		||||
    JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */
 | 
			
		||||
    JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */
 | 
			
		||||
    JanetTable signal_handlers;
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    void **iocp;
 | 
			
		||||
#elif defined(JANET_EV_EPOLL)
 | 
			
		||||
    pthread_attr_t new_thread_attr;
 | 
			
		||||
    JanetHandle selfpipe[2];
 | 
			
		||||
    int epoll;
 | 
			
		||||
    int timerfd;
 | 
			
		||||
    int timer_enabled;
 | 
			
		||||
#elif defined(JANET_EV_KQUEUE)
 | 
			
		||||
    pthread_attr_t new_thread_attr;
 | 
			
		||||
    JanetHandle selfpipe[2];
 | 
			
		||||
    int kq;
 | 
			
		||||
    int timer;
 | 
			
		||||
    int timer_enabled;
 | 
			
		||||
#else
 | 
			
		||||
    JanetStream **streams;
 | 
			
		||||
    size_t stream_count;
 | 
			
		||||
    size_t stream_capacity;
 | 
			
		||||
    pthread_attr_t new_thread_attr;
 | 
			
		||||
    JanetHandle selfpipe[2];
 | 
			
		||||
    struct pollfd *fds;
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -71,10 +71,10 @@ int janet_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
 | 
			
		||||
int janet_string_equalconst(const uint8_t *lhs, const uint8_t *rhs, int32_t rlen, int32_t rhash) {
 | 
			
		||||
    int32_t lhash = janet_string_hash(lhs);
 | 
			
		||||
    int32_t llen = janet_string_length(lhs);
 | 
			
		||||
    if (lhs == rhs)
 | 
			
		||||
        return 1;
 | 
			
		||||
    if (lhash != rhash || llen != rlen)
 | 
			
		||||
        return 0;
 | 
			
		||||
    if (lhs == rhs)
 | 
			
		||||
        return 1;
 | 
			
		||||
    return !memcmp(lhs, rhs, rlen);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -175,8 +175,9 @@ JANET_CORE_FN(cfun_string_slice,
 | 
			
		||||
              "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. ") {
 | 
			
		||||
              "from the end of the string. Note that if `start` is negative it is "
 | 
			
		||||
              "exclusive, and if `end` is negative it is inclusive, to allow a full "
 | 
			
		||||
              "negative slice range.") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_stringv(view.bytes + range.start, range.end - range.start);
 | 
			
		||||
@@ -364,14 +365,13 @@ JANET_CORE_FN(cfun_string_findall,
 | 
			
		||||
 | 
			
		||||
struct replace_state {
 | 
			
		||||
    struct kmp_state kmp;
 | 
			
		||||
    const uint8_t *subst;
 | 
			
		||||
    int32_t substlen;
 | 
			
		||||
    Janet subst;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
 | 
			
		||||
    janet_arity(argc, 3, 4);
 | 
			
		||||
    JanetByteView pat = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView subst = janet_getbytes(argv, 1);
 | 
			
		||||
    Janet subst = argv[1];
 | 
			
		||||
    JanetByteView text = janet_getbytes(argv, 2);
 | 
			
		||||
    int32_t start = 0;
 | 
			
		||||
    if (argc == 4) {
 | 
			
		||||
@@ -380,13 +380,14 @@ static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
 | 
			
		||||
    }
 | 
			
		||||
    kmp_init(&s->kmp, text.bytes, text.len, pat.bytes, pat.len);
 | 
			
		||||
    s->kmp.i = start;
 | 
			
		||||
    s->subst = subst.bytes;
 | 
			
		||||
    s->substlen = subst.len;
 | 
			
		||||
    s->subst = subst;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_replace,
 | 
			
		||||
              "(string/replace patt subst str)",
 | 
			
		||||
              "Replace the first occurrence of `patt` with `subst` in the string `str`. "
 | 
			
		||||
              "If `subst` is a function, it will be called with `patt` only if a match is found, "
 | 
			
		||||
              "and should return the actual replacement text to use. "
 | 
			
		||||
              "Will return the new string if `patt` is found, otherwise returns `str`.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct replace_state s;
 | 
			
		||||
@@ -397,10 +398,11 @@ JANET_CORE_FN(cfun_string_replace,
 | 
			
		||||
        kmp_deinit(&s.kmp);
 | 
			
		||||
        return janet_stringv(s.kmp.text, s.kmp.textlen);
 | 
			
		||||
    }
 | 
			
		||||
    buf = janet_string_begin(s.kmp.textlen - s.kmp.patlen + s.substlen);
 | 
			
		||||
    JanetByteView subst = janet_text_substitution(&s.subst, s.kmp.text + result, s.kmp.patlen, NULL);
 | 
			
		||||
    buf = janet_string_begin(s.kmp.textlen - s.kmp.patlen + subst.len);
 | 
			
		||||
    safe_memcpy(buf, s.kmp.text, result);
 | 
			
		||||
    safe_memcpy(buf + result, s.subst, s.substlen);
 | 
			
		||||
    safe_memcpy(buf + result + s.substlen,
 | 
			
		||||
    safe_memcpy(buf + result, subst.bytes, subst.len);
 | 
			
		||||
    safe_memcpy(buf + result + subst.len,
 | 
			
		||||
                s.kmp.text + result + s.kmp.patlen,
 | 
			
		||||
                s.kmp.textlen - result - s.kmp.patlen);
 | 
			
		||||
    kmp_deinit(&s.kmp);
 | 
			
		||||
@@ -411,6 +413,8 @@ JANET_CORE_FN(cfun_string_replaceall,
 | 
			
		||||
              "(string/replace-all patt subst str)",
 | 
			
		||||
              "Replace all instances of `patt` with `subst` in the string `str`. Overlapping "
 | 
			
		||||
              "matches will not be counted, only the first match in such a span will be replaced. "
 | 
			
		||||
              "If `subst` is a function, it will be called with `patt` once for each match, "
 | 
			
		||||
              "and should return the actual replacement text to use. "
 | 
			
		||||
              "Will return the new string if `patt` is found, otherwise returns `str`.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct replace_state s;
 | 
			
		||||
@@ -419,8 +423,9 @@ JANET_CORE_FN(cfun_string_replaceall,
 | 
			
		||||
    replacesetup(argc, argv, &s);
 | 
			
		||||
    janet_buffer_init(&b, s.kmp.textlen);
 | 
			
		||||
    while ((result = kmp_next(&s.kmp)) >= 0) {
 | 
			
		||||
        JanetByteView subst = janet_text_substitution(&s.subst, s.kmp.text + result, s.kmp.patlen, NULL);
 | 
			
		||||
        janet_buffer_push_bytes(&b, s.kmp.text + lastindex, result - lastindex);
 | 
			
		||||
        janet_buffer_push_bytes(&b, s.subst, s.substlen);
 | 
			
		||||
        janet_buffer_push_bytes(&b, subst.bytes, subst.len);
 | 
			
		||||
        lastindex = result + s.kmp.patlen;
 | 
			
		||||
        kmp_seti(&s.kmp, lastindex);
 | 
			
		||||
    }
 | 
			
		||||
@@ -531,7 +536,30 @@ JANET_CORE_FN(cfun_string_join,
 | 
			
		||||
JANET_CORE_FN(cfun_string_format,
 | 
			
		||||
              "(string/format format & values)",
 | 
			
		||||
              "Similar to C's `snprintf`, but specialized for operating with Janet values. Returns "
 | 
			
		||||
              "a new string.") {
 | 
			
		||||
              "a new string.\n\n"
 | 
			
		||||
              "The following conversion specifiers are supported, where the upper case specifiers generate "
 | 
			
		||||
              "upper case output:\n"
 | 
			
		||||
              "- `c`: ASCII character.\n"
 | 
			
		||||
              "- `d`, `i`: integer, formatted as a decimal number.\n"
 | 
			
		||||
              "- `x`, `X`: integer, formatted as a hexadecimal number.\n"
 | 
			
		||||
              "- `o`: integer, formatted as an octal number.\n"
 | 
			
		||||
              "- `f`, `F`: floating point number, formatted as a decimal number.\n"
 | 
			
		||||
              "- `e`, `E`: floating point number, formatted in scientific notation.\n"
 | 
			
		||||
              "- `g`, `G`: floating point number, formatted in its shortest form.\n"
 | 
			
		||||
              "- `a`, `A`: floating point number, formatted as a hexadecimal number.\n"
 | 
			
		||||
              "- `s`: formatted as a string, precision indicates padding and maximum length.\n"
 | 
			
		||||
              "- `t`: emit the type of the given value.\n"
 | 
			
		||||
              "- `v`: format with (describe x)\n"
 | 
			
		||||
              "- `V`: format with (string x)\n"
 | 
			
		||||
              "- `j`: format to jdn (Janet data notation).\n"
 | 
			
		||||
              "\n"
 | 
			
		||||
              "The following conversion specifiers are used for \"pretty-printing\", where the upper-case "
 | 
			
		||||
              "variants generate colored output. These specifiers can take a precision "
 | 
			
		||||
              "argument to specify the maximum nesting depth to print.\n"
 | 
			
		||||
              "- `p`, `P`: pretty format, truncating if necessary\n"
 | 
			
		||||
              "- `m`, `M`: pretty format without truncating.\n"
 | 
			
		||||
              "- `q`, `Q`: pretty format on one line, truncating if necessary.\n"
 | 
			
		||||
              "- `n`, `N`: pretty format on one line without truncation.\n") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(0);
 | 
			
		||||
    const char *strfrmt = (const char *) janet_getstring(argv, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -34,9 +34,9 @@
 | 
			
		||||
 * because E is a valid digit in bases 15 or greater. For bases greater than
 | 
			
		||||
 * 10, the letters are used as digits. A through Z correspond to the digits 10
 | 
			
		||||
 * through 35, and the lowercase letters have the same values. The radix number
 | 
			
		||||
 * is always in base 10. For example, a hexidecimal number could be written
 | 
			
		||||
 * is always in base 10. For example, a hexadecimal number could be written
 | 
			
		||||
 * '16rdeadbeef'. janet_scan_number also supports some c style syntax for
 | 
			
		||||
 * hexidecimal literals. The previous number could also be written
 | 
			
		||||
 * hexadecimal literals. The previous number could also be written
 | 
			
		||||
 * '0xdeadbeef'.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@@ -301,6 +301,7 @@ int janet_scan_number_base(
 | 
			
		||||
    if (base == 0) {
 | 
			
		||||
        base = 10;
 | 
			
		||||
    }
 | 
			
		||||
    int exp_base = base;
 | 
			
		||||
 | 
			
		||||
    /* Skip leading zeros */
 | 
			
		||||
    while (str < end && (*str == '0' || *str == '.')) {
 | 
			
		||||
@@ -322,6 +323,12 @@ int janet_scan_number_base(
 | 
			
		||||
        } else if (*str == '&') {
 | 
			
		||||
            foundexp = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        } else if (base == 16 && (*str == 'P' || *str == 'p')) { /* IEEE hex float */
 | 
			
		||||
            foundexp = 1;
 | 
			
		||||
            exp_base = 10;
 | 
			
		||||
            base = 2;
 | 
			
		||||
            ex *= 4; /* We need to correct the current exponent after we change the base */
 | 
			
		||||
            break;
 | 
			
		||||
        } else if (base == 10 && (*str == 'E' || *str == 'e')) {
 | 
			
		||||
            foundexp = 1;
 | 
			
		||||
            break;
 | 
			
		||||
@@ -360,9 +367,9 @@ int janet_scan_number_base(
 | 
			
		||||
        }
 | 
			
		||||
        while (str < end) {
 | 
			
		||||
            int digit = digit_lookup[*str & 0x7F];
 | 
			
		||||
            if (*str > 127 || digit >= base) goto error;
 | 
			
		||||
            if (*str > 127 || digit >= exp_base) goto error;
 | 
			
		||||
            if (ee < (INT32_MAX / 40)) {
 | 
			
		||||
                ee = base * ee + digit;
 | 
			
		||||
                ee = exp_base * ee + digit;
 | 
			
		||||
            }
 | 
			
		||||
            str++;
 | 
			
		||||
            seenadigit = 1;
 | 
			
		||||
@@ -489,4 +496,53 @@ int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) {
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Similar to janet_scan_number but allows for
 | 
			
		||||
 * more numeric types with a given suffix. */
 | 
			
		||||
int janet_scan_numeric(
 | 
			
		||||
    const uint8_t *str,
 | 
			
		||||
    int32_t len,
 | 
			
		||||
    Janet *out) {
 | 
			
		||||
    int result;
 | 
			
		||||
    double num;
 | 
			
		||||
    int64_t i64 = 0;
 | 
			
		||||
    uint64_t u64 = 0;
 | 
			
		||||
    if (len < 2 || str[len - 2] != ':') {
 | 
			
		||||
        result = janet_scan_number_base(str, len, 0, &num);
 | 
			
		||||
        *out = janet_wrap_number(num);
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
    switch (str[len - 1]) {
 | 
			
		||||
        default:
 | 
			
		||||
            return 1;
 | 
			
		||||
        case 'n':
 | 
			
		||||
            result = janet_scan_number_base(str, len - 2, 0, &num);
 | 
			
		||||
            *out = janet_wrap_number(num);
 | 
			
		||||
            return result;
 | 
			
		||||
        /* Condition is inverted janet_scan_int64 and janet_scan_uint64 */
 | 
			
		||||
        case 's':
 | 
			
		||||
            result = !janet_scan_int64(str, len - 2, &i64);
 | 
			
		||||
            *out = janet_wrap_s64(i64);
 | 
			
		||||
            return result;
 | 
			
		||||
        case 'u':
 | 
			
		||||
            result = !janet_scan_uint64(str, len - 2, &u64);
 | 
			
		||||
            *out = janet_wrap_u64(u64);
 | 
			
		||||
            return result;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void janet_buffer_dtostr(JanetBuffer *buffer, double x) {
 | 
			
		||||
#define BUFSIZE 32
 | 
			
		||||
    janet_buffer_extra(buffer, BUFSIZE);
 | 
			
		||||
    int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%.17g", x);
 | 
			
		||||
#undef BUFSIZE
 | 
			
		||||
    /* fix locale issues with commas */
 | 
			
		||||
    for (int i = 0; i < count; i++) {
 | 
			
		||||
        char c = buffer->data[buffer->count + i];
 | 
			
		||||
        if (c == ',') {
 | 
			
		||||
            buffer->data[buffer->count + i] = '.';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    buffer->count += count;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -294,6 +294,16 @@ JANET_CORE_FN(cfun_struct_to_table,
 | 
			
		||||
    return janet_wrap_table(tab);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_struct_rawget,
 | 
			
		||||
              "(struct/rawget st key)",
 | 
			
		||||
              "Gets a value from a struct `st` without looking at the prototype struct. "
 | 
			
		||||
              "If `st` does not contain the key directly, the function will return "
 | 
			
		||||
              "nil without checking the prototype. Returns the value in the struct.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetStruct st = janet_getstruct(argv, 0);
 | 
			
		||||
    return janet_struct_rawget(st, argv[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load the struct module */
 | 
			
		||||
void janet_lib_struct(JanetTable *env) {
 | 
			
		||||
    JanetRegExt struct_cfuns[] = {
 | 
			
		||||
@@ -301,6 +311,7 @@ void janet_lib_struct(JanetTable *env) {
 | 
			
		||||
        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_CORE_REG("struct/rawget", cfun_struct_rawget),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, struct_cfuns);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -108,6 +108,7 @@ static const uint8_t **janet_symcache_findmem(
 | 
			
		||||
        }
 | 
			
		||||
notfound:
 | 
			
		||||
    *success = 0;
 | 
			
		||||
    janet_assert(firstEmpty != NULL, "symcache failed to get memory");
 | 
			
		||||
    return firstEmpty;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -233,6 +234,7 @@ const uint8_t *janet_symbol_gen(void) {
 | 
			
		||||
    head->hash = hash;
 | 
			
		||||
    sym = (uint8_t *)(head->data);
 | 
			
		||||
    memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter));
 | 
			
		||||
    sym[head->length] = 0;
 | 
			
		||||
    janet_symcache_put((const uint8_t *)sym, bucket);
 | 
			
		||||
    return (const uint8_t *)sym;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,7 +67,7 @@ static JanetTable *janet_table_init_impl(JanetTable *table, int32_t capacity, in
 | 
			
		||||
    return table;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Initialize a table (for use withs scratch memory) */
 | 
			
		||||
/* Initialize a table (for use with scratch memory) */
 | 
			
		||||
JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
 | 
			
		||||
    return janet_table_init_impl(table, capacity, 1);
 | 
			
		||||
}
 | 
			
		||||
@@ -87,11 +87,27 @@ void janet_table_deinit(JanetTable *table) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Create a new table */
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_table(int32_t capacity) {
 | 
			
		||||
    JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
 | 
			
		||||
    return janet_table_init_impl(table, capacity, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_table_weakk(int32_t capacity) {
 | 
			
		||||
    JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKK, sizeof(JanetTable));
 | 
			
		||||
    return janet_table_init_impl(table, capacity, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_table_weakv(int32_t capacity) {
 | 
			
		||||
    JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKV, sizeof(JanetTable));
 | 
			
		||||
    return janet_table_init_impl(table, capacity, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetTable *janet_table_weakkv(int32_t capacity) {
 | 
			
		||||
    JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE_WEAKKV, sizeof(JanetTable));
 | 
			
		||||
    return janet_table_init_impl(table, capacity, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find the bucket that contains the given key. Will also return
 | 
			
		||||
 * bucket where key should go if not in the table. */
 | 
			
		||||
JanetKV *janet_table_find(JanetTable *t, Janet key) {
 | 
			
		||||
@@ -111,12 +127,11 @@ static void janet_table_rehash(JanetTable *t, int32_t size) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    int32_t i, oldcapacity;
 | 
			
		||||
    oldcapacity = t->capacity;
 | 
			
		||||
    int32_t oldcapacity = t->capacity;
 | 
			
		||||
    t->data = newdata;
 | 
			
		||||
    t->capacity = size;
 | 
			
		||||
    t->deleted = 0;
 | 
			
		||||
    for (i = 0; i < oldcapacity; i++) {
 | 
			
		||||
    for (int32_t i = 0; i < oldcapacity; i++) {
 | 
			
		||||
        JanetKV *kv = olddata + i;
 | 
			
		||||
        if (!janet_checktype(kv->key, JANET_NIL)) {
 | 
			
		||||
            JanetKV *newkv = janet_table_find(t, kv->key);
 | 
			
		||||
@@ -298,12 +313,40 @@ JANET_CORE_FN(cfun_table_new,
 | 
			
		||||
              "Creates a new empty table with pre-allocated memory "
 | 
			
		||||
              "for `capacity` entries. This means that if one knows the number of "
 | 
			
		||||
              "entries going into a table on creation, extra memory allocation "
 | 
			
		||||
              "can be avoided. Returns the new table.") {
 | 
			
		||||
              "can be avoided. "
 | 
			
		||||
              "Returns the new table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getnat(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_table(cap));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_weak,
 | 
			
		||||
              "(table/weak capacity)",
 | 
			
		||||
              "Creates a new empty table with weak references to keys and values. Similar to `table/new`. "
 | 
			
		||||
              "Returns the new table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getnat(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_table_weakkv(cap));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_weak_keys,
 | 
			
		||||
              "(table/weak-keys capacity)",
 | 
			
		||||
              "Creates a new empty table with weak references to keys and normal references to values. Similar to `table/new`. "
 | 
			
		||||
              "Returns the new table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getnat(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_table_weakk(cap));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_weak_values,
 | 
			
		||||
              "(table/weak-values capacity)",
 | 
			
		||||
              "Creates a new empty table with normal references to keys and weak references to values. Similar to `table/new`. "
 | 
			
		||||
              "Returns the new table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getnat(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_table_weakv(cap));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_getproto,
 | 
			
		||||
              "(table/getproto tab)",
 | 
			
		||||
              "Get the prototype table of a table. Returns nil if the table "
 | 
			
		||||
@@ -329,12 +372,14 @@ JANET_CORE_FN(cfun_table_setproto,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
              "(table/to-struct tab &opt proto)",
 | 
			
		||||
              "Convert a table to a struct. Returns a new struct.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetTable *t = janet_gettable(argv, 0);
 | 
			
		||||
    return janet_wrap_struct(janet_table_to_struct(t));
 | 
			
		||||
    JanetStruct proto = janet_optstruct(argv, argc, 1, NULL);
 | 
			
		||||
    JanetStruct st = janet_table_to_struct(t);
 | 
			
		||||
    janet_struct_proto(st) = proto;
 | 
			
		||||
    return janet_wrap_struct(st);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_rawget,
 | 
			
		||||
@@ -377,6 +422,9 @@ JANET_CORE_FN(cfun_table_proto_flatten,
 | 
			
		||||
void janet_lib_table(JanetTable *env) {
 | 
			
		||||
    JanetRegExt table_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("table/new", cfun_table_new),
 | 
			
		||||
        JANET_CORE_REG("table/weak", cfun_table_weak),
 | 
			
		||||
        JANET_CORE_REG("table/weak-keys", cfun_table_weak_keys),
 | 
			
		||||
        JANET_CORE_REG("table/weak-values", cfun_table_weak_values),
 | 
			
		||||
        JANET_CORE_REG("table/to-struct", cfun_table_tostruct),
 | 
			
		||||
        JANET_CORE_REG("table/getproto", cfun_table_getproto),
 | 
			
		||||
        JANET_CORE_REG("table/setproto", cfun_table_setproto),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -69,9 +69,9 @@ JANET_CORE_FN(cfun_tuple_slice,
 | 
			
		||||
              "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.") {
 | 
			
		||||
              "from the end of the input. Note that if `start` is negative it is "
 | 
			
		||||
              "exclusive, and if `end` is negative it is inclusive, to allow a full "
 | 
			
		||||
              "negative slice range. Returns the new tuple.") {
 | 
			
		||||
    JanetView view = janet_getindexed(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_n(view.items + range.start, range.end - range.start));
 | 
			
		||||
@@ -116,6 +116,34 @@ JANET_CORE_FN(cfun_tuple_setmap,
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_tuple_join,
 | 
			
		||||
              "(tuple/join & parts)",
 | 
			
		||||
              "Create a tuple by joining together other tuples and arrays.") {
 | 
			
		||||
    janet_arity(argc, 0, -1);
 | 
			
		||||
    int32_t total_len = 0;
 | 
			
		||||
    for (int32_t i = 0; i < argc; i++) {
 | 
			
		||||
        int32_t len = 0;
 | 
			
		||||
        const Janet *vals = NULL;
 | 
			
		||||
        if (!janet_indexed_view(argv[i], &vals, &len)) {
 | 
			
		||||
            janet_panicf("expected indexed type for argument %d, got %v", i, argv[i]);
 | 
			
		||||
        }
 | 
			
		||||
        if (INT32_MAX - total_len < len) {
 | 
			
		||||
            janet_panic("tuple too large");
 | 
			
		||||
        }
 | 
			
		||||
        total_len += len;
 | 
			
		||||
    }
 | 
			
		||||
    Janet *tup = janet_tuple_begin(total_len);
 | 
			
		||||
    Janet *tup_cursor = tup;
 | 
			
		||||
    for (int32_t i = 0; i < argc; i++) {
 | 
			
		||||
        int32_t len = 0;
 | 
			
		||||
        const Janet *vals = NULL;
 | 
			
		||||
        janet_indexed_view(argv[i], &vals, &len);
 | 
			
		||||
        memcpy(tup_cursor, vals, len * sizeof(Janet));
 | 
			
		||||
        tup_cursor += len;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_end(tup));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Load the tuple module */
 | 
			
		||||
void janet_lib_tuple(JanetTable *env) {
 | 
			
		||||
    JanetRegExt tuple_cfuns[] = {
 | 
			
		||||
@@ -124,6 +152,7 @@ void janet_lib_tuple(JanetTable *env) {
 | 
			
		||||
        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_CORE_REG("tuple/join", cfun_tuple_join),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, tuple_cfuns);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										237
									
								
								src/core/util.c
									
									
									
									
									
								
							
							
						
						
									
										237
									
								
								src/core/util.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,9 +39,11 @@
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#ifdef JANET_DYNAMIC_MODULES
 | 
			
		||||
#include <psapi.h>
 | 
			
		||||
#ifdef JANET_MSVC
 | 
			
		||||
#pragma comment (lib, "Psapi.lib")
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_APPLE
 | 
			
		||||
#include <AvailabilityMacros.h>
 | 
			
		||||
@@ -77,6 +79,7 @@ const char *const janet_type_names[16] = {
 | 
			
		||||
    "pointer"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Docstring for signal lists these */
 | 
			
		||||
const char *const janet_signal_names[14] = {
 | 
			
		||||
    "ok",
 | 
			
		||||
    "error",
 | 
			
		||||
@@ -90,10 +93,11 @@ const char *const janet_signal_names[14] = {
 | 
			
		||||
    "user5",
 | 
			
		||||
    "user6",
 | 
			
		||||
    "user7",
 | 
			
		||||
    "user8",
 | 
			
		||||
    "user9"
 | 
			
		||||
    "interrupt",
 | 
			
		||||
    "await"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Docstring for fiber/status lists these */
 | 
			
		||||
const char *const janet_status_names[16] = {
 | 
			
		||||
    "dead",
 | 
			
		||||
    "error",
 | 
			
		||||
@@ -107,19 +111,26 @@ const char *const janet_status_names[16] = {
 | 
			
		||||
    "user5",
 | 
			
		||||
    "user6",
 | 
			
		||||
    "user7",
 | 
			
		||||
    "user8",
 | 
			
		||||
    "user9",
 | 
			
		||||
    "interrupted",
 | 
			
		||||
    "suspended",
 | 
			
		||||
    "new",
 | 
			
		||||
    "alive"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
uint32_t janet_hash_mix(uint32_t input, uint32_t more) {
 | 
			
		||||
    uint32_t mix1 = (more + 0x9e3779b9 + (input << 6) + (input >> 2));
 | 
			
		||||
    return input ^ (0x9e3779b9 + (mix1 << 6) + (mix1 >> 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_PRF
 | 
			
		||||
 | 
			
		||||
int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
 | 
			
		||||
    if (NULL == str || len == 0) return 5381;
 | 
			
		||||
    const uint8_t *end = str + len;
 | 
			
		||||
    uint32_t hash = 5381;
 | 
			
		||||
    while (str < end)
 | 
			
		||||
        hash = (hash << 5) + hash + *str++;
 | 
			
		||||
    hash = janet_hash_mix(hash, (uint32_t) len);
 | 
			
		||||
    return (int32_t) hash;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -235,11 +246,6 @@ int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
uint32_t janet_hash_mix(uint32_t input, uint32_t more) {
 | 
			
		||||
    uint32_t mix1 = (more + 0x9e3779b9 + (input << 6) + (input >> 2));
 | 
			
		||||
    return input ^ (0x9e3779b9 + (mix1 << 6) + (mix1 >> 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Computes hash of an array of values */
 | 
			
		||||
int32_t janet_array_calchash(const Janet *array, int32_t len) {
 | 
			
		||||
    const Janet *end = array + len;
 | 
			
		||||
@@ -496,7 +502,7 @@ typedef struct {
 | 
			
		||||
static void namebuf_init(NameBuf *namebuf, const char *prefix) {
 | 
			
		||||
    size_t plen = strlen(prefix);
 | 
			
		||||
    namebuf->plen = plen;
 | 
			
		||||
    namebuf->buf = janet_malloc(namebuf->plen + 256);
 | 
			
		||||
    namebuf->buf = janet_smalloc(namebuf->plen + 256);
 | 
			
		||||
    if (NULL == namebuf->buf) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -505,12 +511,12 @@ static void namebuf_init(NameBuf *namebuf, const char *prefix) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void namebuf_deinit(NameBuf *namebuf) {
 | 
			
		||||
    janet_free(namebuf->buf);
 | 
			
		||||
    janet_sfree(namebuf->buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static char *namebuf_name(NameBuf *namebuf, const char *suffix) {
 | 
			
		||||
    size_t slen = strlen(suffix);
 | 
			
		||||
    namebuf->buf = janet_realloc(namebuf->buf, namebuf->plen + 2 + slen);
 | 
			
		||||
    namebuf->buf = janet_srealloc(namebuf->buf, namebuf->plen + 2 + slen);
 | 
			
		||||
    if (NULL == namebuf->buf) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
@@ -660,6 +666,59 @@ JanetBinding janet_binding_from_entry(Janet entry) {
 | 
			
		||||
    return binding;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* If the value at the given address can be coerced to a byte view,
 | 
			
		||||
   return that byte view. If it can't, replace the value at the address
 | 
			
		||||
   with the result of janet_to_string, and return a byte view over that
 | 
			
		||||
   string. */
 | 
			
		||||
static JanetByteView memoize_byte_view(Janet *value) {
 | 
			
		||||
    JanetByteView result;
 | 
			
		||||
    if (!janet_bytes_view(*value, &result.bytes, &result.len)) {
 | 
			
		||||
        JanetString str = janet_to_string(*value);
 | 
			
		||||
        *value = janet_wrap_string(str);
 | 
			
		||||
        result.bytes = str;
 | 
			
		||||
        result.len = janet_string_length(str);
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetByteView to_byte_view(Janet value) {
 | 
			
		||||
    JanetByteView result;
 | 
			
		||||
    if (!janet_bytes_view(value, &result.bytes, &result.len)) {
 | 
			
		||||
        JanetString str = janet_to_string(value);
 | 
			
		||||
        result.bytes = str;
 | 
			
		||||
        result.len = janet_string_length(str);
 | 
			
		||||
    }
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetByteView janet_text_substitution(
 | 
			
		||||
    Janet *subst,
 | 
			
		||||
    const uint8_t *bytes,
 | 
			
		||||
    uint32_t len,
 | 
			
		||||
    JanetArray *extra_argv) {
 | 
			
		||||
    int32_t extra_argc = extra_argv == NULL ? 0 : extra_argv->count;
 | 
			
		||||
    JanetType type = janet_type(*subst);
 | 
			
		||||
    switch (type) {
 | 
			
		||||
        case JANET_FUNCTION:
 | 
			
		||||
        case JANET_CFUNCTION: {
 | 
			
		||||
            int32_t argc = 1 + extra_argc;
 | 
			
		||||
            Janet *argv = janet_tuple_begin(argc);
 | 
			
		||||
            argv[0] = janet_stringv(bytes, len);
 | 
			
		||||
            for (int32_t i = 0; i < extra_argc; i++) {
 | 
			
		||||
                argv[i + 1] = extra_argv->data[i];
 | 
			
		||||
            }
 | 
			
		||||
            janet_tuple_end(argv);
 | 
			
		||||
            if (type == JANET_FUNCTION) {
 | 
			
		||||
                return to_byte_view(janet_call(janet_unwrap_function(*subst), argc, argv));
 | 
			
		||||
            } else {
 | 
			
		||||
                return to_byte_view(janet_unwrap_cfunction(*subst)(argc, argv));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        default:
 | 
			
		||||
            return memoize_byte_view(subst);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
 | 
			
		||||
    Janet entry = janet_table_get(env, janet_wrap_symbol(sym));
 | 
			
		||||
    return janet_binding_from_entry(entry);
 | 
			
		||||
@@ -701,15 +760,25 @@ int janet_indexed_view(Janet seq, const Janet **data, int32_t *len) {
 | 
			
		||||
/* Read both strings and buffer as unsigned character array + int32_t len.
 | 
			
		||||
 * Returns 1 if the view can be constructed and 0 if the type is invalid. */
 | 
			
		||||
int janet_bytes_view(Janet str, const uint8_t **data, int32_t *len) {
 | 
			
		||||
    if (janet_checktype(str, JANET_STRING) || janet_checktype(str, JANET_SYMBOL) ||
 | 
			
		||||
            janet_checktype(str, JANET_KEYWORD)) {
 | 
			
		||||
    JanetType t = janet_type(str);
 | 
			
		||||
    if (t == JANET_STRING || t == JANET_SYMBOL || t == JANET_KEYWORD) {
 | 
			
		||||
        *data = janet_unwrap_string(str);
 | 
			
		||||
        *len = janet_string_length(janet_unwrap_string(str));
 | 
			
		||||
        return 1;
 | 
			
		||||
    } else if (janet_checktype(str, JANET_BUFFER)) {
 | 
			
		||||
    } else if (t == JANET_BUFFER) {
 | 
			
		||||
        *data = janet_unwrap_buffer(str)->data;
 | 
			
		||||
        *len = janet_unwrap_buffer(str)->count;
 | 
			
		||||
        return 1;
 | 
			
		||||
    } else if (t == JANET_ABSTRACT) {
 | 
			
		||||
        void *abst = janet_unwrap_abstract(str);
 | 
			
		||||
        const JanetAbstractType *atype = janet_abstract_type(abst);
 | 
			
		||||
        if (NULL == atype->bytes) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        JanetByteView view = atype->bytes(abst, janet_abstract_size(abst));
 | 
			
		||||
        *data = view.bytes;
 | 
			
		||||
        *len = view.len;
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -739,6 +808,13 @@ int janet_checkint(Janet x) {
 | 
			
		||||
    return janet_checkintrange(dval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_checkuint(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_NUMBER))
 | 
			
		||||
        return 0;
 | 
			
		||||
    double dval = janet_unwrap_number(x);
 | 
			
		||||
    return janet_checkuintrange(dval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_checkint64(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_NUMBER))
 | 
			
		||||
        return 0;
 | 
			
		||||
@@ -750,7 +826,21 @@ int janet_checkuint64(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_NUMBER))
 | 
			
		||||
        return 0;
 | 
			
		||||
    double dval = janet_unwrap_number(x);
 | 
			
		||||
    return dval >= 0 && dval <= JANET_INTMAX_DOUBLE && dval == (uint64_t) dval;
 | 
			
		||||
    return janet_checkuint64range(dval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_checkint16(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_NUMBER))
 | 
			
		||||
        return 0;
 | 
			
		||||
    double dval = janet_unwrap_number(x);
 | 
			
		||||
    return janet_checkint16range(dval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_checkuint16(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_NUMBER))
 | 
			
		||||
        return 0;
 | 
			
		||||
    double dval = janet_unwrap_number(x);
 | 
			
		||||
    return janet_checkuint16range(dval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_checksize(Janet x) {
 | 
			
		||||
@@ -809,38 +899,91 @@ int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffe
 | 
			
		||||
/* Clock shims for various platforms */
 | 
			
		||||
#ifdef JANET_GETTIME
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    FILETIME ftime;
 | 
			
		||||
    GetSystemTimeAsFileTime(&ftime);
 | 
			
		||||
    int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
 | 
			
		||||
    /* Windows epoch is January 1, 1601 apparently */
 | 
			
		||||
    wintime -= 116444736000000000LL;
 | 
			
		||||
    spec->tv_sec  = wintime / 10000000LL;
 | 
			
		||||
    /* Resolution is 100 nanoseconds. */
 | 
			
		||||
    spec->tv_nsec = wintime % 10000000LL * 100;
 | 
			
		||||
#include <profileapi.h>
 | 
			
		||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
 | 
			
		||||
    if (source == JANET_TIME_REALTIME) {
 | 
			
		||||
        FILETIME ftime;
 | 
			
		||||
        GetSystemTimeAsFileTime(&ftime);
 | 
			
		||||
        int64_t wintime = (int64_t)(ftime.dwLowDateTime) | ((int64_t)(ftime.dwHighDateTime) << 32);
 | 
			
		||||
        /* Windows epoch is January 1, 1601 apparently */
 | 
			
		||||
        wintime -= 116444736000000000LL;
 | 
			
		||||
        spec->tv_sec  = wintime / 10000000LL;
 | 
			
		||||
        /* Resolution is 100 nanoseconds. */
 | 
			
		||||
        spec->tv_nsec = wintime % 10000000LL * 100;
 | 
			
		||||
    } else if (source == JANET_TIME_MONOTONIC) {
 | 
			
		||||
        LARGE_INTEGER count;
 | 
			
		||||
        LARGE_INTEGER perf_freq;
 | 
			
		||||
        QueryPerformanceCounter(&count);
 | 
			
		||||
        QueryPerformanceFrequency(&perf_freq);
 | 
			
		||||
        spec->tv_sec = count.QuadPart / perf_freq.QuadPart;
 | 
			
		||||
        spec->tv_nsec = (long)((count.QuadPart % perf_freq.QuadPart) * 1000000000 / perf_freq.QuadPart);
 | 
			
		||||
    } else if (source == JANET_TIME_CPUTIME) {
 | 
			
		||||
        FILETIME creationTime, exitTime, kernelTime, userTime;
 | 
			
		||||
        GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime);
 | 
			
		||||
        int64_t tmp = ((int64_t)userTime.dwHighDateTime << 32) + userTime.dwLowDateTime;
 | 
			
		||||
        spec->tv_sec = tmp / 10000000LL;
 | 
			
		||||
        spec->tv_nsec = tmp % 10000000LL * 100;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
/* clock_gettime() wasn't available on Mac until 10.12. */
 | 
			
		||||
#elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12)
 | 
			
		||||
#include <mach/clock.h>
 | 
			
		||||
#include <mach/mach.h>
 | 
			
		||||
int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    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;
 | 
			
		||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
 | 
			
		||||
    if (source == JANET_TIME_REALTIME) {
 | 
			
		||||
        clock_serv_t cclock;
 | 
			
		||||
        mach_timespec_t mts;
 | 
			
		||||
        host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
 | 
			
		||||
        clock_get_time(cclock, &mts);
 | 
			
		||||
        mach_port_deallocate(mach_task_self(), cclock);
 | 
			
		||||
        spec->tv_sec = mts.tv_sec;
 | 
			
		||||
        spec->tv_nsec = mts.tv_nsec;
 | 
			
		||||
    } else if (source == JANET_TIME_MONOTONIC) {
 | 
			
		||||
        clock_serv_t cclock;
 | 
			
		||||
        int nsecs;
 | 
			
		||||
        mach_msg_type_number_t count;
 | 
			
		||||
        host_get_clock_service(mach_host_self(), clock, &cclock);
 | 
			
		||||
        clock_get_attributes(cclock, CLOCK_GET_TIME_RES, (clock_attr_t)&nsecs, &count);
 | 
			
		||||
        mach_port_deallocate(mach_task_self(), cclock);
 | 
			
		||||
        clock_getres(CLOCK_MONOTONIC, spec);
 | 
			
		||||
    }
 | 
			
		||||
    if (source == JANET_TIME_CPUTIME) {
 | 
			
		||||
        clock_t tmp = clock();
 | 
			
		||||
        spec->tv_sec = tmp;
 | 
			
		||||
        spec->tv_nsec = (tmp - spec->tv_sec) * 1.0e9;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    return clock_gettime(CLOCK_REALTIME, spec);
 | 
			
		||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source) {
 | 
			
		||||
    clockid_t cid = CLOCK_REALTIME;
 | 
			
		||||
    if (source == JANET_TIME_REALTIME) {
 | 
			
		||||
        cid = CLOCK_REALTIME;
 | 
			
		||||
    } else if (source == JANET_TIME_MONOTONIC) {
 | 
			
		||||
        cid = CLOCK_MONOTONIC;
 | 
			
		||||
    } else if (source == JANET_TIME_CPUTIME) {
 | 
			
		||||
        cid = CLOCK_PROCESS_CPUTIME_ID;
 | 
			
		||||
    }
 | 
			
		||||
    return clock_gettime(cid, spec);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Better strerror (thread-safe if available) */
 | 
			
		||||
const char *janet_strerror(int e) {
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    /* Microsoft strerror seems sane here and is thread safe by default */
 | 
			
		||||
    return strerror(e);
 | 
			
		||||
#elif defined(__GLIBC__)
 | 
			
		||||
    /* See https://linux.die.net/man/3/strerror_r */
 | 
			
		||||
    return strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf));
 | 
			
		||||
#else
 | 
			
		||||
    strerror_r(e, janet_vm.strerror_buf, sizeof(janet_vm.strerror_buf));
 | 
			
		||||
    return janet_vm.strerror_buf;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Setting C99 standard makes this not available, but it should
 | 
			
		||||
 * work/link properly if we detect a BSD */
 | 
			
		||||
#if defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
 | 
			
		||||
@@ -848,18 +991,22 @@ void arc4random_buf(void *buf, size_t nbytes);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int janet_cryptorand(uint8_t *out, size_t n) {
 | 
			
		||||
#ifndef JANET_NO_CRYPTORAND
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    for (size_t i = 0; i < n; i += sizeof(unsigned int)) {
 | 
			
		||||
        unsigned int v;
 | 
			
		||||
        if (rand_s(&v))
 | 
			
		||||
            return -1;
 | 
			
		||||
        for (int32_t j = 0; (j < sizeof(unsigned int)) && (i + j < n); j++) {
 | 
			
		||||
        for (int32_t j = 0; (j < (int32_t) sizeof(unsigned int)) && (i + j < n); j++) {
 | 
			
		||||
            out[i + j] = v & 0xff;
 | 
			
		||||
            v = v >> 8;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
#elif defined(JANET_LINUX) || ( defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_7) )
 | 
			
		||||
#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
 | 
			
		||||
    arc4random_buf(out, n);
 | 
			
		||||
    return 0;
 | 
			
		||||
#else
 | 
			
		||||
    /* We should be able to call getrandom on linux, but it doesn't seem
 | 
			
		||||
       to be uniformly supported on linux distros.
 | 
			
		||||
       On Mac, arc4random_buf wasn't available on until 10.7.
 | 
			
		||||
@@ -881,12 +1028,10 @@ int janet_cryptorand(uint8_t *out, size_t n) {
 | 
			
		||||
    }
 | 
			
		||||
    RETRY_EINTR(rc, close(randfd));
 | 
			
		||||
    return 0;
 | 
			
		||||
#elif defined(JANET_BSD) || defined(MAC_OS_X_VERSION_10_7)
 | 
			
		||||
    arc4random_buf(out, n);
 | 
			
		||||
    return 0;
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
    (void) n;
 | 
			
		||||
    (void) out;
 | 
			
		||||
    (void) n;
 | 
			
		||||
    return -1;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
@@ -910,6 +1055,13 @@ char *get_processed_name(const char *name) {
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(JANET_NO_DYNAMIC_MODULES)
 | 
			
		||||
 | 
			
		||||
const char *error_clib(void) {
 | 
			
		||||
    return "dynamic modules not supported";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
#if defined(JANET_WINDOWS)
 | 
			
		||||
 | 
			
		||||
static char error_clib_buf[256];
 | 
			
		||||
@@ -957,6 +1109,7 @@ void *symbol_clib(HINSTANCE clib, const char *sym) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Alloc function macro fills */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,7 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
@@ -49,11 +50,11 @@
 | 
			
		||||
#ifndef JANET_EXIT
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#define JANET_EXIT(m) do { \
 | 
			
		||||
    fprintf(stderr, "C runtime error at line %d in file %s: %s\n",\
 | 
			
		||||
    fprintf(stderr, "janet internal error at line %d in file %s: %s\n",\
 | 
			
		||||
        __LINE__,\
 | 
			
		||||
        __FILE__,\
 | 
			
		||||
        (m));\
 | 
			
		||||
    exit(1);\
 | 
			
		||||
    abort();\
 | 
			
		||||
} while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -80,6 +81,8 @@ void janet_memempty(JanetKV *mem, int32_t count);
 | 
			
		||||
void *janet_memalloc_empty(int32_t count);
 | 
			
		||||
JanetTable *janet_get_core_table(const char *name);
 | 
			
		||||
void janet_def_addflags(JanetFuncDef *def);
 | 
			
		||||
void janet_buffer_dtostr(JanetBuffer *buffer, double x);
 | 
			
		||||
const char *janet_strerror(int e);
 | 
			
		||||
const void *janet_strbinsearch(
 | 
			
		||||
    const void *tab,
 | 
			
		||||
    size_t tabcount,
 | 
			
		||||
@@ -93,6 +96,11 @@ void janet_buffer_format(
 | 
			
		||||
    Janet *argv);
 | 
			
		||||
Janet janet_next_impl(Janet ds, Janet key, int is_interpreter);
 | 
			
		||||
JanetBinding janet_binding_from_entry(Janet entry);
 | 
			
		||||
JanetByteView janet_text_substitution(
 | 
			
		||||
    Janet *subst,
 | 
			
		||||
    const uint8_t *bytes,
 | 
			
		||||
    uint32_t len,
 | 
			
		||||
    JanetArray *extra_args);
 | 
			
		||||
 | 
			
		||||
/* Registry functions */
 | 
			
		||||
void janet_registry_put(
 | 
			
		||||
@@ -121,7 +129,12 @@ void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetReg
 | 
			
		||||
 | 
			
		||||
/* Clock gettime */
 | 
			
		||||
#ifdef JANET_GETTIME
 | 
			
		||||
int janet_gettime(struct timespec *spec);
 | 
			
		||||
enum JanetTimeSource {
 | 
			
		||||
    JANET_TIME_REALTIME,
 | 
			
		||||
    JANET_TIME_MONOTONIC,
 | 
			
		||||
    JANET_TIME_CPUTIME
 | 
			
		||||
};
 | 
			
		||||
int janet_gettime(struct timespec *spec, enum JanetTimeSource source);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* strdup */
 | 
			
		||||
@@ -129,13 +142,13 @@ int janet_gettime(struct timespec *spec);
 | 
			
		||||
#define strdup(x) _strdup(x)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
 | 
			
		||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libraries
 | 
			
		||||
 * with native code. */
 | 
			
		||||
#if defined(JANET_NO_DYNAMIC_MODULES)
 | 
			
		||||
typedef int Clib;
 | 
			
		||||
#define load_clib(name) ((void) name, 0)
 | 
			
		||||
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
 | 
			
		||||
#define error_clib() "dynamic libraries not supported"
 | 
			
		||||
const char *error_clib(void);
 | 
			
		||||
#define free_clib(c) ((void) (c), 0)
 | 
			
		||||
#elif defined(JANET_WINDOWS)
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
@@ -150,7 +163,7 @@ typedef void *Clib;
 | 
			
		||||
#define load_clib(name) dlopen((name), RTLD_NOW)
 | 
			
		||||
#define free_clib(lib) dlclose((lib))
 | 
			
		||||
#define symbol_clib(lib, sym) dlsym((lib), (sym))
 | 
			
		||||
#define error_clib() dlerror()
 | 
			
		||||
#define error_clib dlerror
 | 
			
		||||
#endif
 | 
			
		||||
char *get_processed_name(const char *name);
 | 
			
		||||
 | 
			
		||||
@@ -177,9 +190,6 @@ void janet_lib_debug(JanetTable *env);
 | 
			
		||||
#ifdef JANET_PEG
 | 
			
		||||
void janet_lib_peg(JanetTable *env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_TYPED_ARRAY
 | 
			
		||||
void janet_lib_typed_array(JanetTable *env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
void janet_lib_inttypes(JanetTable *env);
 | 
			
		||||
#endif
 | 
			
		||||
@@ -190,7 +200,11 @@ extern const JanetAbstractType janet_address_type;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
void janet_lib_ev(JanetTable *env);
 | 
			
		||||
void janet_ev_mark(void);
 | 
			
		||||
void janet_async_start_fiber(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
 | 
			
		||||
int janet_make_pipe(JanetHandle handles[2], int mode);
 | 
			
		||||
#ifdef JANET_FILEWATCH
 | 
			
		||||
void janet_lib_filewatch(JanetTable *env);
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI
 | 
			
		||||
void janet_lib_ffi(JanetTable *env);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
@@ -272,6 +272,7 @@ int janet_equals(Janet x, Janet y) {
 | 
			
		||||
                const Janet *t1 = janet_unwrap_tuple(x);
 | 
			
		||||
                const Janet *t2 = janet_unwrap_tuple(y);
 | 
			
		||||
                if (t1 == t2) break;
 | 
			
		||||
                if (JANET_TUPLE_FLAG_BRACKETCTOR & (janet_tuple_flag(t1) ^ janet_tuple_flag(t2))) return 0;
 | 
			
		||||
                if (janet_tuple_hash(t1) != janet_tuple_hash(t2)) return 0;
 | 
			
		||||
                if (janet_tuple_length(t1) != janet_tuple_length(t2)) return 0;
 | 
			
		||||
                push_traversal_node(janet_tuple_head(t1), janet_tuple_head(t2), 0);
 | 
			
		||||
@@ -321,6 +322,7 @@ int32_t janet_hash(Janet x) {
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_TUPLE:
 | 
			
		||||
            hash = janet_tuple_hash(janet_unwrap_tuple(x));
 | 
			
		||||
            hash += (janet_tuple_flag(janet_unwrap_tuple(x)) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : 0;
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_STRUCT:
 | 
			
		||||
            hash = janet_struct_hash(janet_unwrap_struct(x));
 | 
			
		||||
@@ -412,6 +414,9 @@ int janet_compare(Janet x, Janet y) {
 | 
			
		||||
            case JANET_TUPLE: {
 | 
			
		||||
                const Janet *lhs = janet_unwrap_tuple(x);
 | 
			
		||||
                const Janet *rhs = janet_unwrap_tuple(y);
 | 
			
		||||
                if (JANET_TUPLE_FLAG_BRACKETCTOR & (janet_tuple_flag(lhs) ^ janet_tuple_flag(rhs))) {
 | 
			
		||||
                    return (janet_tuple_flag(lhs) & JANET_TUPLE_FLAG_BRACKETCTOR) ? 1 : -1;
 | 
			
		||||
                }
 | 
			
		||||
                push_traversal_node(janet_tuple_head(lhs), janet_tuple_head(rhs), 1);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -434,20 +439,21 @@ int janet_compare(Janet x, Janet y) {
 | 
			
		||||
    return status - 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t getter_checkint(Janet key, int32_t max) {
 | 
			
		||||
static int32_t getter_checkint(JanetType type, Janet key, int32_t max) {
 | 
			
		||||
    if (!janet_checkint(key)) goto bad;
 | 
			
		||||
    int32_t ret = janet_unwrap_integer(key);
 | 
			
		||||
    if (ret < 0) goto bad;
 | 
			
		||||
    if (ret >= max) goto bad;
 | 
			
		||||
    return ret;
 | 
			
		||||
bad:
 | 
			
		||||
    janet_panicf("expected integer key in range [0, %d), got %v", max, key);
 | 
			
		||||
    janet_panicf("expected integer key for %s in range [0, %d), got %v", janet_type_names[type], max, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Gets a value and returns. Can panic. */
 | 
			
		||||
Janet janet_in(Janet ds, Janet key) {
 | 
			
		||||
    Janet value;
 | 
			
		||||
    switch (janet_type(ds)) {
 | 
			
		||||
    JanetType type = janet_type(ds);
 | 
			
		||||
    switch (type) {
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, ds);
 | 
			
		||||
            break;
 | 
			
		||||
@@ -459,19 +465,19 @@ Janet janet_in(Janet ds, Janet key) {
 | 
			
		||||
            break;
 | 
			
		||||
        case JANET_ARRAY: {
 | 
			
		||||
            JanetArray *array = janet_unwrap_array(ds);
 | 
			
		||||
            int32_t index = getter_checkint(key, array->count);
 | 
			
		||||
            int32_t index = getter_checkint(type, key, array->count);
 | 
			
		||||
            value = array->data[index];
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_TUPLE: {
 | 
			
		||||
            const Janet *tuple = janet_unwrap_tuple(ds);
 | 
			
		||||
            int32_t len = janet_tuple_length(tuple);
 | 
			
		||||
            value = tuple[getter_checkint(key, len)];
 | 
			
		||||
            value = tuple[getter_checkint(type, key, len)];
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_BUFFER: {
 | 
			
		||||
            JanetBuffer *buffer = janet_unwrap_buffer(ds);
 | 
			
		||||
            int32_t index = getter_checkint(key, buffer->count);
 | 
			
		||||
            int32_t index = getter_checkint(type, key, buffer->count);
 | 
			
		||||
            value = janet_wrap_integer(buffer->data[index]);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -479,7 +485,7 @@ Janet janet_in(Janet ds, Janet key) {
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
        case JANET_KEYWORD: {
 | 
			
		||||
            const uint8_t *str = janet_unwrap_string(ds);
 | 
			
		||||
            int32_t index = getter_checkint(key, janet_string_length(str));
 | 
			
		||||
            int32_t index = getter_checkint(type, key, janet_string_length(str));
 | 
			
		||||
            value = janet_wrap_integer(str[index]);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -651,6 +657,15 @@ int32_t janet_length(Janet x) {
 | 
			
		||||
        case JANET_TABLE:
 | 
			
		||||
            return janet_unwrap_table(x)->count;
 | 
			
		||||
        case JANET_ABSTRACT: {
 | 
			
		||||
            void *abst = janet_unwrap_abstract(x);
 | 
			
		||||
            const JanetAbstractType *type = janet_abstract_type(abst);
 | 
			
		||||
            if (type->length != NULL) {
 | 
			
		||||
                size_t len = type->length(abst, janet_abstract_size(abst));
 | 
			
		||||
                if (len > INT32_MAX) {
 | 
			
		||||
                    janet_panicf("invalid integer length %u", len);
 | 
			
		||||
                }
 | 
			
		||||
                return (int32_t)(len);
 | 
			
		||||
            }
 | 
			
		||||
            Janet argv[1] = { x };
 | 
			
		||||
            Janet len = janet_mcall("length", 1, argv);
 | 
			
		||||
            if (!janet_checkint(len))
 | 
			
		||||
@@ -679,6 +694,21 @@ Janet janet_lengthv(Janet x) {
 | 
			
		||||
        case JANET_TABLE:
 | 
			
		||||
            return janet_wrap_integer(janet_unwrap_table(x)->count);
 | 
			
		||||
        case JANET_ABSTRACT: {
 | 
			
		||||
            void *abst = janet_unwrap_abstract(x);
 | 
			
		||||
            const JanetAbstractType *type = janet_abstract_type(abst);
 | 
			
		||||
            if (type->length != NULL) {
 | 
			
		||||
                size_t len = type->length(abst, janet_abstract_size(abst));
 | 
			
		||||
                /* If len is always less then double, we can never overflow */
 | 
			
		||||
#ifdef JANET_32
 | 
			
		||||
                return janet_wrap_number(len);
 | 
			
		||||
#else
 | 
			
		||||
                if (len < (size_t) JANET_INTMAX_INT64) {
 | 
			
		||||
                    return janet_wrap_number((double) len);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_panicf("integer length %u too large", len);
 | 
			
		||||
                }
 | 
			
		||||
#endif
 | 
			
		||||
            }
 | 
			
		||||
            Janet argv[1] = { x };
 | 
			
		||||
            return janet_mcall("length", 1, argv);
 | 
			
		||||
        }
 | 
			
		||||
@@ -728,13 +758,14 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_put(Janet ds, Janet key, Janet value) {
 | 
			
		||||
    switch (janet_type(ds)) {
 | 
			
		||||
    JanetType type = janet_type(ds);
 | 
			
		||||
    switch (type) {
 | 
			
		||||
        default:
 | 
			
		||||
            janet_panicf("expected %T, got %v",
 | 
			
		||||
                         JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds);
 | 
			
		||||
        case JANET_ARRAY: {
 | 
			
		||||
            JanetArray *array = janet_unwrap_array(ds);
 | 
			
		||||
            int32_t index = getter_checkint(key, INT32_MAX - 1);
 | 
			
		||||
            int32_t index = getter_checkint(type, key, INT32_MAX - 1);
 | 
			
		||||
            if (index >= array->count) {
 | 
			
		||||
                janet_array_setcount(array, index + 1);
 | 
			
		||||
            }
 | 
			
		||||
@@ -743,7 +774,7 @@ void janet_put(Janet ds, Janet key, Janet value) {
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_BUFFER: {
 | 
			
		||||
            JanetBuffer *buffer = janet_unwrap_buffer(ds);
 | 
			
		||||
            int32_t index = getter_checkint(key, INT32_MAX - 1);
 | 
			
		||||
            int32_t index = getter_checkint(type, key, INT32_MAX - 1);
 | 
			
		||||
            if (!janet_checkint(value))
 | 
			
		||||
                janet_panicf("can only put integers in buffers, got %v", value);
 | 
			
		||||
            if (index >= buffer->count) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -40,7 +40,7 @@ void *janet_v_grow(void *v, int32_t increment, int32_t itemsize) {
 | 
			
		||||
 | 
			
		||||
/* Convert a buffer to normal allocated memory (forget capacity) */
 | 
			
		||||
void *janet_v_flattenmem(void *v, int32_t itemsize) {
 | 
			
		||||
    int32_t *p;
 | 
			
		||||
    char *p;
 | 
			
		||||
    if (NULL == v) return NULL;
 | 
			
		||||
    size_t size = (size_t) itemsize * janet_v__cnt(v);
 | 
			
		||||
    p = janet_malloc(size);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										141
									
								
								src/core/vm.c
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								src/core/vm.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -116,7 +116,6 @@
 | 
			
		||||
#else
 | 
			
		||||
#define vm_maybe_auto_suspend(COND) do { \
 | 
			
		||||
    if ((COND) && janet_vm.auto_suspend) { \
 | 
			
		||||
        janet_vm.auto_suspend = 0; \
 | 
			
		||||
        fiber->flags |= (JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP); \
 | 
			
		||||
        vm_return(JANET_SIGNAL_INTERRUPT, janet_wrap_nil()); \
 | 
			
		||||
    } \
 | 
			
		||||
@@ -138,7 +137,7 @@
 | 
			
		||||
            vm_pcnext();\
 | 
			
		||||
        }\
 | 
			
		||||
    }
 | 
			
		||||
#define _vm_bitop_immediate(op, type1)\
 | 
			
		||||
#define _vm_bitop_immediate(op, type1, rangecheck, msg)\
 | 
			
		||||
    {\
 | 
			
		||||
        Janet op1 = stack[B];\
 | 
			
		||||
        if (!janet_checktype(op1, JANET_NUMBER)) {\
 | 
			
		||||
@@ -147,13 +146,15 @@
 | 
			
		||||
            stack[A] = janet_mcall(#op, 2, _argv);\
 | 
			
		||||
            vm_checkgc_pcnext();\
 | 
			
		||||
        } else {\
 | 
			
		||||
            type1 x1 = (type1) janet_unwrap_integer(op1);\
 | 
			
		||||
            stack[A] = janet_wrap_integer(x1 op CS);\
 | 
			
		||||
            double y1 = janet_unwrap_number(op1);\
 | 
			
		||||
            if (!rangecheck(y1)) { vm_commit(); janet_panicf("value %v out of range for " msg, op1); }\
 | 
			
		||||
            type1 x1 = (type1) y1;\
 | 
			
		||||
            stack[A] = janet_wrap_number((type1) (x1 op CS));\
 | 
			
		||||
            vm_pcnext();\
 | 
			
		||||
        }\
 | 
			
		||||
    }
 | 
			
		||||
#define vm_bitop_immediate(op) _vm_bitop_immediate(op, int32_t);
 | 
			
		||||
#define vm_bitopu_immediate(op) _vm_bitop_immediate(op, uint32_t);
 | 
			
		||||
#define vm_bitop_immediate(op) _vm_bitop_immediate(op, int32_t, janet_checkintrange, "32-bit signed integers");
 | 
			
		||||
#define vm_bitopu_immediate(op) _vm_bitop_immediate(op, uint32_t, janet_checkuintrange, "32-bit unsigned integers");
 | 
			
		||||
#define _vm_binop(op, wrap)\
 | 
			
		||||
    {\
 | 
			
		||||
        Janet op1 = stack[B];\
 | 
			
		||||
@@ -170,14 +171,18 @@
 | 
			
		||||
        }\
 | 
			
		||||
    }
 | 
			
		||||
#define vm_binop(op) _vm_binop(op, janet_wrap_number)
 | 
			
		||||
#define _vm_bitop(op, type1)\
 | 
			
		||||
#define _vm_bitop(op, type1, rangecheck, msg)\
 | 
			
		||||
    {\
 | 
			
		||||
        Janet op1 = stack[B];\
 | 
			
		||||
        Janet op2 = stack[C];\
 | 
			
		||||
        if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) {\
 | 
			
		||||
            type1 x1 = (type1) janet_unwrap_integer(op1);\
 | 
			
		||||
            int32_t x2 = janet_unwrap_integer(op2);\
 | 
			
		||||
            stack[A] = janet_wrap_integer(x1 op x2);\
 | 
			
		||||
            double y1 = janet_unwrap_number(op1);\
 | 
			
		||||
            double y2 = janet_unwrap_number(op2);\
 | 
			
		||||
            if (!rangecheck(y1)) { vm_commit(); janet_panicf("value %v out of range for " msg, op1); }\
 | 
			
		||||
            if (!janet_checkintrange(y2)) { vm_commit(); janet_panicf("rhs must be valid 32-bit signed integer, got %f", op2); }\
 | 
			
		||||
            type1 x1 = (type1) y1;\
 | 
			
		||||
            int32_t x2 = (int32_t) y2;\
 | 
			
		||||
            stack[A] = janet_wrap_number((type1) (x1 op x2));\
 | 
			
		||||
            vm_pcnext();\
 | 
			
		||||
        } else {\
 | 
			
		||||
            vm_commit();\
 | 
			
		||||
@@ -185,8 +190,8 @@
 | 
			
		||||
            vm_checkgc_pcnext();\
 | 
			
		||||
        }\
 | 
			
		||||
    }
 | 
			
		||||
#define vm_bitop(op) _vm_bitop(op, int32_t)
 | 
			
		||||
#define vm_bitopu(op) _vm_bitop(op, uint32_t)
 | 
			
		||||
#define vm_bitop(op) _vm_bitop(op, int32_t, janet_checkintrange, "32-bit signed integers")
 | 
			
		||||
#define vm_bitopu(op) _vm_bitop(op, uint32_t, janet_checkuintrange, "32-bit unsigned integers")
 | 
			
		||||
#define vm_compop(op) \
 | 
			
		||||
    {\
 | 
			
		||||
        Janet op1 = stack[B];\
 | 
			
		||||
@@ -295,6 +300,16 @@ static Janet janet_method_lookup(Janet x, const char *name) {
 | 
			
		||||
    return method_to_fun(janet_ckeywordv(name), x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_unary_call(const char *method, Janet arg) {
 | 
			
		||||
    Janet m = janet_method_lookup(arg, method);
 | 
			
		||||
    if (janet_checktype(m, JANET_NIL)) {
 | 
			
		||||
        janet_panicf("could not find method :%s for %v", method, arg);
 | 
			
		||||
    } else {
 | 
			
		||||
        Janet argv[1] = { arg };
 | 
			
		||||
        return janet_method_invoke(m, 1, argv);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Call a method first on the righthand side, and then on the left hand side with a prefix */
 | 
			
		||||
static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lhs, Janet rhs) {
 | 
			
		||||
    Janet lm = janet_method_lookup(lhs, lmethod);
 | 
			
		||||
@@ -303,7 +318,7 @@ static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lh
 | 
			
		||||
        Janet lr = janet_method_lookup(rhs, rmethod);
 | 
			
		||||
        Janet argv[2] = { rhs, lhs };
 | 
			
		||||
        if (janet_checktype(lr, JANET_NIL)) {
 | 
			
		||||
            janet_panicf("could not find method :%s for %v, or :%s for %v",
 | 
			
		||||
            janet_panicf("could not find method :%s for %v or :%s for %v",
 | 
			
		||||
                         lmethod, lhs,
 | 
			
		||||
                         rmethod, rhs);
 | 
			
		||||
        }
 | 
			
		||||
@@ -331,11 +346,13 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        &&label_JOP_RETURN_NIL,
 | 
			
		||||
        &&label_JOP_ADD_IMMEDIATE,
 | 
			
		||||
        &&label_JOP_ADD,
 | 
			
		||||
        &&label_JOP_SUBTRACT_IMMEDIATE,
 | 
			
		||||
        &&label_JOP_SUBTRACT,
 | 
			
		||||
        &&label_JOP_MULTIPLY_IMMEDIATE,
 | 
			
		||||
        &&label_JOP_MULTIPLY,
 | 
			
		||||
        &&label_JOP_DIVIDE_IMMEDIATE,
 | 
			
		||||
        &&label_JOP_DIVIDE,
 | 
			
		||||
        &&label_JOP_DIVIDE_FLOOR,
 | 
			
		||||
        &&label_JOP_MODULO,
 | 
			
		||||
        &&label_JOP_REMAINDER,
 | 
			
		||||
        &&label_JOP_BAND,
 | 
			
		||||
@@ -576,8 +593,6 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op,
 | 
			
		||||
        &&label_unknown_op
 | 
			
		||||
    };
 | 
			
		||||
#endif
 | 
			
		||||
@@ -667,6 +682,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    VM_OP(JOP_ADD)
 | 
			
		||||
    vm_binop(+);
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_SUBTRACT_IMMEDIATE)
 | 
			
		||||
    vm_binop_immediate(-);
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_SUBTRACT)
 | 
			
		||||
    vm_binop(-);
 | 
			
		||||
 | 
			
		||||
@@ -682,14 +700,33 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    VM_OP(JOP_DIVIDE)
 | 
			
		||||
    vm_binop( /);
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_DIVIDE_FLOOR) {
 | 
			
		||||
        Janet op1 = stack[B];
 | 
			
		||||
        Janet op2 = stack[C];
 | 
			
		||||
        if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) {
 | 
			
		||||
            double x1 = janet_unwrap_number(op1);
 | 
			
		||||
            double x2 = janet_unwrap_number(op2);
 | 
			
		||||
            stack[A] = janet_wrap_number(floor(x1 / x2));
 | 
			
		||||
            vm_pcnext();
 | 
			
		||||
        } else {
 | 
			
		||||
            vm_commit();
 | 
			
		||||
            stack[A] = janet_binop_call("div", "rdiv", op1, op2);
 | 
			
		||||
            vm_checkgc_pcnext();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_MODULO) {
 | 
			
		||||
        Janet op1 = stack[B];
 | 
			
		||||
        Janet op2 = stack[C];
 | 
			
		||||
        if (janet_checktype(op1, JANET_NUMBER) && janet_checktype(op2, JANET_NUMBER)) {
 | 
			
		||||
            double x1 = janet_unwrap_number(op1);
 | 
			
		||||
            double x2 = janet_unwrap_number(op2);
 | 
			
		||||
            double intres = x2 * floor(x1 / x2);
 | 
			
		||||
            stack[A] = janet_wrap_number(x1 - intres);
 | 
			
		||||
            if (x2 == 0) {
 | 
			
		||||
                stack[A] = janet_wrap_number(x1);
 | 
			
		||||
            } else {
 | 
			
		||||
                double intres = x2 * floor(x1 / x2);
 | 
			
		||||
                stack[A] = janet_wrap_number(x1 - intres);
 | 
			
		||||
            }
 | 
			
		||||
            vm_pcnext();
 | 
			
		||||
        } else {
 | 
			
		||||
            vm_commit();
 | 
			
		||||
@@ -724,9 +761,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_BNOT) {
 | 
			
		||||
        Janet op = stack[E];
 | 
			
		||||
        vm_assert_type(op, JANET_NUMBER);
 | 
			
		||||
        stack[A] = janet_wrap_integer(~janet_unwrap_integer(op));
 | 
			
		||||
        vm_pcnext();
 | 
			
		||||
        if (janet_checktype(op, JANET_NUMBER)) {
 | 
			
		||||
            stack[A] = janet_wrap_integer(~janet_unwrap_integer(op));
 | 
			
		||||
            vm_pcnext();
 | 
			
		||||
        } else {
 | 
			
		||||
            vm_commit();
 | 
			
		||||
            stack[A] = janet_unary_call("~", op);
 | 
			
		||||
            vm_checkgc_pcnext();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_SHIFT_RIGHT_UNSIGNED)
 | 
			
		||||
@@ -756,14 +798,14 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_JUMP)
 | 
			
		||||
    vm_maybe_auto_suspend(DS <= 0);
 | 
			
		||||
    pc += DS;
 | 
			
		||||
    vm_maybe_auto_suspend(DS < 0);
 | 
			
		||||
    vm_next();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_JUMP_IF)
 | 
			
		||||
    if (janet_truthy(stack[A])) {
 | 
			
		||||
        vm_maybe_auto_suspend(ES <= 0);
 | 
			
		||||
        pc += ES;
 | 
			
		||||
        vm_maybe_auto_suspend(ES < 0);
 | 
			
		||||
    } else {
 | 
			
		||||
        pc++;
 | 
			
		||||
    }
 | 
			
		||||
@@ -773,15 +815,15 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    if (janet_truthy(stack[A])) {
 | 
			
		||||
        pc++;
 | 
			
		||||
    } else {
 | 
			
		||||
        vm_maybe_auto_suspend(ES <= 0);
 | 
			
		||||
        pc += ES;
 | 
			
		||||
        vm_maybe_auto_suspend(ES < 0);
 | 
			
		||||
    }
 | 
			
		||||
    vm_next();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_JUMP_IF_NIL)
 | 
			
		||||
    if (janet_checktype(stack[A], JANET_NIL)) {
 | 
			
		||||
        vm_maybe_auto_suspend(ES <= 0);
 | 
			
		||||
        pc += ES;
 | 
			
		||||
        vm_maybe_auto_suspend(ES < 0);
 | 
			
		||||
    } else {
 | 
			
		||||
        pc++;
 | 
			
		||||
    }
 | 
			
		||||
@@ -791,8 +833,8 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    if (janet_checktype(stack[A], JANET_NIL)) {
 | 
			
		||||
        pc++;
 | 
			
		||||
    } else {
 | 
			
		||||
        vm_maybe_auto_suspend(ES <= 0);
 | 
			
		||||
        pc += ES;
 | 
			
		||||
        vm_maybe_auto_suspend(ES < 0);
 | 
			
		||||
    }
 | 
			
		||||
    vm_next();
 | 
			
		||||
 | 
			
		||||
@@ -819,7 +861,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_EQUALS_IMMEDIATE)
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) == (double) CS);
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_checktype(stack[B], JANET_NUMBER) && (janet_unwrap_number(stack[B]) == (double) CS));
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_NOT_EQUALS)
 | 
			
		||||
@@ -827,7 +869,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_NOT_EQUALS_IMMEDIATE)
 | 
			
		||||
    stack[A] = janet_wrap_boolean(janet_unwrap_number(stack[B]) != (double) CS);
 | 
			
		||||
    stack[A] = janet_wrap_boolean(!janet_checktype(stack[B], JANET_NUMBER) || (janet_unwrap_number(stack[B]) != (double) CS));
 | 
			
		||||
    vm_pcnext();
 | 
			
		||||
 | 
			
		||||
    VM_OP(JOP_COMPARE)
 | 
			
		||||
@@ -918,7 +960,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
            int32_t i;
 | 
			
		||||
            for (i = 0; i < elen; ++i) {
 | 
			
		||||
                int32_t inherit = fd->environments[i];
 | 
			
		||||
                if (inherit == -1) {
 | 
			
		||||
                if (inherit == -1 || inherit >= func->def->environments_length) {
 | 
			
		||||
                    JanetStackFrame *frame = janet_stack_frame(stack);
 | 
			
		||||
                    if (!frame->env) {
 | 
			
		||||
                        /* Lazy capture of current stack frame */
 | 
			
		||||
@@ -980,7 +1022,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
            if (func->gc.flags & JANET_FUNCFLAG_TRACE) {
 | 
			
		||||
                vm_do_trace(func, fiber->stacktop - fiber->stackstart, fiber->data + fiber->stackstart);
 | 
			
		||||
            }
 | 
			
		||||
            janet_stack_frame(stack)->pc = pc;
 | 
			
		||||
            vm_commit();
 | 
			
		||||
            if (janet_fiber_funcframe(fiber, func)) {
 | 
			
		||||
                int32_t n = fiber->stacktop - fiber->stackstart;
 | 
			
		||||
                janet_panicf("%v called with %d argument%s, expected %d",
 | 
			
		||||
@@ -1226,7 +1268,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
/*
 | 
			
		||||
 * Execute a single instruction in the fiber. Does this by inspecting
 | 
			
		||||
 * the fiber, setting a breakpoint at the next instruction, executing, and
 | 
			
		||||
 * reseting breakpoints to how they were prior. Yes, it's a bit hacky.
 | 
			
		||||
 * resetting breakpoints to how they were prior. Yes, it's a bit hacky.
 | 
			
		||||
 */
 | 
			
		||||
JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) {
 | 
			
		||||
    /* No finished or currently alive fibers. */
 | 
			
		||||
@@ -1331,7 +1373,10 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
 | 
			
		||||
 | 
			
		||||
    /* Run vm */
 | 
			
		||||
    janet_vm.fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
 | 
			
		||||
    int old_coerce_error = janet_vm.coerce_error;
 | 
			
		||||
    janet_vm.coerce_error = 1;
 | 
			
		||||
    JanetSignal signal = run_vm(janet_vm.fiber, janet_wrap_nil());
 | 
			
		||||
    janet_vm.coerce_error = old_coerce_error;
 | 
			
		||||
 | 
			
		||||
    /* Teardown */
 | 
			
		||||
    janet_vm.stackn = oldn;
 | 
			
		||||
@@ -1342,6 +1387,15 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (signal != JANET_SIGNAL_OK) {
 | 
			
		||||
        /* Should match logic in janet_signalv */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
        if (janet_vm.root_fiber != NULL && signal == JANET_SIGNAL_EVENT) {
 | 
			
		||||
            janet_vm.root_fiber->sched_id++;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        if (signal != JANET_SIGNAL_ERROR) {
 | 
			
		||||
            *janet_vm.return_reg = janet_wrap_string(janet_formatc("%v coerced from %s to error", *janet_vm.return_reg, janet_signal_names[signal]));
 | 
			
		||||
        }
 | 
			
		||||
        janet_panicv(*janet_vm.return_reg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1388,8 +1442,10 @@ void janet_try_init(JanetTryState *state) {
 | 
			
		||||
    state->vm_fiber = janet_vm.fiber;
 | 
			
		||||
    state->vm_jmp_buf = janet_vm.signal_buf;
 | 
			
		||||
    state->vm_return_reg = janet_vm.return_reg;
 | 
			
		||||
    state->coerce_error = janet_vm.coerce_error;
 | 
			
		||||
    janet_vm.return_reg = &(state->payload);
 | 
			
		||||
    janet_vm.signal_buf = &(state->buf);
 | 
			
		||||
    janet_vm.coerce_error = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_restore(JanetTryState *state) {
 | 
			
		||||
@@ -1398,6 +1454,7 @@ void janet_restore(JanetTryState *state) {
 | 
			
		||||
    janet_vm.fiber = state->vm_fiber;
 | 
			
		||||
    janet_vm.signal_buf = state->vm_jmp_buf;
 | 
			
		||||
    janet_vm.return_reg = state->vm_return_reg;
 | 
			
		||||
    janet_vm.coerce_error = state->coerce_error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *out) {
 | 
			
		||||
@@ -1423,6 +1480,7 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
 | 
			
		||||
        if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
 | 
			
		||||
            *out = in;
 | 
			
		||||
            janet_fiber_set_status(fiber, sig);
 | 
			
		||||
            fiber->last_value = child->last_value;
 | 
			
		||||
            return sig;
 | 
			
		||||
        }
 | 
			
		||||
        /* Check if we need any special handling for certain opcodes */
 | 
			
		||||
@@ -1516,7 +1574,7 @@ JanetSignal janet_pcall(
 | 
			
		||||
        fiber = janet_fiber(fun, 64, argc, argv);
 | 
			
		||||
    }
 | 
			
		||||
    if (f) *f = fiber;
 | 
			
		||||
    if (!fiber) {
 | 
			
		||||
    if (NULL == fiber) {
 | 
			
		||||
        *out = janet_cstringv("arity mismatch");
 | 
			
		||||
        return JANET_SIGNAL_ERROR;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1542,9 +1600,11 @@ int janet_init(void) {
 | 
			
		||||
 | 
			
		||||
    /* Garbage collection */
 | 
			
		||||
    janet_vm.blocks = NULL;
 | 
			
		||||
    janet_vm.weak_blocks = NULL;
 | 
			
		||||
    janet_vm.next_collection = 0;
 | 
			
		||||
    janet_vm.gc_interval = 0x400000;
 | 
			
		||||
    janet_vm.block_count = 0;
 | 
			
		||||
    janet_vm.gc_mark_phase = 0;
 | 
			
		||||
 | 
			
		||||
    janet_symcache_init();
 | 
			
		||||
 | 
			
		||||
@@ -1559,13 +1619,16 @@ int janet_init(void) {
 | 
			
		||||
    janet_vm.scratch_len = 0;
 | 
			
		||||
    janet_vm.scratch_cap = 0;
 | 
			
		||||
 | 
			
		||||
    /* Sandbox flags */
 | 
			
		||||
    janet_vm.sandbox_flags = 0;
 | 
			
		||||
 | 
			
		||||
    /* Initialize registry */
 | 
			
		||||
    janet_vm.registry = NULL;
 | 
			
		||||
    janet_vm.registry_cap = 0;
 | 
			
		||||
    janet_vm.registry_count = 0;
 | 
			
		||||
    janet_vm.registry_dirty = 0;
 | 
			
		||||
 | 
			
		||||
    /* Intialize abstract registry */
 | 
			
		||||
    /* Initialize abstract registry */
 | 
			
		||||
    janet_vm.abstract_registry = janet_table(0);
 | 
			
		||||
    janet_gcroot(janet_wrap_table(janet_vm.abstract_registry));
 | 
			
		||||
 | 
			
		||||
@@ -1600,6 +1663,18 @@ int janet_init(void) {
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Disable some features at runtime with no way to re-enable them */
 | 
			
		||||
void janet_sandbox(uint32_t flags) {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_SANDBOX);
 | 
			
		||||
    janet_vm.sandbox_flags |= flags;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_sandbox_assert(uint32_t forbidden_flags) {
 | 
			
		||||
    if (forbidden_flags & janet_vm.sandbox_flags) {
 | 
			
		||||
        janet_panic("operation forbidden by sandbox");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Clear all memory associated with the VM */
 | 
			
		||||
void janet_deinit(void) {
 | 
			
		||||
    janet_clear_memory();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -43,10 +43,10 @@ int (janet_truthy)(Janet x) {
 | 
			
		||||
    return janet_truthy(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const JanetKV *(janet_unwrap_struct)(Janet x) {
 | 
			
		||||
JanetStruct(janet_unwrap_struct)(Janet x) {
 | 
			
		||||
    return janet_unwrap_struct(x);
 | 
			
		||||
}
 | 
			
		||||
const Janet *(janet_unwrap_tuple)(Janet x) {
 | 
			
		||||
JanetTuple(janet_unwrap_tuple)(Janet x) {
 | 
			
		||||
    return janet_unwrap_tuple(x);
 | 
			
		||||
}
 | 
			
		||||
JanetFiber *(janet_unwrap_fiber)(Janet x) {
 | 
			
		||||
@@ -61,16 +61,16 @@ JanetTable *(janet_unwrap_table)(Janet x) {
 | 
			
		||||
JanetBuffer *(janet_unwrap_buffer)(Janet x) {
 | 
			
		||||
    return janet_unwrap_buffer(x);
 | 
			
		||||
}
 | 
			
		||||
const uint8_t *(janet_unwrap_string)(Janet x) {
 | 
			
		||||
JanetString(janet_unwrap_string)(Janet x) {
 | 
			
		||||
    return janet_unwrap_string(x);
 | 
			
		||||
}
 | 
			
		||||
const uint8_t *(janet_unwrap_symbol)(Janet x) {
 | 
			
		||||
JanetSymbol(janet_unwrap_symbol)(Janet x) {
 | 
			
		||||
    return janet_unwrap_symbol(x);
 | 
			
		||||
}
 | 
			
		||||
const uint8_t *(janet_unwrap_keyword)(Janet x) {
 | 
			
		||||
JanetKeyword(janet_unwrap_keyword)(Janet x) {
 | 
			
		||||
    return janet_unwrap_keyword(x);
 | 
			
		||||
}
 | 
			
		||||
void *(janet_unwrap_abstract)(Janet x) {
 | 
			
		||||
JanetAbstract(janet_unwrap_abstract)(Janet x) {
 | 
			
		||||
    return janet_unwrap_abstract(x);
 | 
			
		||||
}
 | 
			
		||||
void *(janet_unwrap_pointer)(Janet x) {
 | 
			
		||||
@@ -102,22 +102,22 @@ Janet(janet_wrap_false)(void) {
 | 
			
		||||
Janet(janet_wrap_boolean)(int x) {
 | 
			
		||||
    return janet_wrap_boolean(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_string)(const uint8_t *x) {
 | 
			
		||||
Janet(janet_wrap_string)(JanetString x) {
 | 
			
		||||
    return janet_wrap_string(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_symbol)(const uint8_t *x) {
 | 
			
		||||
Janet(janet_wrap_symbol)(JanetSymbol x) {
 | 
			
		||||
    return janet_wrap_symbol(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_keyword)(const uint8_t *x) {
 | 
			
		||||
Janet(janet_wrap_keyword)(JanetKeyword x) {
 | 
			
		||||
    return janet_wrap_keyword(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_array)(JanetArray *x) {
 | 
			
		||||
    return janet_wrap_array(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_tuple)(const Janet *x) {
 | 
			
		||||
Janet(janet_wrap_tuple)(JanetTuple x) {
 | 
			
		||||
    return janet_wrap_tuple(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_struct)(const JanetKV *x) {
 | 
			
		||||
Janet(janet_wrap_struct)(JanetStruct x) {
 | 
			
		||||
    return janet_wrap_struct(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_fiber)(JanetFiber *x) {
 | 
			
		||||
@@ -135,7 +135,7 @@ Janet(janet_wrap_cfunction)(JanetCFunction x) {
 | 
			
		||||
Janet(janet_wrap_table)(JanetTable *x) {
 | 
			
		||||
    return janet_wrap_table(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_abstract)(void *x) {
 | 
			
		||||
Janet(janet_wrap_abstract)(JanetAbstract x) {
 | 
			
		||||
    return janet_wrap_abstract(x);
 | 
			
		||||
}
 | 
			
		||||
Janet(janet_wrap_pointer)(void *x) {
 | 
			
		||||
@@ -317,4 +317,3 @@ JANET_WRAP_DEFINE(pointer, void *, JANET_POINTER, pointer)
 | 
			
		||||
#undef JANET_WRAP_DEFINE
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,7 @@ extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Detect OS and endianess.
 | 
			
		||||
 * Detect OS and endianness.
 | 
			
		||||
 * From webkit source. There is likely some extreneous
 | 
			
		||||
 * detection for unsupported platforms
 | 
			
		||||
 */
 | 
			
		||||
@@ -67,6 +67,11 @@ extern "C" {
 | 
			
		||||
#define JANET_LINUX 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Check for Cygwin */
 | 
			
		||||
#if defined(__CYGWIN__)
 | 
			
		||||
#define JANET_CYGWIN 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Check Unix */
 | 
			
		||||
#if defined(_AIX) \
 | 
			
		||||
    || defined(__APPLE__) /* Darwin */ \
 | 
			
		||||
@@ -87,6 +92,16 @@ extern "C" {
 | 
			
		||||
#define JANET_WINDOWS 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Check if compiling with MSVC - else assume a GCC-like compiler by default */
 | 
			
		||||
#ifdef _MSC_VER
 | 
			
		||||
#define JANET_MSVC
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Check Mingw 32-bit and 64-bit */
 | 
			
		||||
#ifdef __MINGW32__
 | 
			
		||||
#define JANET_MINGW
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Check 64-bit vs 32-bit */
 | 
			
		||||
#if ((defined(__x86_64__) || defined(_M_X64)) \
 | 
			
		||||
     && (defined(JANET_POSIX) || defined(JANET_WINDOWS))) \
 | 
			
		||||
@@ -96,7 +111,9 @@ extern "C" {
 | 
			
		||||
    || (defined(__sparc__) && defined(__arch64__) || defined (__sparcv9)) /* BE */ \
 | 
			
		||||
    || defined(__s390x__) /* S390 64-bit (BE) */ \
 | 
			
		||||
    || (defined(__ppc64__) || defined(__PPC64__)) \
 | 
			
		||||
    || defined(__aarch64__) /* ARM 64-bit */
 | 
			
		||||
    || defined(__aarch64__) /* ARM 64-bit */ \
 | 
			
		||||
    || (defined(__riscv) && (__riscv_xlen == 64)) /* RISC-V 64-bit */ \
 | 
			
		||||
    || defined(__loongarch64) /* LoongArch64 64-bit */
 | 
			
		||||
#define JANET_64 1
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_32 1
 | 
			
		||||
@@ -166,11 +183,18 @@ extern "C" {
 | 
			
		||||
/* Enable or disable the FFI library. Currently, FFI only enabled on
 | 
			
		||||
 * x86-64 operating systems. */
 | 
			
		||||
#ifndef JANET_NO_FFI
 | 
			
		||||
#if !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64))
 | 
			
		||||
#if !defined(__EMSCRIPTEN__)
 | 
			
		||||
#define JANET_FFI
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* If FFI is enabled and FFI-JIT is not disabled... */
 | 
			
		||||
#ifdef JANET_FFI
 | 
			
		||||
#ifndef JANET_NO_FFI_JIT
 | 
			
		||||
#define JANET_FFI_JIT
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable the assembler. Enabled by default. */
 | 
			
		||||
#ifndef JANET_NO_ASSEMBLER
 | 
			
		||||
#define JANET_ASSEMBLER
 | 
			
		||||
@@ -186,6 +210,11 @@ extern "C" {
 | 
			
		||||
#define JANET_EV
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable the filewatch/ module */
 | 
			
		||||
#if !defined(JANET_NO_FILEWATCH)
 | 
			
		||||
#define JANET_FILEWATCH
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable networking */
 | 
			
		||||
#if defined(JANET_EV) && !defined(JANET_NO_NET) && !defined(__EMSCRIPTEN__)
 | 
			
		||||
#define JANET_NET
 | 
			
		||||
@@ -211,16 +240,34 @@ extern "C" {
 | 
			
		||||
#define JANET_EV_KQUEUE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Use poll as last resort */
 | 
			
		||||
#if !defined(JANET_WINDOWS) && !defined(JANET_EV_EPOLL) && !defined(JANET_EV_KQUEUE)
 | 
			
		||||
#define JANET_EV_POLL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* How to export symbols */
 | 
			
		||||
#ifndef JANET_EXPORT
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#define JANET_EXPORT __declspec(dllexport)
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_EXPORT __attribute__((visibility ("default")))
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* How declare API functions */
 | 
			
		||||
#ifndef JANET_API
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#ifdef JANET_DLL_IMPORT
 | 
			
		||||
#define JANET_API __declspec(dllimport)
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_API __declspec(dllexport)
 | 
			
		||||
#endif
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_API __attribute__((visibility ("default")))
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Tell complier some functions don't return */
 | 
			
		||||
/* Tell compiler some functions don't return */
 | 
			
		||||
#ifndef JANET_NO_RETURN
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#define JANET_NO_RETURN __declspec(noreturn)
 | 
			
		||||
@@ -230,7 +277,7 @@ extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Prevent some recursive functions from recursing too deeply
 | 
			
		||||
 * ands crashing (the parser). Instead, error out. */
 | 
			
		||||
 * and crashing (the parser). Instead, error out. */
 | 
			
		||||
#define JANET_RECURSION_GUARD 1024
 | 
			
		||||
 | 
			
		||||
/* Maximum depth to follow table prototypes before giving up and returning nil. */
 | 
			
		||||
@@ -257,10 +304,11 @@ extern "C" {
 | 
			
		||||
#ifndef JANET_NO_NANBOX
 | 
			
		||||
#ifdef JANET_32
 | 
			
		||||
#define JANET_NANBOX_32
 | 
			
		||||
#elif defined(__x86_64__) || defined(_WIN64)
 | 
			
		||||
#elif defined(__x86_64__) || defined(_WIN64) || defined(__riscv)
 | 
			
		||||
/* We will only enable nanboxing by default on 64 bit systems
 | 
			
		||||
 * on x86. This is mainly because the approach is tied to the
 | 
			
		||||
 * implicit 47 bit address space. */
 | 
			
		||||
 * for x64 and risc-v. This is mainly because the approach is tied to the
 | 
			
		||||
 * implicit 47 bit address space. Many arches allow/require this, but not all,
 | 
			
		||||
 * and it requires cooperation from the OS. ARM should also work in many configurations. */
 | 
			
		||||
#define JANET_NANBOX_64
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
@@ -311,6 +359,7 @@ typedef struct {
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
typedef struct JanetOSMutex JanetOSMutex;
 | 
			
		||||
typedef struct JanetOSRWLock JanetOSRWLock;
 | 
			
		||||
typedef struct JanetChannel JanetChannel;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/***** END SECTION CONFIG *****/
 | 
			
		||||
@@ -330,10 +379,9 @@ typedef struct JanetOSRWLock JanetOSRWLock;
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* What to do when out of memory */
 | 
			
		||||
#ifndef JANET_OUT_OF_MEMORY
 | 
			
		||||
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
 | 
			
		||||
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "%s:%d - janet out of memory\n", __FILE__, __LINE__); exit(1); } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BSD
 | 
			
		||||
@@ -370,12 +418,11 @@ typedef enum {
 | 
			
		||||
    JANET_SIGNAL_USER6,
 | 
			
		||||
    JANET_SIGNAL_USER7,
 | 
			
		||||
    JANET_SIGNAL_USER8,
 | 
			
		||||
    JANET_SIGNAL_USER9
 | 
			
		||||
    JANET_SIGNAL_USER9,
 | 
			
		||||
    JANET_SIGNAL_INTERRUPT = JANET_SIGNAL_USER8,
 | 
			
		||||
    JANET_SIGNAL_EVENT = JANET_SIGNAL_USER9,
 | 
			
		||||
} JanetSignal;
 | 
			
		||||
 | 
			
		||||
#define JANET_SIGNAL_EVENT JANET_SIGNAL_USER9
 | 
			
		||||
#define JANET_SIGNAL_INTERRUPT JANET_SIGNAL_USER8
 | 
			
		||||
 | 
			
		||||
/* Fiber statuses - mostly corresponds to signals. */
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_STATUS_DEAD,
 | 
			
		||||
@@ -425,6 +472,7 @@ typedef struct JanetReg JanetReg;
 | 
			
		||||
typedef struct JanetRegExt JanetRegExt;
 | 
			
		||||
typedef struct JanetMethod JanetMethod;
 | 
			
		||||
typedef struct JanetSourceMapping JanetSourceMapping;
 | 
			
		||||
typedef struct JanetSymbolMap JanetSymbolMap;
 | 
			
		||||
typedef struct JanetView JanetView;
 | 
			
		||||
typedef struct JanetByteView JanetByteView;
 | 
			
		||||
typedef struct JanetDictView JanetDictView;
 | 
			
		||||
@@ -538,69 +586,83 @@ typedef void *JanetAbstract;
 | 
			
		||||
 | 
			
		||||
#define JANET_STREAM_CLOSED 0x1
 | 
			
		||||
#define JANET_STREAM_SOCKET 0x2
 | 
			
		||||
#define JANET_STREAM_IOCP 0x4
 | 
			
		||||
#define JANET_STREAM_UNREGISTERED 0x4
 | 
			
		||||
#define JANET_STREAM_READABLE 0x200
 | 
			
		||||
#define JANET_STREAM_WRITABLE 0x400
 | 
			
		||||
#define JANET_STREAM_ACCEPTABLE 0x800
 | 
			
		||||
#define JANET_STREAM_UDPSERVER 0x1000
 | 
			
		||||
#define JANET_STREAM_TOCLOSE 0x10000
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_ASYNC_EVENT_INIT,
 | 
			
		||||
    JANET_ASYNC_EVENT_MARK,
 | 
			
		||||
    JANET_ASYNC_EVENT_DEINIT,
 | 
			
		||||
    JANET_ASYNC_EVENT_CLOSE,
 | 
			
		||||
    JANET_ASYNC_EVENT_ERR,
 | 
			
		||||
    JANET_ASYNC_EVENT_HUP,
 | 
			
		||||
    JANET_ASYNC_EVENT_READ,
 | 
			
		||||
    JANET_ASYNC_EVENT_WRITE,
 | 
			
		||||
    JANET_ASYNC_EVENT_CANCEL,
 | 
			
		||||
    JANET_ASYNC_EVENT_COMPLETE, /* Used on windows for IOCP */
 | 
			
		||||
    JANET_ASYNC_EVENT_USER
 | 
			
		||||
    JANET_ASYNC_EVENT_INIT = 0,
 | 
			
		||||
    JANET_ASYNC_EVENT_MARK = 1,
 | 
			
		||||
    JANET_ASYNC_EVENT_DEINIT = 2,
 | 
			
		||||
    JANET_ASYNC_EVENT_CLOSE = 3,
 | 
			
		||||
    JANET_ASYNC_EVENT_ERR = 4,
 | 
			
		||||
    JANET_ASYNC_EVENT_HUP = 5,
 | 
			
		||||
    JANET_ASYNC_EVENT_READ = 6,
 | 
			
		||||
    JANET_ASYNC_EVENT_WRITE = 7,
 | 
			
		||||
    JANET_ASYNC_EVENT_COMPLETE = 8, /* Used on windows for IOCP */
 | 
			
		||||
    JANET_ASYNC_EVENT_FAILED = 9 /* Used on windows for IOCP */
 | 
			
		||||
} JanetAsyncEvent;
 | 
			
		||||
 | 
			
		||||
#define JANET_ASYNC_LISTEN_READ (1 << JANET_ASYNC_EVENT_READ)
 | 
			
		||||
#define JANET_ASYNC_LISTEN_WRITE (1 << JANET_ASYNC_EVENT_WRITE)
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
    JANET_ASYNC_STATUS_NOT_DONE,
 | 
			
		||||
    JANET_ASYNC_STATUS_DONE
 | 
			
		||||
} JanetAsyncStatus;
 | 
			
		||||
    JANET_ASYNC_LISTEN_READ = 1,
 | 
			
		||||
    JANET_ASYNC_LISTEN_WRITE,
 | 
			
		||||
    JANET_ASYNC_LISTEN_BOTH
 | 
			
		||||
} JanetAsyncMode;
 | 
			
		||||
 | 
			
		||||
/* Typedefs */
 | 
			
		||||
typedef struct JanetListenerState JanetListenerState;
 | 
			
		||||
typedef struct JanetStream JanetStream;
 | 
			
		||||
typedef JanetAsyncStatus(*JanetListener)(JanetListenerState *state, JanetAsyncEvent event);
 | 
			
		||||
 | 
			
		||||
/* Wrapper around file descriptors and HANDLEs that can be polled. */
 | 
			
		||||
struct JanetStream {
 | 
			
		||||
    JanetHandle handle;
 | 
			
		||||
    uint32_t flags;
 | 
			
		||||
    /* Linked list of all in-flight IO routines for this stream */
 | 
			
		||||
    JanetListenerState *state;
 | 
			
		||||
    uint32_t index;
 | 
			
		||||
    JanetFiber *read_fiber;
 | 
			
		||||
    JanetFiber *write_fiber;
 | 
			
		||||
    const void *methods; /* Methods for this stream */
 | 
			
		||||
    /* internal - used to disallow multiple concurrent reads / writes on the same stream.
 | 
			
		||||
     * this constraint may be lifted later but allowing such would require more internal book keeping
 | 
			
		||||
     * for some implementations. You can read and write at the same time on the same stream, though. */
 | 
			
		||||
    int _mask;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Interface for state machine based event loop */
 | 
			
		||||
struct JanetListenerState {
 | 
			
		||||
    JanetListener machine;
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
    JanetStream *stream;
 | 
			
		||||
    void *event; /* Used to pass data from asynchronous IO event. Contents depend on both
 | 
			
		||||
                    implementation of the event loop and the particular event. */
 | 
			
		||||
typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event);
 | 
			
		||||
 | 
			
		||||
/* Start listening for events from a stream on the current root fiber. After
 | 
			
		||||
 * calling this, users should call janet_await() before returning from the
 | 
			
		||||
 * current C Function. This also will call janet_await.
 | 
			
		||||
 * mode is which events to listen for, and callback is the function pointer to
 | 
			
		||||
 * call when ever an event is sent from the event loop. state is an optional (can be NULL)
 | 
			
		||||
 * pointer to data allocated with janet_malloc. This pointer will be passed to callback as
 | 
			
		||||
 * fiber->ev_state. It will also be freed for you by the runtime when the event loop determines
 | 
			
		||||
 * it can no longer be referenced. On windows, the contents of state MUST contained an OVERLAPPED struct at the 0 offset. */
 | 
			
		||||
 | 
			
		||||
JANET_API void janet_async_start_fiber(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
 | 
			
		||||
JANET_API JANET_NO_RETURN void janet_async_start(JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, void *state);
 | 
			
		||||
 | 
			
		||||
/* Do not send any more events to the given callback. Call this after scheduling fiber to be resume
 | 
			
		||||
 * or canceled. */
 | 
			
		||||
JANET_API void janet_async_end(JanetFiber *fiber);
 | 
			
		||||
 | 
			
		||||
/* Needed for windows to mark a fiber as waiting for an IOCP completion event. Noop on other platforms. */
 | 
			
		||||
JANET_API void janet_async_in_flight(JanetFiber *fiber);
 | 
			
		||||
 | 
			
		||||
/* On some platforms, it is important to be able to control if a stream is edge-trigger or level triggered.
 | 
			
		||||
 * For example, a server that is accepting connections might want to be level triggered or edge-triggered
 | 
			
		||||
 * depending on expected service. */
 | 
			
		||||
JANET_API void janet_stream_edge_triggered(JanetStream *stream);
 | 
			
		||||
JANET_API void janet_stream_level_triggered(JanetStream *stream);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Janet uses atomic integers in several places for synchronization between threads and
 | 
			
		||||
 * signals. Define them here */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    void *tag; /* Used to associate listeners with an overlapped structure */
 | 
			
		||||
    int bytes; /* Used to track how many bytes were transfered. */
 | 
			
		||||
#endif
 | 
			
		||||
    /* internal */
 | 
			
		||||
    size_t _index;
 | 
			
		||||
    int _mask;
 | 
			
		||||
    JanetListenerState *_next;
 | 
			
		||||
};
 | 
			
		||||
typedef long JanetAtomicInt;
 | 
			
		||||
#else
 | 
			
		||||
typedef int32_t JanetAtomicInt;
 | 
			
		||||
#endif
 | 
			
		||||
JANET_API JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x);
 | 
			
		||||
JANET_API JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x);
 | 
			
		||||
JANET_API JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x);
 | 
			
		||||
 | 
			
		||||
/* We provide three possible implementations of Janets. The preferred
 | 
			
		||||
 * nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the
 | 
			
		||||
@@ -628,10 +690,10 @@ struct JanetListenerState {
 | 
			
		||||
 * external bindings, we should prefer using the Head structs directly, and
 | 
			
		||||
 * use the host language to add sugar around the manipulation of the Janet types. */
 | 
			
		||||
 | 
			
		||||
JANET_API JanetStructHead *janet_struct_head(const JanetKV *st);
 | 
			
		||||
JANET_API JanetStructHead *janet_struct_head(JanetStruct st);
 | 
			
		||||
JANET_API JanetAbstractHead *janet_abstract_head(const void *abstract);
 | 
			
		||||
JANET_API JanetStringHead *janet_string_head(const uint8_t *s);
 | 
			
		||||
JANET_API JanetTupleHead *janet_tuple_head(const Janet *tuple);
 | 
			
		||||
JANET_API JanetStringHead *janet_string_head(JanetString s);
 | 
			
		||||
JANET_API JanetTupleHead *janet_tuple_head(JanetTuple tuple);
 | 
			
		||||
 | 
			
		||||
/* Some language bindings won't have access to the macro versions. */
 | 
			
		||||
 | 
			
		||||
@@ -640,16 +702,16 @@ JANET_API int janet_checktype(Janet x, JanetType type);
 | 
			
		||||
JANET_API int janet_checktypes(Janet x, int typeflags);
 | 
			
		||||
JANET_API int janet_truthy(Janet x);
 | 
			
		||||
 | 
			
		||||
JANET_API const JanetKV *janet_unwrap_struct(Janet x);
 | 
			
		||||
JANET_API const Janet *janet_unwrap_tuple(Janet x);
 | 
			
		||||
JANET_API JanetStruct janet_unwrap_struct(Janet x);
 | 
			
		||||
JANET_API JanetTuple janet_unwrap_tuple(Janet x);
 | 
			
		||||
JANET_API JanetFiber *janet_unwrap_fiber(Janet x);
 | 
			
		||||
JANET_API JanetArray *janet_unwrap_array(Janet x);
 | 
			
		||||
JANET_API JanetTable *janet_unwrap_table(Janet x);
 | 
			
		||||
JANET_API JanetBuffer *janet_unwrap_buffer(Janet x);
 | 
			
		||||
JANET_API const uint8_t *janet_unwrap_string(Janet x);
 | 
			
		||||
JANET_API const uint8_t *janet_unwrap_symbol(Janet x);
 | 
			
		||||
JANET_API const uint8_t *janet_unwrap_keyword(Janet x);
 | 
			
		||||
JANET_API void *janet_unwrap_abstract(Janet x);
 | 
			
		||||
JANET_API JanetString janet_unwrap_string(Janet x);
 | 
			
		||||
JANET_API JanetSymbol janet_unwrap_symbol(Janet x);
 | 
			
		||||
JANET_API JanetKeyword janet_unwrap_keyword(Janet x);
 | 
			
		||||
JANET_API JanetAbstract janet_unwrap_abstract(Janet x);
 | 
			
		||||
JANET_API void *janet_unwrap_pointer(Janet x);
 | 
			
		||||
JANET_API JanetFunction *janet_unwrap_function(Janet x);
 | 
			
		||||
JANET_API JanetCFunction janet_unwrap_cfunction(Janet x);
 | 
			
		||||
@@ -662,18 +724,18 @@ JANET_API Janet janet_wrap_number(double x);
 | 
			
		||||
JANET_API Janet janet_wrap_true(void);
 | 
			
		||||
JANET_API Janet janet_wrap_false(void);
 | 
			
		||||
JANET_API Janet janet_wrap_boolean(int x);
 | 
			
		||||
JANET_API Janet janet_wrap_string(const uint8_t *x);
 | 
			
		||||
JANET_API Janet janet_wrap_symbol(const uint8_t *x);
 | 
			
		||||
JANET_API Janet janet_wrap_keyword(const uint8_t *x);
 | 
			
		||||
JANET_API Janet janet_wrap_string(JanetString x);
 | 
			
		||||
JANET_API Janet janet_wrap_symbol(JanetSymbol x);
 | 
			
		||||
JANET_API Janet janet_wrap_keyword(JanetKeyword x);
 | 
			
		||||
JANET_API Janet janet_wrap_array(JanetArray *x);
 | 
			
		||||
JANET_API Janet janet_wrap_tuple(const Janet *x);
 | 
			
		||||
JANET_API Janet janet_wrap_struct(const JanetKV *x);
 | 
			
		||||
JANET_API Janet janet_wrap_tuple(JanetTuple x);
 | 
			
		||||
JANET_API Janet janet_wrap_struct(JanetStruct x);
 | 
			
		||||
JANET_API Janet janet_wrap_fiber(JanetFiber *x);
 | 
			
		||||
JANET_API Janet janet_wrap_buffer(JanetBuffer *x);
 | 
			
		||||
JANET_API Janet janet_wrap_function(JanetFunction *x);
 | 
			
		||||
JANET_API Janet janet_wrap_cfunction(JanetCFunction x);
 | 
			
		||||
JANET_API Janet janet_wrap_table(JanetTable *x);
 | 
			
		||||
JANET_API Janet janet_wrap_abstract(void *x);
 | 
			
		||||
JANET_API Janet janet_wrap_abstract(JanetAbstract x);
 | 
			
		||||
JANET_API Janet janet_wrap_pointer(void *x);
 | 
			
		||||
JANET_API Janet janet_wrap_integer(int32_t x);
 | 
			
		||||
 | 
			
		||||
@@ -705,6 +767,7 @@ JANET_API Janet janet_wrap_integer(int32_t x);
 | 
			
		||||
        ? janet_nanbox_isnumber(x) \
 | 
			
		||||
        : janet_nanbox_checkauxtype((x), (t)))
 | 
			
		||||
 | 
			
		||||
/* Use JANET_API so that modules will use a local version of these functions if possible */
 | 
			
		||||
JANET_API void *janet_nanbox_to_pointer(Janet x);
 | 
			
		||||
JANET_API Janet janet_nanbox_from_pointer(void *p, uint64_t tagmask);
 | 
			
		||||
JANET_API Janet janet_nanbox_from_cpointer(const void *p, uint64_t tagmask);
 | 
			
		||||
@@ -751,14 +814,14 @@ JANET_API Janet janet_nanbox_from_bits(uint64_t bits);
 | 
			
		||||
#define janet_wrap_pointer(s) janet_nanbox_wrap_((s), JANET_POINTER)
 | 
			
		||||
 | 
			
		||||
/* Unwrap the pointer types */
 | 
			
		||||
#define janet_unwrap_struct(x) ((const JanetKV *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_tuple(x) ((const Janet *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_struct(x) ((JanetStruct)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_tuple(x) ((JanetTuple)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_fiber(x) ((JanetFiber *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_array(x) ((JanetArray *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_table(x) ((JanetTable *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_buffer(x) ((JanetBuffer *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_string(x) ((const uint8_t *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_symbol(x) ((const uint8_t *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_string(x) ((JanetString)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_symbol(x) ((JanetSymbol)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_keyword(x) ((const uint8_t *)janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_abstract(x) (janet_nanbox_to_pointer(x))
 | 
			
		||||
#define janet_unwrap_pointer(x) (janet_nanbox_to_pointer(x))
 | 
			
		||||
@@ -800,15 +863,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
 | 
			
		||||
#define janet_wrap_cfunction(s) janet_nanbox32_from_tagp(JANET_CFUNCTION, (void *)(s))
 | 
			
		||||
#define janet_wrap_pointer(s) janet_nanbox32_from_tagp(JANET_POINTER, (void *)(s))
 | 
			
		||||
 | 
			
		||||
#define janet_unwrap_struct(x) ((const JanetKV *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_tuple(x) ((const Janet *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_struct(x) ((JanetStruct)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_tuple(x) ((JanetTuple)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_fiber(x) ((JanetFiber *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_array(x) ((JanetArray *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_table(x) ((JanetTable *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_buffer(x) ((JanetBuffer *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_string(x) ((const uint8_t *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_symbol(x) ((const uint8_t *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_keyword(x) ((const uint8_t *)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_string(x) ((JanetString)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_symbol(x) ((JanetSymbol)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_keyword(x) ((JanetKeyword)(x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_abstract(x) ((x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_pointer(x) ((x).tagged.payload.pointer)
 | 
			
		||||
#define janet_unwrap_function(x) ((JanetFunction *)(x).tagged.payload.pointer)
 | 
			
		||||
@@ -823,15 +886,15 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
 | 
			
		||||
#define janet_truthy(x) \
 | 
			
		||||
    ((x).type != JANET_NIL && ((x).type != JANET_BOOLEAN || ((x).as.u64 & 0x1)))
 | 
			
		||||
 | 
			
		||||
#define janet_unwrap_struct(x) ((const JanetKV *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_tuple(x) ((const Janet *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_struct(x) ((JanetStruct)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_tuple(x) ((JanetTuple)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_fiber(x) ((JanetFiber *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_array(x) ((JanetArray *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_table(x) ((JanetTable *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_buffer(x) ((JanetBuffer *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_string(x) ((const uint8_t *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_symbol(x) ((const uint8_t *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_keyword(x) ((const uint8_t *)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_string(x) ((JanetString)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_symbol(x) ((JanetSymbol)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_keyword(x) ((JanetKeyword)(x).as.pointer)
 | 
			
		||||
#define janet_unwrap_abstract(x) ((x).as.pointer)
 | 
			
		||||
#define janet_unwrap_pointer(x) ((x).as.pointer)
 | 
			
		||||
#define janet_unwrap_function(x) ((JanetFunction *)(x).as.pointer)
 | 
			
		||||
@@ -842,13 +905,20 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
 | 
			
		||||
/* End of tagged union implementation */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JANET_API int janet_checkint16(Janet x);
 | 
			
		||||
JANET_API int janet_checkuint16(Janet x);
 | 
			
		||||
JANET_API int janet_checkint(Janet x);
 | 
			
		||||
JANET_API int janet_checkuint(Janet x);
 | 
			
		||||
JANET_API int janet_checkint64(Janet x);
 | 
			
		||||
JANET_API int janet_checkuint64(Janet x);
 | 
			
		||||
JANET_API int janet_checksize(Janet x);
 | 
			
		||||
JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at);
 | 
			
		||||
#define janet_checkint16range(x) ((x) >= INT16_MIN && (x) <= INT16_MAX && (x) == (int16_t)(x))
 | 
			
		||||
#define janet_checkuint16range(x) ((x) >= 0 && (x) <= UINT16_MAX && (x) == (uint16_t)(x))
 | 
			
		||||
#define janet_checkintrange(x) ((x) >= INT32_MIN && (x) <= INT32_MAX && (x) == (int32_t)(x))
 | 
			
		||||
#define janet_checkuintrange(x) ((x) >= 0 && (x) <= UINT32_MAX && (x) == (uint32_t)(x))
 | 
			
		||||
#define janet_checkint64range(x) ((x) >= JANET_INTMIN_DOUBLE && (x) <= JANET_INTMAX_DOUBLE && (x) == (int64_t)(x))
 | 
			
		||||
#define janet_checkuint64range(x) ((x) >= 0 && (x) <= JANET_INTMAX_DOUBLE && (x) == (uint64_t)(x))
 | 
			
		||||
#define janet_unwrap_integer(x) ((int32_t) janet_unwrap_number(x))
 | 
			
		||||
#define janet_wrap_integer(x) janet_wrap_number((int32_t)(x))
 | 
			
		||||
 | 
			
		||||
@@ -861,7 +931,7 @@ struct JanetGCObject {
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
    union {
 | 
			
		||||
        JanetGCObject *next;
 | 
			
		||||
        int32_t refcount; /* For threaded abstract types */
 | 
			
		||||
        volatile JanetAtomicInt refcount; /* For threaded abstract types */
 | 
			
		||||
    } data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -884,8 +954,10 @@ struct JanetFiber {
 | 
			
		||||
     * that is, fibers that are scheduled on the event loop and behave much like threads
 | 
			
		||||
     * in a multi-tasking system. It would be possible to move these fields to a new
 | 
			
		||||
     * type, say "JanetTask", that as separate from fibers to save a bit of space. */
 | 
			
		||||
    JanetListenerState *waiting;
 | 
			
		||||
    uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */
 | 
			
		||||
    JanetEVCallback ev_callback; /* Call this before starting scheduled fibers */
 | 
			
		||||
    JanetStream *ev_stream; /* which stream we are waiting on */
 | 
			
		||||
    void *ev_state; /* Extra data for ev callback state. On windows, first element must be OVERLAPPED. */
 | 
			
		||||
    void *supervisor_channel; /* Channel to push self to when complete */
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
@@ -980,6 +1052,7 @@ struct JanetAbstractHead {
 | 
			
		||||
/* Some function definition flags */
 | 
			
		||||
#define JANET_FUNCDEF_FLAG_VARARG 0x10000
 | 
			
		||||
#define JANET_FUNCDEF_FLAG_NEEDSENV 0x20000
 | 
			
		||||
#define JANET_FUNCDEF_FLAG_HASSYMBOLMAP 0x40000
 | 
			
		||||
#define JANET_FUNCDEF_FLAG_HASNAME 0x80000
 | 
			
		||||
#define JANET_FUNCDEF_FLAG_HASSOURCE 0x100000
 | 
			
		||||
#define JANET_FUNCDEF_FLAG_HASDEFS 0x200000
 | 
			
		||||
@@ -995,6 +1068,14 @@ struct JanetSourceMapping {
 | 
			
		||||
    int32_t column;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Symbol to slot mapping & lifetime structure. */
 | 
			
		||||
struct JanetSymbolMap {
 | 
			
		||||
    uint32_t birth_pc;
 | 
			
		||||
    uint32_t death_pc;
 | 
			
		||||
    uint32_t slot_index;
 | 
			
		||||
    const uint8_t *symbol;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* A function definition. Contains information needed to instantiate closures. */
 | 
			
		||||
struct JanetFuncDef {
 | 
			
		||||
    JanetGCObject gc;
 | 
			
		||||
@@ -1008,6 +1089,7 @@ struct JanetFuncDef {
 | 
			
		||||
    JanetSourceMapping *sourcemap;
 | 
			
		||||
    JanetString source;
 | 
			
		||||
    JanetString name;
 | 
			
		||||
    JanetSymbolMap *symbolmap;
 | 
			
		||||
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
    int32_t slotcount; /* The amount of stack space required for the function */
 | 
			
		||||
@@ -1018,6 +1100,7 @@ struct JanetFuncDef {
 | 
			
		||||
    int32_t bytecode_length;
 | 
			
		||||
    int32_t environments_length;
 | 
			
		||||
    int32_t defs_length;
 | 
			
		||||
    int32_t symbolmap_length;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* A function environment */
 | 
			
		||||
@@ -1093,6 +1176,8 @@ struct JanetAbstractType {
 | 
			
		||||
    int32_t (*hash)(void *p, size_t len);
 | 
			
		||||
    Janet(*next)(void *p, Janet key);
 | 
			
		||||
    Janet(*call)(void *p, int32_t argc, Janet *argv);
 | 
			
		||||
    size_t (*length)(void *p, size_t len);
 | 
			
		||||
    JanetByteView(*bytes)(void *p, size_t len);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Some macros to let us add extra types to JanetAbstract types without
 | 
			
		||||
@@ -1110,7 +1195,9 @@ struct JanetAbstractType {
 | 
			
		||||
#define JANET_ATEND_COMPARE     NULL,JANET_ATEND_HASH
 | 
			
		||||
#define JANET_ATEND_HASH        NULL,JANET_ATEND_NEXT
 | 
			
		||||
#define JANET_ATEND_NEXT        NULL,JANET_ATEND_CALL
 | 
			
		||||
#define JANET_ATEND_CALL
 | 
			
		||||
#define JANET_ATEND_CALL        NULL,JANET_ATEND_LENGTH
 | 
			
		||||
#define JANET_ATEND_LENGTH      NULL,JANET_ATEND_BYTES
 | 
			
		||||
#define JANET_ATEND_BYTES
 | 
			
		||||
 | 
			
		||||
struct JanetReg {
 | 
			
		||||
    const char *name;
 | 
			
		||||
@@ -1174,6 +1261,7 @@ typedef struct {
 | 
			
		||||
    /* new state */
 | 
			
		||||
    jmp_buf buf;
 | 
			
		||||
    Janet payload;
 | 
			
		||||
    int coerce_error;
 | 
			
		||||
} JanetTryState;
 | 
			
		||||
 | 
			
		||||
/***** END SECTION TYPES *****/
 | 
			
		||||
@@ -1219,11 +1307,13 @@ enum JanetOpCode {
 | 
			
		||||
    JOP_RETURN_NIL,
 | 
			
		||||
    JOP_ADD_IMMEDIATE,
 | 
			
		||||
    JOP_ADD,
 | 
			
		||||
    JOP_SUBTRACT_IMMEDIATE,
 | 
			
		||||
    JOP_SUBTRACT,
 | 
			
		||||
    JOP_MULTIPLY_IMMEDIATE,
 | 
			
		||||
    JOP_MULTIPLY,
 | 
			
		||||
    JOP_DIVIDE_IMMEDIATE,
 | 
			
		||||
    JOP_DIVIDE,
 | 
			
		||||
    JOP_DIVIDE_FLOOR,
 | 
			
		||||
    JOP_MODULO,
 | 
			
		||||
    JOP_REMAINDER,
 | 
			
		||||
    JOP_BAND,
 | 
			
		||||
@@ -1332,6 +1422,7 @@ 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 JanetStream *janet_stream_ext(JanetHandle handle, uint32_t flags, const JanetMethod *methods, size_t size); /* Allow for type punning streams */
 | 
			
		||||
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);
 | 
			
		||||
@@ -1343,9 +1434,7 @@ JANET_API void janet_stream_flags(JanetStream *stream, uint32_t flags);
 | 
			
		||||
JANET_API void janet_schedule(JanetFiber *fiber, Janet value);
 | 
			
		||||
JANET_API void janet_cancel(JanetFiber *fiber, Janet value);
 | 
			
		||||
JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig);
 | 
			
		||||
 | 
			
		||||
/* Start a state machine listening for events from a stream */
 | 
			
		||||
JANET_API JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user);
 | 
			
		||||
JANET_API void janet_schedule_soon(JanetFiber *fiber, Janet value, JanetSignal sig);
 | 
			
		||||
 | 
			
		||||
/* Shorthand for yielding to event loop in C */
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_await(void);
 | 
			
		||||
@@ -1354,6 +1443,7 @@ 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_addtimeout_nil(double sec);
 | 
			
		||||
JANET_API void janet_ev_inc_refcount(void);
 | 
			
		||||
JANET_API void janet_ev_dec_refcount(void);
 | 
			
		||||
 | 
			
		||||
@@ -1364,6 +1454,14 @@ JANET_API void *janet_abstract_threaded(const JanetAbstractType *atype, size_t s
 | 
			
		||||
JANET_API int32_t janet_abstract_incref(void *abst);
 | 
			
		||||
JANET_API int32_t janet_abstract_decref(void *abst);
 | 
			
		||||
 | 
			
		||||
/* Expose channel utilities */
 | 
			
		||||
JanetChannel *janet_channel_make(uint32_t limit);
 | 
			
		||||
JanetChannel *janet_channel_make_threaded(uint32_t limit);
 | 
			
		||||
JanetChannel *janet_getchannel(const Janet *argv, int32_t n);
 | 
			
		||||
JanetChannel *janet_optchannel(const Janet *argv, int32_t argc, int32_t n, JanetChannel *dflt);
 | 
			
		||||
JANET_API int janet_channel_give(JanetChannel *channel, Janet x);
 | 
			
		||||
JANET_API int janet_channel_take(JanetChannel *channel, Janet *out);
 | 
			
		||||
 | 
			
		||||
/* Expose some OS sync primitives */
 | 
			
		||||
JANET_API size_t janet_os_mutex_size(void);
 | 
			
		||||
JANET_API size_t janet_os_rwlock_size(void);
 | 
			
		||||
@@ -1433,22 +1531,22 @@ JANET_API void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGeneric
 | 
			
		||||
JANET_API void janet_ev_default_threaded_callback(JanetEVGenericMessage return_value);
 | 
			
		||||
 | 
			
		||||
/* Read async from a stream */
 | 
			
		||||
JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_readchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes);
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_recv(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_recvchunk(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_recvfrom(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int flags);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Write async to a stream */
 | 
			
		||||
JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf);
 | 
			
		||||
JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_write_string(JanetStream *stream, JanetString str);
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags);
 | 
			
		||||
JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags);
 | 
			
		||||
JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags);
 | 
			
		||||
JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_send_buffer(JanetStream *stream, JanetBuffer *buf, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_send_string(JanetStream *stream, JanetString str, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_sendto_buffer(JanetStream *stream, JanetBuffer *buf, void *dest, int flags);
 | 
			
		||||
JANET_NO_RETURN JANET_API void janet_ev_sendto_string(JanetStream *stream, JanetString str, void *dest, int flags);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1519,6 +1617,9 @@ 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);
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
JANET_API int janet_scan_numeric(const uint8_t *str, int32_t len, Janet *out);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Debugging */
 | 
			
		||||
JANET_API void janet_debug_break(JanetFuncDef *def, int32_t pc);
 | 
			
		||||
@@ -1537,6 +1638,7 @@ JANET_API double janet_rng_double(JanetRNG *rng);
 | 
			
		||||
 | 
			
		||||
/* Array functions */
 | 
			
		||||
JANET_API JanetArray *janet_array(int32_t capacity);
 | 
			
		||||
JANET_API JanetArray *janet_array_weak(int32_t capacity);
 | 
			
		||||
JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n);
 | 
			
		||||
JANET_API void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth);
 | 
			
		||||
JANET_API void janet_array_setcount(JanetArray *array, int32_t count);
 | 
			
		||||
@@ -1545,8 +1647,10 @@ JANET_API Janet janet_array_pop(JanetArray *array);
 | 
			
		||||
JANET_API Janet janet_array_peek(JanetArray *array);
 | 
			
		||||
 | 
			
		||||
/* Buffer functions */
 | 
			
		||||
#define JANET_BUFFER_FLAG_NO_REALLOC 0x10000
 | 
			
		||||
JANET_API JanetBuffer *janet_buffer(int32_t capacity);
 | 
			
		||||
JANET_API JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity);
 | 
			
		||||
JANET_API JanetBuffer *janet_pointer_buffer_unsafe(void *memory, int32_t capacity, int32_t count);
 | 
			
		||||
JANET_API void janet_buffer_deinit(JanetBuffer *buffer);
 | 
			
		||||
JANET_API void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth);
 | 
			
		||||
JANET_API void janet_buffer_setcount(JanetBuffer *buffer, int32_t count);
 | 
			
		||||
@@ -1564,7 +1668,7 @@ JANET_API void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x);
 | 
			
		||||
#define JANET_TUPLE_FLAG_BRACKETCTOR 0x10000
 | 
			
		||||
 | 
			
		||||
#define janet_tuple_head(t) ((JanetTupleHead *)((char *)t - offsetof(JanetTupleHead, data)))
 | 
			
		||||
#define janet_tuple_from_head(gcobject) ((const Janet *)((char *)gcobject + offsetof(JanetTupleHead, data)))
 | 
			
		||||
#define janet_tuple_from_head(gcobject) ((JanetTuple)((char *)gcobject + offsetof(JanetTupleHead, data)))
 | 
			
		||||
#define janet_tuple_length(t) (janet_tuple_head(t)->length)
 | 
			
		||||
#define janet_tuple_hash(t) (janet_tuple_head(t)->hash)
 | 
			
		||||
#define janet_tuple_sm_line(t) (janet_tuple_head(t)->sm_line)
 | 
			
		||||
@@ -1610,7 +1714,7 @@ JANET_API JanetSymbol janet_symbol_gen(void);
 | 
			
		||||
 | 
			
		||||
/* Structs */
 | 
			
		||||
#define janet_struct_head(t) ((JanetStructHead *)((char *)t - offsetof(JanetStructHead, data)))
 | 
			
		||||
#define janet_struct_from_head(t) ((const JanetKV *)((char *)gcobject + offsetof(JanetStructHead, data)))
 | 
			
		||||
#define janet_struct_from_head(t) ((JanetStruct)((char *)gcobject + offsetof(JanetStructHead, data)))
 | 
			
		||||
#define janet_struct_length(t) (janet_struct_head(t)->length)
 | 
			
		||||
#define janet_struct_capacity(t) (janet_struct_head(t)->capacity)
 | 
			
		||||
#define janet_struct_hash(t) (janet_struct_head(t)->hash)
 | 
			
		||||
@@ -1640,11 +1744,15 @@ 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);
 | 
			
		||||
JANET_API JanetTable *janet_table_weakk(int32_t capacity);
 | 
			
		||||
JANET_API JanetTable *janet_table_weakv(int32_t capacity);
 | 
			
		||||
JANET_API JanetTable *janet_table_weakkv(int32_t capacity);
 | 
			
		||||
 | 
			
		||||
/* Fiber */
 | 
			
		||||
JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv);
 | 
			
		||||
JANET_API JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv);
 | 
			
		||||
JANET_API JanetFiberStatus janet_fiber_status(JanetFiber *fiber);
 | 
			
		||||
JANET_API int janet_fiber_can_resume(JanetFiber *fiber);
 | 
			
		||||
JANET_API JanetFiber *janet_current_fiber(void);
 | 
			
		||||
JANET_API JanetFiber *janet_root_fiber(void);
 | 
			
		||||
 | 
			
		||||
@@ -1702,6 +1810,7 @@ JANET_API void janet_gcpressure(size_t s);
 | 
			
		||||
/* Functions */
 | 
			
		||||
JANET_API JanetFuncDef *janet_funcdef_alloc(void);
 | 
			
		||||
JANET_API JanetFunction *janet_thunk(JanetFuncDef *def);
 | 
			
		||||
JANET_API JanetFunction *janet_thunk_delay(Janet x);
 | 
			
		||||
JANET_API int janet_verify(JanetFuncDef *def);
 | 
			
		||||
 | 
			
		||||
/* Pretty printing */
 | 
			
		||||
@@ -1750,6 +1859,7 @@ JANET_API void janet_vm_free(JanetVM *vm);
 | 
			
		||||
JANET_API void janet_vm_save(JanetVM *into);
 | 
			
		||||
JANET_API void janet_vm_load(JanetVM *from);
 | 
			
		||||
JANET_API void janet_interpreter_interrupt(JanetVM *vm);
 | 
			
		||||
JANET_API void janet_interpreter_interrupt_handled(JanetVM *vm);
 | 
			
		||||
JANET_API JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out);
 | 
			
		||||
JANET_API JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig);
 | 
			
		||||
JANET_API JanetSignal janet_pcall(JanetFunction *fun, int32_t argn, const Janet *argv, Janet *out, JanetFiber **f);
 | 
			
		||||
@@ -1759,6 +1869,28 @@ JANET_API Janet janet_mcall(const char *name, int32_t argc, Janet *argv);
 | 
			
		||||
JANET_API void janet_stacktrace(JanetFiber *fiber, Janet err);
 | 
			
		||||
JANET_API void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix);
 | 
			
		||||
 | 
			
		||||
/* Sandboxing API */
 | 
			
		||||
#define JANET_SANDBOX_SANDBOX 1
 | 
			
		||||
#define JANET_SANDBOX_SUBPROCESS 2
 | 
			
		||||
#define JANET_SANDBOX_NET_CONNECT 4
 | 
			
		||||
#define JANET_SANDBOX_NET_LISTEN 8
 | 
			
		||||
#define JANET_SANDBOX_FFI_DEFINE 16
 | 
			
		||||
#define JANET_SANDBOX_FS_WRITE 32
 | 
			
		||||
#define JANET_SANDBOX_FS_READ 64
 | 
			
		||||
#define JANET_SANDBOX_HRTIME 128
 | 
			
		||||
#define JANET_SANDBOX_ENV 256
 | 
			
		||||
#define JANET_SANDBOX_DYNAMIC_MODULES 512
 | 
			
		||||
#define JANET_SANDBOX_FS_TEMP 1024
 | 
			
		||||
#define JANET_SANDBOX_FFI_USE 2048
 | 
			
		||||
#define JANET_SANDBOX_FFI_JIT 4096
 | 
			
		||||
#define JANET_SANDBOX_SIGNAL 8192
 | 
			
		||||
#define JANET_SANDBOX_FFI (JANET_SANDBOX_FFI_DEFINE | JANET_SANDBOX_FFI_USE | JANET_SANDBOX_FFI_JIT)
 | 
			
		||||
#define JANET_SANDBOX_FS (JANET_SANDBOX_FS_WRITE | JANET_SANDBOX_FS_READ | JANET_SANDBOX_FS_TEMP)
 | 
			
		||||
#define JANET_SANDBOX_NET (JANET_SANDBOX_NET_CONNECT | JANET_SANDBOX_NET_LISTEN)
 | 
			
		||||
#define JANET_SANDBOX_ALL (UINT32_MAX)
 | 
			
		||||
JANET_API void janet_sandbox(uint32_t flags);
 | 
			
		||||
JANET_API void janet_sandbox_assert(uint32_t forbidden_flags);
 | 
			
		||||
 | 
			
		||||
/* Scratch Memory API */
 | 
			
		||||
typedef void (*JanetScratchFinalizer)(void *);
 | 
			
		||||
 | 
			
		||||
@@ -1817,7 +1949,7 @@ JANET_API Janet janet_resolve_core(const char *name);
 | 
			
		||||
/* sourcemaps only */
 | 
			
		||||
#define JANET_REG_S(JNAME, CNAME) {JNAME, CNAME, NULL, __FILE__, CNAME##_sourceline_}
 | 
			
		||||
#define JANET_FN_S(CNAME, USAGE, DOCSTRING) \
 | 
			
		||||
    static int32_t CNAME##_sourceline_ = __LINE__; \
 | 
			
		||||
    static const int32_t CNAME##_sourceline_ = __LINE__; \
 | 
			
		||||
    Janet CNAME (int32_t argc, Janet *argv)
 | 
			
		||||
#define JANET_DEF_S(ENV, JNAME, VAL, DOC) \
 | 
			
		||||
    janet_def_sm(ENV, JNAME, VAL, NULL, __FILE__, __LINE__)
 | 
			
		||||
@@ -1833,13 +1965,12 @@ JANET_API Janet janet_resolve_core(const char *name);
 | 
			
		||||
/* sourcemaps and docstrings */
 | 
			
		||||
#define JANET_REG_SD(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, __FILE__, CNAME##_sourceline_}
 | 
			
		||||
#define JANET_FN_SD(CNAME, USAGE, DOCSTRING) \
 | 
			
		||||
    static int32_t CNAME##_sourceline_ = __LINE__; \
 | 
			
		||||
    static const int32_t CNAME##_sourceline_ = __LINE__; \
 | 
			
		||||
    static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \
 | 
			
		||||
    Janet CNAME (int32_t argc, Janet *argv)
 | 
			
		||||
#define JANET_DEF_SD(ENV, JNAME, VAL, DOC) \
 | 
			
		||||
    janet_def_sm(ENV, JNAME, VAL, DOC, __FILE__, __LINE__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Choose defaults for source mapping and docstring based on config defs */
 | 
			
		||||
#if defined(JANET_NO_SOURCEMAPS) && defined(JANET_NO_DOCSTRINGS)
 | 
			
		||||
#define JANET_REG JANET_REG_
 | 
			
		||||
@@ -1876,10 +2007,10 @@ JANET_API void janet_register(const char *name, JanetCFunction cfun);
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef JANET_ENTRY_NAME
 | 
			
		||||
#define JANET_MODULE_ENTRY \
 | 
			
		||||
    JANET_MODULE_PREFIX JANET_API JanetBuildConfig _janet_mod_config(void) { \
 | 
			
		||||
    JANET_MODULE_PREFIX JANET_EXPORT JanetBuildConfig _janet_mod_config(void) { \
 | 
			
		||||
        return janet_config_current(); \
 | 
			
		||||
    } \
 | 
			
		||||
    JANET_MODULE_PREFIX JANET_API void _janet_init
 | 
			
		||||
    JANET_MODULE_PREFIX JANET_EXPORT void _janet_init
 | 
			
		||||
#else
 | 
			
		||||
#define JANET_MODULE_ENTRY JANET_MODULE_PREFIX JANET_API void JANET_ENTRY_NAME
 | 
			
		||||
#endif
 | 
			
		||||
@@ -1907,6 +2038,7 @@ JANET_API JanetTable *janet_gettable(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetStruct janet_getstruct(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetString janet_getstring(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API const char *janet_getcstring(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API const char *janet_getcbytes(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetSymbol janet_getsymbol(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetKeyword janet_getkeyword(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetBuffer *janet_getbuffer(const Janet *argv, int32_t n);
 | 
			
		||||
@@ -1918,7 +2050,10 @@ JANET_API void *janet_getpointer(const Janet *argv, int32_t n);
 | 
			
		||||
 | 
			
		||||
JANET_API int32_t janet_getnat(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API int32_t janet_getinteger(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API int16_t janet_getinteger16(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API int64_t janet_getinteger64(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API uint32_t janet_getuinteger(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API uint16_t janet_getuinteger16(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API uint64_t janet_getuinteger64(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API size_t janet_getsize(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetView janet_getindexed(const Janet *argv, int32_t n);
 | 
			
		||||
@@ -1927,6 +2062,8 @@ JANET_API JanetDictView janet_getdictionary(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API void *janet_getabstract(const Janet *argv, int32_t n, const JanetAbstractType *at);
 | 
			
		||||
JANET_API JanetRange janet_getslice(int32_t argc, const Janet *argv);
 | 
			
		||||
JANET_API int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const char *which);
 | 
			
		||||
JANET_API int32_t janet_getstartrange(const Janet *argv, int32_t argc, int32_t n, int32_t length);
 | 
			
		||||
JANET_API int32_t janet_getendrange(const Janet *argv, int32_t argc, int32_t n, int32_t length);
 | 
			
		||||
JANET_API int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which);
 | 
			
		||||
JANET_API uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags);
 | 
			
		||||
 | 
			
		||||
@@ -1936,6 +2073,7 @@ JANET_API JanetTuple janet_opttuple(const Janet *argv, int32_t argc, int32_t n,
 | 
			
		||||
JANET_API JanetStruct janet_optstruct(const Janet *argv, int32_t argc, int32_t n, JanetStruct dflt);
 | 
			
		||||
JANET_API JanetString janet_optstring(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
 | 
			
		||||
JANET_API const char *janet_optcstring(const Janet *argv, int32_t argc, int32_t n, const char *dflt);
 | 
			
		||||
JANET_API const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt);
 | 
			
		||||
JANET_API JanetSymbol janet_optsymbol(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
 | 
			
		||||
JANET_API JanetKeyword janet_optkeyword(const Janet *argv, int32_t argc, int32_t n, JanetString dflt);
 | 
			
		||||
JANET_API JanetFiber *janet_optfiber(const Janet *argv, int32_t argc, int32_t n, JanetFiber *dflt);
 | 
			
		||||
@@ -1984,6 +2122,7 @@ JANET_API int janet_cryptorand(uint8_t *out, size_t n);
 | 
			
		||||
JANET_API void janet_marshal_size(JanetMarshalContext *ctx, size_t value);
 | 
			
		||||
JANET_API void janet_marshal_int(JanetMarshalContext *ctx, int32_t value);
 | 
			
		||||
JANET_API void janet_marshal_int64(JanetMarshalContext *ctx, int64_t value);
 | 
			
		||||
JANET_API void janet_marshal_ptr(JanetMarshalContext *ctx, const void *value);
 | 
			
		||||
JANET_API void janet_marshal_byte(JanetMarshalContext *ctx, uint8_t value);
 | 
			
		||||
JANET_API void janet_marshal_bytes(JanetMarshalContext *ctx, const uint8_t *bytes, size_t len);
 | 
			
		||||
JANET_API void janet_marshal_janet(JanetMarshalContext *ctx, Janet x);
 | 
			
		||||
@@ -1993,10 +2132,12 @@ JANET_API void janet_unmarshal_ensure(JanetMarshalContext *ctx, size_t size);
 | 
			
		||||
JANET_API size_t janet_unmarshal_size(JanetMarshalContext *ctx);
 | 
			
		||||
JANET_API int32_t janet_unmarshal_int(JanetMarshalContext *ctx);
 | 
			
		||||
JANET_API int64_t janet_unmarshal_int64(JanetMarshalContext *ctx);
 | 
			
		||||
JANET_API void *janet_unmarshal_ptr(JanetMarshalContext *ctx);
 | 
			
		||||
JANET_API uint8_t janet_unmarshal_byte(JanetMarshalContext *ctx);
 | 
			
		||||
JANET_API void janet_unmarshal_bytes(JanetMarshalContext *ctx, uint8_t *dest, size_t len);
 | 
			
		||||
JANET_API Janet janet_unmarshal_janet(JanetMarshalContext *ctx);
 | 
			
		||||
JANET_API JanetAbstract janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size);
 | 
			
		||||
JANET_API JanetAbstract janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size);
 | 
			
		||||
JANET_API void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p);
 | 
			
		||||
 | 
			
		||||
JANET_API void janet_register_abstract_type(const JanetAbstractType *at);
 | 
			
		||||
@@ -2035,11 +2176,16 @@ typedef enum {
 | 
			
		||||
    RULE_TO,           /* [rule] */
 | 
			
		||||
    RULE_THRU,         /* [rule] */
 | 
			
		||||
    RULE_LENPREFIX,    /* [rule_a, rule_b (repeat rule_b rule_a times)] */
 | 
			
		||||
    RULE_READINT,      /* [(signedness << 4) | (endianess << 5) | bytewidth, tag] */
 | 
			
		||||
    RULE_READINT,      /* [(signedness << 4) | (endianness << 5) | bytewidth, tag] */
 | 
			
		||||
    RULE_LINE,         /* [tag] */
 | 
			
		||||
    RULE_COLUMN,       /* [tag] */
 | 
			
		||||
    RULE_UNREF,        /* [rule, tag] */
 | 
			
		||||
    RULE_CAPTURE_NUM   /* [rule, tag] */
 | 
			
		||||
    RULE_CAPTURE_NUM,  /* [rule, tag] */
 | 
			
		||||
    RULE_SUB,          /* [rule, rule] */
 | 
			
		||||
    RULE_TIL,          /* [rule, rule] */
 | 
			
		||||
    RULE_SPLIT,        /* [rule, rule] */
 | 
			
		||||
    RULE_NTH,          /* [nth, rule, tag] */
 | 
			
		||||
    RULE_ONLY_TAGS,    /* [rule] */
 | 
			
		||||
} JanetPegOpcod;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
* Copyright (c) 2025 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,9 @@
 | 
			
		||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
 | 
			
		||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
 | 
			
		||||
#endif
 | 
			
		||||
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
 | 
			
		||||
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void janet_line_init();
 | 
			
		||||
@@ -144,8 +147,11 @@ static void setup_console_output(void) {
 | 
			
		||||
    DWORD dwMode = 0;
 | 
			
		||||
    GetConsoleMode(hOut, &dwMode);
 | 
			
		||||
    dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
 | 
			
		||||
    dwMode |= ENABLE_PROCESSED_OUTPUT;
 | 
			
		||||
    SetConsoleMode(hOut, dwMode);
 | 
			
		||||
    SetConsoleOutputCP(65001);
 | 
			
		||||
    if (IsValidCodePage(65001)) {
 | 
			
		||||
        SetConsoleOutputCP(65001);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ansi terminal raw mode */
 | 
			
		||||
@@ -296,6 +302,7 @@ static char *sdup(const char *s) {
 | 
			
		||||
    return memcpy(mem, s, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
static int curpos(void) {
 | 
			
		||||
    char buf[32];
 | 
			
		||||
    int cols, rows;
 | 
			
		||||
@@ -311,6 +318,7 @@ static int curpos(void) {
 | 
			
		||||
    if (sscanf(buf + 2, "%d;%d", &rows, &cols) != 2) return -1;
 | 
			
		||||
    return cols;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static int getcols(void) {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
@@ -494,10 +502,10 @@ static void kright(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void krightw(void) {
 | 
			
		||||
    while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) {
 | 
			
		||||
    while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) {
 | 
			
		||||
        gbl_pos++;
 | 
			
		||||
    }
 | 
			
		||||
    while (gbl_pos != gbl_len && isspace(gbl_buf[gbl_pos])) {
 | 
			
		||||
    while (gbl_pos != gbl_len && !isspace(gbl_buf[gbl_pos])) {
 | 
			
		||||
        gbl_pos++;
 | 
			
		||||
    }
 | 
			
		||||
    refresh();
 | 
			
		||||
@@ -540,7 +548,6 @@ static void kdeletew(void) {
 | 
			
		||||
    refresh();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* See tools/symchargen.c */
 | 
			
		||||
static int is_symbol_char_gen(uint8_t c) {
 | 
			
		||||
    if (c & 0x80) return 1;
 | 
			
		||||
@@ -860,7 +867,7 @@ static int line() {
 | 
			
		||||
    if (write_console((char *) gbl_prompt, gbl_plen) == -1) return -1;
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        char c;
 | 
			
		||||
        char seq[3];
 | 
			
		||||
        char seq[5];
 | 
			
		||||
 | 
			
		||||
        int rc;
 | 
			
		||||
        do {
 | 
			
		||||
@@ -950,6 +957,7 @@ static int line() {
 | 
			
		||||
                break;
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
            case 26: /* ctrl-z */
 | 
			
		||||
                clearlines();
 | 
			
		||||
                norawmode();
 | 
			
		||||
                kill(getpid(), SIGSTOP);
 | 
			
		||||
                rawmode();
 | 
			
		||||
@@ -983,6 +991,20 @@ static int line() {
 | 
			
		||||
                                default:
 | 
			
		||||
                                    break;
 | 
			
		||||
                            }
 | 
			
		||||
                        } else if (seq[2] == ';') {
 | 
			
		||||
                            if (read_console(seq + 3, 2) == -1) break;
 | 
			
		||||
                            if (seq[3] == '5') {
 | 
			
		||||
                                switch (seq[4]) {
 | 
			
		||||
                                    case 'C': /* ctrl-right */
 | 
			
		||||
                                        krightw();
 | 
			
		||||
                                        break;
 | 
			
		||||
                                    case 'D': /* ctrl-left */
 | 
			
		||||
                                        kleftw();
 | 
			
		||||
                                        break;
 | 
			
		||||
                                    default:
 | 
			
		||||
                                        break;
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (seq[0] == 'O') {
 | 
			
		||||
                        if (read_console(seq + 1, 1) == -1) break;
 | 
			
		||||
@@ -1155,6 +1177,7 @@ int main(int argc, char **argv) {
 | 
			
		||||
    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);
 | 
			
		||||
    janet_gcroot(janet_wrap_fiber(fiber));
 | 
			
		||||
    fiber->env = env;
 | 
			
		||||
 | 
			
		||||
    /* Run the fiber in an event loop */
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user