mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 01:23:04 +00:00 
			
		
		
		
	Compare commits
	
		
			1765 Commits
		
	
	
		
			newjpm
			...
			no-critica
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					624afe1336 | ||
| 
						 | 
					3dab9737e2 | ||
| 
						 | 
					e601e8faab | ||
| 
						 | 
					07cf63622f | ||
| 
						 | 
					8e7b1e9ce0 | ||
| 
						 | 
					355c514f0e | ||
| 
						 | 
					976329abc1 | ||
| 
						 | 
					ab3e843433 | ||
| 
						 | 
					148e108864 | ||
| 
						 | 
					c90c737345 | ||
| 
						 | 
					13b9976382 | ||
| 
						 | 
					095a81286a | ||
| 
						 | 
					82416e4e4e | ||
| 
						 | 
					ae51434a05 | ||
| 
						 | 
					bb6ac423a7 | ||
| 
						 | 
					c5ba3c0513 | ||
| 
						 | 
					e9c6678614 | ||
| 
						 | 
					800457c1bf | ||
| 
						 | 
					2a85781616 | ||
| 
						 | 
					7c15e7f7dc | ||
| 
						 | 
					896c28b0c8 | ||
| 
						 | 
					e7bb0dd58e | ||
| 
						 | 
					4e02f27eb9 | ||
| 
						 | 
					fd234461d7 | ||
| 
						 | 
					eabb215391 | ||
| 
						 | 
					deede6bae0 | ||
| 
						 | 
					697fdcff6d | ||
| 
						 | 
					ad8a5cb6c7 | ||
| 
						 | 
					99abada2c2 | ||
| 
						 | 
					0624936711 | ||
| 
						 | 
					f764788b36 | ||
| 
						 | 
					4701bc6543 | ||
| 
						 | 
					156fb0c999 | ||
| 
						 | 
					bf34340737 | ||
| 
						 | 
					20535e8626 | ||
| 
						 | 
					1ead670e33 | ||
| 
						 | 
					3ad86108f2 | ||
| 
						 | 
					0aee7765cf | ||
| 
						 | 
					4894a4673a | ||
| 
						 | 
					f00d3199c3 | ||
| 
						 | 
					e34a8545e6 | ||
| 
						 | 
					f974c0667b | ||
| 
						 | 
					ddc122958b | ||
| 
						 | 
					2e363bf29c | ||
| 
						 | 
					312f9faae8 | ||
| 
						 | 
					8c9cd63cb1 | ||
| 
						 | 
					2af3f21d69 | ||
| 
						 | 
					c4e3fa03fa | ||
| 
						 | 
					91b7bcad3d | ||
| 
						 | 
					8d2a9c1148 | ||
| 
						 | 
					f1d47bd05a | ||
| 
						 | 
					58b1491592 | ||
| 
						 | 
					21a6ed3bd3 | ||
| 
						 | 
					e815c91e85 | ||
| 
						 | 
					d96e584869 | ||
| 
						 | 
					f4ecb5a90f | ||
| 
						 | 
					f181948aa9 | ||
| 
						 | 
					bbe6b90331 | ||
| 
						 | 
					27f01e2664 | ||
| 
						 | 
					877967966a | ||
| 
						 | 
					56c5a0ca09 | ||
| 
						 | 
					f3ad13c2d4 | ||
| 
						 | 
					8ac4eec370 | ||
| 
						 | 
					92e91259c3 | ||
| 
						 | 
					e355cb07e0 | ||
| 
						 | 
					5bbfcdacd5 | ||
| 
						 | 
					790a4f2636 | ||
| 
						 | 
					84bb84b0b7 | ||
| 
						 | 
					29f2b5c345 | ||
| 
						 | 
					4643c8fa35 | ||
| 
						 | 
					a8e2c8e5b8 | ||
| 
						 | 
					3d3e880f52 | ||
| 
						 | 
					bef8ba5e06 | ||
| 
						 | 
					523639362c | ||
| 
						 | 
					4b6d5e5671 | ||
| 
						 | 
					a695454dae | ||
| 
						 | 
					f2eaa5dee8 | ||
| 
						 | 
					b27c830d90 | ||
| 
						 | 
					92a852f2df | ||
| 
						 | 
					647e218bed | ||
| 
						 | 
					5ebe945ffd | ||
| 
						 | 
					6254fffad0 | ||
| 
						 | 
					5705b2f6c7 | ||
| 
						 | 
					90a33bc88a | ||
| 
						 | 
					1ba077c87d | ||
| 
						 | 
					34629ae314 | ||
| 
						 | 
					3edc4f35b2 | ||
| 
						 | 
					06d01c099f | ||
| 
						 | 
					d493eaf485 | ||
| 
						 | 
					332f123abe | ||
| 
						 | 
					38e841fc5c | ||
| 
						 | 
					e8187fdee5 | ||
| 
						 | 
					2fedb67cb3 | ||
| 
						 | 
					bdab93c999 | ||
| 
						 | 
					a9ff8b388f | ||
| 
						 | 
					b12dfd784e | ||
| 
						 | 
					e2cc8f2965 | ||
| 
						 | 
					17524d2ed3 | ||
| 
						 | 
					d2ee4aa074 | ||
| 
						 | 
					363e32d455 | ||
| 
						 | 
					31920e574d | ||
| 
						 | 
					cf714ed591 | ||
| 
						 | 
					b458404b41 | ||
| 
						 | 
					707463a645 | ||
| 
						 | 
					eac37ab869 | ||
| 
						 | 
					a24e5b1eaa | ||
| 
						 | 
					09ac85b1b9 | ||
| 
						 | 
					87c1eab7d4 | ||
| 
						 | 
					5a29a28c11 | ||
| 
						 | 
					2ed186664f | ||
| 
						 | 
					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 | ||
| 
						 | 
					54b52bbeb5 | ||
| 
						 | 
					1174c68d9a | ||
| 
						 | 
					448ea7167f | ||
| 
						 | 
					6b27008c99 | ||
| 
						 | 
					725c785882 | ||
| 
						 | 
					ab068cff67 | ||
| 
						 | 
					9dc03adfda | ||
| 
						 | 
					49f9e4eddf | ||
| 
						 | 
					43c47ac44c | ||
| 
						 | 
					1cebe64664 | ||
| 
						 | 
					f33c381043 | ||
| 
						 | 
					3479841c77 | ||
| 
						 | 
					6a899968a9 | ||
| 
						 | 
					bb8405a36e | ||
| 
						 | 
					c7bc711f63 | ||
| 
						 | 
					e326071c35 | ||
| 
						 | 
					ad6a669381 | ||
| 
						 | 
					e4c9dafc9a | ||
| 
						 | 
					dfc0aefd87 | ||
| 
						 | 
					356b39c6f5 | ||
| 
						 | 
					8da7bb6b68 | ||
| 
						 | 
					9341081a4d | ||
| 
						 | 
					324a086eb4 | ||
| 
						 | 
					ed595f52c2 | ||
| 
						 | 
					64ad0023bb | ||
| 
						 | 
					fe5f661d15 | ||
| 
						 | 
					ff26e3a8ba | ||
| 
						 | 
					14657a762c | ||
| 
						 | 
					4754fa3902 | ||
| 
						 | 
					f302f87337 | ||
| 
						 | 
					94dbcde292 | ||
| 
						 | 
					4336a174b1 | ||
| 
						 | 
					0adb13ed71 | ||
| 
						 | 
					e78a3d1c19 | ||
| 
						 | 
					c099ec05ee | ||
| 
						 | 
					a20612478e | ||
| 
						 | 
					f778e8bbd1 | ||
| 
						 | 
					7203c046f9 | ||
| 
						 | 
					754b61c593 | ||
| 
						 | 
					927e9e4e4d | ||
| 
						 | 
					699f9622d7 | ||
| 
						 | 
					765eb84c33 | ||
| 
						 | 
					03ba1f7021 | ||
| 
						 | 
					1f7f20788c | ||
| 
						 | 
					c59dd29190 | ||
| 
						 | 
					99f63a41a3 | ||
| 
						 | 
					a575f5df36 | ||
| 
						 | 
					12a1849090 | ||
| 
						 | 
					0817e627ee | ||
| 
						 | 
					14d90239a7 | ||
| 
						 | 
					f5d11dc656 | ||
| 
						 | 
					6dcf5bf077 | ||
| 
						 | 
					ac2082e9b3 | ||
| 
						 | 
					dbac495bee | ||
| 
						 | 
					fe5ccb163e | ||
| 
						 | 
					1aea5ee007 | ||
| 
						 | 
					13cd9f8067 | ||
| 
						 | 
					34496ecaf0 | ||
| 
						 | 
					c043b1d949 | ||
| 
						 | 
					9a6d2a7b32 | ||
| 
						 | 
					f8a9efa8e4 | ||
| 
						 | 
					5b2169e0d1 | ||
| 
						 | 
					2c927ea768 | ||
| 
						 | 
					f4bbcdcbc8 | ||
| 
						 | 
					79c375b1af | ||
| 
						 | 
					f443a3b3a1 | ||
| 
						 | 
					684d2d63f4 | ||
| 
						 | 
					1900d8f843 | ||
| 
						 | 
					3c2af95d21 | ||
| 
						 | 
					b35414ea0f | ||
| 
						 | 
					fb5b056f7b | ||
| 
						 | 
					7248c1dfdb | ||
| 
						 | 
					4c7ea9e893 | ||
| 
						 | 
					c7801ce277 | ||
| 
						 | 
					f741a8e3ff | ||
| 
						 | 
					6a92e8b609 | ||
| 
						 | 
					9da91a8217 | ||
| 
						 | 
					69853c8e5c | ||
| 
						 | 
					1f41b6c138 | ||
| 
						 | 
					e001efa9fd | ||
| 
						 | 
					435e64d4cf | ||
| 
						 | 
					f296c8f5fb | ||
| 
						 | 
					8d0e6ed32f | ||
| 
						 | 
					b6a36afffe | ||
| 
						 | 
					e422abc269 | ||
| 
						 | 
					221d71d07b | ||
| 
						 | 
					9f35f0837e | ||
| 
						 | 
					515891b035 | ||
| 
						 | 
					94a506876f | ||
| 
						 | 
					9bde57854a | ||
| 
						 | 
					f456369941 | ||
| 
						 | 
					8f0a1ffe5d | ||
| 
						 | 
					e4bafc621a | ||
| 
						 | 
					cfa39ab3b0 | ||
| 
						 | 
					47e91bfd89 | ||
| 
						 | 
					eecc388ebd | ||
| 
						 | 
					0a15a5ee56 | ||
| 
						 | 
					cfaae47cea | ||
| 
						 | 
					c1a0352592 | ||
| 
						 | 
					965f45aa3f | ||
| 
						 | 
					6ea27fe836 | ||
| 
						 | 
					0dccc22b38 | ||
| 
						 | 
					cbe833962b | ||
| 
						 | 
					b5720f6f10 | ||
| 
						 | 
					56b4e0b0ec | ||
| 
						 | 
					e316ccb1e0 | ||
| 
						 | 
					a6f93efd39 | ||
| 
						 | 
					20511cf608 | ||
| 
						 | 
					1a1dd39367 | ||
| 
						 | 
					589981bdcb | ||
| 
						 | 
					89546776b2 | ||
| 
						 | 
					f0d7b3cd12 | ||
| 
						 | 
					e37be627e0 | ||
| 
						 | 
					d803561582 | ||
| 
						 | 
					a1aab4008f | ||
| 
						 | 
					a1172529bf | ||
| 
						 | 
					1d905bf07f | ||
| 
						 | 
					eed678a14b | ||
| 
						 | 
					b1bdffbc34 | ||
| 
						 | 
					cff718f37d | ||
| 
						 | 
					40e9430278 | ||
| 
						 | 
					62fc55fc74 | ||
| 
						 | 
					80729353c8 | ||
| 
						 | 
					105ba5e124 | ||
| 
						 | 
					ad1b50d1f5 | ||
| 
						 | 
					1905437abe | ||
| 
						 | 
					87fc339c45 | ||
| 
						 | 
					3af7d61d3e | ||
| 
						 | 
					a45ef7a856 | ||
| 
						 | 
					299998055d | ||
| 
						 | 
					c9586d39ed | ||
| 
						 | 
					2e9f67f4e4 | ||
| 
						 | 
					e318170fea | ||
| 
						 | 
					73c4289792 | ||
| 
						 | 
					ea45d7ee47 | ||
| 
						 | 
					6d970725e7 | ||
| 
						 | 
					458c2c6d88 | ||
| 
						 | 
					0cc53a8964 | ||
| 
						 | 
					0bc96304a9 | ||
| 
						 | 
					c75b088ff8 | ||
| 
						 | 
					181f0341f5 | ||
| 
						 | 
					33bb08d53b | ||
| 
						 | 
					6d188f6e44 | ||
| 
						 | 
					c3648331f1 | ||
| 
						 | 
					a5b66029d3 | ||
| 
						 | 
					49bfe80191 | ||
| 
						 | 
					a5def77bfe | ||
| 
						 | 
					9ecb5b4791 | ||
| 
						 | 
					1cc48a370a | ||
| 
						 | 
					f1ec8d1e11 | ||
| 
						 | 
					55c34cd84f | ||
| 
						 | 
					aca52d1e36 | ||
| 
						 | 
					6f90df26a5 | ||
| 
						 | 
					9d9cb378ff | ||
| 
						 | 
					f92aac14aa | ||
| 
						 | 
					3f27d78ab5 | ||
| 
						 | 
					282d1ba22f | ||
| 
						 | 
					94c19575b1 | ||
| 
						 | 
					e3e485285b | ||
| 
						 | 
					986e36720e | ||
| 
						 | 
					74348ab6c2 | ||
| 
						 | 
					8d1ad99f42 | ||
| 
						 | 
					e69bbff195 | ||
| 
						 | 
					c9f33bbde0 | ||
| 
						 | 
					9c9f9d4fa6 | ||
| 
						 | 
					2f64a6b0cb | ||
| 
						 | 
					dfa78ad3c6 | ||
| 
						 | 
					677ae46f0c | ||
| 
						 | 
					6ada2a458f | ||
| 
						 | 
					8145f3b68d | ||
| 
						 | 
					48289acee6 | ||
| 
						 | 
					e5a989c6f9 | ||
| 
						 | 
					4c56704935 | ||
| 
						 | 
					9cda44f443 | ||
| 
						 | 
					431451bac2 | ||
| 
						 | 
					395ca7feea | ||
| 
						 | 
					e0b7533c39 | ||
| 
						 | 
					5b2a402930 | ||
| 
						 | 
					85129a1873 | ||
| 
						 | 
					487d333024 | ||
| 
						 | 
					fe7d35171f | ||
| 
						 | 
					b3aed13567 | ||
| 
						 | 
					a9d4d2bfa3 | ||
| 
						 | 
					1ff521683f | ||
| 
						 | 
					0395a03b6b | ||
| 
						 | 
					7fda7709ff | ||
| 
						 | 
					65a9200cff | ||
| 
						 | 
					473eec26c1 | ||
| 
						 | 
					9fa945ad93 | ||
| 
						 | 
					a895219d2f | ||
| 
						 | 
					427f7c362e | ||
| 
						 | 
					73f5c41fae | ||
| 
						 | 
					b4ec168401 | ||
| 
						 | 
					726d35c766 | ||
| 
						 | 
					6db796e10c | ||
| 
						 | 
					c38d9134cd | ||
| 
						 | 
					471204b163 | ||
| 
						 | 
					7f23bfa66d | ||
| 
						 | 
					9287b26042 | ||
| 
						 | 
					e22936fbf8 | ||
| 
						 | 
					04ace9fc16 | ||
| 
						 | 
					8466b333fb | ||
| 
						 | 
					96602612ba | ||
| 
						 | 
					690b98bff9 | ||
| 
						 | 
					8329131bfe | ||
| 
						 | 
					9986aab326 | ||
| 
						 | 
					0b105bc535 | ||
| 
						 | 
					51ac9c9506 | ||
| 
						 | 
					0310176696 | ||
| 
						 | 
					84a7a2bc3e | ||
| 
						 | 
					1e66a7e555 | ||
| 
						 | 
					2bffb9d682 | ||
| 
						 | 
					811125a760 | ||
| 
						 | 
					0dd91082a1 | ||
| 
						 | 
					c80587868e | ||
| 
						 | 
					8c52dc86c7 | ||
| 
						 | 
					be24592bc3 | ||
| 
						 | 
					0d1a5c621d | ||
| 
						 | 
					8a3eff3b65 | ||
| 
						 | 
					b1050b884d | ||
| 
						 | 
					181d883a1d | ||
| 
						 | 
					e01b65fd3d | ||
| 
						 | 
					bbd74b5ae2 | ||
| 
						 | 
					d5a5c49357 | ||
| 
						 | 
					a964b164a6 | ||
| 
						 | 
					1aac0489d7 | ||
| 
						 | 
					e474755887 | ||
| 
						 | 
					bf9a60f70d | ||
| 
						 | 
					a2ba0913d3 | ||
| 
						 | 
					f74df41fff | ||
| 
						 | 
					2a950e4ce9 | ||
| 
						 | 
					f05e5f908e | ||
| 
						 | 
					43139b43b1 | ||
| 
						 | 
					5811b47aad | ||
| 
						 | 
					54e3db4d8c | ||
| 
						 | 
					7491421c31 | ||
| 
						 | 
					9d0da74347 | ||
| 
						 | 
					e9870b293f | ||
| 
						 | 
					ab910d060b | ||
| 
						 | 
					b60ef68ac6 | ||
| 
						 | 
					c9986936ed | ||
| 
						 | 
					d77be46644 | ||
| 
						 | 
					3715d7a184 | ||
| 
						 | 
					1c96c7163a | ||
| 
						 | 
					9f733b25db | ||
| 
						 | 
					1419a33b64 | ||
| 
						 | 
					f270739f9f | ||
| 
						 | 
					e51a391286 | ||
| 
						 | 
					c815185574 | ||
| 
						 | 
					8045e29a52 | ||
| 
						 | 
					bbb3e16fd1 | ||
| 
						 | 
					3cd1657387 | ||
| 
						 | 
					d7ea122cf7 | ||
| 
						 | 
					6aea7c7f70 | ||
| 
						 | 
					56ba1d9cd3 | ||
| 
						 | 
					408b03ae0d | ||
| 
						 | 
					d94fd746af | ||
| 
						 | 
					dbd1316d1e | ||
| 
						 | 
					75845c0283 | ||
| 
						 | 
					88db9751d7 | ||
| 
						 | 
					6f645c4cb7 | ||
| 
						 | 
					4e31d85349 | ||
| 
						 | 
					de542a81c0 | ||
| 
						 | 
					461576e7a2 | ||
| 
						 | 
					21bd62b1ce | ||
| 
						 | 
					838cd1157c | ||
| 
						 | 
					2f068b91d8 | ||
| 
						 | 
					aba87bf1bd | ||
| 
						 | 
					e64da8ede4 | ||
| 
						 | 
					a9f38dfce4 | ||
| 
						 | 
					a097537a03 | ||
| 
						 | 
					66e0b53cf6 | ||
| 
						 | 
					06f2e81dd5 | ||
| 
						 | 
					40ae2e812f | ||
| 
						 | 
					06f613e40b | ||
| 
						 | 
					61c8c1e8d2 | ||
| 
						 | 
					ee924ee310 | ||
| 
						 | 
					fad0ce3ced | ||
| 
						 | 
					d396180939 | ||
| 
						 | 
					0d089abe67 | ||
| 
						 | 
					ed5c1dfc3c | ||
| 
						 | 
					6b949a7375 | ||
| 
						 | 
					3028e2908f | ||
| 
						 | 
					578803b01f | ||
| 
						 | 
					46738825c0 | ||
| 
						 | 
					56357699cb | ||
| 
						 | 
					fe8e718183 | ||
| 
						 | 
					1eb34989d4 | ||
| 
						 | 
					2f3b4c8bfb | ||
| 
						 | 
					6412768000 | ||
| 
						 | 
					82688b9a44 | ||
| 
						 | 
					651e12cfe4 | ||
| 
						 | 
					4118d581af | ||
| 
						 | 
					62608bec03 | ||
| 
						 | 
					71cffc973d | ||
| 
						 | 
					a8e49d084b | ||
| 
						 | 
					db631097b1 | ||
| 
						 | 
					0d31674166 | ||
| 
						 | 
					cb5af974a4 | ||
| 
						 | 
					f2f421a0a2 | ||
| 
						 | 
					413c46e2ee | ||
| 
						 | 
					3b412d51f0 | ||
| 
						 | 
					4931e2aee2 | ||
| 
						 | 
					ffadf673cf | ||
| 
						 | 
					5b5a7e5a24 | ||
| 
						 | 
					ab53208f47 | ||
| 
						 | 
					7c407705e8 | ||
| 
						 | 
					60378ff941 | ||
| 
						 | 
					30a0c77d19 | ||
| 
						 | 
					07ec89276b | ||
| 
						 | 
					a37dc1af9d | ||
| 
						 | 
					03458df140 | ||
| 
						 | 
					164eb9659e | ||
| 
						 | 
					99cfbaa63b | ||
| 
						 | 
					8d8a6534e3 | ||
| 
						 | 
					938c5013c9 | ||
| 
						 | 
					ea9d5ec793 | ||
| 
						 | 
					ec65f038a8 | ||
| 
						 | 
					199ec36d40 | ||
| 
						 | 
					1326ded048 | ||
| 
						 | 
					8347439644 | ||
| 
						 | 
					cddc2a8280 | ||
| 
						 | 
					97a8938407 | ||
| 
						 | 
					939d1dcae9 | ||
| 
						 | 
					9d5cc5c11f | ||
| 
						 | 
					d998f24d26 | ||
| 
						 | 
					d543f8857b | ||
| 
						 | 
					c48a942d22 | ||
| 
						 | 
					e1602618c3 | ||
| 
						 | 
					36be240623 | ||
| 
						 | 
					04e499c97f | ||
| 
						 | 
					f586a8a9dc | ||
| 
						 | 
					5112ed77d6 | ||
| 
						 | 
					bf29a54272 | ||
| 
						 | 
					6d9286a202 | ||
| 
						 | 
					92fdd07ca3 | ||
| 
						 | 
					1c937ad960 | ||
| 
						 | 
					f9891a5c04 | ||
| 
						 | 
					e8ad311d84 | ||
| 
						 | 
					545c09e202 | ||
| 
						 | 
					4dc281a05f | ||
| 
						 | 
					3a0af8caad | ||
| 
						 | 
					8ff2fecb26 | ||
| 
						 | 
					1855c6aed5 | ||
| 
						 | 
					d4c6643311 | ||
| 
						 | 
					e8c738002b | ||
| 
						 | 
					309c3aaeb8 | ||
| 
						 | 
					1f8bcadb3b | ||
| 
						 | 
					6f4af5fef8 | ||
| 
						 | 
					868cdb9f8b | ||
| 
						 | 
					2f76a429ef | ||
| 
						 | 
					a69799aa42 | ||
| 
						 | 
					139bef2142 | ||
| 
						 | 
					8ba142bcf4 | ||
| 
						 | 
					c49e4966f6 | ||
| 
						 | 
					516fa4e49d | ||
| 
						 | 
					6bf9f89429 | ||
| 
						 | 
					a0ddfcb109 | ||
| 
						 | 
					3df7921fdc | ||
| 
						 | 
					6172a9ca2d | ||
| 
						 | 
					4a40e57cf0 | ||
| 
						 | 
					cdedda4ca1 | ||
| 
						 | 
					e6babd84f7 | ||
| 
						 | 
					868ec1a7e3 | ||
| 
						 | 
					e08394c870 | ||
| 
						 | 
					a99500aebf | ||
| 
						 | 
					aa5095c23b | ||
| 
						 | 
					9e0f36e5a7 | ||
| 
						 | 
					d481d079ba | ||
| 
						 | 
					bc9ec7ac4a | ||
| 
						 | 
					6f7e81067c | ||
| 
						 | 
					af946f398e | ||
| 
						 | 
					c7ca26e9c7 | ||
| 
						 | 
					ef7129f45d | ||
| 
						 | 
					a20bdd334a | ||
| 
						 | 
					2ef49a92cc | ||
| 
						 | 
					75f56b68c6 | ||
| 
						 | 
					d34d319d89 | ||
| 
						 | 
					6660c1da38 | ||
| 
						 | 
					4e263b8c39 | ||
| 
						 | 
					3cb604df02 | ||
| 
						 | 
					af9dc7a69e | ||
| 
						 | 
					1247e69c78 | ||
| 
						 | 
					aab0e4315d | ||
| 
						 | 
					14f6517733 | ||
| 
						 | 
					5d75effb37 | ||
| 
						 | 
					ab4f18954b | ||
| 
						 | 
					e1460c65e8 | ||
| 
						 | 
					425a0fcf07 | ||
| 
						 | 
					7205ee5e0a | ||
| 
						 | 
					72c5db8910 | ||
| 
						 | 
					3067f4be3a | ||
| 
						 | 
					2aa1ccdd76 | ||
| 
						 | 
					0284df503f | ||
| 
						 | 
					2833a983d8 | ||
| 
						 | 
					39c6be7cb7 | ||
| 
						 | 
					fdc94c1353 | ||
| 
						 | 
					9cc4e48124 | ||
| 
						 | 
					34c7f15d6d | ||
| 
						 | 
					899a9b025e | ||
| 
						 | 
					deb4315383 | ||
| 
						 | 
					9a06660fdb | ||
| 
						 | 
					5c35d24e13 | ||
| 
						 | 
					03f99752a7 | ||
| 
						 | 
					fd37567c18 | ||
| 
						 | 
					6e38bf1578 | ||
| 
						 | 
					8b2d278840 | ||
| 
						 | 
					06aa0a124d | ||
| 
						 | 
					eb4595158d | ||
| 
						 | 
					32103441f1 | ||
| 
						 | 
					7ed0aa6630 | ||
| 
						 | 
					f690229f31 | ||
| 
						 | 
					f3bab72a86 | ||
| 
						 | 
					2bd63c2d27 | ||
| 
						 | 
					545d9e85e9 | ||
| 
						 | 
					21a4ab4ec7 | ||
| 
						 | 
					66fbbeb5ec | ||
| 
						 | 
					55879c7b6d | ||
| 
						 | 
					66c4e5a5e2 | ||
| 
						 | 
					884139e246 | ||
| 
						 | 
					c3d7b1541e | ||
| 
						 | 
					51ada4d70b | ||
| 
						 | 
					e3a5d52c5e | ||
| 
						 | 
					559fd70737 | ||
| 
						 | 
					e0dba85cbb | ||
| 
						 | 
					74c9cf03d0 | ||
| 
						 | 
					0774e79e4f | ||
| 
						 | 
					a3ec37741a | ||
| 
						 | 
					9bf5cd83c3 | ||
| 
						 | 
					f0da793f99 | ||
| 
						 | 
					684f3ac172 | ||
| 
						 | 
					3e5bd460a5 | ||
| 
						 | 
					3b1d787fbe | ||
| 
						 | 
					980f55ff69 | ||
| 
						 | 
					52ed68bfeb | ||
| 
						 | 
					be0d4c28e4 | ||
| 
						 | 
					79807bf2ab | ||
| 
						 | 
					e48ca1a03f | ||
| 
						 | 
					eae18ce973 | ||
| 
						 | 
					591344ca9d | ||
| 
						 | 
					fbe067823e | ||
| 
						 | 
					ffece911e6 | ||
| 
						 | 
					186afa9651 | ||
| 
						 | 
					6b3037106a | ||
| 
						 | 
					1bf22288ee | ||
| 
						 | 
					3cec470f25 | ||
| 
						 | 
					e1ec0d13ae | ||
| 
						 | 
					924fe97fc3 | ||
| 
						 | 
					504411eade | ||
| 
						 | 
					038ca1b9ca | ||
| 
						 | 
					544b192f8c | ||
| 
						 | 
					7748ccdb8e | ||
| 
						 | 
					64e29c6fce | ||
| 
						 | 
					acdf097998 | ||
| 
						 | 
					ba3107c1fa | ||
| 
						 | 
					9985f787eb | ||
| 
						 | 
					d6f41bcf98 | ||
| 
						 | 
					50bced49ad | ||
| 
						 | 
					4fd7470bbf | ||
| 
						 | 
					033c6f1fdb | ||
| 
						 | 
					6c58347916 | ||
| 
						 | 
					cccbdc164c | ||
| 
						 | 
					cea14a6869 | ||
| 
						 | 
					9b4b24edf7 | ||
| 
						 | 
					8b10a5fb7c | ||
| 
						 | 
					b0d0d9cad2 | ||
| 
						 | 
					d5c8eb048a | ||
| 
						 | 
					9abee3f29a | ||
| 
						 | 
					bf9b6b1301 | ||
| 
						 | 
					8cd57025a0 | ||
| 
						 | 
					faf60b6b1f | ||
| 
						 | 
					da2c1be49c | ||
| 
						 | 
					92c02449f4 | ||
| 
						 | 
					e381622a9a | ||
| 
						 | 
					b799223ebc | ||
| 
						 | 
					40ef224a95 | ||
| 
						 | 
					a4c20b6e1c | ||
| 
						 | 
					e6ee867f72 | ||
| 
						 | 
					468a31f515 | ||
| 
						 | 
					4d746794cc | ||
| 
						 | 
					02d2a66ef2 | ||
| 
						 | 
					4638baf545 | ||
| 
						 | 
					2be23d3768 | ||
| 
						 | 
					b39b1746ba | ||
| 
						 | 
					24f97510b0 | ||
| 
						 | 
					325d5399fa | ||
| 
						 | 
					d8f6fbf594 | ||
| 
						 | 
					21b3e4052c | ||
| 
						 | 
					bf2928805e | ||
| 
						 | 
					7d2bf334c8 | ||
| 
						 | 
					7446802a70 | ||
| 
						 | 
					077bf5ebae | ||
| 
						 | 
					c9bef39f96 | ||
| 
						 | 
					3740eadb7d | ||
| 
						 | 
					e29fa66a74 | ||
| 
						 | 
					ca5406c8e4 | ||
| 
						 | 
					7217caacd1 | ||
| 
						 | 
					8081082251 | ||
| 
						 | 
					1597ca0de5 | ||
| 
						 | 
					8c938ceff9 | ||
| 
						 | 
					65a6945ea5 | ||
| 
						 | 
					02640812af | ||
| 
						 | 
					ba761d5c35 | ||
| 
						 | 
					48a3b1f07f | ||
| 
						 | 
					4370cb77e7 | ||
| 
						 | 
					470e8f6fc7 | ||
| 
						 | 
					b270d88427 | ||
| 
						 | 
					66ce247129 | ||
| 
						 | 
					6ad016c587 | ||
| 
						 | 
					532dac1b95 | ||
| 
						 | 
					2a4bcc262f | ||
| 
						 | 
					1ce2361daf | ||
| 
						 | 
					6e8584e8e0 | ||
| 
						 | 
					121aa91139 | ||
| 
						 | 
					bbc07c72d3 | ||
| 
						 | 
					43b48fdbea | ||
| 
						 | 
					604f97aba1 | ||
| 
						 | 
					dc980081cd | ||
| 
						 | 
					981f03fef3 | ||
| 
						 | 
					d40133dc72 | ||
| 
						 | 
					c9fa586fce | ||
| 
						 | 
					b847a7d90b | ||
| 
						 | 
					8b67108dc8 | ||
| 
						 | 
					b559f9625a | ||
| 
						 | 
					1736c9b0f8 | ||
| 
						 | 
					4fb2d8d318 | ||
| 
						 | 
					95891eb0a5 | ||
| 
						 | 
					c133443eb7 | ||
| 
						 | 
					8f0641f36c | ||
| 
						 | 
					f48dbde736 | ||
| 
						 | 
					f2e4c1ae9a | ||
| 
						 | 
					a4aef38cc0 | ||
| 
						 | 
					b445ecde51 | ||
| 
						 | 
					a209a01284 | ||
| 
						 | 
					7037532943 | ||
| 
						 | 
					bb405ee1aa | ||
| 
						 | 
					ef23356309 | ||
| 
						 | 
					1613e2593c | ||
| 
						 | 
					5464a7a379 | ||
| 
						 | 
					bb1331e449 | ||
| 
						 | 
					acbebc5631 | ||
| 
						 | 
					e1c4fc29de | ||
| 
						 | 
					b903433284 | ||
| 
						 | 
					31a7fdc7b6 | ||
| 
						 | 
					9909adb665 | ||
| 
						 | 
					26f8ba48ee | ||
| 
						 | 
					29ea408980 | ||
| 
						 | 
					0bb7ca7441 | ||
| 
						 | 
					a992644c62 | ||
| 
						 | 
					1c15926e6f | ||
| 
						 | 
					c921315b3e | ||
| 
						 | 
					ab740f92db | ||
| 
						 | 
					1d7390fa7c | ||
| 
						 | 
					0ab96b8e47 | ||
| 
						 | 
					6f6edd37ef | ||
| 
						 | 
					f4282de068 | ||
| 
						 | 
					85c85c07b7 | ||
| 
						 | 
					7abcb1579a | ||
| 
						 | 
					7ce733cc16 | ||
| 
						 | 
					41a3c5f846 | ||
| 
						 | 
					7734e77dfc | ||
| 
						 | 
					257c8b65c2 | ||
| 
						 | 
					846c9e5e12 | ||
| 
						 | 
					685d2b460c | ||
| 
						 | 
					bd71e1cd02 | ||
| 
						 | 
					43a5e12449 | ||
| 
						 | 
					ca97510a52 | ||
| 
						 | 
					50b753cb44 | ||
| 
						 | 
					5ca6704c4d | ||
| 
						 | 
					49142fa385 | ||
| 
						 | 
					d631d29cb4 | ||
| 
						 | 
					01b7891347 | ||
| 
						 | 
					c786a4cbeb | ||
| 
						 | 
					1920ecd668 | ||
| 
						 | 
					c8827424e7 | ||
| 
						 | 
					cc066dd6a1 | ||
| 
						 | 
					eb0b37f729 | ||
| 
						 | 
					e552757edc | ||
| 
						 | 
					87b8dffe23 | ||
| 
						 | 
					81b5904188 | ||
| 
						 | 
					894a3b2fe2 | ||
| 
						 | 
					b75b3e3984 | ||
| 
						 | 
					dea4906144 | ||
| 
						 | 
					97e5117a3f | ||
| 
						 | 
					037215f7c4 | ||
| 
						 | 
					0277187fde | ||
| 
						 | 
					c80a3c1401 | ||
| 
						 | 
					5614f85ea1 | ||
| 
						 | 
					1a3c8692e6 | ||
| 
						 | 
					f2e8691ad5 | ||
| 
						 | 
					c94d7574bc | ||
| 
						 | 
					a38cb5df18 | ||
| 
						 | 
					5407868620 | ||
| 
						 | 
					7edf77561b | ||
| 
						 | 
					a78cbd91da | ||
| 
						 | 
					bb5c3773f1 | ||
| 
						 | 
					2e641a266d | ||
| 
						 | 
					3a787afec6 | ||
| 
						 | 
					34019222c2 | ||
| 
						 | 
					5f3378213b | ||
| 
						 | 
					547fda6a40 | ||
| 
						 | 
					2080ac3bda | ||
| 
						 | 
					61769c8f16 | ||
| 
						 | 
					934e091410 | ||
| 
						 | 
					7f7ee75954 | ||
| 
						 | 
					e76b8da269 | ||
| 
						 | 
					7e5f226480 | ||
| 
						 | 
					2f634184f0 | ||
| 
						 | 
					e3e01466ee | ||
| 
						 | 
					025918cfcc | ||
| 
						 | 
					28fb76e602 | ||
| 
						 | 
					b0f97393a3 | ||
| 
						 | 
					2a7041e751 | ||
| 
						 | 
					58c78d0d78 | ||
| 
						 | 
					eed158afdd | ||
| 
						 | 
					1c7505e04a | ||
| 
						 | 
					617da24942 | ||
| 
						 | 
					98bdbfd3d5 | ||
| 
						 | 
					b289f253c7 | ||
| 
						 | 
					aabae03305 | ||
| 
						 | 
					194d645551 | ||
| 
						 | 
					889d6f9e43 | ||
| 
						 | 
					151de093d0 | ||
| 
						 | 
					cc13e45f21 | ||
| 
						 | 
					7492a4c871 | ||
| 
						 | 
					d20543b92c | ||
| 
						 | 
					59aab2ebbd | ||
| 
						 | 
					08f7b1b9e5 | ||
| 
						 | 
					f2ac1c15e6 | ||
| 
						 | 
					eaf8f198c1 | ||
| 
						 | 
					2955286606 | ||
| 
						 | 
					40561340a8 | ||
| 
						 | 
					4f00a7db88 | ||
| 
						 | 
					acc21d0b76 | ||
| 
						 | 
					db5df70d0c | ||
| 
						 | 
					a6073dc237 | ||
| 
						 | 
					92c132381e | ||
| 
						 | 
					d0575e4087 | ||
| 
						 | 
					5ca48b96af | ||
| 
						 | 
					2a9f30fc8a | ||
| 
						 | 
					ba89a81a3e | ||
| 
						 | 
					5f32300592 | ||
| 
						 | 
					15b4d9363b | ||
| 
						 | 
					ceca0e7f0e | ||
| 
						 | 
					700770b883 | ||
| 
						 | 
					8365037be5 | ||
| 
						 | 
					dfaba7daa6 | ||
| 
						 | 
					5756934144 | ||
| 
						 | 
					7b3ab2727f | ||
| 
						 | 
					714ba808dd | ||
| 
						 | 
					6e94e03baa | ||
| 
						 | 
					ac98dbccb8 | ||
| 
						 | 
					6e3355d7f2 | ||
| 
						 | 
					97907906c5 | ||
| 
						 | 
					eb84200f28 | ||
| 
						 | 
					caaa26e153 | ||
| 
						 | 
					030dd747e9 | ||
| 
						 | 
					dccb98bb92 | ||
| 
						 | 
					e356b7141c | ||
| 
						 | 
					4cae7e6d5d | ||
| 
						 | 
					cc07b4a89a | ||
| 
						 | 
					7e8154e648 | ||
| 
						 | 
					dfee997e45 | ||
| 
						 | 
					f6b7cb9c49 | ||
| 
						 | 
					4452d0e0f5 | ||
| 
						 | 
					7fba44ccce | ||
| 
						 | 
					6f1695ecd4 | ||
| 
						 | 
					76acbf9bb6 | ||
| 
						 | 
					2769a62bb3 | ||
| 
						 | 
					160dd830a0 | ||
| 
						 | 
					aafc595e3a | ||
| 
						 | 
					202783c67a | ||
| 
						 | 
					f11b2c5a0d | ||
| 
						 | 
					e8a86013da | ||
| 
						 | 
					a89c377c92 | ||
| 
						 | 
					54d73f6722 | ||
| 
						 | 
					2e58f5f0d4 | ||
| 
						 | 
					e7ea39f410 | ||
| 
						 | 
					a125218d03 | ||
| 
						 | 
					55b8563c08 | ||
| 
						 | 
					aea1f59f6e | ||
| 
						 | 
					ab27b789e4 | ||
| 
						 | 
					3a1a59f1eb | ||
| 
						 | 
					c20a76cddb | ||
| 
						 | 
					1ef6db16ed | ||
| 
						 | 
					230b734663 | ||
| 
						 | 
					dc414f1239 | ||
| 
						 | 
					dafd2329c5 | ||
| 
						 | 
					12cfda1f58 | ||
| 
						 | 
					96b4e71704 | ||
| 
						 | 
					edb415d1a8 | ||
| 
						 | 
					72c1d1c484 | ||
| 
						 | 
					41a7154aa5 | ||
| 
						 | 
					346d024e48 | ||
| 
						 | 
					04a248dc37 | ||
| 
						 | 
					5defc3b914 | ||
| 
						 | 
					04ca945ecf | ||
| 
						 | 
					d687db71e7 | ||
| 
						 | 
					af08124229 | ||
| 
						 | 
					fab65d6c40 | ||
| 
						 | 
					4d983e54b5 | 
@@ -1,4 +1,4 @@
 | 
			
		||||
image: freebsd/12.x
 | 
			
		||||
image: freebsd/14.x
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
@@ -9,4 +9,4 @@ tasks:
 | 
			
		||||
    gmake
 | 
			
		||||
    gmake test
 | 
			
		||||
    sudo gmake install
 | 
			
		||||
    gmake test-install
 | 
			
		||||
    sudo gmake uninstall
 | 
			
		||||
 
 | 
			
		||||
@@ -19,5 +19,8 @@ tasks:
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
    sudo ninja install
 | 
			
		||||
    sudo jpm --verbose install circlet
 | 
			
		||||
    sudo jpm --verbose install spork
 | 
			
		||||
- 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.6
 | 
			
		||||
sources:
 | 
			
		||||
- https://git.sr.ht/~bakpakin/janet
 | 
			
		||||
packages:
 | 
			
		||||
@@ -11,9 +11,10 @@ tasks:
 | 
			
		||||
    gmake test
 | 
			
		||||
    doas gmake install
 | 
			
		||||
    gmake test-install
 | 
			
		||||
    doas gmake uninstall
 | 
			
		||||
- meson_min: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dtyped_array=false -Dreduced_os=true
 | 
			
		||||
    meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
 | 
			
		||||
    cd build_meson_min
 | 
			
		||||
    ninja
 | 
			
		||||
- meson_prf: |
 | 
			
		||||
@@ -29,5 +30,3 @@ tasks:
 | 
			
		||||
    ninja
 | 
			
		||||
    ninja test
 | 
			
		||||
    doas ninja install
 | 
			
		||||
    doas jpm --verbose install circlet
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,4 @@
 | 
			
		||||
*.janet linguist-language=Clojure
 | 
			
		||||
 | 
			
		||||
*.janet linguist-language=Janet
 | 
			
		||||
*.janet text eol=lf
 | 
			
		||||
*.c text eol=lf
 | 
			
		||||
*.h text eol=lf
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 }}"
 | 
			
		||||
							
								
								
									
										118
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,118 @@
 | 
			
		||||
name: Release
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    tags:
 | 
			
		||||
      - "v*.*.*"
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
 | 
			
		||||
  release:
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write  # for softprops/action-gh-release to create GitHub release
 | 
			
		||||
    name: Build release binaries
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ ubuntu-latest, macos-13 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Set the version
 | 
			
		||||
        run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set the platform
 | 
			
		||||
        run: echo "platform=$(tr '[A-Z]' '[a-z]' <<< $RUNNER_OS)" >> $GITHUB_ENV
 | 
			
		||||
      - name: Compile the project
 | 
			
		||||
        run: make clean && make
 | 
			
		||||
      - name: Build the artifact
 | 
			
		||||
        run: JANET_DIST_DIR=janet-${{ env.version }}-${{ env.platform }} make build/janet-${{ env.version }}-${{ env.platform }}-x64.tar.gz
 | 
			
		||||
      - name: Draft the release
 | 
			
		||||
        uses: softprops/action-gh-release@v1
 | 
			
		||||
        with:
 | 
			
		||||
          draft: true
 | 
			
		||||
          files: |
 | 
			
		||||
            build/*.gz
 | 
			
		||||
            build/janet.h
 | 
			
		||||
            build/c/janet.c
 | 
			
		||||
            build/c/shell.c
 | 
			
		||||
 | 
			
		||||
  release-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
 | 
			
		||||
    name: Build release binaries for windows
 | 
			
		||||
    runs-on: windows-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Setup MSVC
 | 
			
		||||
        uses: ilammy/msvc-dev-cmd@v1
 | 
			
		||||
      - name: Build the project
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: build_win all
 | 
			
		||||
      - name: Draft the release
 | 
			
		||||
        uses: softprops/action-gh-release@v1
 | 
			
		||||
        with:
 | 
			
		||||
          draft: true
 | 
			
		||||
          files: |
 | 
			
		||||
            ./dist/*.zip
 | 
			
		||||
            ./*.zip
 | 
			
		||||
            ./*.msi
 | 
			
		||||
 | 
			
		||||
  release-cosmo:
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write  # for softprops/action-gh-release to create GitHub release
 | 
			
		||||
    name: Build release binaries for Cosmo
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: create build folder
 | 
			
		||||
        run: |
 | 
			
		||||
          sudo mkdir -p /sc
 | 
			
		||||
          sudo chmod -R 0777 /sc
 | 
			
		||||
      - name: setup Cosmopolitan Libc
 | 
			
		||||
        run: bash ./.github/cosmo/setup
 | 
			
		||||
      - name: Set the version
 | 
			
		||||
        run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
 | 
			
		||||
      - name: Set the platform
 | 
			
		||||
        run: echo "platform=cosmo" >> $GITHUB_ENV
 | 
			
		||||
      - name: build Janet APE binary
 | 
			
		||||
        run: bash ./.github/cosmo/build
 | 
			
		||||
      - name: push binary to github
 | 
			
		||||
        uses: softprops/action-gh-release@v1
 | 
			
		||||
        with:
 | 
			
		||||
          draft: true
 | 
			
		||||
          files: |
 | 
			
		||||
            /sc/cosmocc/janet.com
 | 
			
		||||
							
								
								
									
										138
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
name: Test
 | 
			
		||||
 | 
			
		||||
on: [push, pull_request]
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
 | 
			
		||||
  test-posix:
 | 
			
		||||
    name: Build and test on POSIX systems
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ ubuntu-latest, macos-latest, macos-13 ]
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Compile the project
 | 
			
		||||
        run: make clean && make
 | 
			
		||||
      - name: Test the project
 | 
			
		||||
        run: make test
 | 
			
		||||
 | 
			
		||||
  test-windows:
 | 
			
		||||
    name: Build and test on Windows
 | 
			
		||||
    strategy:
 | 
			
		||||
      matrix:
 | 
			
		||||
        os: [ windows-latest, windows-2022 ]
 | 
			
		||||
    runs-on: ${{ matrix.os }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout the repository
 | 
			
		||||
        uses: actions/checkout@master
 | 
			
		||||
      - name: Setup MSVC
 | 
			
		||||
        uses: ilammy/msvc-dev-cmd@v1
 | 
			
		||||
      - name: Build the project
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: build_win
 | 
			
		||||
      - name: Test the project
 | 
			
		||||
        shell: cmd
 | 
			
		||||
        run: build_win test
 | 
			
		||||
      - 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-2022 ]
 | 
			
		||||
    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"
 | 
			
		||||
							
								
								
									
										18
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -34,7 +34,11 @@ local
 | 
			
		||||
 | 
			
		||||
# Common test files I use.
 | 
			
		||||
temp.janet
 | 
			
		||||
temp.c
 | 
			
		||||
temp*janet
 | 
			
		||||
temp*.c
 | 
			
		||||
scratch.janet
 | 
			
		||||
scratch.c
 | 
			
		||||
 | 
			
		||||
# Emscripten
 | 
			
		||||
*.bc
 | 
			
		||||
@@ -44,6 +48,8 @@ janet.wasm
 | 
			
		||||
# Generated files
 | 
			
		||||
*.gen.h
 | 
			
		||||
*.gen.c
 | 
			
		||||
*.tmp
 | 
			
		||||
temp.*
 | 
			
		||||
 | 
			
		||||
# Generate test files
 | 
			
		||||
*.out
 | 
			
		||||
@@ -56,6 +62,7 @@ xxd.exe
 | 
			
		||||
# VSCode
 | 
			
		||||
.vs
 | 
			
		||||
.clangd
 | 
			
		||||
.cache
 | 
			
		||||
 | 
			
		||||
# Swap files
 | 
			
		||||
*.swp
 | 
			
		||||
@@ -67,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 ###
 | 
			
		||||
@@ -118,6 +128,9 @@ vgcore.*
 | 
			
		||||
*.idb
 | 
			
		||||
*.pdb
 | 
			
		||||
 | 
			
		||||
# GGov
 | 
			
		||||
*.gcov
 | 
			
		||||
 | 
			
		||||
# Kernel Module Compile Results
 | 
			
		||||
*.mod*
 | 
			
		||||
*.cmd
 | 
			
		||||
@@ -126,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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,25 +0,0 @@
 | 
			
		||||
language: c
 | 
			
		||||
script:
 | 
			
		||||
- make
 | 
			
		||||
- make test
 | 
			
		||||
- sudo make install
 | 
			
		||||
- make test-install
 | 
			
		||||
- JANET_DIST_DIR=janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME} make build/janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME}.tar.gz
 | 
			
		||||
compiler:
 | 
			
		||||
- clang
 | 
			
		||||
- gcc
 | 
			
		||||
os:
 | 
			
		||||
- linux
 | 
			
		||||
- osx
 | 
			
		||||
before_deploy: 
 | 
			
		||||
deploy:
 | 
			
		||||
  provider: releases
 | 
			
		||||
  api_key:
 | 
			
		||||
    secure: JSqAOTH1jmfVlbOuPO3BbY1BhPq+ddiBNPCxuAyKHoVwfO4eNAmq9COI+UwCMWY3dg+YlspufRwkHj//B7QQ6hPbSsKu+Mapu6gr/CAE/jxbfO/E98LkIkUwbGjplwtzw2kiBkHN/Bu6J5X76cwo4D8nwQ1JIcV3nWtoG87t7H4W0R4AYQkbLGAPylgUFr11YMPx2cRBBqCdLAGIrny7kQ/0cRBfkN81R/gUJv/q3OjmUvY7sALXp7mFdZb75QPSilKIDuVUU5hLvPYTeRl6cWI/M+m5SmGZx1rjv5S9Qaw070XoNyt9JAADtbOUnADKvDguDZIP1FCuT1Gb+cnJPzrvk6+OBU9s8UjCTFtgV+LKlhmRZcwV5YQBE94PKRMJNC6VvIWM7UeQ8Zhm1jmQS6ONNWbuoUAlkZP57NtDQa2x0GT2wkubNSQKlaY+6/gwTD9KAJIzaZG7HYXH7b+4g7VbccCyhDAtDZtXgrOIS4WAkNc8rWezRO4H0qHMyON9aCEb0eTE8hWIufbx6ymG4gUxnYO+AkrEYMCwQvU6lS8BsevkaMTVtSShqlQtJ9FRlmJA3MA2ONyqzQXJENqRydyVbpFrKSv+0HbMyhEc5BoKbt0QcTh/slouNV4eASNar/GKN7aP8XKGUeMwIoCcRpP+3ehmwX9SUw7Ah5S42pA=
 | 
			
		||||
  file: build/janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME}.tar.gz
 | 
			
		||||
  draft: true
 | 
			
		||||
  skip_cleanup: true
 | 
			
		||||
  on:
 | 
			
		||||
    tags: true
 | 
			
		||||
    repo: janet-lang/janet
 | 
			
		||||
    condition: "$CC = clang"
 | 
			
		||||
							
								
								
									
										370
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										370
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,6 +1,368 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## Unreleased - ???
 | 
			
		||||
- Add `gcperthread` callback for abstract types. This lets threaded abstracts have a finalizer that is called per thread, as well as a global finalizer.
 | 
			
		||||
- Add `JANET_DO_ERROR_*` flags to describe the return value of `janet_dobytes` and `janet_dostring`.
 | 
			
		||||
 | 
			
		||||
## 1.39.1 - 2025-08-30
 | 
			
		||||
- Add support for chdir in os/spawn on older macOS versions
 | 
			
		||||
- Expose channels properly in C API
 | 
			
		||||
 | 
			
		||||
## 1.39.0 - 2025-08-24
 | 
			
		||||
- Various bug fixes
 | 
			
		||||
- Add `net/socket`
 | 
			
		||||
- Add support for illumos OS
 | 
			
		||||
- Raise helpful errors for incorrect arguments to `import`.
 | 
			
		||||
- Allow configuring `JANET_THREAD_LOCAL` during builds to allow multi-threading on unknown compilers.
 | 
			
		||||
- Make `ffi/write` append to a buffer instead of insert at 0 by default.
 | 
			
		||||
- Add `os/getpid` to get the current process id.
 | 
			
		||||
- Add `:out` option to `os/spawn` to be able to redirect stderr to stdout with pipes.
 | 
			
		||||
  Add `interrupt?` argument to `ev/deadline` to use VM interruptions.
 | 
			
		||||
 | 
			
		||||
## 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 collisions from pointer hashing
 | 
			
		||||
- Add optional parameter to `marshal` to skip cycle checking code
 | 
			
		||||
 | 
			
		||||
## 1.24.1 - 2022-08-24
 | 
			
		||||
- Fix FFI bug on Linux/Posix
 | 
			
		||||
- Improve parse error messages for bad delimiters.
 | 
			
		||||
- Add optional `name` parameter to the `short-fn` macro.
 | 
			
		||||
 | 
			
		||||
## 1.24.0 - 2022-08-14
 | 
			
		||||
- Add FFI support to 64-bit windows compiled with MSVC
 | 
			
		||||
- Don't process shared object names passed to dlopen.
 | 
			
		||||
- Add better support for windows console in the default shell.c for auto-completion and
 | 
			
		||||
  other shell-like input features.
 | 
			
		||||
- Improve default error message from `assert`.
 | 
			
		||||
- Add the `tabseq` macro for simpler table comprehensions.
 | 
			
		||||
- Allow setting `(dyn :task-id)` in fibers to improve context in supervisor messages. Prior to
 | 
			
		||||
  this change, supervisor messages over threaded channels would be from ambiguous threads/fibers.
 | 
			
		||||
 | 
			
		||||
## 1.23.0 - 2022-06-20
 | 
			
		||||
- Add experimental `ffi/` module for interfacing with dynamic libraries and raw function pointers. Only available
 | 
			
		||||
  on 64 bit linux, mac, and bsd systems.
 | 
			
		||||
- Allow using `&named` in function prototypes for named arguments. This is a more ergonomic
 | 
			
		||||
  variant of `&keys` that isn't as redundant, more self documenting, and allows extension to
 | 
			
		||||
  things like default arguments.
 | 
			
		||||
- Add `delay` macro for lazy evaluate-and-save thunks.
 | 
			
		||||
- Remove pthread.h from janet.h for easier includes.
 | 
			
		||||
- Add `debugger` - an easy to use debugger function that just takes a fiber.
 | 
			
		||||
- `dofile` will now start a debugger on errors if the environment it is passed has `:debug` set.
 | 
			
		||||
- Add `debugger-on-status` function, which can be passed to `run-context` to start a debugger on
 | 
			
		||||
  abnormal fiber signals.
 | 
			
		||||
- Allow running scripts with the `-d` flag to use the built-in debugger on errors and breakpoints.
 | 
			
		||||
- Add mutexes (locks) and reader-writer locks to ev module for thread coordination.
 | 
			
		||||
- Add `parse-all` as a generalization of the `parse` function.
 | 
			
		||||
- Add `os/cpu-count` to get the number of available processors on a machine
 | 
			
		||||
 | 
			
		||||
## 1.22.0 - 2022-05-09
 | 
			
		||||
- Prohibit negative size argument to `table/new`.
 | 
			
		||||
- Add `module/value`.
 | 
			
		||||
- Remove `file/popen`. Use `os/spawn` with the `:pipe` options instead.
 | 
			
		||||
- Fix bug in peg `thru` and `to` combinators.
 | 
			
		||||
- Fix printing issue in `doc` macro.
 | 
			
		||||
- Numerous updates to function docstrings
 | 
			
		||||
- Add `defdyn` aliases for various dynamic bindings used in core.
 | 
			
		||||
- Install `janet.h` symlink to make Janet native libraries and applications
 | 
			
		||||
  easier to build without `jpm`.
 | 
			
		||||
 | 
			
		||||
## 1.21.2 - 2022-04-01
 | 
			
		||||
- C functions `janet_dobytes` and `janet_dostring` will now enter the event loop if it is enabled.
 | 
			
		||||
- Fix hashing regression - hash of negative 0 must be the same as positive 0 since they are equal.
 | 
			
		||||
- The `flycheck` function no longer pollutes the module/cache
 | 
			
		||||
- Fix quasiquote bug in compiler
 | 
			
		||||
- Disallow use of `cancel` and `resume` on fibers scheduled or created with `ev/go`, as well as the root
 | 
			
		||||
  fiber.
 | 
			
		||||
 | 
			
		||||
## 1.20.0 - 2022-1-27
 | 
			
		||||
- Add `:missing-symbol` hook to `compile` that will act as a catch-all macro for undefined symbols.
 | 
			
		||||
- Add `:redef` dynamic binding that will allow users to redefine top-level bindings with late binding. This
 | 
			
		||||
  is intended for development use.
 | 
			
		||||
- Fix a bug with reading from a stream returned by `os/open` on Windows and Linux.
 | 
			
		||||
- Add `:ppc64` as a detectable OS type.
 | 
			
		||||
- Add `& more` support for destructuring in the match macro.
 | 
			
		||||
- Add `& more` support for destructuring in all binding forms (`def`).
 | 
			
		||||
 | 
			
		||||
## 1.19.2 - 2021-12-06
 | 
			
		||||
- Fix bug with missing status lines in some stack traces.
 | 
			
		||||
- Update hash function to have better statistical properties.
 | 
			
		||||
 | 
			
		||||
## 1.19.1 - 2021-12-04
 | 
			
		||||
- Add an optional `prefix` parameter to `debug/stacktrace` to allow printing prettier error messages.
 | 
			
		||||
- Remove appveyor for CI pipeline
 | 
			
		||||
- Fixed a bug that prevented sending threaded abstracts over threaded channels.
 | 
			
		||||
- Fix bug in the `map` function with arity at least 3.
 | 
			
		||||
 | 
			
		||||
## 1.19.0 - 2021-11-27
 | 
			
		||||
- Add `math/log-gamma` to replace `math/gamma`, and change `math/gamma` to be the expected gamma function.
 | 
			
		||||
- Fix leaking file-descriptors in os/spawn and os/execute.
 | 
			
		||||
- Ctrl-C will now raise SIGINT.
 | 
			
		||||
- Allow quoted literals in the `match` macro to behave as expected in patterns.
 | 
			
		||||
- Fix windows net related bug for TCP servers.
 | 
			
		||||
- Allow evaluating ev streams with dofile.
 | 
			
		||||
- Fix `ev` related bug with operations on already closed file descriptors.
 | 
			
		||||
- Add struct and table agnostic `getproto` function.
 | 
			
		||||
- Add a number of functions related to structs.
 | 
			
		||||
- Add prototypes to structs. Structs can now inherit from other structs, just like tables.
 | 
			
		||||
- Create a struct with a prototype with `struct/with-proto`.
 | 
			
		||||
- Deadlocked channels will no longer exit early - instead they will hang, which is more intuitive.
 | 
			
		||||
 | 
			
		||||
## 1.18.1 - 2021-10-16
 | 
			
		||||
- Fix some documentation typos
 | 
			
		||||
- Fix - Set pipes passed to subprocess to blocking mode.
 | 
			
		||||
- Fix `-r` switch in repl.
 | 
			
		||||
 | 
			
		||||
## 1.18.0 - 2021-10-10
 | 
			
		||||
- Allow `ev/cancel` to work on already scheduled fibers.
 | 
			
		||||
- Fix bugs with ev/ module.
 | 
			
		||||
- Add optional `base` argument to scan-number
 | 
			
		||||
- Add `-i` flag to janet binary to make it easier to run image files from the command line
 | 
			
		||||
- Remove `thread/` module.
 | 
			
		||||
- Add `(number ...)` pattern to peg for more efficient number parsing using Janet's
 | 
			
		||||
  scan-number function without immediate string creation.
 | 
			
		||||
 | 
			
		||||
## 1.17.2 - 2021-09-18
 | 
			
		||||
- Remove include of windows.h from janet.h. This caused issues on certain projects.
 | 
			
		||||
- Fix formatting in doc-format to better handle special characters in signatures.
 | 
			
		||||
- Fix some marshalling bugs.
 | 
			
		||||
- Add optional Makefile target to install jpm as well.
 | 
			
		||||
- Supervisor channels in threads will no longer include a wasteful copy of the fiber in every
 | 
			
		||||
  message across a thread.
 | 
			
		||||
- Allow passing a closure to `ev/thread` as well as a whole fiber.
 | 
			
		||||
- Allow passing a closure directly to `ev/go` to spawn fibers on the event loop.
 | 
			
		||||
 | 
			
		||||
## 1.17.1 - 2021-08-29
 | 
			
		||||
- Fix docstring typos
 | 
			
		||||
- Add `make install-jpm-git` to make jpm co-install simpler if using the Makefile.
 | 
			
		||||
- Fix bugs with starting ev/threads and fiber marshaling.
 | 
			
		||||
 | 
			
		||||
## 1.17.0 - 2021-08-21
 | 
			
		||||
- Add the `-E` flag for one-liners with the `short-fn` syntax for argument passing.
 | 
			
		||||
- Add support for threaded abstract types. Threaded abstract types can easily be shared between threads.
 | 
			
		||||
- Deprecate the `thread` library. Use threaded channels and ev instead.
 | 
			
		||||
- Channels can now be marshalled.
 | 
			
		||||
- Add the ability to close channels with `ev/chan-close` (or `:close`).
 | 
			
		||||
- Add threaded channels with `ev/thread-chan`.
 | 
			
		||||
- Add `JANET_FN` and `JANET_REG` macros to more easily define C functions that export their source mapping information.
 | 
			
		||||
- Add `janet_interpreter_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
 | 
			
		||||
  separately like any other package.
 | 
			
		||||
- Fix issue with `ev/go` when called with an initial value and supervisor.
 | 
			
		||||
- Add the C API functions `janet_vm_save` and `janet_vm_load` to allow
 | 
			
		||||
saving and restoring the entire VM state.
 | 
			
		||||
 | 
			
		||||
## 1.16.1 - 2021-06-09
 | 
			
		||||
- Add `maclintf` - a utility for adding linting messages when inside macros.
 | 
			
		||||
- Print source code of offending line on compiler warnings and errors.
 | 
			
		||||
@@ -20,7 +382,7 @@ All notable changes to this project will be documented in this file.
 | 
			
		||||
- Add compiler warnings and deprecation levels.
 | 
			
		||||
- Add `as-macro` to make using macros within quasiquote easier to do hygienically.
 | 
			
		||||
- Expose `JANET_OUT_OF_MEMORY` as part of the Janet API.
 | 
			
		||||
- Add `native-deps` option to `decalre-native` in `jpm`. This lets native libraries link to other
 | 
			
		||||
- Add `native-deps` option to `declare-native` in `jpm`. This lets native libraries link to other
 | 
			
		||||
  native libraries when building with jpm.
 | 
			
		||||
- Remove the `tarray` module. The functionality of typed arrays will be moved to an external module
 | 
			
		||||
  that can be installed via `jpm`.
 | 
			
		||||
@@ -46,7 +408,7 @@ All notable changes to this project will be documented in this file.
 | 
			
		||||
- 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.
 | 
			
		||||
 | 
			
		||||
@@ -196,13 +558,13 @@ All notable changes to this project will be documented in this file.
 | 
			
		||||
- 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
 | 
			
		||||
 
 | 
			
		||||
@@ -64,6 +64,23 @@ ensure a consistent code style for C.
 | 
			
		||||
All janet code in the project should be formatted similar to the code in core.janet.
 | 
			
		||||
The auto formatting from janet.vim will work well.
 | 
			
		||||
 | 
			
		||||
## Typo Fixing and One-Line changes
 | 
			
		||||
 | 
			
		||||
Typo fixes are welcome, as are simple one line fixes. Do not open many separate pull requests for each
 | 
			
		||||
individual typo fix. This is incredibly annoying to deal with as someone needs to review each PR, run
 | 
			
		||||
CI, and merge. Instead, accumulate batches of typo fixes into a single PR. If there are objections to
 | 
			
		||||
specific changes, these can be addressed in the review process before the final merge, if the changes
 | 
			
		||||
are accepted.
 | 
			
		||||
 | 
			
		||||
Similarly, low effort and bad faith changes are annoying to developers and such issues may be closed
 | 
			
		||||
immediately without response.
 | 
			
		||||
 | 
			
		||||
## Contributions from Automated Tools
 | 
			
		||||
 | 
			
		||||
People making changes found or generated by automated tools MUST note this when opening an issue
 | 
			
		||||
or creating a pull request. This can help give context to developers if the change/issue is
 | 
			
		||||
confusing or nonsensical.
 | 
			
		||||
 | 
			
		||||
## Suggesting Changes
 | 
			
		||||
 | 
			
		||||
To suggest changes, open an issue on GitHub. Check GitHub for other issues
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
Copyright (c) 2021 Calvin Rose and contributors
 | 
			
		||||
Copyright (c) 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										209
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										209
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2021 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,60 +21,113 @@
 | 
			
		||||
################################
 | 
			
		||||
##### 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,
 | 
			
		||||
STRIPFLAGS=-x -S
 | 
			
		||||
 | 
			
		||||
# 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?=$(LDFLAGS)
 | 
			
		||||
RUN:=$(RUN)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
 | 
			
		||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 -g $(COMMON_CFLAGS)
 | 
			
		||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 $(COMMON_CFLAGS) -g
 | 
			
		||||
BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS)
 | 
			
		||||
 | 
			
		||||
# Disable amalgamated build
 | 
			
		||||
ifeq ($(JANET_NO_AMALG), 1)
 | 
			
		||||
	JANET_TARGET_OBJECTS+=$(patsubst src/%.c,build/%.bin.o,$(JANET_CORE_SOURCES))
 | 
			
		||||
	JANET_BOOT_FLAGS+=image-only
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# For installation
 | 
			
		||||
LDCONFIG:=ldconfig "$(LIBDIR)"
 | 
			
		||||
 | 
			
		||||
# Check OS
 | 
			
		||||
UNAME:=$(shell uname -s)
 | 
			
		||||
UNAME?=$(shell uname -s)
 | 
			
		||||
ifeq ($(UNAME), Darwin)
 | 
			
		||||
	CLIBS:=$(CLIBS) -ldl
 | 
			
		||||
	SONAME_SETTER:=-Wl,-install_name,
 | 
			
		||||
	JANET_LIBRARY=build/libjanet.dylib
 | 
			
		||||
	LDCONFIG:=true
 | 
			
		||||
else ifeq ($(UNAME), Linux)
 | 
			
		||||
	CLIBS:=$(CLIBS) -lrt -ldl
 | 
			
		||||
else ifeq ($(UNAME), SunOS)
 | 
			
		||||
	BUILD_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
 | 
			
		||||
	BOOT_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
 | 
			
		||||
	CLIBS:=-lsocket -lm
 | 
			
		||||
	STRIPFLAGS=-x
 | 
			
		||||
	LDCONFIG:=false
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
# For other unix likes, add flags here!
 | 
			
		||||
ifeq ($(UNAME), Haiku)
 | 
			
		||||
	LDCONFIG:=true
 | 
			
		||||
	LDFLAGS=-Wl,--export-dynamic
 | 
			
		||||
endif
 | 
			
		||||
# For Android (termux)
 | 
			
		||||
ifeq ($(UNAME), Linux) # uname on Darwin doesn't recognise -o
 | 
			
		||||
ifeq ($(shell uname -o), Android)
 | 
			
		||||
	CLIBS:=$(CLIBS) -landroid-spawn
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
$(shell mkdir -p build/core build/c build/boot)
 | 
			
		||||
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
 | 
			
		||||
# Mingw
 | 
			
		||||
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 \
 | 
			
		||||
@@ -99,7 +152,9 @@ JANET_CORE_SOURCES=src/core/abstract.c \
 | 
			
		||||
				   src/core/debug.c \
 | 
			
		||||
				   src/core/emit.c \
 | 
			
		||||
				   src/core/ev.c \
 | 
			
		||||
				   src/core/ffi.c \
 | 
			
		||||
				   src/core/fiber.c \
 | 
			
		||||
				   src/core/filewatch.c \
 | 
			
		||||
				   src/core/gc.c \
 | 
			
		||||
				   src/core/inttypes.c \
 | 
			
		||||
				   src/core/io.c \
 | 
			
		||||
@@ -113,12 +168,12 @@ JANET_CORE_SOURCES=src/core/abstract.c \
 | 
			
		||||
				   src/core/regalloc.c \
 | 
			
		||||
				   src/core/run.c \
 | 
			
		||||
				   src/core/specials.c \
 | 
			
		||||
				   src/core/state.c \
 | 
			
		||||
				   src/core/string.c \
 | 
			
		||||
				   src/core/strtod.c \
 | 
			
		||||
				   src/core/struct.c \
 | 
			
		||||
				   src/core/symcache.c \
 | 
			
		||||
				   src/core/table.c \
 | 
			
		||||
				   src/core/thread.c \
 | 
			
		||||
				   src/core/tuple.c \
 | 
			
		||||
				   src/core/util.c \
 | 
			
		||||
				   src/core/value.c \
 | 
			
		||||
@@ -145,42 +200,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 #####
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
SONAME=libjanet.so.1.16
 | 
			
		||||
ifeq ($(UNAME), Darwin)
 | 
			
		||||
SONAME=libjanet.1.40.dylib
 | 
			
		||||
else
 | 
			
		||||
SONAME=libjanet.so.1.40
 | 
			
		||||
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 $@ $^
 | 
			
		||||
 | 
			
		||||
###################
 | 
			
		||||
@@ -192,25 +263,23 @@ $(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
 | 
			
		||||
	./$(JANET_TARGET) -k jpm
 | 
			
		||||
	for f in test/suite*.janet; do $(RUN) ./$(JANET_TARGET) "$$f" || exit; done
 | 
			
		||||
	for f in examples/*.janet; do $(RUN) ./$(JANET_TARGET) -k "$$f"; done
 | 
			
		||||
 | 
			
		||||
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
 | 
			
		||||
	for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
 | 
			
		||||
	for f in examples/*.janet; do ./$(JANET_TARGET) -k "$$f"; done
 | 
			
		||||
	$(VALGRIND_COMMAND) ./$(JANET_TARGET) -k jpm
 | 
			
		||||
 | 
			
		||||
callgrind: $(JANET_TARGET)
 | 
			
		||||
	for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
 | 
			
		||||
@@ -223,21 +292,25 @@ dist: build/janet-dist.tar.gz
 | 
			
		||||
 | 
			
		||||
build/janet-%.tar.gz: $(JANET_TARGET) \
 | 
			
		||||
	build/janet.h \
 | 
			
		||||
	jpm.1 janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
 | 
			
		||||
	README.md build/c/janet.c build/c/shell.c jpm
 | 
			
		||||
	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/
 | 
			
		||||
	cp jpm build/$(JANET_DIST_DIR)/bin/
 | 
			
		||||
	strip $(STRIPFLAGS) '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 jpm.1 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 #####
 | 
			
		||||
@@ -246,16 +319,12 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
 | 
			
		||||
docs: build/doc.html
 | 
			
		||||
 | 
			
		||||
build/doc.html: $(JANET_TARGET) tools/gendoc.janet
 | 
			
		||||
	$(JANET_TARGET) tools/gendoc.janet > build/doc.html
 | 
			
		||||
	$(RUN) $(JANET_TARGET) tools/gendoc.janet > build/doc.html
 | 
			
		||||
 | 
			
		||||
########################
 | 
			
		||||
##### Installation #####
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
build/jpm: jpm $(JANET_TARGET)
 | 
			
		||||
	$(JANET_TARGET) tools/patch-jpm.janet jpm build/jpm "--libpath=$(LIBDIR)" "--headerpath=$(INCLUDEDIR)/janet" "--binpath=$(BINDIR)"
 | 
			
		||||
	chmod +x build/jpm
 | 
			
		||||
 | 
			
		||||
.INTERMEDIATE: build/janet.pc
 | 
			
		||||
build/janet.pc: $(JANET_TARGET)
 | 
			
		||||
	echo 'prefix=$(PREFIX)' > $@
 | 
			
		||||
@@ -266,38 +335,63 @@ build/janet.pc: $(JANET_TARGET)
 | 
			
		||||
	echo "Name: janet" >> $@
 | 
			
		||||
	echo "Url: https://janet-lang.org" >> $@
 | 
			
		||||
	echo "Description: Library for the Janet programming language." >> $@
 | 
			
		||||
	$(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@
 | 
			
		||||
	$(RUN) $(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@
 | 
			
		||||
	echo 'Cflags: -I$${includedir}' >> $@
 | 
			
		||||
	echo 'Libs: -L$${libdir} -ljanet' >> $@
 | 
			
		||||
	echo 'Libs.private: $(CLIBS)' >> $@
 | 
			
		||||
 | 
			
		||||
install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/jpm build/janet.h
 | 
			
		||||
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 $(STRIPFLAGS) '$(DESTDIR)$(BINDIR)/janet'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(LIBDIR)'
 | 
			
		||||
	cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')'
 | 
			
		||||
	if test $(UNAME) = Darwin ; then \
 | 
			
		||||
		cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.$(shell $(JANET_TARGET) -e '(print janet/version)').dylib' ; \
 | 
			
		||||
		ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.dylib' ; \
 | 
			
		||||
		ln -sf libjanet.$(shell $(JANET_TARGET) -e '(print janet/version)').dylib $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
 | 
			
		||||
	else \
 | 
			
		||||
		cp $(JANET_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')' ; \
 | 
			
		||||
		ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.so' ; \
 | 
			
		||||
		ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(DESTDIR)$(LIBDIR)/$(SONAME) ; \
 | 
			
		||||
	fi
 | 
			
		||||
	cp $(JANET_STATIC_LIBRARY) '$(DESTDIR)$(LIBDIR)/libjanet.a'
 | 
			
		||||
	ln -sf $(SONAME) '$(DESTDIR)$(LIBDIR)/libjanet.so'
 | 
			
		||||
	ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(DESTDIR)$(LIBDIR)/$(SONAME)
 | 
			
		||||
	cp -rf build/jpm '$(DESTDIR)$(BINDIR)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	cp jpm.1 '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
 | 
			
		||||
	cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || true
 | 
			
		||||
	cp '$(JANET_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
 | 
			
		||||
	cp '$(JANET_LIBRARY_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
 | 
			
		||||
 | 
			
		||||
install-jpm-git: $(JANET_TARGET)
 | 
			
		||||
	mkdir -p build
 | 
			
		||||
	rm -rf build/jpm
 | 
			
		||||
	git clone --depth=1 --branch='$(JPM_TAG)' https://github.com/janet-lang/jpm.git build/jpm
 | 
			
		||||
	cd build/jpm && PREFIX='$(PREFIX)' \
 | 
			
		||||
		DESTDIR=$(DESTDIR) \
 | 
			
		||||
		JANET_MANPATH='$(JANET_MANPATH)' \
 | 
			
		||||
		JANET_HEADERPATH='$(INCLUDEDIR)/janet' \
 | 
			
		||||
		JANET_BINPATH='$(BINDIR)' \
 | 
			
		||||
		JANET_LIBPATH='$(LIBDIR)' \
 | 
			
		||||
		$(RUN) ../../$(JANET_TARGET) ./bootstrap.janet
 | 
			
		||||
 | 
			
		||||
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'
 | 
			
		||||
	-rm '$(DESTDIR)$(BINDIR)/jpm'
 | 
			
		||||
	-rm -rf '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	-rm -rf '$(DESTDIR)$(INCLUDEDIR)/janet.h'
 | 
			
		||||
	-rm -rf '$(DESTDIR)$(LIBDIR)'/libjanet.*
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_MANPATH)/janet.1'
 | 
			
		||||
	-rm '$(DESTDIR)$(JANET_MANPATH)/jpm.1'
 | 
			
		||||
	# -rm -rf '$(DESTDIR)$(JANET_PATH)'/* - err on the side of correctness here
 | 
			
		||||
 | 
			
		||||
#################
 | 
			
		||||
@@ -305,14 +399,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:
 | 
			
		||||
@@ -320,18 +414,7 @@ clean:
 | 
			
		||||
	-rm -rf test/install/build test/install/modpath
 | 
			
		||||
 | 
			
		||||
test-install:
 | 
			
		||||
	cd test/install \
 | 
			
		||||
		&& rm -rf build .cache .manifests \
 | 
			
		||||
		&& jpm --verbose build \
 | 
			
		||||
		&& jpm --verbose test \
 | 
			
		||||
		&& build/testexec \
 | 
			
		||||
		&& jpm --verbose quickbin testexec.janet build/testexec2 \
 | 
			
		||||
		&& build/testexec2 \
 | 
			
		||||
		&& mkdir -p modpath \
 | 
			
		||||
		&& jpm --verbose --testdeps --modpath=./modpath install https://github.com/janet-lang/json.git
 | 
			
		||||
	cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/jhydro.git
 | 
			
		||||
	cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/path.git
 | 
			
		||||
	cd test/install && jpm --verbose --test --modpath=./modpath install https://github.com/janet-lang/argparse.git
 | 
			
		||||
	echo "JPM has been removed from default install."
 | 
			
		||||
 | 
			
		||||
help:
 | 
			
		||||
	@echo
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										296
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										296
									
								
								README.md
									
									
									
									
									
								
							@@ -1,61 +1,133 @@
 | 
			
		||||
[](https://gitter.im/janet-language/community)
 | 
			
		||||
[](https://janet.zulipchat.com)
 | 
			
		||||
 
 | 
			
		||||
[](https://ci.appveyor.com/project/bakpakin/janet/branch/master)
 | 
			
		||||
[](https://travis-ci.org/janet-lang/janet)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/freebsd.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/openbsd.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml?)
 | 
			
		||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/openbsd.yml?)
 | 
			
		||||
[](https://github.com/janet-lang/janet/actions/workflows/test.yml)
 | 
			
		||||
 | 
			
		||||
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">
 | 
			
		||||
 | 
			
		||||
**Janet** is a functional and imperative programming language and bytecode interpreter. It is a
 | 
			
		||||
lisp-like language, but lists are replaced
 | 
			
		||||
by other data structures (arrays, tables (hash table), struct (immutable hash table), tuples).
 | 
			
		||||
The language also supports bridging to native code written in C, meta-programming with macros, and bytecode assembly.
 | 
			
		||||
**Janet** is a programming language for system scripting, expressive automation, and
 | 
			
		||||
extending programs written in C or C++ with user scripting capabilities.
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
* 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
 | 
			
		||||
* 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
 | 
			
		||||
@@ -63,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
 | 
			
		||||
@@ -82,56 +154,80 @@ the SourceHut mirror is actively maintained.
 | 
			
		||||
 | 
			
		||||
The Makefile is non-portable and requires GNU-flavored make.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```sh
 | 
			
		||||
cd somewhere/my/projects/janet
 | 
			
		||||
make
 | 
			
		||||
make test
 | 
			
		||||
make repl
 | 
			
		||||
make install
 | 
			
		||||
make install-jpm-git
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Find out more about the available make targets by running `make help`.
 | 
			
		||||
 | 
			
		||||
### Alpine Linux
 | 
			
		||||
 | 
			
		||||
To build a statically-linked build of Janet, Alpine Linux + MUSL is a good combination. Janet can also
 | 
			
		||||
be built inside a docker container or similar in this manner.
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
docker run -it --rm alpine /bin/ash
 | 
			
		||||
$ apk add make gcc musl-dev git
 | 
			
		||||
$ git clone https://github.com/janet-lang/janet.git
 | 
			
		||||
$ cd janet
 | 
			
		||||
$ make -j10
 | 
			
		||||
$ make test
 | 
			
		||||
$ make install
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 32-bit Haiku
 | 
			
		||||
 | 
			
		||||
32-bit Haiku build instructions are the same as the UNIX-like build instructions,
 | 
			
		||||
but you need to specify an alternative compiler, such as `gcc-x86`.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```sh
 | 
			
		||||
cd somewhere/my/projects/janet
 | 
			
		||||
make CC=gcc-x86
 | 
			
		||||
make test
 | 
			
		||||
make repl
 | 
			
		||||
make install
 | 
			
		||||
make install-jpm-git
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### FreeBSD
 | 
			
		||||
 | 
			
		||||
FreeBSD build instructions are the same as the UNIX-like build instructions,
 | 
			
		||||
but you need `gmake` to compile. Alternatively, install directly from
 | 
			
		||||
packages, using `pkg install lang/janet`.
 | 
			
		||||
but you need `gmake` to compile. Alternatively, install the package directly with `pkg install lang/janet`.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```sh
 | 
			
		||||
cd somewhere/my/projects/janet
 | 
			
		||||
gmake
 | 
			
		||||
gmake test
 | 
			
		||||
gmake repl
 | 
			
		||||
gmake install
 | 
			
		||||
gmake install-jpm-git
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### NetBSD
 | 
			
		||||
 | 
			
		||||
NetBSD build instructions are the same as the FreeBSD build instructions.
 | 
			
		||||
Alternatively, install directly from packages, using `pkgin install janet`.
 | 
			
		||||
Alternatively, install the package directly with `pkgin install janet`.
 | 
			
		||||
 | 
			
		||||
### illumos
 | 
			
		||||
 | 
			
		||||
Building on illumos is exactly the same as building on FreeBSD.
 | 
			
		||||
 | 
			
		||||
### 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.
 | 
			
		||||
 | 
			
		||||
@@ -167,19 +263,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.
 | 
			
		||||
@@ -194,32 +292,38 @@ Hello, World!
 | 
			
		||||
nil
 | 
			
		||||
janet:3:> (os/exit)
 | 
			
		||||
$ janet -h
 | 
			
		||||
usage: build/janet [options] script args...
 | 
			
		||||
usage: janet [options] script args...
 | 
			
		||||
Options are:
 | 
			
		||||
  -h : Show this help
 | 
			
		||||
  -v : Print the version string
 | 
			
		||||
  -s : Use raw stdin instead of getline like functionality
 | 
			
		||||
  -e code : Execute a string of janet
 | 
			
		||||
  -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
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
If installed, you can also run `man janet` and `man jpm` to get usage information.
 | 
			
		||||
If installed, you can also run `man janet` to get usage information.
 | 
			
		||||
 | 
			
		||||
## Embedding
 | 
			
		||||
 | 
			
		||||
Janet can be embedded in a host program very easily. The normal build
 | 
			
		||||
will create a file `build/janet.c`, which is a single C file
 | 
			
		||||
that contains all the source to Janet. This file, along with
 | 
			
		||||
`src/include/janet.h` and `src/conf/janetconf.h` can be dragged into any C
 | 
			
		||||
project and compiled into the project. Janet should be compiled with `-std=c99`
 | 
			
		||||
`src/include/janet.h` and `src/conf/janetconf.h`, can be dragged into any C
 | 
			
		||||
project and compiled into it. Janet should be compiled with `-std=c99`
 | 
			
		||||
on most compilers, and will need to be linked to the math library, `-lm`, and
 | 
			
		||||
the dynamic linker, `-ldl`, if one wants to be able to load dynamic modules. If
 | 
			
		||||
there is no need for dynamic modules, add the define
 | 
			
		||||
@@ -227,53 +331,87 @@ 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
 | 
			
		||||
 | 
			
		||||
### Why is my terminal spitting out junk when I run the REPL?
 | 
			
		||||
### How fast is it?
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
ensue.
 | 
			
		||||
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.
 | 
			
		||||
 | 
			
		||||
### Where is the example code?
 | 
			
		||||
### Is there a language spec?
 | 
			
		||||
 | 
			
		||||
In the examples directory.
 | 
			
		||||
There is not currently a spec besides the documentation at <https://janet-lang.org>.
 | 
			
		||||
 | 
			
		||||
### Is this Scheme/Common Lisp? Where are the cons cells?
 | 
			
		||||
 | 
			
		||||
Nope. There are no cons cells here.
 | 
			
		||||
 | 
			
		||||
### Is this a Clojure port?
 | 
			
		||||
 | 
			
		||||
No. It's similar to Clojure superficially because I like Lisps and I like the asthetics.
 | 
			
		||||
Internally, Janet is not at all like Clojure.
 | 
			
		||||
No. It's similar to Clojure superficially because I like Lisps and I like the aesthetics.
 | 
			
		||||
Internally, Janet is not at all like Clojure, Scheme, or Common Lisp.
 | 
			
		||||
 | 
			
		||||
### Are the immutable data structures (tuples and structs) implemented as hash tries?
 | 
			
		||||
 | 
			
		||||
No. They are immutable arrays and hash tables. Don't try and use them like Clojure's vectors
 | 
			
		||||
and maps, instead they work well as table keys or other identifiers.
 | 
			
		||||
 | 
			
		||||
### Can I do object-oriented programming with Janet?
 | 
			
		||||
 | 
			
		||||
To some extent, yes. However, it is not the recommended method of abstraction, and performance may suffer.
 | 
			
		||||
That said, tables can be used to make mutable objects with inheritance and polymorphism, where object
 | 
			
		||||
methods are implemented with keywords.
 | 
			
		||||
 | 
			
		||||
```clj
 | 
			
		||||
(def Car @{:honk (fn [self msg] (print "car " self " goes " msg)) })
 | 
			
		||||
(def my-car (table/setproto @{} Car))
 | 
			
		||||
(:honk my-car "Beep!")
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Why can't we add (feature from Clojure) into the core?
 | 
			
		||||
 | 
			
		||||
Usually, one of a few reasons:
 | 
			
		||||
- Often, it already exists in a different form and the Clojure port would be redundant.
 | 
			
		||||
- Clojure programs often generate a lot of garbage and rely on the JVM to clean it up.
 | 
			
		||||
  Janet does not run on the JVM. We admittedly have a much more primitive GC.
 | 
			
		||||
- 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.
 | 
			
		||||
  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` flag if further issues
 | 
			
		||||
ensue.
 | 
			
		||||
 | 
			
		||||
## Why is it called "Janet"?
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								appveyor.yml
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								appveyor.yml
									
									
									
									
									
								
							@@ -1,55 +0,0 @@
 | 
			
		||||
version: build-{build}
 | 
			
		||||
clone_folder: c:\projects\janet
 | 
			
		||||
image:
 | 
			
		||||
- Visual Studio 2019
 | 
			
		||||
configuration:
 | 
			
		||||
- Release
 | 
			
		||||
platform:
 | 
			
		||||
- x64
 | 
			
		||||
- x86
 | 
			
		||||
environment:
 | 
			
		||||
  matrix:
 | 
			
		||||
  - arch: Win64
 | 
			
		||||
matrix:
 | 
			
		||||
  fast_finish: true
 | 
			
		||||
 | 
			
		||||
# skip unsupported combinations
 | 
			
		||||
init:
 | 
			
		||||
    - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %platform%
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
    - set JANET_BUILD=%appveyor_repo_commit:~0,7%
 | 
			
		||||
    - build_win all
 | 
			
		||||
    - refreshenv
 | 
			
		||||
    # We need to reload vcvars after refreshing
 | 
			
		||||
    - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" %platform%
 | 
			
		||||
    - build_win test-install
 | 
			
		||||
    - set janet_outname=%appveyor_repo_tag_name%
 | 
			
		||||
    - if "%janet_outname%"=="" set /P janet_outname=<build\version.txt
 | 
			
		||||
build: off
 | 
			
		||||
 | 
			
		||||
artifacts:
 | 
			
		||||
    - name: janet.c
 | 
			
		||||
      path: dist\janet.c
 | 
			
		||||
      type: File
 | 
			
		||||
    - name: janet.h
 | 
			
		||||
      path: dist\janet.h
 | 
			
		||||
      type: File
 | 
			
		||||
    - name: shell.c
 | 
			
		||||
      path: dist\shell.c
 | 
			
		||||
      type: File
 | 
			
		||||
    - name: "janet-$(janet_outname)-windows-%platform%"
 | 
			
		||||
      path: dist
 | 
			
		||||
      type: Zip
 | 
			
		||||
    - path: "janet-$(janet_outname)-windows-%platform%-installer.msi"
 | 
			
		||||
      type: File
 | 
			
		||||
 | 
			
		||||
deploy:
 | 
			
		||||
  description: 'The Janet Programming Language.'
 | 
			
		||||
  provider: GitHub
 | 
			
		||||
  auth_token:
 | 
			
		||||
    secure: lwEXy09qhj2jSH9s1C/KvCkAUqJSma8phFR+0kbsfUc3rVxpNK5uD3z9Md0SjYRx
 | 
			
		||||
  artifact: /(janet|shell).*/
 | 
			
		||||
  draft: true
 | 
			
		||||
  on:
 | 
			
		||||
      APPVEYOR_REPO_TAG: true
 | 
			
		||||
@@ -14,13 +14,18 @@
 | 
			
		||||
@if "%1"=="test" goto TEST
 | 
			
		||||
@if "%1"=="dist" goto DIST
 | 
			
		||||
@if "%1"=="install" goto INSTALL
 | 
			
		||||
@if "%1"=="test-install" goto TESTINSTALL
 | 
			
		||||
@if "%1"=="all" goto ALL
 | 
			
		||||
 | 
			
		||||
@rem Set compile and link options here
 | 
			
		||||
@setlocal
 | 
			
		||||
 | 
			
		||||
@rem Example use asan
 | 
			
		||||
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
 | 
			
		||||
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
 | 
			
		||||
 | 
			
		||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
 | 
			
		||||
@set JANET_LINK=link /nologo
 | 
			
		||||
 | 
			
		||||
@set JANET_LINK_STATIC=lib /nologo
 | 
			
		||||
 | 
			
		||||
@rem Add janet build tag
 | 
			
		||||
@@ -44,7 +49,9 @@ for %%f in (src\boot\*.c) do (
 | 
			
		||||
)
 | 
			
		||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
@rem note that there is no default sysroot being baked in
 | 
			
		||||
build\janet_boot . > build\c\janet.c
 | 
			
		||||
@if errorlevel 1 goto :BUILDFAIL
 | 
			
		||||
 | 
			
		||||
@rem Build the sources
 | 
			
		||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
 | 
			
		||||
@@ -54,12 +61,13 @@ build\janet_boot . > build\c\janet.c
 | 
			
		||||
 | 
			
		||||
@rem Build the resources
 | 
			
		||||
rc /nologo /fobuild\janet_win.res janet_win.rc
 | 
			
		||||
@if errorlevel 1 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
 | 
			
		||||
 | 
			
		||||
@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
 | 
			
		||||
 | 
			
		||||
@@ -84,9 +92,11 @@ 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
 | 
			
		||||
@@ -117,8 +127,6 @@ janet.exe tools\patch-header.janet src\include\janet.h src\conf\janetconf.h buil
 | 
			
		||||
copy build\janet.h dist\janet.h
 | 
			
		||||
copy build\libjanet.lib dist\libjanet.lib
 | 
			
		||||
 | 
			
		||||
copy .\jpm dist\jpm
 | 
			
		||||
 | 
			
		||||
@rem Create installer
 | 
			
		||||
janet.exe -e "(->> janet/version (peg/match ''(* :d+ `.` :d+ `.` :d+)) first print)" > build\version.txt
 | 
			
		||||
janet.exe -e "(print (os/arch))" > build\arch.txt
 | 
			
		||||
@@ -131,11 +139,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
 | 
			
		||||
 | 
			
		||||
@@ -147,34 +162,6 @@ FOR %%a in (janet-*-windows-*-installer.msi) DO (
 | 
			
		||||
)
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
@rem Test the installation.
 | 
			
		||||
:TESTINSTALL
 | 
			
		||||
pushd test\install
 | 
			
		||||
call jpm clean
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm test
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --modpath=. install https://github.com/janet-lang/json.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call build\testexec
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose quickbin testexec.janet build\testexec2.exe
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call build\testexec2.exe
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/jhydro.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/path.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
call jpm --verbose --test --modpath=. install https://github.com/janet-lang/argparse.git
 | 
			
		||||
@if errorlevel 1 goto :TESTINSTALLFAIL
 | 
			
		||||
popd
 | 
			
		||||
exit /b 0
 | 
			
		||||
 | 
			
		||||
:TESTINSTALLFAIL
 | 
			
		||||
popd
 | 
			
		||||
goto :TESTFAIL
 | 
			
		||||
 | 
			
		||||
@rem build, test, dist, install. Useful for local dev.
 | 
			
		||||
:ALL
 | 
			
		||||
call %0 build
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								examples/abstract-unix-socket.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								examples/abstract-unix-socket.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# Linux only - uses abstract unix domain sockets
 | 
			
		||||
(ev/spawn (net/server :unix "@abc123" (fn [conn] (print (:read conn 1024)) (:close conn))))
 | 
			
		||||
(ev/sleep 1)
 | 
			
		||||
(def s (net/connect :unix "@abc123" :stream))
 | 
			
		||||
(:write s "hello")
 | 
			
		||||
(:close s)
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
(defn dowork [name n]
 | 
			
		||||
  (print name " starting work...")
 | 
			
		||||
  (os/execute [(dyn :executable) "-e" (string "(os/sleep " n ")")])
 | 
			
		||||
  (os/execute [(dyn :executable) "-e" (string "(os/sleep " n ")")] :p)
 | 
			
		||||
  (print name " finished work!"))
 | 
			
		||||
 | 
			
		||||
# Will be done in parallel
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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)))
 | 
			
		||||
							
								
								
									
										45
									
								
								examples/evlocks.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								examples/evlocks.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
(defn sleep
 | 
			
		||||
  "Sleep the entire thread, not just a single fiber."
 | 
			
		||||
  [n]
 | 
			
		||||
  (os/sleep (* 0.1 n)))
 | 
			
		||||
 | 
			
		||||
(defn work [lock n]
 | 
			
		||||
  (ev/acquire-lock lock)
 | 
			
		||||
  (print "working " n "...")
 | 
			
		||||
  (sleep n)
 | 
			
		||||
  (print "done working...")
 | 
			
		||||
  (ev/release-lock lock))
 | 
			
		||||
 | 
			
		||||
(defn reader
 | 
			
		||||
  [rwlock n]
 | 
			
		||||
  (ev/acquire-rlock rwlock)
 | 
			
		||||
  (print "reading " n "...")
 | 
			
		||||
  (sleep n)
 | 
			
		||||
  (print "done reading " n "...")
 | 
			
		||||
  (ev/release-rlock rwlock))
 | 
			
		||||
 | 
			
		||||
(defn writer
 | 
			
		||||
  [rwlock n]
 | 
			
		||||
  (ev/acquire-wlock rwlock)
 | 
			
		||||
  (print "writing " n "...")
 | 
			
		||||
  (sleep n)
 | 
			
		||||
  (print "done writing...")
 | 
			
		||||
  (ev/release-wlock rwlock))
 | 
			
		||||
 | 
			
		||||
(defn test-lock
 | 
			
		||||
  []
 | 
			
		||||
  (def lock (ev/lock))
 | 
			
		||||
  (for i 3 7
 | 
			
		||||
    (ev/spawn-thread
 | 
			
		||||
      (work lock i))))
 | 
			
		||||
 | 
			
		||||
(defn test-rwlock
 | 
			
		||||
  []
 | 
			
		||||
  (def rwlock (ev/rwlock))
 | 
			
		||||
  (for i 0 20
 | 
			
		||||
    (if (> 0.1 (math/random))
 | 
			
		||||
      (ev/spawn-thread (writer rwlock i))
 | 
			
		||||
      (ev/spawn-thread (reader rwlock i)))))
 | 
			
		||||
 | 
			
		||||
(test-rwlock)
 | 
			
		||||
(test-lock)
 | 
			
		||||
@@ -10,3 +10,13 @@
 | 
			
		||||
(ev/call worker :b 5)
 | 
			
		||||
(ev/sleep 0.3)
 | 
			
		||||
(ev/call worker :c 12)
 | 
			
		||||
 | 
			
		||||
(defn worker2
 | 
			
		||||
  [name]
 | 
			
		||||
  (repeat 10
 | 
			
		||||
    (ev/sleep 0.2)
 | 
			
		||||
    (print name " working")))
 | 
			
		||||
 | 
			
		||||
(ev/go worker2 :bob)
 | 
			
		||||
(ev/go worker2 :joe)
 | 
			
		||||
(ev/go worker2 :sally)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										71
									
								
								examples/ffi/gtk.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								examples/ffi/gtk.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
# :lazy true needed for jpm quickbin
 | 
			
		||||
# lazily loads library on first function use
 | 
			
		||||
# so the `main` function
 | 
			
		||||
# can be marshalled.
 | 
			
		||||
(ffi/context "/usr/lib/libgtk-3.so" :lazy true)
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-application-new :ptr
 | 
			
		||||
  "Add docstrings as needed."
 | 
			
		||||
  [title :string flags :uint])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  g-signal-connect-data :ulong
 | 
			
		||||
  [a :ptr b :ptr c :ptr d :ptr e :ptr f :int])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  g-application-run :int
 | 
			
		||||
  [app :ptr argc :int argv :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-application-window-new :ptr
 | 
			
		||||
  [a :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-button-new-with-label :ptr
 | 
			
		||||
  [a :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-container-add :void
 | 
			
		||||
  [a :ptr b :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-widget-show-all :void
 | 
			
		||||
  [a :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-button-set-label :void
 | 
			
		||||
  [a :ptr b :ptr])
 | 
			
		||||
 | 
			
		||||
(def cb (delay (ffi/trampoline :default)))
 | 
			
		||||
 | 
			
		||||
(defn ffi/array
 | 
			
		||||
  ``Convert a janet array to a buffer that can be passed to FFI functions.
 | 
			
		||||
  For example, to create an array of type `char *` (array of c strings), one
 | 
			
		||||
  could use `(ffi/array ["hello" "world"] :ptr)`. One needs to be careful that
 | 
			
		||||
  array elements are not garbage collected though - the GC can't follow references
 | 
			
		||||
  inside an arbitrary byte buffer.``
 | 
			
		||||
  [arr ctype &opt buf]
 | 
			
		||||
  (default buf @"")
 | 
			
		||||
  (each el arr
 | 
			
		||||
    (ffi/write ctype el buf))
 | 
			
		||||
  buf)
 | 
			
		||||
 | 
			
		||||
(defn on-active
 | 
			
		||||
  [app]
 | 
			
		||||
  (def window (gtk-application-window-new app))
 | 
			
		||||
  (def btn (gtk-button-new-with-label "Click Me!"))
 | 
			
		||||
  (g-signal-connect-data btn "clicked" (cb)
 | 
			
		||||
                         (fn [btn] (gtk-button-set-label btn "Hello World"))
 | 
			
		||||
                         nil 1)
 | 
			
		||||
  (gtk-container-add window btn)
 | 
			
		||||
  (gtk-widget-show-all window))
 | 
			
		||||
 | 
			
		||||
(defn main
 | 
			
		||||
  [&]
 | 
			
		||||
  (def app (gtk-application-new "org.janet-lang.example.HelloApp" 0))
 | 
			
		||||
  (g-signal-connect-data app "activate" (cb) on-active nil 1)
 | 
			
		||||
  # manually build an array with ffi/write
 | 
			
		||||
  # - we are responsible for preventing gc when the arg array is used
 | 
			
		||||
  (def argv (ffi/array (dyn *args*) :string))
 | 
			
		||||
  (g-application-run app (length (dyn *args*)) argv))
 | 
			
		||||
							
								
								
									
										227
									
								
								examples/ffi/so.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								examples/ffi/so.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#define EXPORTER __declspec(dllexport)
 | 
			
		||||
#else
 | 
			
		||||
#define EXPORTER
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Structs */
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int a, b;
 | 
			
		||||
    float c, d;
 | 
			
		||||
} Split;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    float c, d;
 | 
			
		||||
    int a, b;
 | 
			
		||||
} SplitFlip;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int u, v, w, x, y, z;
 | 
			
		||||
} SixInts;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int a;
 | 
			
		||||
    int b;
 | 
			
		||||
} intint;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int a;
 | 
			
		||||
    int b;
 | 
			
		||||
    int c;
 | 
			
		||||
} intintint;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint64_t a;
 | 
			
		||||
    uint64_t b;
 | 
			
		||||
} uint64pair;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int64_t a;
 | 
			
		||||
    int64_t b;
 | 
			
		||||
    int64_t c;
 | 
			
		||||
} big;
 | 
			
		||||
 | 
			
		||||
/* Functions */
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int int_fn(int a, int b) {
 | 
			
		||||
    return (a << 2) + b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double my_fn(int64_t a, int64_t b, const char *x) {
 | 
			
		||||
    return (double)(a + b) + 0.5 + strlen(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_fn(double x, double y, double z) {
 | 
			
		||||
    return (x + y) * z * 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_many(double x, double y, double z, double w, double a, double b) {
 | 
			
		||||
    return x + y + z + w + a + b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_lots(
 | 
			
		||||
    double a,
 | 
			
		||||
    double b,
 | 
			
		||||
    double c,
 | 
			
		||||
    double d,
 | 
			
		||||
    double e,
 | 
			
		||||
    double f,
 | 
			
		||||
    double g,
 | 
			
		||||
    double h,
 | 
			
		||||
    double i,
 | 
			
		||||
    double j) {
 | 
			
		||||
    return i + j;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_lots_2(
 | 
			
		||||
    double a,
 | 
			
		||||
    double b,
 | 
			
		||||
    double c,
 | 
			
		||||
    double d,
 | 
			
		||||
    double e,
 | 
			
		||||
    double f,
 | 
			
		||||
    double g,
 | 
			
		||||
    double h,
 | 
			
		||||
    double i,
 | 
			
		||||
    double j) {
 | 
			
		||||
    return a +
 | 
			
		||||
           10.0 * b +
 | 
			
		||||
           100.0 * c +
 | 
			
		||||
           1000.0 * d +
 | 
			
		||||
           10000.0 * e +
 | 
			
		||||
           100000.0 * f +
 | 
			
		||||
           1000000.0 * g +
 | 
			
		||||
           10000000.0 * h +
 | 
			
		||||
           100000000.0 * i +
 | 
			
		||||
           1000000000.0 * j;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double float_fn(float x, float y, float z) {
 | 
			
		||||
    return (x + y) * z;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int intint_fn(double x, intint ii) {
 | 
			
		||||
    printf("double: %g\n", x);
 | 
			
		||||
    return ii.a + ii.b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int intintint_fn(double x, intintint iii) {
 | 
			
		||||
    printf("double: %g\n", x);
 | 
			
		||||
    return iii.a + iii.b + iii.c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
intint return_struct(int i) {
 | 
			
		||||
    intint ret;
 | 
			
		||||
    ret.a = i;
 | 
			
		||||
    ret.b = i * i;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
big struct_big(int i, double d) {
 | 
			
		||||
    big ret;
 | 
			
		||||
    ret.a = i;
 | 
			
		||||
    ret.b = (int64_t) d;
 | 
			
		||||
    ret.c = ret.a + ret.b + 1000;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
void void_fn(void) {
 | 
			
		||||
    printf("void fn ran\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
void void_fn_2(double y) {
 | 
			
		||||
    printf("y = %f\n", y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
void void_ret_fn(int x) {
 | 
			
		||||
    printf("void fn ran: %d\n", x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int intintint_fn_2(intintint iii, int i) {
 | 
			
		||||
    fprintf(stderr, "iii.a = %d, iii.b = %d, iii.c = %d, i = %d\n", iii.a, iii.b, iii.c, i);
 | 
			
		||||
    return i * (iii.a + iii.b + iii.c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
float split_fn(Split s) {
 | 
			
		||||
    return s.a * s.c + s.b * s.d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
float split_flip_fn(SplitFlip s) {
 | 
			
		||||
    return s.a * s.c + s.b * s.d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
Split split_ret_fn(int x, float y) {
 | 
			
		||||
    Split ret;
 | 
			
		||||
    ret.a = x;
 | 
			
		||||
    ret.b = x;
 | 
			
		||||
    ret.c = y;
 | 
			
		||||
    ret.d = y;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
SplitFlip split_flip_ret_fn(int x, float y) {
 | 
			
		||||
    SplitFlip ret;
 | 
			
		||||
    ret.a = x;
 | 
			
		||||
    ret.b = x;
 | 
			
		||||
    ret.c = y;
 | 
			
		||||
    ret.d = y;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
SixInts sixints_fn(void) {
 | 
			
		||||
    return (SixInts) {
 | 
			
		||||
        6666, 1111, 2222, 3333, 4444, 5555
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int sixints_fn_2(int x, SixInts s) {
 | 
			
		||||
    return x + s.u + s.v + s.w + s.x + s.y + s.z;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int sixints_fn_3(SixInts s, int x) {
 | 
			
		||||
    return x + s.u + s.v + s.w + s.x + s.y + s.z;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								examples/ffi/test.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								examples/ffi/test.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
#
 | 
			
		||||
# Simple FFI test script that tests against a simple shared object
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(def is-windows (= :windows (os/which)))
 | 
			
		||||
(def ffi/loc (string "examples/ffi/so." (if is-windows "dll" "so")))
 | 
			
		||||
(def ffi/source-loc "examples/ffi/so.c")
 | 
			
		||||
 | 
			
		||||
(if is-windows
 | 
			
		||||
  (os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
 | 
			
		||||
  (os/execute ["cc" ffi/source-loc "-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))
 | 
			
		||||
(def six-ints (ffi/struct :int :int :int :int :int :int))
 | 
			
		||||
 | 
			
		||||
(ffi/defbind int-fn :int [a :int b :int])
 | 
			
		||||
(ffi/defbind double-fn :double [a :double b :double c :double])
 | 
			
		||||
(ffi/defbind double-many :double
 | 
			
		||||
  [x :double y :double z :double w :double a :double b :double])
 | 
			
		||||
(ffi/defbind double-lots :double
 | 
			
		||||
  [a :double b :double c :double d :double e :double f :double g :double h :double i :double j :double])
 | 
			
		||||
(ffi/defbind float-fn :double
 | 
			
		||||
  [x :float y :float z :float])
 | 
			
		||||
(ffi/defbind intint-fn :int
 | 
			
		||||
  [x :double ii [:int :int]])
 | 
			
		||||
(ffi/defbind return-struct [:int :int]
 | 
			
		||||
  [i :int])
 | 
			
		||||
(ffi/defbind intintint-fn :int
 | 
			
		||||
  [x :double iii intintint])
 | 
			
		||||
(ffi/defbind struct-big big
 | 
			
		||||
  [i :int d :double])
 | 
			
		||||
(ffi/defbind void-fn :void [])
 | 
			
		||||
(ffi/defbind double-lots-2 :double
 | 
			
		||||
  [a :double
 | 
			
		||||
   b :double
 | 
			
		||||
   c :double
 | 
			
		||||
   d :double
 | 
			
		||||
   e :double
 | 
			
		||||
   f :double
 | 
			
		||||
   g :double
 | 
			
		||||
   h :double
 | 
			
		||||
   i :double
 | 
			
		||||
   j :double])
 | 
			
		||||
(ffi/defbind void-fn-2 :void [y :double])
 | 
			
		||||
(ffi/defbind intintint-fn-2 :int [iii intintint i :int])
 | 
			
		||||
(ffi/defbind split-fn :float [s split])
 | 
			
		||||
(ffi/defbind split-flip-fn :float [s split-flip])
 | 
			
		||||
(ffi/defbind split-ret-fn split [x :int y :float])
 | 
			
		||||
(ffi/defbind split-flip-ret-fn split-flip [x :int y :float])
 | 
			
		||||
(ffi/defbind sixints-fn six-ints [])
 | 
			
		||||
(ffi/defbind sixints-fn-2 :int [x :int s six-ints])
 | 
			
		||||
(ffi/defbind sixints-fn-3 :int [s six-ints x :int])
 | 
			
		||||
(ffi/defbind 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
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(defn check-round-trip
 | 
			
		||||
  [t value]
 | 
			
		||||
  (def buf (ffi/write t value))
 | 
			
		||||
  (def same-value (ffi/read t buf))
 | 
			
		||||
  (assert (deep= value same-value)
 | 
			
		||||
          (string/format "round trip %j (got %j)" value same-value)))
 | 
			
		||||
 | 
			
		||||
(check-round-trip :bool true)
 | 
			
		||||
(check-round-trip :bool false)
 | 
			
		||||
(check-round-trip :void nil)
 | 
			
		||||
(check-round-trip :void nil)
 | 
			
		||||
(check-round-trip :s8 10)
 | 
			
		||||
(check-round-trip :s8 0)
 | 
			
		||||
(check-round-trip :s8 -10)
 | 
			
		||||
(check-round-trip :u8 10)
 | 
			
		||||
(check-round-trip :u8 0)
 | 
			
		||||
(check-round-trip :s16 10)
 | 
			
		||||
(check-round-trip :s16 0)
 | 
			
		||||
(check-round-trip :s16 -12312)
 | 
			
		||||
(check-round-trip :u16 10)
 | 
			
		||||
(check-round-trip :u16 0)
 | 
			
		||||
(check-round-trip :u32 0)
 | 
			
		||||
(check-round-trip :u32 10)
 | 
			
		||||
(check-round-trip :u32 0xFFFF7777)
 | 
			
		||||
(check-round-trip :s32 0x7FFF7777)
 | 
			
		||||
(check-round-trip :s32 0)
 | 
			
		||||
(check-round-trip :s32 -1234567)
 | 
			
		||||
 | 
			
		||||
(def s (ffi/struct :s8 :s8 :s8 :float))
 | 
			
		||||
(check-round-trip s [1 3 5 123.5])
 | 
			
		||||
(check-round-trip s [-1 -3 -5 -123.5])
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Call functions
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(tracev (sixints-fn))
 | 
			
		||||
(tracev (sixints-fn-2 100 [1 2 3 4 5 6]))
 | 
			
		||||
(tracev (sixints-fn-3 [1 2 3 4 5 6] 200))
 | 
			
		||||
(tracev (split-ret-fn 10 12))
 | 
			
		||||
(tracev (split-flip-ret-fn 10 12))
 | 
			
		||||
(tracev (split-flip-ret-fn 12 10))
 | 
			
		||||
(tracev (intintint-fn-2 [10 20 30] 3))
 | 
			
		||||
(tracev (split-fn [5 6 1.2 3.4]))
 | 
			
		||||
(tracev (void-fn-2 10.3))
 | 
			
		||||
(tracev (double-many 1 2 3 4 5 6))
 | 
			
		||||
(tracev (string/format "%.17g" (double-many 1 2 3 4 5 6)))
 | 
			
		||||
(tracev (type (double-many 1 2 3 4 5 6)))
 | 
			
		||||
(tracev (double-lots-2 0 1 2 3 4 5 6 7 8 9))
 | 
			
		||||
(tracev (void-fn))
 | 
			
		||||
(tracev (int-fn 10 20))
 | 
			
		||||
(tracev (double-fn 1.5 2.5 3.5))
 | 
			
		||||
(tracev (double-lots 1 2 3 4 5 6 7 8 9 10))
 | 
			
		||||
(tracev (float-fn 8 4 17))
 | 
			
		||||
(tracev (intint-fn 123.456 [10 20]))
 | 
			
		||||
(tracev (intintint-fn 123.456 [10 20 30]))
 | 
			
		||||
(tracev (return-struct 42))
 | 
			
		||||
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
 | 
			
		||||
(tracev (struct-big 11 99.5))
 | 
			
		||||
(tracev (int-fn-aliased 10 20))
 | 
			
		||||
 | 
			
		||||
(assert (= [10 10 12 12] (split-ret-fn 10 12)))
 | 
			
		||||
(assert (= [12 12 10 10] (split-flip-ret-fn 10 12)))
 | 
			
		||||
(assert (= 183 (intintint-fn-2 [10 20 31] 3)))
 | 
			
		||||
(assert (= 264 (math/round (* 10 (split-fn [5 6 1.2 3.4])))))
 | 
			
		||||
(assert (= 9876543210 (double-lots-2 0 1 2 3 4 5 6 7 8 9)))
 | 
			
		||||
(assert (= 60 (int-fn 10 20)))
 | 
			
		||||
(assert (= 42 (double-fn 1.5 2.5 3.5)))
 | 
			
		||||
(assert (= 21 (math/round (double-many 1 2 3 4 5 6.01))))
 | 
			
		||||
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
 | 
			
		||||
(assert (= 204 (float-fn 8 4 17)))
 | 
			
		||||
(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.")
 | 
			
		||||
							
								
								
									
										7
									
								
								examples/ffi/win32.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/ffi/win32.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
(ffi/context "user32.dll")
 | 
			
		||||
 | 
			
		||||
(ffi/defbind MessageBoxA :int
 | 
			
		||||
  [w :ptr text :string cap :string typ :int])
 | 
			
		||||
 | 
			
		||||
(MessageBoxA nil "Hello, World!" "Test" 0)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								examples/jitfn/hello.bin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/jitfn/hello.bin
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										17
									
								
								examples/jitfn/hello.nasm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								examples/jitfn/hello.nasm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
BITS 64
 | 
			
		||||
 | 
			
		||||
;;;
 | 
			
		||||
;;; Code
 | 
			
		||||
;;;
 | 
			
		||||
mov rax, 1          ; write(
 | 
			
		||||
mov rdi, 1          ;   STDOUT_FILENO,
 | 
			
		||||
lea rsi, [rel msg]  ;   msg,
 | 
			
		||||
mov rdx, msglen     ;   sizeof(msg)
 | 
			
		||||
syscall             ; );
 | 
			
		||||
ret                 ; return;
 | 
			
		||||
 | 
			
		||||
;;;
 | 
			
		||||
;;; Constants
 | 
			
		||||
;;;
 | 
			
		||||
msg: db "Hello, world!", 10
 | 
			
		||||
msglen: equ $ - msg
 | 
			
		||||
							
								
								
									
										13
									
								
								examples/jitfn/jitfn.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								examples/jitfn/jitfn.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
###
 | 
			
		||||
### Relies on NASM being installed to assemble code.
 | 
			
		||||
### Only works on x86-64 Linux.
 | 
			
		||||
###
 | 
			
		||||
### Before running, compile hello.nasm to hello.bin with
 | 
			
		||||
### $ nasm hello.nasm -o hello.bin
 | 
			
		||||
 | 
			
		||||
(def bin (slurp "hello.bin"))
 | 
			
		||||
(def f (ffi/jitfn bin))
 | 
			
		||||
(def signature (ffi/signature :default :void))
 | 
			
		||||
(ffi/call f signature)
 | 
			
		||||
(print "called a jitted function with FFI!")
 | 
			
		||||
(print "machine code: " (describe (string/slice f)))
 | 
			
		||||
							
								
								
									
										2
									
								
								examples/lineloop.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								examples/lineloop.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
(while (not (empty? (def line (getline))))
 | 
			
		||||
  (prin "line: " line))
 | 
			
		||||
							
								
								
									
										30
									
								
								examples/marshal-stress.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								examples/marshal-stress.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
(defn init-db [c]
 | 
			
		||||
  (def res @{:clients @{}})
 | 
			
		||||
  (var i 0)
 | 
			
		||||
  (repeat c
 | 
			
		||||
    (def n (string "client" i))
 | 
			
		||||
    (put-in res [:clients n] @{:name n :projects @{}})
 | 
			
		||||
    (++ i)
 | 
			
		||||
    (repeat c
 | 
			
		||||
      (def pn (string "project" i))
 | 
			
		||||
      (put-in res [:clients n :projects pn] @{:name pn})
 | 
			
		||||
      (++ i)
 | 
			
		||||
      (repeat c
 | 
			
		||||
        (def tn (string "task" i))
 | 
			
		||||
        (put-in res [:clients n :projects pn :tasks tn] @{:name pn})
 | 
			
		||||
        (++ i))))
 | 
			
		||||
  res)
 | 
			
		||||
 | 
			
		||||
(loop [c :range [30 80 1]]
 | 
			
		||||
  (var s (os/clock))
 | 
			
		||||
  (print "Marshal DB with " c " clients, "
 | 
			
		||||
         (* c c) " projects and "
 | 
			
		||||
         (* c c c) " tasks. "
 | 
			
		||||
         "Total " (+ (* c c c) (* c c) c) " tables")
 | 
			
		||||
  (def buf (marshal (init-db c) @{} @""))
 | 
			
		||||
  (print "Buffer is " (length buf) " bytes")
 | 
			
		||||
  (print "Duration " (- (os/clock) s))
 | 
			
		||||
  (set s (os/clock))
 | 
			
		||||
  (gccollect)
 | 
			
		||||
  (print "Collected garbage in " (- (os/clock) s)))
 | 
			
		||||
 | 
			
		||||
@@ -76,9 +76,16 @@ void num_array_put(void *p, Janet key, Janet value) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet num_array_length(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    num_array *array = (num_array *)janet_getabstract(argv, 0, &num_array_type);
 | 
			
		||||
    return janet_wrap_number(array->size);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetMethod methods[] = {
 | 
			
		||||
    {"scale", num_array_scale},
 | 
			
		||||
    {"sum", num_array_sum},
 | 
			
		||||
    {"length", num_array_length},
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -109,6 +116,11 @@ static const JanetReg cfuns[] = {
 | 
			
		||||
        "(numarray/scale numarray factor)\n\n"
 | 
			
		||||
        "scale numarray by factor"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "sum", num_array_sum,
 | 
			
		||||
        "(numarray/sum numarray)\n\n"
 | 
			
		||||
        "sums numarray"
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
(import build/numarray)
 | 
			
		||||
(import /build/numarray)
 | 
			
		||||
 | 
			
		||||
(def a (numarray/new 30))
 | 
			
		||||
(print (get a 20))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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 [{:name "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")))
 | 
			
		||||
							
								
								
									
										22
									
								
								examples/threaded-channels.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								examples/threaded-channels.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
(def chan (ev/thread-chan 10))
 | 
			
		||||
 | 
			
		||||
(ev/spawn
 | 
			
		||||
  (ev/sleep 0)
 | 
			
		||||
  (print "started fiber!")
 | 
			
		||||
  (ev/give chan (math/random))
 | 
			
		||||
  (ev/give chan (math/random))
 | 
			
		||||
  (ev/give chan (math/random))
 | 
			
		||||
  (ev/sleep 0.5)
 | 
			
		||||
  (for i 0 10
 | 
			
		||||
    (print "giving to channel...")
 | 
			
		||||
    (ev/give chan (math/random))
 | 
			
		||||
    (ev/sleep 1))
 | 
			
		||||
  (print "finished fiber!")
 | 
			
		||||
  (:close chan))
 | 
			
		||||
 | 
			
		||||
(ev/do-thread
 | 
			
		||||
  (print "started thread!")
 | 
			
		||||
  (ev/sleep 1)
 | 
			
		||||
  (while (def x (do (print "taking from channel...") (ev/take chan)))
 | 
			
		||||
    (print "got " x " from thread!"))
 | 
			
		||||
  (print "finished thread!"))
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
(defn worker-main
 | 
			
		||||
  "Sends 11 messages back to parent"
 | 
			
		||||
  [parent]
 | 
			
		||||
  (def name (thread/receive))
 | 
			
		||||
  (def interval (thread/receive))
 | 
			
		||||
  (for i 0 10
 | 
			
		||||
    (os/sleep interval)
 | 
			
		||||
    (:send parent (string/format "thread %s wakeup no. %d" name i)))
 | 
			
		||||
  (:send parent name))
 | 
			
		||||
 | 
			
		||||
(defn make-worker
 | 
			
		||||
  [name interval]
 | 
			
		||||
  (-> (thread/new worker-main)
 | 
			
		||||
      (:send name)
 | 
			
		||||
      (:send interval)))
 | 
			
		||||
 | 
			
		||||
(def bob (make-worker "bob" 0.02))
 | 
			
		||||
(def joe (make-worker "joe" 0.03))
 | 
			
		||||
(def sam (make-worker "sam" 0.05))
 | 
			
		||||
 | 
			
		||||
# Receive out of order
 | 
			
		||||
(for i 0 33
 | 
			
		||||
  (print (thread/receive)))
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Recursive Thread Tree - should pause for a bit, and then print a cool zigzag.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(def rng (math/rng (os/cryptorand 16)))
 | 
			
		||||
 | 
			
		||||
(defn choose [& xs]
 | 
			
		||||
  (in xs (:int rng (length xs))))
 | 
			
		||||
 | 
			
		||||
(defn worker-tree
 | 
			
		||||
  [parent]
 | 
			
		||||
  (def name (thread/receive))
 | 
			
		||||
  (def depth (thread/receive))
 | 
			
		||||
  (if (< depth 5)
 | 
			
		||||
    (do
 | 
			
		||||
    (defn subtree []
 | 
			
		||||
      (-> (thread/new worker-tree)
 | 
			
		||||
          (:send (string name "/" (choose "bob" "marley" "harry" "suki" "anna" "yu")))
 | 
			
		||||
          (:send (inc depth))))
 | 
			
		||||
    (let [l (subtree)
 | 
			
		||||
          r (subtree)
 | 
			
		||||
          lrep (thread/receive)
 | 
			
		||||
          rrep (thread/receive)]
 | 
			
		||||
      (:send parent [name ;lrep ;rrep])))
 | 
			
		||||
    (do
 | 
			
		||||
      (:send parent [name]))))
 | 
			
		||||
 | 
			
		||||
(-> (thread/new worker-tree) (:send "adam") (:send 0))
 | 
			
		||||
(def lines (thread/receive))
 | 
			
		||||
(map print lines)
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Receive timeout
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(def slow (make-worker "slow-loras" 0.5))
 | 
			
		||||
(for i 0 50
 | 
			
		||||
  (try
 | 
			
		||||
    (let [msg (thread/receive 0.1)]
 | 
			
		||||
      (print "\n" msg))
 | 
			
		||||
    ([err] (prin ".") (:flush stdout))))
 | 
			
		||||
 | 
			
		||||
(print "\ndone timing, timeouts ending.")
 | 
			
		||||
(try (while true (print (thread/receive))) ([err] (print "done")))
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
# An example of using Janet's extensible module system
 | 
			
		||||
# to import files from URL. To try this, run `janet -l examples/urlloader.janet`
 | 
			
		||||
# from the repl, and then:
 | 
			
		||||
# An example of using Janet's extensible module system to import files from
 | 
			
		||||
# URL. To try this, run `janet -l ./examples/urlloader.janet` from the command
 | 
			
		||||
# line, and then at the REPL type:
 | 
			
		||||
#
 | 
			
		||||
# (import https://raw.githubusercontent.com/janet-lang/janet/master/examples/colors.janet :as c)
 | 
			
		||||
#
 | 
			
		||||
# This will import a file using curl. You can then try
 | 
			
		||||
# This will import a file using curl. You can then try:
 | 
			
		||||
#
 | 
			
		||||
# (print (c/color :green "Hello!"))
 | 
			
		||||
#
 | 
			
		||||
@@ -13,9 +13,9 @@
 | 
			
		||||
 | 
			
		||||
(defn- load-url
 | 
			
		||||
  [url args]
 | 
			
		||||
  (def f (file/popen (string "curl " url)))
 | 
			
		||||
  (def res (dofile f :source url ;args))
 | 
			
		||||
  (try (file/close f) ([err] nil))
 | 
			
		||||
  (def p (os/spawn ["curl" url "-s"] :p {:out :pipe}))
 | 
			
		||||
  (def res (dofile (p :out) :source url ;args))
 | 
			
		||||
  (:wait p)
 | 
			
		||||
  res)
 | 
			
		||||
 | 
			
		||||
(defn- check-http-url
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
							
								
								
									
										32
									
								
								janet.1
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								janet.1
									
									
									
									
									
								
							@@ -3,8 +3,9 @@
 | 
			
		||||
janet \- run the Janet language abstract machine
 | 
			
		||||
.SH SYNOPSIS
 | 
			
		||||
.B janet
 | 
			
		||||
[\fB\-hvsrpnqk\fR]
 | 
			
		||||
[\fB\-hvsrpnqik\fR]
 | 
			
		||||
[\fB\-e\fR \fISOURCE\fR]
 | 
			
		||||
[\fB\-E\fR \fISOURCE ...ARGUMENTS\fR]
 | 
			
		||||
[\fB\-l\fR \fIMODULE\fR]
 | 
			
		||||
[\fB\-m\fR \fIPATH\fR]
 | 
			
		||||
[\fB\-c\fR \fIMODULE JIMAGE\fR]
 | 
			
		||||
@@ -162,6 +163,16 @@ Read raw input from stdin and forgo prompt history and other readline-like featu
 | 
			
		||||
Execute a string of Janet source. Source code is executed in the order it is encountered, so earlier
 | 
			
		||||
arguments are executed before later ones.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-E\ code\ arguments...
 | 
			
		||||
Execute a single Janet expression as a Janet short-fn, passing the remaining command line arguments to the expression. This allows
 | 
			
		||||
more concise one-liners with command line arguments.
 | 
			
		||||
 | 
			
		||||
Example: janet -E '(print $0)' 12 is equivalent to '((short-fn (print $0)) 12)', which is in turn equivalent to
 | 
			
		||||
`((fn [k] (print k)) 12)`
 | 
			
		||||
 | 
			
		||||
See docs for the `short-fn` function for more details.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-d
 | 
			
		||||
Enable debug mode. On all terminating signals as well the debug signal, this will
 | 
			
		||||
@@ -172,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
 | 
			
		||||
@@ -199,7 +214,7 @@ Don't execute a script, only compile it to check for errors. Useful for linting
 | 
			
		||||
.BR \-m\ syspath
 | 
			
		||||
Set the dynamic binding :syspath to the string syspath so that Janet will load system modules
 | 
			
		||||
from a directory different than the default. The default is set when Janet is built, and defaults to
 | 
			
		||||
/usr/local/lib/janet on Linux/Posix, and C:/Janet/Library on Windows. This option supersedes JANET_PATH.
 | 
			
		||||
/usr/local/lib/janet on Linux/Posix. On Windows, there is no default value. This option supersedes JANET_PATH.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-c\ source\ output
 | 
			
		||||
@@ -207,6 +222,11 @@ Precompiles Janet source code into an image, a binary dump that can be efficient
 | 
			
		||||
Source should be a path to the Janet module to compile, and output should be the file path of
 | 
			
		||||
resulting image. Output should usually end with the .jimage extension.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-i
 | 
			
		||||
When this flag is passed, a script passed to the interpreter will be treated as a janet image file
 | 
			
		||||
rather than a janet source file.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-l\ lib
 | 
			
		||||
Import a Janet module before running a script or repl. Multiple files can be loaded
 | 
			
		||||
@@ -235,7 +255,7 @@ 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 colon-separated list of directory names on Linux/Posix, and a semicolon-separated list on Windows. Note that a typical setup (i.e. not NixOS / Guix) will only use a single directory.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_PROFILE
 | 
			
		||||
@@ -252,5 +272,11 @@ This variable does nothing in the default configuration of Janet, as PRF is disa
 | 
			
		||||
cannot be defined for this variable to have an effect.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B NO_COLOR
 | 
			
		||||
.RS
 | 
			
		||||
Turn off color by default in the repl and in the error handler of scripts. This can be changed at runtime
 | 
			
		||||
via dynamic bindings *err-color* and *pretty-format*, or via the command line parameters -n and -N.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.SH AUTHOR
 | 
			
		||||
Written by Calvin Rose <calsrose@gmail.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										298
									
								
								jpm.1
									
									
									
									
									
								
							
							
						
						
									
										298
									
								
								jpm.1
									
									
									
									
									
								
							@@ -1,298 +0,0 @@
 | 
			
		||||
.TH JPM 1
 | 
			
		||||
.SH NAME
 | 
			
		||||
jpm \- the Janet Project Manager, a build tool for Janet 
 | 
			
		||||
.SH SYNOPSIS
 | 
			
		||||
.B jpm
 | 
			
		||||
[\fB\-\-flag ...\fR]
 | 
			
		||||
[\fB\-\-option=value ...\fR]
 | 
			
		||||
.IR command
 | 
			
		||||
.IR args ...
 | 
			
		||||
.SH DESCRIPTION
 | 
			
		||||
jpm is the build tool that ships with a standard Janet install. It is
 | 
			
		||||
used for building Janet projects, installing dependencies, installing
 | 
			
		||||
projects, building native modules, and exporting your Janet project to a
 | 
			
		||||
standalone executable. Although not required for working with Janet, it
 | 
			
		||||
removes much of the boilerplate with installing dependencies and
 | 
			
		||||
building native modules. jpm requires only Janet to run, and uses git
 | 
			
		||||
to install dependencies (jpm will work without git installed).
 | 
			
		||||
.SH DOCUMENTATION
 | 
			
		||||
 | 
			
		||||
jpm has several subcommands, each used for managing either a single Janet project or
 | 
			
		||||
all Janet modules installed on the system. Global commands, those that manage modules
 | 
			
		||||
at the system level, do things like install and uninstall packages, as well as clear the cache.
 | 
			
		||||
More interesting are the local commands. For more information on jpm usage, see https://janet-lang.org/docs/index.html
 | 
			
		||||
 | 
			
		||||
.SH FLAGS
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-nocolor
 | 
			
		||||
Disable color in the jpm debug repl.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-verbose
 | 
			
		||||
Print detailed messages of what jpm is doing, including compilation commands and other shell commands.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-test
 | 
			
		||||
If passed to jpm install, runs tests before installing. Will run tests recursively on dependencies.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-offline
 | 
			
		||||
Prevents jpm from going to network to get dependencies - all dependencies should be in the cache or this command will fail.
 | 
			
		||||
Use this flag with the deps and update-pkgs subcommands. This is not a surefire way to prevent a build script from accessing
 | 
			
		||||
the network, for example, a build script that invokes curl will still have network access.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-auto\-shebang
 | 
			
		||||
Prepends installed scripts with a generated shebang line, such that they will use a janet binary located in JANET_BINPATH.
 | 
			
		||||
 | 
			
		||||
.SH OPTIONS
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-modpath=/some/path
 | 
			
		||||
Set the path to install modules to. Defaults to $JANET_MODPATH, $JANET_PATH, or (dyn :syspath) in that order. You most likely don't need this.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-headerpath=/some/path
 | 
			
		||||
Set the path the jpm will include when building C source code. This lets
 | 
			
		||||
you specify the location of janet.h and janetconf.h on your system. On a
 | 
			
		||||
normal install, this option is not needed.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-binpath=/some/path
 | 
			
		||||
Set the path that jpm will install scripts and standalone executables to. Executables
 | 
			
		||||
defined via declare-execuatble or scripts declared via declare-binscript will be installed
 | 
			
		||||
here when jpm install is run. Defaults to $JANET_BINPATH, or a reasonable default for the system.
 | 
			
		||||
See JANET_BINPATH for more.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-libpath=/some/path
 | 
			
		||||
Sets the path jpm will use to look for libjanet.a for building standalone executables. libjanet.so
 | 
			
		||||
is \fBnot\fR used for building native modules or standalone executables, only
 | 
			
		||||
for linking into applications that want to embed janet as a dynamic module.
 | 
			
		||||
Linking statically might be a better idea, even in that case. Defaults to
 | 
			
		||||
$JANET_LIBPATH, or a reasonable default. See JANET_LIBPATH for more.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-compiler=$CC
 | 
			
		||||
Sets the C compiler used for compiling native modules and standalone executables. Defaults
 | 
			
		||||
to cc.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-cpp\-compiler=$CXX
 | 
			
		||||
Sets the C++ compiler used for compiling native modules and standalone executables. Defaults
 | 
			
		||||
to c++..
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-linker
 | 
			
		||||
Sets the linker used to create native modules and executables. Only used on windows, where
 | 
			
		||||
it defaults to link.exe.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-pkglist=https://github.com/janet-lang/pkgs.git
 | 
			
		||||
Sets the git repository for the package listing used to resolve shorthand package names.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-\-archiver=$AR
 | 
			
		||||
Sets the command used for creating static libraries, use for linking into the standalone executable.
 | 
			
		||||
Native modules are compiled twice, once a normal native module (shared object), and once as an
 | 
			
		||||
archive. Defaults to ar.
 | 
			
		||||
 | 
			
		||||
.SH COMMANDS
 | 
			
		||||
.TP
 | 
			
		||||
.BR help
 | 
			
		||||
Shows the usage text and exits immediately.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR build
 | 
			
		||||
Builds all artifacts specified in the project.janet file in the current directory. Artifacts will
 | 
			
		||||
be created in the ./build/ directory.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR install\ [\fBrepo...\fR]
 | 
			
		||||
When run with no arguments, installs all installable artifacts in the current project to
 | 
			
		||||
the current JANET_MODPATH for modules and JANET_BINPATH for executables and scripts. Can also
 | 
			
		||||
take an optional git repository URL and will install all artifacts in that repository instead.
 | 
			
		||||
When run with an argument, install does not need to be run from a jpm project directory. Will also
 | 
			
		||||
install multiple dependencies in one command.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR uninstall\ [\fBname...\fR]
 | 
			
		||||
Uninstall a project installed with install. uninstall expects the name of the project, not the
 | 
			
		||||
repository url, path to installed file, or executable name. The name of the project must be specified
 | 
			
		||||
at the top of the project.janet file in the declare-project form. If no name is given, uninstalls
 | 
			
		||||
the current project if installed. Will also uninstall multiple packages in one command.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clean
 | 
			
		||||
Remove all artifacts created by jpm. This just deletes the build folder.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR test
 | 
			
		||||
Runs jpm tests. jpm will run all janet source files in the test directory as tests. A test
 | 
			
		||||
is considered failing if it exits with a non-zero exit code.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR deps
 | 
			
		||||
Install all dependencies that this project requires recursively. jpm does not
 | 
			
		||||
resolve dependency issues, like conflicting versions of the same module are required, or
 | 
			
		||||
different modules with the same name. Dependencies are installed with git, so deps requires
 | 
			
		||||
git to be on the PATH.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clear-cache
 | 
			
		||||
jpm caches git repositories that are needed to install modules from a remote
 | 
			
		||||
source in a global cache ($JANET_PATH/.cache). If these dependencies are out of
 | 
			
		||||
date or too large, clear-cache will remove the cache and jpm will rebuild it
 | 
			
		||||
when needed. clear-cache is a global command, so a project.janet is not
 | 
			
		||||
required.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR list-installed
 | 
			
		||||
List all installed packages in the current syspath.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR list-pkgs\ [\fBsearch\fR]
 | 
			
		||||
List all package aliases in the current package listing that contain the given search string.
 | 
			
		||||
If no search string is given, prints the entire listing.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR clear-manifest
 | 
			
		||||
jpm creates a manifest directory that contains a list of all installed files.
 | 
			
		||||
By deleting this directory, jpm will think that nothing is installed and will
 | 
			
		||||
try reinstalling everything on the jpm deps or jpm load-lockfile commands. Be careful with
 | 
			
		||||
this command, as it may leave extra files on your system and shouldn't be needed
 | 
			
		||||
most of the time in a healthy install.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR run\ [\fBrule\fR]
 | 
			
		||||
Run a given rule defined in project.janet. Project definitions files (project.janet) usually
 | 
			
		||||
contain a few artifact declarations, which set up rules that jpm can then resolve, or execute.
 | 
			
		||||
A project.janet can also create custom rules to create arbitrary files or run arbitrary code, much
 | 
			
		||||
like make. run will run a single rule or build a single file.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR rules
 | 
			
		||||
List all rules that can be run via run. This is useful for exploring rules in the project.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR rule-tree\ [\fBroot\fR]\ [\fBdepth\fR]
 | 
			
		||||
Show rule dependency tree in a pretty format. Optionally provide a rule to use as the tree
 | 
			
		||||
root, as well as a max depth to print. By default, prints the full tree for all rules. This
 | 
			
		||||
can be quite long, so it is recommended to give a root rule.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR show-paths
 | 
			
		||||
Show all of the paths used when installing and building artifacts.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR update-pkgs
 | 
			
		||||
Update the package listing by installing the 'pkgs' package. Same as jpm install pkgs
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR quickbin\ [\fBentry\fR]\ [\fBexecutable\fR]
 | 
			
		||||
Create a standalone, statically linked executable from a Janet source file that contains a main function.
 | 
			
		||||
The main function is the entry point of the program and will receive command line arguments
 | 
			
		||||
as function arguments. The entry file can import other modules, including native C modules, and
 | 
			
		||||
jpm will attempt to include the dependencies into the generated executable.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR debug-repl
 | 
			
		||||
Load the current project.janet file and start a repl in it's environment. This lets a user better
 | 
			
		||||
debug the project file, as well as run rules manually.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR make-lockfile\ [\fBfilename\fR]
 | 
			
		||||
Create a lockfile. A lockfile is a record that describes what dependencies were installed at the
 | 
			
		||||
time of the lockfile's creation, including exact versions. A lockfile can then be later used
 | 
			
		||||
to set up that environment on a different machine via load-lockfile. By default, the lockfile
 | 
			
		||||
is created at lockfile.jdn, although any path can be used.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR load-lockfile\ [\fBfilename\fR]
 | 
			
		||||
Install dependencies from a lockfile previously created with make-lockfile. By default, will look
 | 
			
		||||
for a lockfile at lockfile.jdn, although any path can be used.
 | 
			
		||||
 | 
			
		||||
.SH ENVIRONMENT
 | 
			
		||||
 | 
			
		||||
.B JANET_PATH
 | 
			
		||||
.RS
 | 
			
		||||
The location to look for Janet libraries. This is the only environment variable Janet needs to
 | 
			
		||||
find native and source code modules. If no JANET_PATH is set, Janet will look in
 | 
			
		||||
the default location set at compile time, which can be determined with (dyn :syspath)
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_MODPATH
 | 
			
		||||
.RS
 | 
			
		||||
The location that jpm will use to install libraries to. Defaults to JANET_PATH, but you could
 | 
			
		||||
set this to a different directory if you want to. Doing so would let you import Janet modules
 | 
			
		||||
on the normal system path (JANET_PATH or (dyn :syspath)), but install to a different directory. It is also a more reliable way to install.
 | 
			
		||||
This variable is overwritten by the --modpath=/some/path if it is provided.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_HEADERPATH
 | 
			
		||||
.RS
 | 
			
		||||
The location that jpm will look for janet header files (janet.h and janetconf.h) that are used
 | 
			
		||||
to build native modules and standalone executables. If janet.h and janetconf.h are available as
 | 
			
		||||
default includes on your system, this value is not required. If not provided, will default to
 | 
			
		||||
<jpm script location>/../include/janet. The --headerpath=/some/path option will override this
 | 
			
		||||
variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_LIBPATH
 | 
			
		||||
.RS
 | 
			
		||||
Similar to JANET_HEADERPATH, this path is where jpm will look for
 | 
			
		||||
libjanet.a for creating standalone executables. This does not need to be
 | 
			
		||||
set on a normal install. 
 | 
			
		||||
If not provided, this will default to <jpm script location>/../lib.
 | 
			
		||||
The --libpath=/some/path option will override this variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_BINPATH
 | 
			
		||||
.RS
 | 
			
		||||
The directory where jpm will install binary scripts and executables to.
 | 
			
		||||
Defaults to
 | 
			
		||||
(dyn :syspath)/bin
 | 
			
		||||
The --binpath=/some/path will override this variable.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_PKGLIST
 | 
			
		||||
.RS
 | 
			
		||||
The git repository URL that contains a listing of packages. This allows installing packages with shortnames, which
 | 
			
		||||
is mostly a convenience. However, package dependencies can use short names, package listings
 | 
			
		||||
can be used to choose a particular set of dependency versions for a whole project.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JANET_GIT
 | 
			
		||||
.RS
 | 
			
		||||
An optional path to a git executable to use to clone git dependencies. By default, uses "git" on the current $PATH. You shouldn't need to set this
 | 
			
		||||
if you have a normal install of git.
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
.B JPM_OS_WHICH
 | 
			
		||||
.RS
 | 
			
		||||
Use this option to override the C compiler and build system auto-detection for the host operating system. For example, set this
 | 
			
		||||
environment variable to "posix" to make sure that on platforms like MinGW, you will use GCC instead of MSVC. On most platforms, users will not need to
 | 
			
		||||
set this environment variable. Set this to one of the following
 | 
			
		||||
strings:
 | 
			
		||||
.IP
 | 
			
		||||
\- windows
 | 
			
		||||
.IP
 | 
			
		||||
\- macos
 | 
			
		||||
.IP
 | 
			
		||||
\- linux
 | 
			
		||||
.IP
 | 
			
		||||
\- freebsd
 | 
			
		||||
.IP
 | 
			
		||||
\- openbsd
 | 
			
		||||
.IP
 | 
			
		||||
\- netbsd
 | 
			
		||||
.IP
 | 
			
		||||
\- bsd
 | 
			
		||||
.IP
 | 
			
		||||
\- posix
 | 
			
		||||
.RE
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.SH AUTHOR
 | 
			
		||||
Written by Calvin Rose <calsrose@gmail.com>
 | 
			
		||||
							
								
								
									
										183
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								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,35 @@
 | 
			
		||||
 | 
			
		||||
project('janet', 'c',
 | 
			
		||||
  default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
 | 
			
		||||
  version : '1.16.1')
 | 
			
		||||
  version : '1.40.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')
 | 
			
		||||
 | 
			
		||||
# Link options
 | 
			
		||||
@@ -60,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'))
 | 
			
		||||
@@ -72,13 +92,22 @@ conf.set('JANET_NO_UMASK', not get_option('umask'))
 | 
			
		||||
conf.set('JANET_NO_REALPATH', not get_option('realpath'))
 | 
			
		||||
conf.set('JANET_NO_PROCESSES', not get_option('processes'))
 | 
			
		||||
conf.set('JANET_SIMPLE_GETLINE', get_option('simple_getline'))
 | 
			
		||||
conf.set('JANET_EV_EPOLL', get_option('epoll'))
 | 
			
		||||
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
 | 
			
		||||
if get_option('arch_name') != ''
 | 
			
		||||
  conf.set('JANET_ARCH_NAME', get_option('arch_name'))
 | 
			
		||||
endif
 | 
			
		||||
if get_option('thread_local_prefix') != ''
 | 
			
		||||
  conf.set('JANET_THREAD_LOCAL', get_option('thread_local_prefix'))
 | 
			
		||||
endif
 | 
			
		||||
jconf = configure_file(output : 'janetconf.h',
 | 
			
		||||
  configuration : conf)
 | 
			
		||||
 | 
			
		||||
@@ -113,7 +142,9 @@ core_src = [
 | 
			
		||||
  'src/core/debug.c',
 | 
			
		||||
  'src/core/emit.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',
 | 
			
		||||
@@ -127,12 +158,12 @@ core_src = [
 | 
			
		||||
  'src/core/regalloc.c',
 | 
			
		||||
  'src/core/run.c',
 | 
			
		||||
  'src/core/specials.c',
 | 
			
		||||
  'src/core/state.c',
 | 
			
		||||
  'src/core/string.c',
 | 
			
		||||
  'src/core/strtod.c',
 | 
			
		||||
  'src/core/struct.c',
 | 
			
		||||
  'src/core/symcache.c',
 | 
			
		||||
  'src/core/table.c',
 | 
			
		||||
  'src/core/thread.c',
 | 
			
		||||
  'src/core/tuple.c',
 | 
			
		||||
  'src/core/util.c',
 | 
			
		||||
  'src/core/value.c',
 | 
			
		||||
@@ -154,47 +185,65 @@ 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],
 | 
			
		||||
  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 : [
 | 
			
		||||
    janet_boot, meson.current_source_dir(),
 | 
			
		||||
    'JANET_PATH', janet_path, 'JANET_HEADERPATH', header_path
 | 
			
		||||
    'JANET_PATH', janet_path
 | 
			
		||||
  ])
 | 
			
		||||
 | 
			
		||||
janet_dependencies = [m_dep, dl_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)
 | 
			
		||||
@@ -205,7 +254,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
 | 
			
		||||
@@ -221,17 +270,38 @@ 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-ev2.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())
 | 
			
		||||
@@ -241,14 +311,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')
 | 
			
		||||
@@ -258,18 +329,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@'])
 | 
			
		||||
if get_option('peg') and not get_option('reduced_os') and get_option('processes')
 | 
			
		||||
  install_man('jpm.1')
 | 
			
		||||
  patched_jpm = custom_target('patched-jpm',
 | 
			
		||||
    input : ['tools/patch-jpm.janet', 'jpm'],
 | 
			
		||||
    install : true,
 | 
			
		||||
    install_dir : get_option('bindir'),
 | 
			
		||||
    build_by_default : true,
 | 
			
		||||
    output : ['jpm'],
 | 
			
		||||
    command : [janet_nativeclient, '@INPUT@', '@OUTPUT@',
 | 
			
		||||
      '--binpath=' + join_paths(get_option('prefix'), get_option('bindir')),
 | 
			
		||||
      '--libpath=' + join_paths(get_option('prefix'), get_option('libdir')),
 | 
			
		||||
      '--headerpath=' + join_paths(get_option('prefix'), get_option('includedir'))])
 | 
			
		||||
 | 
			
		||||
# Create a version of the janet.h header that matches what jpm often expects
 | 
			
		||||
if meson.version().version_compare('>=0.61')
 | 
			
		||||
  install_symlink('janet.h', pointing_to: 'janet/janet_' + meson.project_version() + '.h', install_dir: get_option('includedir'))
 | 
			
		||||
  install_symlink('janet.h', pointing_to: 'janet_' + meson.project_version() + '.h', install_dir: join_paths(get_option('includedir'), 'janet'))
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,12 +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('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)
 | 
			
		||||
@@ -24,4 +30,7 @@ option('max_macro_expand', type : 'integer', min : 1, max : 8000, value : 200)
 | 
			
		||||
option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7fffffff)
 | 
			
		||||
 | 
			
		||||
option('arch_name', type : 'string', value: '')
 | 
			
		||||
option('thread_local_prefix', type : 'string', value: '')
 | 
			
		||||
option('os_name', type : 'string', value: '')
 | 
			
		||||
option('shared', type : 'boolean', value: true)
 | 
			
		||||
option('cryptorand', type : 'boolean', value: true)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 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) 2021 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3297
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
							
						
						
									
										3297
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 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) 2021 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) 2021 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) 2021 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,15 +4,16 @@
 | 
			
		||||
#define JANETCONF_H
 | 
			
		||||
 | 
			
		||||
#define JANET_VERSION_MAJOR 1
 | 
			
		||||
#define JANET_VERSION_MINOR 16
 | 
			
		||||
#define JANET_VERSION_PATCH 1
 | 
			
		||||
#define JANET_VERSION_MINOR 40
 | 
			
		||||
#define JANET_VERSION_PATCH 0
 | 
			
		||||
#define JANET_VERSION_EXTRA ""
 | 
			
		||||
#define JANET_VERSION "1.16.1"
 | 
			
		||||
#define JANET_VERSION "1.40.0"
 | 
			
		||||
 | 
			
		||||
/* #define JANET_BUILD "local" */
 | 
			
		||||
 | 
			
		||||
/* These settings all affect linking, so use cautiously. */
 | 
			
		||||
/* #define JANET_SINGLE_THREADED */
 | 
			
		||||
/* #define JANET_THREAD_LOCAL _Thread_local */
 | 
			
		||||
/* #define JANET_NO_DYNAMIC_MODULES */
 | 
			
		||||
/* #define JANET_NO_NANBOX */
 | 
			
		||||
/* #define JANET_API __attribute__((visibility ("default"))) */
 | 
			
		||||
@@ -29,9 +30,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 */
 | 
			
		||||
@@ -46,7 +51,12 @@
 | 
			
		||||
/* #define JANET_STACK_MAX 16384 */
 | 
			
		||||
/* #define JANET_OS_NAME my-custom-os */
 | 
			
		||||
/* #define JANET_ARCH_NAME pdp-8 */
 | 
			
		||||
/* #define JANET_EV_EPOLL */
 | 
			
		||||
/* #define JANET_EV_NO_EPOLL */
 | 
			
		||||
/* #define JANET_EV_NO_KQUEUE */
 | 
			
		||||
/* #define JANET_NO_INTERPRETER_INTERRUPT */
 | 
			
		||||
/* #define JANET_NO_IPV6 */
 | 
			
		||||
/* #define JANET_NO_CRYPTORAND */
 | 
			
		||||
/* #define JANET_USE_STDATOMIC */
 | 
			
		||||
 | 
			
		||||
/* Custom vm allocator support */
 | 
			
		||||
/* #include <mimalloc.h> */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 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,7 +23,15 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "gc.h"
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Create new userdata */
 | 
			
		||||
@@ -43,3 +51,154 @@ void *janet_abstract_end(void *x) {
 | 
			
		||||
void *janet_abstract(const JanetAbstractType *atype, size_t size) {
 | 
			
		||||
    return janet_abstract_end(janet_abstract_begin(atype, size));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Threaded abstracts
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void *janet_abstract_begin_threaded(const JanetAbstractType *atype, size_t size) {
 | 
			
		||||
    JanetAbstractHead *header = janet_malloc(sizeof(JanetAbstractHead) + size);
 | 
			
		||||
    if (NULL == header) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm.next_collection += size + sizeof(JanetAbstractHead);
 | 
			
		||||
    header->gc.flags = JANET_MEMORY_THREADED_ABSTRACT;
 | 
			
		||||
    header->gc.data.next = NULL; /* Clear memory for address sanitizers */
 | 
			
		||||
    header->gc.data.refcount = 1;
 | 
			
		||||
    header->size = size;
 | 
			
		||||
    header->type = atype;
 | 
			
		||||
    void *abstract = (void *) & (header->data);
 | 
			
		||||
    janet_table_put(&janet_vm.threaded_abstracts, janet_wrap_abstract(abstract), janet_wrap_false());
 | 
			
		||||
    return abstract;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_abstract_end_threaded(void *x) {
 | 
			
		||||
    janet_gc_settype((void *)(janet_abstract_head(x)), JANET_MEMORY_THREADED_ABSTRACT);
 | 
			
		||||
    return x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size) {
 | 
			
		||||
    return janet_abstract_end_threaded(janet_abstract_begin_threaded(atype, size));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Refcounting primitives and sync primitives */
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
 | 
			
		||||
size_t janet_os_mutex_size(void) {
 | 
			
		||||
    return sizeof(SRWLOCK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t janet_os_rwlock_size(void) {
 | 
			
		||||
    return sizeof(void *);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
 | 
			
		||||
    InitializeSRWLock((PSRWLOCK) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_deinit(JanetOSMutex *mutex) {
 | 
			
		||||
    /* no op? */
 | 
			
		||||
    (void) mutex;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_lock(JanetOSMutex *mutex) {
 | 
			
		||||
    AcquireSRWLockExclusive((PSRWLOCK) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
 | 
			
		||||
    ReleaseSRWLockExclusive((PSRWLOCK) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {
 | 
			
		||||
    InitializeSRWLock((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_deinit(JanetOSRWLock *rwlock) {
 | 
			
		||||
    /* no op? */
 | 
			
		||||
    (void) rwlock;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_rlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    AcquireSRWLockShared((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_wlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    AcquireSRWLockExclusive((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_runlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    ReleaseSRWLockShared((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    ReleaseSRWLockExclusive((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
size_t janet_os_mutex_size(void) {
 | 
			
		||||
    return sizeof(pthread_mutex_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t janet_os_rwlock_size(void) {
 | 
			
		||||
    return sizeof(pthread_rwlock_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutexattr_t attr;
 | 
			
		||||
    pthread_mutexattr_init(&attr);
 | 
			
		||||
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
 | 
			
		||||
    pthread_mutex_init((pthread_mutex_t *) mutex, &attr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_deinit(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_destroy((pthread_mutex_t *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_lock(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_lock((pthread_mutex_t *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
 | 
			
		||||
    int ret = pthread_mutex_unlock((pthread_mutex_t *) mutex);
 | 
			
		||||
    if (ret) janet_panic("cannot release lock");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {
 | 
			
		||||
    pthread_rwlock_init((pthread_rwlock_t *) rwlock, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_deinit(JanetOSRWLock *rwlock) {
 | 
			
		||||
    pthread_rwlock_destroy((pthread_rwlock_t *) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_rlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    pthread_rwlock_rdlock((pthread_rwlock_t *) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_wlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    pthread_rwlock_wrlock((pthread_rwlock_t *) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_runlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    pthread_rwlock_unlock((pthread_rwlock_t *) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    pthread_rwlock_unlock((pthread_rwlock_t *) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
int32_t janet_abstract_incref(void *abst) {
 | 
			
		||||
    return janet_atomic_inc(&janet_abstract_head(abst)->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t janet_abstract_decref(void *abst) {
 | 
			
		||||
    return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										247
									
								
								src/core/array.c
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								src/core/array.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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,12 +30,10 @@
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
        janet_vm.next_collection += capacity * sizeof(Janet);
 | 
			
		||||
        data = (Janet *) janet_malloc(sizeof(Janet) * (size_t) capacity);
 | 
			
		||||
        if (NULL == data) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -72,7 +83,7 @@ void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth) {
 | 
			
		||||
    if (NULL == newData) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_next_collection += (capacity - array->capacity) * sizeof(Janet);
 | 
			
		||||
    janet_vm.next_collection += (capacity - array->capacity) * sizeof(Janet);
 | 
			
		||||
    array->data = newData;
 | 
			
		||||
    array->capacity = capacity;
 | 
			
		||||
}
 | 
			
		||||
@@ -122,16 +133,30 @@ Janet janet_array_peek(JanetArray *array) {
 | 
			
		||||
 | 
			
		||||
/* C Functions */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_new,
 | 
			
		||||
              "(array/new capacity)",
 | 
			
		||||
              "Creates a new empty array with a pre-allocated capacity. The same as "
 | 
			
		||||
              "`(array)` but can be more efficient if the maximum size of an array is known.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getinteger(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(cap);
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_new_filled(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_weak,
 | 
			
		||||
              "(array/weak capacity)",
 | 
			
		||||
              "Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getinteger(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array_weak(cap);
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_array_new_filled,
 | 
			
		||||
              "(array/new-filled count &opt value)",
 | 
			
		||||
              "Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    int32_t count = janet_getinteger(argv, 0);
 | 
			
		||||
    int32_t count = janet_getnat(argv, 0);
 | 
			
		||||
    Janet x = (argc == 2) ? argv[1] : janet_wrap_nil();
 | 
			
		||||
    JanetArray *array = janet_array(count);
 | 
			
		||||
    for (int32_t i = 0; i < count; i++) {
 | 
			
		||||
@@ -141,7 +166,10 @@ static Janet cfun_array_new_filled(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_fill,
 | 
			
		||||
              "(array/fill arr &opt value)",
 | 
			
		||||
              "Replace all elements of an array with `value` (defaulting to nil) without changing the length of the array. "
 | 
			
		||||
              "Returns the modified array.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    Janet x = (argc == 2) ? argv[1] : janet_wrap_nil();
 | 
			
		||||
@@ -151,19 +179,26 @@ static Janet cfun_array_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_pop(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_pop,
 | 
			
		||||
              "(array/pop arr)",
 | 
			
		||||
              "Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
 | 
			
		||||
              "the input array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    return janet_array_pop(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_peek(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_peek,
 | 
			
		||||
              "(array/peek arr)",
 | 
			
		||||
              "Returns the last element of the array. Does not modify the array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    return janet_array_peek(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_push(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_push,
 | 
			
		||||
              "(array/push arr & 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) {
 | 
			
		||||
@@ -176,7 +211,12 @@ static Janet cfun_array_push(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_ensure,
 | 
			
		||||
              "(array/ensure arr capacity growth)",
 | 
			
		||||
              "Ensures that the memory backing the array is large enough for `capacity` "
 | 
			
		||||
              "items at the given rate of growth. `capacity` and `growth` must be integers. "
 | 
			
		||||
              "If the backing capacity is already enough, then this function does nothing. "
 | 
			
		||||
              "Otherwise, the backing memory will be reallocated so that there is enough space.") {
 | 
			
		||||
    janet_fixarity(argc, 3);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    int32_t newcount = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -186,7 +226,13 @@ static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_slice,
 | 
			
		||||
              "(array/slice arrtup &opt start end)",
 | 
			
		||||
              "Takes a slice of array or tuple from `start` to `end`. The range is half open, "
 | 
			
		||||
              "[start, end). Indexes can also be negative, indicating indexing from the "
 | 
			
		||||
              "end of the array. By default, `start` is 0 and `end` is the length of the array. "
 | 
			
		||||
              "Note that 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);
 | 
			
		||||
    JanetArray *array = janet_array(range.end - range.start);
 | 
			
		||||
@@ -196,7 +242,12 @@ static Janet cfun_array_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_concat,
 | 
			
		||||
              "(array/concat arr & parts)",
 | 
			
		||||
              "Concatenates a variable number of arrays (and tuples) into the first argument, "
 | 
			
		||||
              "which must be an array. If any of the parts are arrays or tuples, their elements will "
 | 
			
		||||
              "be inserted into the array. Otherwise, each part in `parts` will be appended to `arr` in order. "
 | 
			
		||||
              "Return the modified array `arr`.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
@@ -210,6 +261,11 @@ static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
                int32_t j, len = 0;
 | 
			
		||||
                const Janet *vals = NULL;
 | 
			
		||||
                janet_indexed_view(argv[i], &vals, &len);
 | 
			
		||||
                if (array->data == vals) {
 | 
			
		||||
                    int32_t newcount = array->count + len;
 | 
			
		||||
                    janet_array_ensure(array, newcount, 2);
 | 
			
		||||
                    janet_indexed_view(argv[i], &vals, &len);
 | 
			
		||||
                }
 | 
			
		||||
                for (j = 0; j < len; j++)
 | 
			
		||||
                    janet_array_push(array, vals[j]);
 | 
			
		||||
            }
 | 
			
		||||
@@ -219,7 +275,37 @@ static Janet cfun_array_concat(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_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, 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);
 | 
			
		||||
@@ -245,13 +331,18 @@ static Janet cfun_array_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_remove(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_remove,
 | 
			
		||||
              "(array/remove arr at &opt n)",
 | 
			
		||||
              "Remove up to `n` elements starting at index `at` in array `arr`. `at` can index from "
 | 
			
		||||
              "the end of the array with a negative index, and `n` must be a non-negative integer. "
 | 
			
		||||
              "By default, `n` is 1. "
 | 
			
		||||
              "Returns the array.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    int32_t at = janet_getinteger(argv, 1);
 | 
			
		||||
    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);
 | 
			
		||||
@@ -270,7 +361,9 @@ static Janet cfun_array_remove(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_trim(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_trim,
 | 
			
		||||
              "(array/trim arr)",
 | 
			
		||||
              "Set the backing capacity of an array to its current length. Returns the modified array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    if (array->count) {
 | 
			
		||||
@@ -290,103 +383,35 @@ static Janet cfun_array_trim(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_array_clear(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_array_clear,
 | 
			
		||||
              "(array/clear arr)",
 | 
			
		||||
              "Empties an array, setting it's count to 0 but does not free the backing capacity. "
 | 
			
		||||
              "Returns the modified array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetArray *array = janet_getarray(argv, 0);
 | 
			
		||||
    array->count = 0;
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg array_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "array/new", cfun_array_new,
 | 
			
		||||
        JDOC("(array/new capacity)\n\n"
 | 
			
		||||
             "Creates a new empty array with a pre-allocated capacity. The same as "
 | 
			
		||||
             "(array) but can be more efficient if the maximum size of an array is known.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/new-filled", cfun_array_new_filled,
 | 
			
		||||
        JDOC("(array/new-filled count &opt value)\n\n"
 | 
			
		||||
             "Creates a new array of count elements, all set to value, which defaults to nil. Returns the new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/fill", cfun_array_fill,
 | 
			
		||||
        JDOC("(array/fill arr &opt value)\n\n"
 | 
			
		||||
             "Replace all elements of an array with value (defaulting to nil) without changing the length of the array. "
 | 
			
		||||
             "Returns the modified array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/pop", cfun_array_pop,
 | 
			
		||||
        JDOC("(array/pop arr)\n\n"
 | 
			
		||||
             "Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
 | 
			
		||||
             "the input array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/peek", cfun_array_peek,
 | 
			
		||||
        JDOC("(array/peek arr)\n\n"
 | 
			
		||||
             "Returns the last element of the array. Does not modify the array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/push", cfun_array_push,
 | 
			
		||||
        JDOC("(array/push arr x)\n\n"
 | 
			
		||||
             "Insert an element in the end of an array. Modifies the input array and returns it.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/ensure", cfun_array_ensure,
 | 
			
		||||
        JDOC("(array/ensure arr capacity growth)\n\n"
 | 
			
		||||
             "Ensures that the memory backing the array is large enough for capacity "
 | 
			
		||||
             "items at the given rate of growth. Capacity and growth must be integers. "
 | 
			
		||||
             "If the backing capacity is already enough, then this function does nothing. "
 | 
			
		||||
             "Otherwise, the backing memory will be reallocated so that there is enough space.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/slice", cfun_array_slice,
 | 
			
		||||
        JDOC("(array/slice arrtup &opt start end)\n\n"
 | 
			
		||||
             "Takes a slice of array or tuple from start to end. The range is half open, "
 | 
			
		||||
             "[start, end). Indexes can also be negative, indicating indexing from the end of the "
 | 
			
		||||
             "end of the array. By default, start is 0 and end is the length of the array. "
 | 
			
		||||
             "Note that index -1 is synonymous with index (length arrtup) to allow a full "
 | 
			
		||||
             "negative slice range. Returns a new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/concat", cfun_array_concat,
 | 
			
		||||
        JDOC("(array/concat arr & parts)\n\n"
 | 
			
		||||
             "Concatenates a variable number of arrays (and tuples) into the first argument "
 | 
			
		||||
             "which must be an array. If any of the parts are arrays or tuples, their elements will "
 | 
			
		||||
             "be inserted into the array. Otherwise, each part in parts will be appended to arr in order. "
 | 
			
		||||
             "Return the modified array arr.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/insert", cfun_array_insert,
 | 
			
		||||
        JDOC("(array/insert arr at & xs)\n\n"
 | 
			
		||||
             "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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/remove", cfun_array_remove,
 | 
			
		||||
        JDOC("(array/remove arr at &opt n)\n\n"
 | 
			
		||||
             "Remove up to n elements starting at index at in array arr. at can index from "
 | 
			
		||||
             "the end of the array with a negative index, and n must be a non-negative integer. "
 | 
			
		||||
             "By default, n is 1. "
 | 
			
		||||
             "Returns the array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/trim", cfun_array_trim,
 | 
			
		||||
        JDOC("(array/trim arr)\n\n"
 | 
			
		||||
             "Set the backing capacity of an array to its current length. Returns the modified array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "array/clear", cfun_array_clear,
 | 
			
		||||
        JDOC("(array/clear arr)\n\n"
 | 
			
		||||
             "Empties an array, setting it's count to 0 but does not free the backing capacity. "
 | 
			
		||||
             "Returns the modified array.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Load the array module */
 | 
			
		||||
void janet_lib_array(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, array_cfuns);
 | 
			
		||||
    JanetRegExt array_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("array/new", cfun_array_new),
 | 
			
		||||
        JANET_CORE_REG("array/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),
 | 
			
		||||
        JANET_CORE_REG("array/peek", cfun_array_peek),
 | 
			
		||||
        JANET_CORE_REG("array/push", cfun_array_push),
 | 
			
		||||
        JANET_CORE_REG("array/ensure", cfun_array_ensure),
 | 
			
		||||
        JANET_CORE_REG("array/slice", cfun_array_slice),
 | 
			
		||||
        JANET_CORE_REG("array/concat", cfun_array_concat),
 | 
			
		||||
        JANET_CORE_REG("array/insert", cfun_array_insert),
 | 
			
		||||
        JANET_CORE_REG("array/remove", cfun_array_remove),
 | 
			
		||||
        JANET_CORE_REG("array/trim", cfun_array_trim),
 | 
			
		||||
        JANET_CORE_REG("array/clear", cfun_array_clear),
 | 
			
		||||
        JANET_CORE_REG("array/join", cfun_array_join),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, array_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										184
									
								
								src/core/asm.c
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								src/core/asm.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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,13 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("vararg"));
 | 
			
		||||
    if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
 | 
			
		||||
 | 
			
		||||
    /* Initialize slotcount */
 | 
			
		||||
    def->slotcount = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG) + def->arity;
 | 
			
		||||
 | 
			
		||||
    /* Check structarg */
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("structarg"));
 | 
			
		||||
    if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
 | 
			
		||||
 | 
			
		||||
    /* Check source */
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("source"));
 | 
			
		||||
    if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x);
 | 
			
		||||
@@ -597,6 +611,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
 | 
			
		||||
    /* Parse sub funcdefs */
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("closures"));
 | 
			
		||||
    if (janet_checktype(x, JANET_NIL)) {
 | 
			
		||||
        x = janet_get1(s, janet_ckeywordv("defs"));
 | 
			
		||||
    }
 | 
			
		||||
    if (janet_indexed_view(x, &arr, &count)) {
 | 
			
		||||
        int32_t i;
 | 
			
		||||
        for (i = 0; i < count; i++) {
 | 
			
		||||
@@ -709,16 +726,70 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Set symbolmap */
 | 
			
		||||
    def->symbolmap = NULL;
 | 
			
		||||
    def->symbolmap_length = 0;
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("symbolmap"));
 | 
			
		||||
    if (janet_indexed_view(x, &arr, &count)) {
 | 
			
		||||
        def->symbolmap_length = count;
 | 
			
		||||
        def->symbolmap = janet_malloc(sizeof(JanetSymbolMap) * (size_t)count);
 | 
			
		||||
        if (NULL == def->symbolmap) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        for (i = 0; i < count; i++) {
 | 
			
		||||
            const Janet *tup;
 | 
			
		||||
            Janet entry = arr[i];
 | 
			
		||||
            JanetSymbolMap ss;
 | 
			
		||||
            if (!janet_checktype(entry, JANET_TUPLE)) {
 | 
			
		||||
                janet_asm_error(&a, "expected tuple");
 | 
			
		||||
            }
 | 
			
		||||
            tup = janet_unwrap_tuple(entry);
 | 
			
		||||
            if (janet_keyeq(tup[0], "upvalue")) {
 | 
			
		||||
                ss.birth_pc = UINT32_MAX;
 | 
			
		||||
            } else if (!janet_checkint(tup[0])) {
 | 
			
		||||
                janet_asm_error(&a, "expected integer");
 | 
			
		||||
            } else {
 | 
			
		||||
                ss.birth_pc = janet_unwrap_integer(tup[0]);
 | 
			
		||||
            }
 | 
			
		||||
            if (!janet_checkint(tup[1])) {
 | 
			
		||||
                janet_asm_error(&a, "expected integer");
 | 
			
		||||
            }
 | 
			
		||||
            if (!janet_checkint(tup[2])) {
 | 
			
		||||
                janet_asm_error(&a, "expected integer");
 | 
			
		||||
            }
 | 
			
		||||
            if (!janet_checktype(tup[3], JANET_SYMBOL)) {
 | 
			
		||||
                janet_asm_error(&a, "expected symbol");
 | 
			
		||||
            }
 | 
			
		||||
            ss.death_pc = janet_unwrap_integer(tup[1]);
 | 
			
		||||
            ss.slot_index = janet_unwrap_integer(tup[2]);
 | 
			
		||||
            ss.symbol = janet_unwrap_symbol(tup[3]);
 | 
			
		||||
            def->symbolmap[i] = ss;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (def->symbolmap_length) def->flags |= JANET_FUNCDEF_FLAG_HASSYMBOLMAP;
 | 
			
		||||
 | 
			
		||||
    /* Set environments */
 | 
			
		||||
    def->environments =
 | 
			
		||||
        janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
 | 
			
		||||
    if (NULL == def->environments) {
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("environments"));
 | 
			
		||||
    if (janet_indexed_view(x, &arr, &count)) {
 | 
			
		||||
        def->environments_length = count;
 | 
			
		||||
        if (def->environments_length) {
 | 
			
		||||
            def->environments = janet_realloc(def->environments, def->environments_length * sizeof(int32_t));
 | 
			
		||||
        }
 | 
			
		||||
        for (int32_t i = 0; i < count; i++) {
 | 
			
		||||
            if (!janet_checkint(arr[i])) {
 | 
			
		||||
                janet_asm_error(&a, "expected integer");
 | 
			
		||||
            }
 | 
			
		||||
            def->environments[i] = janet_unwrap_integer(arr[i]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (def->environments_length && NULL == def->environments) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Verify the func def */
 | 
			
		||||
    if (janet_verify(def)) {
 | 
			
		||||
        janet_asm_error(&a, "invalid assembly");
 | 
			
		||||
    int verify_status = janet_verify(def);
 | 
			
		||||
    if (verify_status) {
 | 
			
		||||
        janet_asm_errorv(&a, janet_formatc("invalid assembly (%d)", verify_status));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Add final flags */
 | 
			
		||||
@@ -861,6 +932,29 @@ static Janet janet_disasm_slotcount(JanetFuncDef *def) {
 | 
			
		||||
    return janet_wrap_integer(def->slotcount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_symbolslots(JanetFuncDef *def) {
 | 
			
		||||
    if (def->symbolmap == NULL) {
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    JanetArray *symbolslots = janet_array(def->symbolmap_length);
 | 
			
		||||
    Janet upvaluekw = janet_ckeywordv("upvalue");
 | 
			
		||||
    for (int32_t i = 0; i < def->symbolmap_length; i++) {
 | 
			
		||||
        JanetSymbolMap ss = def->symbolmap[i];
 | 
			
		||||
        Janet *t = janet_tuple_begin(4);
 | 
			
		||||
        if (ss.birth_pc == UINT32_MAX) {
 | 
			
		||||
            t[0] = upvaluekw;
 | 
			
		||||
        } else {
 | 
			
		||||
            t[0] = janet_wrap_integer(ss.birth_pc);
 | 
			
		||||
        }
 | 
			
		||||
        t[1] = janet_wrap_integer(ss.death_pc);
 | 
			
		||||
        t[2] = janet_wrap_integer(ss.slot_index);
 | 
			
		||||
        t[3] = janet_wrap_symbol(ss.symbol);
 | 
			
		||||
        symbolslots->data[i] = janet_wrap_tuple(janet_tuple_end(t));
 | 
			
		||||
    }
 | 
			
		||||
    symbolslots->count = def->symbolmap_length;
 | 
			
		||||
    return janet_wrap_array(symbolslots);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_bytecode(JanetFuncDef *def) {
 | 
			
		||||
    JanetArray *bcode = janet_array(def->bytecode_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->bytecode_length; i++) {
 | 
			
		||||
@@ -884,6 +978,10 @@ static Janet janet_disasm_vararg(JanetFuncDef *def) {
 | 
			
		||||
    return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_VARARG);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_structarg(JanetFuncDef *def) {
 | 
			
		||||
    return janet_wrap_boolean(def->flags & JANET_FUNCDEF_FLAG_STRUCTARG);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet janet_disasm_constants(JanetFuncDef *def) {
 | 
			
		||||
    JanetArray *constants = janet_array(def->constants_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->constants_length; i++) {
 | 
			
		||||
@@ -933,8 +1031,10 @@ Janet janet_disasm(JanetFuncDef *def) {
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("bytecode"), janet_disasm_bytecode(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("source"), janet_disasm_source(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("vararg"), janet_disasm_vararg(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("structarg"), janet_disasm_structarg(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("name"), janet_disasm_name(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("slotcount"), janet_disasm_slotcount(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("symbolmap"), janet_disasm_symbolslots(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("constants"), janet_disasm_constants(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("sourcemap"), janet_disasm_sourcemap(def));
 | 
			
		||||
    janet_table_put(ret, janet_ckeywordv("environments"), janet_disasm_environments(def));
 | 
			
		||||
@@ -942,18 +1042,40 @@ Janet janet_disasm(JanetFuncDef *def) {
 | 
			
		||||
    return janet_wrap_struct(janet_table_to_struct(ret));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* C Function for assembly */
 | 
			
		||||
static Janet cfun_asm(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_asm,
 | 
			
		||||
              "(asm assembly)",
 | 
			
		||||
              "Returns a new function that is the compiled result of the assembly.\n"
 | 
			
		||||
              "The syntax for the assembly can be found on the Janet website, and should correspond\n"
 | 
			
		||||
              "to the return value of disasm. Will throw an\n"
 | 
			
		||||
              "error on invalid assembly.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetAssembleResult res;
 | 
			
		||||
    res = janet_asm(argv[0], 0);
 | 
			
		||||
    if (res.status != JANET_ASSEMBLE_OK) {
 | 
			
		||||
        janet_panics(res.error);
 | 
			
		||||
        janet_panics(res.error ? res.error : janet_cstring("invalid assembly"));
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_function(janet_thunk(res.funcdef));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_disasm(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_disasm,
 | 
			
		||||
              "(disasm func &opt field)",
 | 
			
		||||
              "Returns assembly that could be used to compile the given function. "
 | 
			
		||||
              "func must be a function, not a c function. Will throw on error on a badly "
 | 
			
		||||
              "typed argument. If given a field name, will only return that part of the function assembly. "
 | 
			
		||||
              "Possible fields are:\n\n"
 | 
			
		||||
              "* :arity - number of required and optional arguments.\n"
 | 
			
		||||
              "* :min-arity - minimum number of arguments function can be called with.\n"
 | 
			
		||||
              "* :max-arity - maximum number of arguments function can be called with.\n"
 | 
			
		||||
              "* :vararg - true if function can take a variable number of arguments.\n"
 | 
			
		||||
              "* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n"
 | 
			
		||||
              "* :source - name of source file that this function was compiled from.\n"
 | 
			
		||||
              "* :name - name of function.\n"
 | 
			
		||||
              "* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n"
 | 
			
		||||
              "* :symbolmap - all symbols and their slots.\n"
 | 
			
		||||
              "* :constants - an array of constants referenced by this function.\n"
 | 
			
		||||
              "* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n"
 | 
			
		||||
              "* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n"
 | 
			
		||||
              "* :defs - other function definitions that this function may instantiate.\n") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetFunction *f = janet_getfunction(argv, 0);
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
@@ -965,6 +1087,7 @@ static Janet cfun_disasm(int32_t argc, Janet *argv) {
 | 
			
		||||
        if (!janet_cstrcmp(kw, "source")) return janet_disasm_source(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "name")) return janet_disasm_name(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "vararg")) return janet_disasm_vararg(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "structarg")) return janet_disasm_structarg(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "slotcount")) return janet_disasm_slotcount(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "constants")) return janet_disasm_constants(f->def);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "sourcemap")) return janet_disasm_sourcemap(f->def);
 | 
			
		||||
@@ -976,41 +1099,14 @@ static Janet cfun_disasm(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg asm_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "asm", cfun_asm,
 | 
			
		||||
        JDOC("(asm assembly)\n\n"
 | 
			
		||||
             "Returns a new function that is the compiled result of the assembly.\n"
 | 
			
		||||
             "The syntax for the assembly can be found on the Janet website, and should correspond\n"
 | 
			
		||||
             "to the return value of disasm. Will throw an\n"
 | 
			
		||||
             "error on invalid assembly.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "disasm", cfun_disasm,
 | 
			
		||||
        JDOC("(disasm func &opt field)\n\n"
 | 
			
		||||
             "Returns assembly that could be used to compile the given function.\n"
 | 
			
		||||
             "func must be a function, not a c function. Will throw on error on a badly\n"
 | 
			
		||||
             "typed argument. If given a field name, will only return that part of the function assembly.\n"
 | 
			
		||||
             "Possible fields are:\n\n"
 | 
			
		||||
             "* :arity - number of required and optional arguments.\n\n"
 | 
			
		||||
             "* :min-arity - minimum number of arguments function can be called with.\n\n"
 | 
			
		||||
             "* :max-arity - maximum number of arguments function can be called with.\n\n"
 | 
			
		||||
             "* :vararg - true if function can take a variable number of arguments.\n\n"
 | 
			
		||||
             "* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n\n"
 | 
			
		||||
             "* :source - name of source file that this function was compiled from.\n\n"
 | 
			
		||||
             "* :name - name of function.\n\n"
 | 
			
		||||
             "* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n\n"
 | 
			
		||||
             "* :constants - an array of constants referenced by this function.\n\n"
 | 
			
		||||
             "* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n\n"
 | 
			
		||||
             "* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n\n"
 | 
			
		||||
             "* :defs - other function definitions that this function may instantiate.\n")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Load the library */
 | 
			
		||||
void janet_lib_asm(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, asm_cfuns);
 | 
			
		||||
    JanetRegExt asm_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("asm", cfun_asm),
 | 
			
		||||
        JANET_CORE_REG("disasm", cfun_disasm),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, asm_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -162,28 +192,52 @@ void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x) {
 | 
			
		||||
 | 
			
		||||
/* C functions */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_new,
 | 
			
		||||
              "(buffer/new capacity)",
 | 
			
		||||
              "Creates a new, empty buffer with enough backing memory for `capacity` bytes. "
 | 
			
		||||
              "Returns a new buffer of length 0.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getinteger(argv, 0);
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(cap);
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_new_filled(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_new_filled,
 | 
			
		||||
              "(buffer/new-filled count &opt byte)",
 | 
			
		||||
              "Creates a new buffer of length `count` filled with `byte`. By default, `byte` is 0. "
 | 
			
		||||
              "Returns the new buffer.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    int32_t count = janet_getinteger(argv, 0);
 | 
			
		||||
    if (count < 0) count = 0;
 | 
			
		||||
    int32_t byte = 0;
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
        byte = janet_getinteger(argv, 1) & 0xFF;
 | 
			
		||||
    }
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(count);
 | 
			
		||||
    if (buffer->data)
 | 
			
		||||
    if (buffer->data && count > 0)
 | 
			
		||||
        memset(buffer->data, byte, count);
 | 
			
		||||
    buffer->count = count;
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_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. "
 | 
			
		||||
              "Returns the modified buffer.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int32_t byte = 0;
 | 
			
		||||
@@ -196,9 +250,13 @@ static Janet cfun_buffer_fill(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_trim(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_trim,
 | 
			
		||||
              "(buffer/trim buffer)",
 | 
			
		||||
              "Set the backing capacity of the buffer to the current length of the buffer. Returns the "
 | 
			
		||||
              "modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    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);
 | 
			
		||||
@@ -211,7 +269,10 @@ static Janet cfun_buffer_trim(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_u8,
 | 
			
		||||
              "(buffer/push-byte buffer & xs)",
 | 
			
		||||
              "Append bytes to a buffer. Will expand the buffer as necessary. "
 | 
			
		||||
              "Returns the modified buffer. Will throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -221,7 +282,11 @@ static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_word(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_word,
 | 
			
		||||
              "(buffer/push-word buffer & xs)",
 | 
			
		||||
              "Append machine words to a buffer. The 4 bytes of the integer are appended "
 | 
			
		||||
              "in twos complement, little endian order, unsigned for all x. Returns the modified buffer. Will "
 | 
			
		||||
              "throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -235,7 +300,12 @@ static Janet cfun_buffer_word(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_chars(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_chars,
 | 
			
		||||
              "(buffer/push-string buffer & xs)",
 | 
			
		||||
              "Push byte sequences onto the end of a buffer. "
 | 
			
		||||
              "Will accept any of strings, keywords, symbols, and buffers. "
 | 
			
		||||
              "Returns the modified buffer. "
 | 
			
		||||
              "Will throw an error if the buffer overflows.") {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
@@ -250,11 +320,135 @@ static Janet cfun_buffer_chars(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_push(int32_t argc, Janet *argv) {
 | 
			
		||||
    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 {
 | 
			
		||||
@@ -266,18 +460,53 @@ static Janet cfun_buffer_push(int32_t argc, Janet *argv) {
 | 
			
		||||
            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];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_clear(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_clear,
 | 
			
		||||
              "(buffer/clear buffer)",
 | 
			
		||||
              "Sets the size of a buffer to 0 and empties it. The buffer retains "
 | 
			
		||||
              "its memory so it can be efficiently refilled. Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    buffer->count = 0;
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_popn(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_popn,
 | 
			
		||||
              "(buffer/popn buffer n)",
 | 
			
		||||
              "Removes the last `n` bytes from the buffer. Returns the modified buffer.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    int32_t n = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -290,7 +519,12 @@ static Janet cfun_buffer_popn(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_slice(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_slice,
 | 
			
		||||
              "(buffer/slice bytes &opt start end)",
 | 
			
		||||
              "Takes a slice of a byte sequence from `start` to `end`. The range is half open, "
 | 
			
		||||
              "[start, end). Indexes can also be negative, indicating indexing from the end of the "
 | 
			
		||||
              "end of the array. By default, `start` is 0 and `end` is the length of the buffer. "
 | 
			
		||||
              "Returns a new buffer.") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(range.end - range.start);
 | 
			
		||||
@@ -314,7 +548,9 @@ static void bitloc(int32_t argc, Janet *argv, JanetBuffer **b, int32_t *index, i
 | 
			
		||||
    *bit = which_bit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitset(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitset,
 | 
			
		||||
              "(buffer/bit-set buffer index)",
 | 
			
		||||
              "Sets the bit at the given bit-index. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -323,7 +559,9 @@ static Janet cfun_buffer_bitset(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitclear(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitclear,
 | 
			
		||||
              "(buffer/bit-clear buffer index)",
 | 
			
		||||
              "Clears the bit at the given bit-index. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -332,7 +570,9 @@ static Janet cfun_buffer_bitclear(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bitget(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bitget,
 | 
			
		||||
              "(buffer/bit buffer index)",
 | 
			
		||||
              "Gets the bit at the given bit-index. Returns true if the bit is set, false if not.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -340,7 +580,9 @@ static Janet cfun_buffer_bitget(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_boolean(buffer->data[index] & (1 << bit));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_bittoggle(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_bittoggle,
 | 
			
		||||
              "(buffer/bit-toggle buffer index)",
 | 
			
		||||
              "Toggles the bit at the given bit index in buffer. Returns the buffer.") {
 | 
			
		||||
    int bit;
 | 
			
		||||
    int32_t index;
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
@@ -349,20 +591,26 @@ static Janet cfun_buffer_bittoggle(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_blit(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_blit,
 | 
			
		||||
              "(buffer/blit dest src &opt dest-start src-start src-end)",
 | 
			
		||||
              "Insert the contents of `src` into `dest`. Can optionally take indices that "
 | 
			
		||||
              "indicate which part of `src` to copy into which part of `dest`. Indices can be "
 | 
			
		||||
              "negative in order to index from the end of `src` or `dest`. Returns `dest`.") {
 | 
			
		||||
    janet_arity(argc, 2, 5);
 | 
			
		||||
    JanetBuffer *dest = janet_getbuffer(argv, 0);
 | 
			
		||||
    JanetByteView src = janet_getbytes(argv, 1);
 | 
			
		||||
    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 {
 | 
			
		||||
@@ -386,7 +634,10 @@ static Janet cfun_buffer_blit(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_buffer_format(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_format,
 | 
			
		||||
              "(buffer/format buffer format & args)",
 | 
			
		||||
              "Snprintf like functionality for printing values into a buffer. Returns "
 | 
			
		||||
              "the modified buffer.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_getbuffer(argv, 0);
 | 
			
		||||
    const char *strfrmt = (const char *) janet_getstring(argv, 1);
 | 
			
		||||
@@ -394,116 +645,55 @@ static Janet cfun_buffer_format(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg buffer_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/new", cfun_buffer_new,
 | 
			
		||||
        JDOC("(buffer/new capacity)\n\n"
 | 
			
		||||
             "Creates a new, empty buffer with enough backing memory for capacity bytes. "
 | 
			
		||||
             "Returns a new buffer of length 0.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/new-filled", cfun_buffer_new_filled,
 | 
			
		||||
        JDOC("(buffer/new-filled count &opt byte)\n\n"
 | 
			
		||||
             "Creates a new buffer of length count filled with byte. By default, byte is 0. "
 | 
			
		||||
             "Returns the new buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/fill", cfun_buffer_fill,
 | 
			
		||||
        JDOC("(buffer/fill buffer &opt byte)\n\n"
 | 
			
		||||
             "Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. "
 | 
			
		||||
             "Returns the modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/trim", cfun_buffer_trim,
 | 
			
		||||
        JDOC("(buffer/trim buffer)\n\n"
 | 
			
		||||
             "Set the backing capacity of the buffer to the current length of the buffer. Returns the "
 | 
			
		||||
             "modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/push-byte", cfun_buffer_u8,
 | 
			
		||||
        JDOC("(buffer/push-byte buffer & xs)\n\n"
 | 
			
		||||
             "Append bytes to a buffer. Will expand the buffer as necessary. "
 | 
			
		||||
             "Returns the modified buffer. Will throw an error if the buffer overflows.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/push-word", cfun_buffer_word,
 | 
			
		||||
        JDOC("(buffer/push-word buffer & xs)\n\n"
 | 
			
		||||
             "Append machine words to a buffer. The 4 bytes of the integer are appended "
 | 
			
		||||
             "in twos complement, little endian order, unsigned for all x. Returns the modified buffer. Will "
 | 
			
		||||
             "throw an error if the buffer overflows.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/push-string", cfun_buffer_chars,
 | 
			
		||||
        JDOC("(buffer/push-string buffer & xs)\n\n"
 | 
			
		||||
             "Push byte sequences onto the end of a buffer. "
 | 
			
		||||
             "Will accept any of strings, keywords, symbols, and buffers. "
 | 
			
		||||
             "Returns the modified buffer. "
 | 
			
		||||
             "Will throw an error if the buffer overflows.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/push", cfun_buffer_push,
 | 
			
		||||
        JDOC("(buffer/push buffer & xs)\n\n"
 | 
			
		||||
             "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.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/popn", cfun_buffer_popn,
 | 
			
		||||
        JDOC("(buffer/popn buffer n)\n\n"
 | 
			
		||||
             "Removes the last n bytes from the buffer. Returns the modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/clear", cfun_buffer_clear,
 | 
			
		||||
        JDOC("(buffer/clear buffer)\n\n"
 | 
			
		||||
             "Sets the size of a buffer to 0 and empties it. The buffer retains "
 | 
			
		||||
             "its memory so it can be efficiently refilled. Returns the modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/slice", cfun_buffer_slice,
 | 
			
		||||
        JDOC("(buffer/slice bytes &opt start end)\n\n"
 | 
			
		||||
             "Takes a slice of a byte sequence from start to end. The range is half open, "
 | 
			
		||||
             "[start, end). Indexes can also be negative, indicating indexing from the end of the "
 | 
			
		||||
             "end of the array. By default, start is 0 and end is the length of the buffer. "
 | 
			
		||||
             "Returns a new buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/bit-set", cfun_buffer_bitset,
 | 
			
		||||
        JDOC("(buffer/bit-set buffer index)\n\n"
 | 
			
		||||
             "Sets the bit at the given bit-index. Returns the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/bit-clear", cfun_buffer_bitclear,
 | 
			
		||||
        JDOC("(buffer/bit-clear buffer index)\n\n"
 | 
			
		||||
             "Clears the bit at the given bit-index. Returns the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/bit", cfun_buffer_bitget,
 | 
			
		||||
        JDOC("(buffer/bit buffer index)\n\n"
 | 
			
		||||
             "Gets the bit at the given bit-index. Returns true if the bit is set, false if not.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/bit-toggle", cfun_buffer_bittoggle,
 | 
			
		||||
        JDOC("(buffer/bit-toggle buffer index)\n\n"
 | 
			
		||||
             "Toggles the bit at the given bit index in buffer. Returns the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/blit", cfun_buffer_blit,
 | 
			
		||||
        JDOC("(buffer/blit dest src &opt dest-start src-start src-end)\n\n"
 | 
			
		||||
             "Insert the contents of src into dest. Can optionally take indices that "
 | 
			
		||||
             "indicate which part of src to copy into which part of dest. Indices can be "
 | 
			
		||||
             "negative to index from the end of src or dest. Returns dest.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "buffer/format", cfun_buffer_format,
 | 
			
		||||
        JDOC("(buffer/format buffer format & args)\n\n"
 | 
			
		||||
             "Snprintf like functionality for printing values into a buffer. Returns "
 | 
			
		||||
             " the modified buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
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) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, buffer_cfuns);
 | 
			
		||||
    JanetRegExt buffer_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("buffer/new", cfun_buffer_new),
 | 
			
		||||
        JANET_CORE_REG("buffer/new-filled", cfun_buffer_new_filled),
 | 
			
		||||
        JANET_CORE_REG("buffer/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),
 | 
			
		||||
        JANET_CORE_REG("buffer/bit-set", cfun_buffer_bitset),
 | 
			
		||||
        JANET_CORE_REG("buffer/bit-clear", cfun_buffer_bitclear),
 | 
			
		||||
        JANET_CORE_REG("buffer/bit", cfun_buffer_bitget),
 | 
			
		||||
        JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle),
 | 
			
		||||
        JANET_CORE_REG("buffer/blit", cfun_buffer_blit),
 | 
			
		||||
        JANET_CORE_REG("buffer/format", cfun_buffer_format),
 | 
			
		||||
        JANET_CORE_REG("buffer/format-at", cfun_buffer_format_at),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, buffer_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										258
									
								
								src/core/capi.c
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								src/core/capi.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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) {
 | 
			
		||||
@@ -51,15 +61,27 @@ JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_signalv(JanetSignal sig, Janet message) {
 | 
			
		||||
    if (janet_vm_return_reg != NULL) {
 | 
			
		||||
        *janet_vm_return_reg = message;
 | 
			
		||||
        if (NULL != janet_vm_fiber) {
 | 
			
		||||
            janet_vm_fiber->flags |= JANET_FIBER_DID_LONGJUMP;
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
#if defined(JANET_BSD) || defined(JANET_APPLE)
 | 
			
		||||
        _longjmp(*janet_vm_jmp_buf, sig);
 | 
			
		||||
        _longjmp(*janet_vm.signal_buf, sig);
 | 
			
		||||
#else
 | 
			
		||||
        longjmp(*janet_vm_jmp_buf, sig);
 | 
			
		||||
        longjmp(*janet_vm.signal_buf, sig);
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        const char *str = (const char *)janet_formatc("janet top level signal - %v\n", message);
 | 
			
		||||
@@ -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_panicf("string %v contains embedded 0s");
 | 
			
		||||
    if (!janet_checktype(argv[n], JANET_STRING)) {
 | 
			
		||||
        janet_panic_type(argv[n], n, JANET_TFLAG_STRING);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_getcbytes(argv, n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *janet_getcbytes(const Janet *argv, int32_t n) {
 | 
			
		||||
    /* Ensure buffer 0-padded */
 | 
			
		||||
    if (janet_checktype(argv[n], JANET_BUFFER)) {
 | 
			
		||||
        JanetBuffer *b = janet_unwrap_buffer(argv[n]);
 | 
			
		||||
        if ((b->gc.flags & JANET_BUFFER_FLAG_NO_REALLOC) && b->count == b->capacity) {
 | 
			
		||||
            /* Make a copy with janet_smalloc in the rare case we have a buffer that
 | 
			
		||||
             * cannot be realloced and pushing a 0 byte would panic. */
 | 
			
		||||
            char *new_string = janet_smalloc(b->count + 1);
 | 
			
		||||
            memcpy(new_string, b->data, b->count);
 | 
			
		||||
            new_string[b->count] = 0;
 | 
			
		||||
            if (strlen(new_string) != (size_t) b->count) goto badzeros;
 | 
			
		||||
            return new_string;
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Ensure trailing 0 */
 | 
			
		||||
            janet_buffer_push_u8(b, 0);
 | 
			
		||||
            b->count--;
 | 
			
		||||
            if (strlen((char *)b->data) != (size_t) b->count) goto badzeros;
 | 
			
		||||
            return (const char *) b->data;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, n);
 | 
			
		||||
    const char *cstr = (const char *)view.bytes;
 | 
			
		||||
    if (strlen(cstr) != (size_t) view.len) goto badzeros;
 | 
			
		||||
    return cstr;
 | 
			
		||||
 | 
			
		||||
badzeros:
 | 
			
		||||
    janet_panic("bytes contain embedded 0s");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *janet_optcbytes(const Janet *argv, int32_t argc, int32_t n, const char *dflt) {
 | 
			
		||||
    if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
 | 
			
		||||
        return dflt;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_getcbytes(argv, n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t janet_getnat(const Janet *argv, int32_t n) {
 | 
			
		||||
@@ -259,12 +315,53 @@ 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]);
 | 
			
		||||
#else
 | 
			
		||||
    Janet x = argv[n];
 | 
			
		||||
    if (!janet_checkint64(x)) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected 64 bit signed integer, got %v", n, x);
 | 
			
		||||
    }
 | 
			
		||||
    return (int64_t) janet_unwrap_number(x);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t janet_getuinteger64(const Janet *argv, int32_t n) {
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
    return janet_unwrap_u64(argv[n]);
 | 
			
		||||
#else
 | 
			
		||||
    Janet x = argv[n];
 | 
			
		||||
    if (!janet_checkuint64(x)) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected 64 bit unsigned integer, got %v", n, x);
 | 
			
		||||
    }
 | 
			
		||||
    return (uint64_t) janet_unwrap_number(x);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t janet_getsize(const Janet *argv, int32_t n) {
 | 
			
		||||
@@ -280,16 +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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -336,51 +447,64 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_dyn(const char *name) {
 | 
			
		||||
    if (!janet_vm_fiber) {
 | 
			
		||||
        if (!janet_vm_top_dyns) return janet_wrap_nil();
 | 
			
		||||
        return janet_table_get(janet_vm_top_dyns, janet_ckeywordv(name));
 | 
			
		||||
    if (!janet_vm.fiber) {
 | 
			
		||||
        if (!janet_vm.top_dyns) return janet_wrap_nil();
 | 
			
		||||
        return janet_table_get(janet_vm.top_dyns, janet_ckeywordv(name));
 | 
			
		||||
    }
 | 
			
		||||
    if (janet_vm_fiber->env) {
 | 
			
		||||
        return janet_table_get(janet_vm_fiber->env, janet_ckeywordv(name));
 | 
			
		||||
    if (janet_vm.fiber->env) {
 | 
			
		||||
        return janet_table_get(janet_vm.fiber->env, janet_ckeywordv(name));
 | 
			
		||||
    } else {
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_setdyn(const char *name, Janet value) {
 | 
			
		||||
    if (!janet_vm_fiber) {
 | 
			
		||||
        if (!janet_vm_top_dyns) janet_vm_top_dyns = janet_table(10);
 | 
			
		||||
        janet_table_put(janet_vm_top_dyns, janet_ckeywordv(name), value);
 | 
			
		||||
    if (!janet_vm.fiber) {
 | 
			
		||||
        if (!janet_vm.top_dyns) janet_vm.top_dyns = janet_table(10);
 | 
			
		||||
        janet_table_put(janet_vm.top_dyns, janet_ckeywordv(name), value);
 | 
			
		||||
    } else {
 | 
			
		||||
        if (!janet_vm_fiber->env) {
 | 
			
		||||
            janet_vm_fiber->env = janet_table(1);
 | 
			
		||||
        if (!janet_vm.fiber->env) {
 | 
			
		||||
            janet_vm.fiber->env = janet_table(1);
 | 
			
		||||
        }
 | 
			
		||||
        janet_table_put(janet_vm_fiber->env, janet_ckeywordv(name), value);
 | 
			
		||||
        janet_table_put(janet_vm.fiber->env, janet_ckeywordv(name), value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
@@ -433,9 +557,51 @@ 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetAtomicInt janet_atomic_load_relaxed(JanetAtomicInt volatile *x) {
 | 
			
		||||
#ifdef _MSC_VER
 | 
			
		||||
    return _InterlockedOr(x, 0);
 | 
			
		||||
#elif defined(JANET_USE_STDATOMIC)
 | 
			
		||||
    return atomic_load_explicit(x, memory_order_relaxed);
 | 
			
		||||
#else
 | 
			
		||||
    return __atomic_load_n(x, __ATOMIC_RELAXED);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Some definitions for function-like macros */
 | 
			
		||||
 | 
			
		||||
JANET_API JanetStructHead *(janet_struct_head)(const JanetKV *st) {
 | 
			
		||||
JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) {
 | 
			
		||||
    return janet_struct_head(st);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -443,10 +609,10 @@ JANET_API JanetAbstractHead *(janet_abstract_head)(const void *abstract) {
 | 
			
		||||
    return janet_abstract_head(abstract);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_API JanetStringHead *(janet_string_head)(const uint8_t *s) {
 | 
			
		||||
JANET_API JanetStringHead *(janet_string_head)(JanetString s) {
 | 
			
		||||
    return janet_string_head(s);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_API JanetTupleHead *(janet_tuple_head)(const Janet *tuple) {
 | 
			
		||||
JANET_API JanetTupleHead *(janet_tuple_head)(JanetTuple tuple) {
 | 
			
		||||
    return janet_tuple_head(tuple);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 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) 2021 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);
 | 
			
		||||
@@ -197,6 +207,39 @@ void janetc_popscope_keepslot(JanetCompiler *c, JanetSlot retslot) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int lookup_missing(
 | 
			
		||||
    JanetCompiler *c,
 | 
			
		||||
    const uint8_t *sym,
 | 
			
		||||
    JanetFunction *handler,
 | 
			
		||||
    JanetBinding *out) {
 | 
			
		||||
    int32_t minar = handler->def->min_arity;
 | 
			
		||||
    int32_t maxar = handler->def->max_arity;
 | 
			
		||||
    if (minar > 1 || maxar < 1) {
 | 
			
		||||
        janetc_error(c, janet_cstring("missing symbol lookup handler must take 1 argument"));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    Janet args[1] = { janet_wrap_symbol(sym) };
 | 
			
		||||
    JanetFiber *fiberp = janet_fiber(handler, 64, 1, args);
 | 
			
		||||
    if (NULL == fiberp) {
 | 
			
		||||
        janetc_error(c, janet_cstring("failed to call missing symbol lookup handler"));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    fiberp->env = c->env;
 | 
			
		||||
    int lock = janet_gclock();
 | 
			
		||||
    Janet tempOut;
 | 
			
		||||
    JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &tempOut);
 | 
			
		||||
    janet_gcunlock(lock);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK) {
 | 
			
		||||
        janetc_error(c, janet_formatc("(lookup) %V", tempOut));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Convert return value as entry. */
 | 
			
		||||
    /* Alternative could use janet_resolve_ext(c->env, sym) to read result from environment. */
 | 
			
		||||
    *out = janet_binding_from_entry(tempOut);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Allow searching for symbols. Return information about the symbol */
 | 
			
		||||
JanetSlot janetc_resolve(
 | 
			
		||||
    JanetCompiler *c,
 | 
			
		||||
@@ -230,6 +273,21 @@ JanetSlot janetc_resolve(
 | 
			
		||||
    /* Symbol not found - check for global */
 | 
			
		||||
    {
 | 
			
		||||
        JanetBinding binding = janet_resolve_ext(c->env, sym);
 | 
			
		||||
        if (binding.type == JANET_BINDING_NONE) {
 | 
			
		||||
            Janet handler = janet_table_get(c->env, janet_ckeywordv("missing-symbol"));
 | 
			
		||||
            switch (janet_type(handler)) {
 | 
			
		||||
                case JANET_NIL:
 | 
			
		||||
                    break;
 | 
			
		||||
                case JANET_FUNCTION:
 | 
			
		||||
                    if (!lookup_missing(c, sym, janet_unwrap_function(handler), &binding))
 | 
			
		||||
                        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
                    break;
 | 
			
		||||
                default:
 | 
			
		||||
                    janetc_error(c, janet_formatc("invalid lookup handler %V", handler));
 | 
			
		||||
                    return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        switch (binding.type) {
 | 
			
		||||
            default:
 | 
			
		||||
            case JANET_BINDING_NONE:
 | 
			
		||||
@@ -239,6 +297,12 @@ JanetSlot janetc_resolve(
 | 
			
		||||
            case JANET_BINDING_MACRO: /* Macro should function like defs when not in calling pos */
 | 
			
		||||
                ret = janetc_cslot(binding.value);
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_BINDING_DYNAMIC_DEF:
 | 
			
		||||
            case JANET_BINDING_DYNAMIC_MACRO:
 | 
			
		||||
                ret = janetc_cslot(binding.value);
 | 
			
		||||
                ret.flags |= JANET_SLOT_REF | JANET_SLOT_NAMED | JANET_SLOTTYPE_ANY;
 | 
			
		||||
                ret.flags &= ~JANET_SLOT_CONSTANT;
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_BINDING_VAR: {
 | 
			
		||||
                ret = janetc_cslot(binding.value);
 | 
			
		||||
                ret.flags |= JANET_SLOT_REF | JANET_SLOT_NAMED | JANET_SLOT_MUTABLE | JANET_SLOTTYPE_ANY;
 | 
			
		||||
@@ -280,6 +344,7 @@ found:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* non-local scope needs to expose its environment */
 | 
			
		||||
    JanetScope *original_scope = scope;
 | 
			
		||||
    pair->keep = 1;
 | 
			
		||||
    while (scope && !(scope->flags & JANET_SCOPE_FUNCTION))
 | 
			
		||||
        scope = scope->parent;
 | 
			
		||||
@@ -301,7 +366,7 @@ found:
 | 
			
		||||
            /* Check if scope already has env. If so, break */
 | 
			
		||||
            len = janet_v_count(scope->envs);
 | 
			
		||||
            for (j = 0; j < len; j++) {
 | 
			
		||||
                if (scope->envs[j] == envindex) {
 | 
			
		||||
                if (scope->envs[j].envindex == envindex) {
 | 
			
		||||
                    scopefound = 1;
 | 
			
		||||
                    envindex = j;
 | 
			
		||||
                    break;
 | 
			
		||||
@@ -310,7 +375,10 @@ found:
 | 
			
		||||
            /* Add the environment if it is not already referenced */
 | 
			
		||||
            if (!scopefound) {
 | 
			
		||||
                len = janet_v_count(scope->envs);
 | 
			
		||||
                janet_v_push(scope->envs, envindex);
 | 
			
		||||
                JanetEnvRef ref;
 | 
			
		||||
                ref.envindex = envindex;
 | 
			
		||||
                ref.scope = original_scope;
 | 
			
		||||
                janet_v_push(scope->envs, ref);
 | 
			
		||||
                envindex = len;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -354,6 +422,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len) {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    JanetSlot *ret = NULL;
 | 
			
		||||
    JanetFopts subopts = janetc_fopts_default(c);
 | 
			
		||||
    subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
    for (i = 0; i < len; i++) {
 | 
			
		||||
        janet_v_push(ret, janetc_value(subopts, vals[i]));
 | 
			
		||||
    }
 | 
			
		||||
@@ -364,6 +433,7 @@ JanetSlot *janetc_toslots(JanetCompiler *c, const Janet *vals, int32_t len) {
 | 
			
		||||
JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
 | 
			
		||||
    JanetSlot *ret = NULL;
 | 
			
		||||
    JanetFopts subopts = janetc_fopts_default(c);
 | 
			
		||||
    subopts.flags |= JANET_FOPTS_ACCEPT_SPLICE;
 | 
			
		||||
    const JanetKV *kvs = NULL;
 | 
			
		||||
    int32_t cap = 0, len = 0;
 | 
			
		||||
    janet_dictionary_view(ds, &kvs, &len, &cap);
 | 
			
		||||
@@ -651,7 +721,7 @@ static int macroexpand1(
 | 
			
		||||
    }
 | 
			
		||||
    Janet macroval;
 | 
			
		||||
    JanetBindingType btype = janet_resolve(c->env, name, ¯oval);
 | 
			
		||||
    if (btype != JANET_BINDING_MACRO ||
 | 
			
		||||
    if (!(btype == JANET_BINDING_MACRO || btype == JANET_BINDING_DYNAMIC_MACRO) ||
 | 
			
		||||
            !janet_checktype(macroval, JANET_FUNCTION))
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
@@ -676,12 +746,14 @@ static int macroexpand1(
 | 
			
		||||
    int lock = janet_gclock();
 | 
			
		||||
    Janet mf_kw = janet_ckeywordv("macro-form");
 | 
			
		||||
    janet_table_put(c->env, mf_kw, x);
 | 
			
		||||
    Janet ml_kw = janet_ckeywordv("macro-lints");
 | 
			
		||||
    if (c->lints) {
 | 
			
		||||
        janet_table_put(c->env, ml_kw, janet_wrap_array(c->lints));
 | 
			
		||||
    }
 | 
			
		||||
    Janet tempOut;
 | 
			
		||||
    JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &tempOut);
 | 
			
		||||
    janet_table_put(c->env, mf_kw, janet_wrap_nil());
 | 
			
		||||
    if (c->lints) {
 | 
			
		||||
        janet_table_put(c->env, janet_ckeywordv("macro-lints"), janet_wrap_array(c->lints));
 | 
			
		||||
    }
 | 
			
		||||
    janet_table_put(c->env, ml_kw, janet_wrap_nil());
 | 
			
		||||
    janet_gcunlock(lock);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK) {
 | 
			
		||||
        const uint8_t *es = janet_formatc("(macro) %V", tempOut);
 | 
			
		||||
@@ -814,7 +886,10 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
 | 
			
		||||
    /* Copy envs */
 | 
			
		||||
    def->environments_length = janet_v_count(scope->envs);
 | 
			
		||||
    def->environments = janet_v_flatten(scope->envs);
 | 
			
		||||
    def->environments = janet_malloc(sizeof(int32_t) * def->environments_length);
 | 
			
		||||
    for (int32_t i = 0; i < def->environments_length; i++) {
 | 
			
		||||
        def->environments[i] = scope->envs[i].envindex;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def->constants_length = janet_v_count(scope->consts);
 | 
			
		||||
    def->constants = janet_v_flatten(scope->consts);
 | 
			
		||||
@@ -859,7 +934,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        int32_t slotchunks = (def->slotcount + 31) >> 5;
 | 
			
		||||
        /* numchunks is min of slotchunks and scope->ua.count */
 | 
			
		||||
        int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks;
 | 
			
		||||
        uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks);
 | 
			
		||||
        uint32_t *chunks = janet_calloc(slotchunks, sizeof(uint32_t));
 | 
			
		||||
        if (NULL == chunks) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -869,9 +944,66 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
 | 
			
		||||
        def->closure_bitset = chunks;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Capture symbol to local mapping */
 | 
			
		||||
    JanetSymbolMap *locals = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Symbol -> upvalue mapping */
 | 
			
		||||
    JanetScope *top = c->scope;
 | 
			
		||||
    while (top->parent) top = top->parent;
 | 
			
		||||
    for (JanetScope *s = top; s != NULL; s = s->child) {
 | 
			
		||||
        for (int32_t j = 0; j < janet_v_count(scope->envs); j++) {
 | 
			
		||||
            JanetEnvRef ref = scope->envs[j];
 | 
			
		||||
            JanetScope *upscope = ref.scope;
 | 
			
		||||
            if (upscope != s) continue;
 | 
			
		||||
            for (int32_t i = 0; i < janet_v_count(upscope->syms); i++) {
 | 
			
		||||
                SymPair pair = upscope->syms[i];
 | 
			
		||||
                if (pair.sym2) {
 | 
			
		||||
                    JanetSymbolMap jsm;
 | 
			
		||||
                    jsm.birth_pc = UINT32_MAX;
 | 
			
		||||
                    jsm.death_pc = j;
 | 
			
		||||
                    jsm.slot_index = pair.slot.index;
 | 
			
		||||
                    jsm.symbol = pair.sym2;
 | 
			
		||||
                    janet_v_push(locals, jsm);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Symbol -> slot mapping */
 | 
			
		||||
    for (int32_t i = 0; i < janet_v_count(scope->syms); i++) {
 | 
			
		||||
        SymPair pair = scope->syms[i];
 | 
			
		||||
        if (pair.sym2) {
 | 
			
		||||
            JanetSymbolMap jsm;
 | 
			
		||||
            if (pair.death_pc == UINT32_MAX) {
 | 
			
		||||
                jsm.death_pc = def->bytecode_length;
 | 
			
		||||
            } else {
 | 
			
		||||
                jsm.death_pc = pair.death_pc - scope->bytecode_start;
 | 
			
		||||
            }
 | 
			
		||||
            /* Handle birth_pc == 0 correctly */
 | 
			
		||||
            if ((uint32_t) scope->bytecode_start > pair.birth_pc) {
 | 
			
		||||
                jsm.birth_pc = 0;
 | 
			
		||||
            } else {
 | 
			
		||||
                jsm.birth_pc = pair.birth_pc - scope->bytecode_start;
 | 
			
		||||
            }
 | 
			
		||||
            janet_assert(jsm.birth_pc <= jsm.death_pc, "birth pc after death pc");
 | 
			
		||||
            janet_assert(jsm.birth_pc < (uint32_t) def->bytecode_length, "bad birth pc");
 | 
			
		||||
            janet_assert(jsm.death_pc <= (uint32_t) def->bytecode_length, "bad death pc");
 | 
			
		||||
            jsm.slot_index = pair.slot.index;
 | 
			
		||||
            jsm.symbol = pair.sym2;
 | 
			
		||||
            janet_v_push(locals, jsm);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    def->symbolmap_length = janet_v_count(locals);
 | 
			
		||||
    def->symbolmap = janet_v_flatten(locals);
 | 
			
		||||
    if (def->symbolmap_length) def->flags |= JANET_FUNCDEF_FLAG_HASSYMBOLMAP;
 | 
			
		||||
 | 
			
		||||
    /* Pop the scope */
 | 
			
		||||
    janetc_popscope(c);
 | 
			
		||||
 | 
			
		||||
    /* Do basic optimization */
 | 
			
		||||
    janet_bytecode_movopt(def);
 | 
			
		||||
    janet_bytecode_remove_noops(def);
 | 
			
		||||
 | 
			
		||||
    return def;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -924,7 +1056,7 @@ JanetCompileResult janet_compile_lint(Janet source,
 | 
			
		||||
 | 
			
		||||
    if (c.result.status == JANET_COMPILE_OK) {
 | 
			
		||||
        JanetFuncDef *def = janetc_pop_funcdef(&c);
 | 
			
		||||
        def->name = janet_cstring("_thunk");
 | 
			
		||||
        def->name = janet_cstring("thunk");
 | 
			
		||||
        janet_def_addflags(def);
 | 
			
		||||
        c.result.funcdef = def;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -942,18 +1074,34 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* C Function for compiling */
 | 
			
		||||
static Janet cfun(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_compile,
 | 
			
		||||
              "(compile ast &opt env source lints)",
 | 
			
		||||
              "Compiles an Abstract Syntax Tree (ast) into a function. "
 | 
			
		||||
              "Pair the compile function with parsing functionality to implement "
 | 
			
		||||
              "eval. Returns a new function and does not modify ast. Returns an error "
 | 
			
		||||
              "struct with keys :line, :column, and :error if compilation fails. "
 | 
			
		||||
              "If a `lints` array is given, linting messages will be appended to the array. "
 | 
			
		||||
              "Each message will be a tuple of the form `(level line col message)`.") {
 | 
			
		||||
    janet_arity(argc, 1, 4);
 | 
			
		||||
    JanetTable *env = argc > 1 ? janet_gettable(argv, 1) : janet_vm_fiber->env;
 | 
			
		||||
    JanetTable *env = (argc > 1 && !janet_checktype(argv[1], JANET_NIL))
 | 
			
		||||
                      ? janet_gettable(argv, 1) : janet_vm.fiber->env;
 | 
			
		||||
    if (NULL == env) {
 | 
			
		||||
        env = janet_table(0);
 | 
			
		||||
        janet_vm_fiber->env = env;
 | 
			
		||||
        janet_vm.fiber->env = env;
 | 
			
		||||
    }
 | 
			
		||||
    const uint8_t *source = NULL;
 | 
			
		||||
    if (argc >= 3) {
 | 
			
		||||
        source = janet_getstring(argv, 2);
 | 
			
		||||
        Janet x = argv[2];
 | 
			
		||||
        if (janet_checktype(x, JANET_STRING)) {
 | 
			
		||||
            source = janet_unwrap_string(x);
 | 
			
		||||
        } else if (janet_checktype(x, JANET_KEYWORD)) {
 | 
			
		||||
            source = janet_unwrap_keyword(x);
 | 
			
		||||
        } else if (!janet_checktype(x, JANET_NIL)) {
 | 
			
		||||
            janet_panic_type(x, 2, JANET_TFLAG_STRING | JANET_TFLAG_KEYWORD);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetArray *lints = (argc >= 4) ? janet_getarray(argv, 3) : NULL;
 | 
			
		||||
    JanetArray *lints = (argc >= 4 && !janet_checktype(argv[3], JANET_NIL))
 | 
			
		||||
                        ? janet_getarray(argv, 3) : NULL;
 | 
			
		||||
    JanetCompileResult res = janet_compile_lint(argv[0], env, source, lints);
 | 
			
		||||
    if (res.status == JANET_COMPILE_OK) {
 | 
			
		||||
        return janet_wrap_function(janet_thunk(res.funcdef));
 | 
			
		||||
@@ -973,20 +1121,10 @@ static Janet cfun(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg compile_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "compile", cfun,
 | 
			
		||||
        JDOC("(compile ast &opt env source lints)\n\n"
 | 
			
		||||
             "Compiles an Abstract Syntax Tree (ast) into a function. "
 | 
			
		||||
             "Pair the compile function with parsing functionality to implement "
 | 
			
		||||
             "eval. Returns a new function and does not modify ast. Returns an error "
 | 
			
		||||
             "struct with keys :line, :column, and :error if compilation fails. "
 | 
			
		||||
             "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)`.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void janet_lib_compile(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, compile_cfuns);
 | 
			
		||||
    JanetRegExt cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("compile", cfun_compile),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										255
									
								
								src/core/debug.c
									
									
									
									
									
								
							
							
						
						
									
										255
									
								
								src/core/debug.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -55,7 +55,7 @@ void janet_debug_find(
 | 
			
		||||
    JanetFuncDef **def_out, int32_t *pc_out,
 | 
			
		||||
    const uint8_t *source, int32_t sourceLine, int32_t sourceColumn) {
 | 
			
		||||
    /* Scan the heap for right func def */
 | 
			
		||||
    JanetGCObject *current = janet_vm_blocks;
 | 
			
		||||
    JanetGCObject *current = janet_vm.blocks;
 | 
			
		||||
    /* Keep track of the best source mapping we have seen so far */
 | 
			
		||||
    int32_t besti = -1;
 | 
			
		||||
    int32_t best_line = -1;
 | 
			
		||||
@@ -86,7 +86,7 @@ void janet_debug_find(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        current = current->next;
 | 
			
		||||
        current = current->data.next;
 | 
			
		||||
    }
 | 
			
		||||
    if (best_def) {
 | 
			
		||||
        *def_out = best_def;
 | 
			
		||||
@@ -96,15 +96,19 @@ void janet_debug_find(
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Error reporting. This can be emulated from within Janet, but for
 | 
			
		||||
 * consitency with the top level code it is defined once. */
 | 
			
		||||
void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
    const char *prefix = janet_checktype(err, JANET_NIL) ? NULL : "";
 | 
			
		||||
    janet_stacktrace_ext(fiber, err, prefix);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Error reporting. This can be emulated from within Janet, but for
 | 
			
		||||
 * consistency with the top level code it is defined once. */
 | 
			
		||||
void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
 | 
			
		||||
 | 
			
		||||
    int32_t fi;
 | 
			
		||||
    const char *errstr = (const char *)janet_to_string(err);
 | 
			
		||||
    JanetFiber **fibers = NULL;
 | 
			
		||||
 | 
			
		||||
    /* Don't print error line if it is nil. */
 | 
			
		||||
    int wrote_error = janet_checktype(err, JANET_NIL);
 | 
			
		||||
    int wrote_error = !prefix;
 | 
			
		||||
 | 
			
		||||
    int print_color = janet_truthy(janet_dyn("err-color"));
 | 
			
		||||
    if (print_color) janet_eprintf("\x1b[31m");
 | 
			
		||||
@@ -118,6 +122,7 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
        fiber = fibers[fi];
 | 
			
		||||
        int32_t i = fiber->frame;
 | 
			
		||||
        while (i > 0) {
 | 
			
		||||
            JanetCFunRegistry *reg = NULL;
 | 
			
		||||
            JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
 | 
			
		||||
            JanetFuncDef *def = NULL;
 | 
			
		||||
            i = frame->prevframe;
 | 
			
		||||
@@ -125,11 +130,10 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
            /* Print prelude to stack frame */
 | 
			
		||||
            if (!wrote_error) {
 | 
			
		||||
                JanetFiberStatus status = janet_fiber_status(fiber);
 | 
			
		||||
                const char *prefix = status == JANET_STATUS_ERROR ? "" : "status ";
 | 
			
		||||
                janet_eprintf("%s%s: %s\n",
 | 
			
		||||
                              prefix,
 | 
			
		||||
                              prefix ? prefix : "",
 | 
			
		||||
                              janet_status_names[status],
 | 
			
		||||
                              errstr);
 | 
			
		||||
                              errstr ? errstr : janet_status_names[status]);
 | 
			
		||||
                wrote_error = 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -144,15 +148,23 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
            } else {
 | 
			
		||||
                JanetCFunction cfun = (JanetCFunction)(frame->pc);
 | 
			
		||||
                if (cfun) {
 | 
			
		||||
                    Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
 | 
			
		||||
                    if (!janet_checktype(name, JANET_NIL))
 | 
			
		||||
                        janet_eprintf(" %s", (const char *)janet_to_string(name));
 | 
			
		||||
                    else
 | 
			
		||||
                    reg = janet_registry_get(cfun);
 | 
			
		||||
                    if (NULL != reg && NULL != reg->name) {
 | 
			
		||||
                        if (reg->name_prefix) {
 | 
			
		||||
                            janet_eprintf(" %s/%s", reg->name_prefix, reg->name);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            janet_eprintf(" %s", reg->name);
 | 
			
		||||
                        }
 | 
			
		||||
                        if (NULL != reg->source_file) {
 | 
			
		||||
                            janet_eprintf(" [%s]", reg->source_file);
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        janet_eprintf(" <cfunction>");
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (frame->flags & JANET_STACKFRAME_TAILCALL)
 | 
			
		||||
                janet_eprintf(" (tailcall)");
 | 
			
		||||
                janet_eprintf(" (tail call)");
 | 
			
		||||
            if (frame->func && frame->pc) {
 | 
			
		||||
                int32_t off = (int32_t)(frame->pc - def->bytecode);
 | 
			
		||||
                if (def->sourcemap) {
 | 
			
		||||
@@ -161,8 +173,18 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf(" pc=%d", off);
 | 
			
		||||
                }
 | 
			
		||||
            } else if (NULL != reg) {
 | 
			
		||||
                /* C Function */
 | 
			
		||||
                if (reg->source_line > 0) {
 | 
			
		||||
                    janet_eprintf(" on line %d", (long) reg->source_line);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            janet_eprintf("\n");
 | 
			
		||||
            /* Print fiber points optionally. Clutters traces but provides info
 | 
			
		||||
            if (i <= 0 && fi > 0) {
 | 
			
		||||
                janet_eprintf("  in parent fiber\n");
 | 
			
		||||
            }
 | 
			
		||||
            */
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -195,7 +217,13 @@ static void helper_find_fun(int32_t argc, Janet *argv, JanetFuncDef **def, int32
 | 
			
		||||
    *bytecode_offset = offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_break(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_break,
 | 
			
		||||
              "(debug/break source line col)",
 | 
			
		||||
              "Sets a breakpoint in `source` at a given line and column. "
 | 
			
		||||
              "Will throw an error if the breakpoint location "
 | 
			
		||||
              "cannot be found. For example\n\n"
 | 
			
		||||
              "\t(debug/break \"core.janet\" 10 4)\n\n"
 | 
			
		||||
              "will set a breakpoint at line 10, 4th column of the file core.janet.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset;
 | 
			
		||||
    helper_find(argc, argv, &def, &offset);
 | 
			
		||||
@@ -203,7 +231,11 @@ static Janet cfun_debug_break(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_unbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_unbreak,
 | 
			
		||||
              "(debug/unbreak source line column)",
 | 
			
		||||
              "Remove a breakpoint with a source key at a given line and column. "
 | 
			
		||||
              "Will throw an error if the breakpoint "
 | 
			
		||||
              "cannot be found.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset = 0;
 | 
			
		||||
    helper_find(argc, argv, &def, &offset);
 | 
			
		||||
@@ -211,7 +243,11 @@ static Janet cfun_debug_unbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_fbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_fbreak,
 | 
			
		||||
              "(debug/fbreak fun &opt pc)",
 | 
			
		||||
              "Set a breakpoint in a given function. pc is an optional offset, which "
 | 
			
		||||
              "is in bytecode instructions. fun is a function value. Will throw an error "
 | 
			
		||||
              "if the offset is too large or negative.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset = 0;
 | 
			
		||||
    helper_find_fun(argc, argv, &def, &offset);
 | 
			
		||||
@@ -219,7 +255,9 @@ static Janet cfun_debug_fbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_unfbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_unfbreak,
 | 
			
		||||
              "(debug/unfbreak fun &opt pc)",
 | 
			
		||||
              "Unset a breakpoint set with debug/fbreak.") {
 | 
			
		||||
    JanetFuncDef *def;
 | 
			
		||||
    int32_t offset;
 | 
			
		||||
    helper_find_fun(argc, argv, &def, &offset);
 | 
			
		||||
@@ -227,7 +265,12 @@ static Janet cfun_debug_unfbreak(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_lineage(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_lineage,
 | 
			
		||||
              "(debug/lineage fib)",
 | 
			
		||||
              "Returns an array of all child fibers from a root fiber. This function "
 | 
			
		||||
              "is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
 | 
			
		||||
              "the fiber handling the error can see which fiber raised the signal. This function should "
 | 
			
		||||
              "be used mostly for debugging purposes.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(0);
 | 
			
		||||
@@ -252,9 +295,20 @@ static Janet doframe(JanetStackFrame *frame) {
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetCFunction cfun = (JanetCFunction)(frame->pc);
 | 
			
		||||
        if (cfun) {
 | 
			
		||||
            Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
 | 
			
		||||
            if (!janet_checktype(name, JANET_NIL)) {
 | 
			
		||||
                janet_table_put(t, janet_ckeywordv("name"), name);
 | 
			
		||||
            JanetCFunRegistry *reg = janet_registry_get(cfun);
 | 
			
		||||
            if (NULL != reg->name) {
 | 
			
		||||
                if (NULL != reg->name_prefix) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("name"), janet_wrap_string(janet_formatc("%s/%s", reg->name_prefix, reg->name)));
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("name"), janet_cstringv(reg->name));
 | 
			
		||||
                }
 | 
			
		||||
                if (NULL != reg->source_file) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source"), janet_cstringv(reg->source_file));
 | 
			
		||||
                }
 | 
			
		||||
                if (reg->source_line > 0) {
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source-line"), janet_wrap_integer(reg->source_line));
 | 
			
		||||
                    janet_table_put(t, janet_ckeywordv("source-column"), janet_wrap_integer(1));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("c"), janet_wrap_true());
 | 
			
		||||
@@ -265,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) {
 | 
			
		||||
@@ -280,11 +335,46 @@ static Janet doframe(JanetStackFrame *frame) {
 | 
			
		||||
        safe_memcpy(slots->data, stack, sizeof(Janet) * def->slotcount);
 | 
			
		||||
        slots->count = def->slotcount;
 | 
			
		||||
        janet_table_put(t, janet_ckeywordv("slots"), janet_wrap_array(slots));
 | 
			
		||||
        /* Add local bindings */
 | 
			
		||||
        if (def->symbolmap) {
 | 
			
		||||
            JanetTable *local_bindings = janet_table(0);
 | 
			
		||||
            for (int32_t i = def->symbolmap_length - 1; i >= 0; i--) {
 | 
			
		||||
                JanetSymbolMap jsm = def->symbolmap[i];
 | 
			
		||||
                Janet value = janet_wrap_nil();
 | 
			
		||||
                uint32_t pc = (uint32_t)(frame->pc - def->bytecode);
 | 
			
		||||
                if (jsm.birth_pc == UINT32_MAX) {
 | 
			
		||||
                    JanetFuncEnv *env = frame->func->envs[jsm.death_pc];
 | 
			
		||||
                    if (env->offset > 0) {
 | 
			
		||||
                        value = env->as.fiber->data[env->offset + jsm.slot_index];
 | 
			
		||||
                    } else {
 | 
			
		||||
                        value = env->as.values[jsm.slot_index];
 | 
			
		||||
                    }
 | 
			
		||||
                } else if (pc >= jsm.birth_pc && pc < jsm.death_pc) {
 | 
			
		||||
                    value = stack[jsm.slot_index];
 | 
			
		||||
                }
 | 
			
		||||
                janet_table_put(local_bindings, janet_wrap_symbol(jsm.symbol), value);
 | 
			
		||||
            }
 | 
			
		||||
            janet_table_put(t, janet_ckeywordv("locals"), janet_wrap_table(local_bindings));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_table(t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_stack(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_stack,
 | 
			
		||||
              "(debug/stack fib)",
 | 
			
		||||
              "Gets information about the stack as an array of tables. Each table "
 | 
			
		||||
              "in the array contains information about a stack frame. The top-most, current "
 | 
			
		||||
              "stack frame is the first table in the array, and the bottom-most stack frame "
 | 
			
		||||
              "is the last value. Each stack frame contains some of the following attributes:\n\n"
 | 
			
		||||
              "* :c - true if the stack frame is a c function invocation\n\n"
 | 
			
		||||
              "* :source-column - the current source column of the stack frame\n\n"
 | 
			
		||||
              "* :function - the function that the stack frame represents\n\n"
 | 
			
		||||
              "* :source-line - the current source line of the stack frame\n\n"
 | 
			
		||||
              "* :name - the human-friendly name of the function\n\n"
 | 
			
		||||
              "* :pc - integer indicating the location of the program counter\n\n"
 | 
			
		||||
              "* :source - string with the file path or other identifier for the source code\n\n"
 | 
			
		||||
              "* :slots - array of all values in each slot\n\n"
 | 
			
		||||
              "* :tail - boolean indicating a tail call") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(0);
 | 
			
		||||
@@ -300,15 +390,24 @@ static Janet cfun_debug_stack(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_stacktrace(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
JANET_CORE_FN(cfun_debug_stacktrace,
 | 
			
		||||
              "(debug/stacktrace fiber &opt err prefix)",
 | 
			
		||||
              "Prints a nice looking stacktrace for a fiber. Can optionally provide "
 | 
			
		||||
              "an error value to print the stack trace with. If `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];
 | 
			
		||||
    janet_stacktrace(fiber, x);
 | 
			
		||||
    const char *prefix = janet_optcstring(argv, argc, 2, NULL);
 | 
			
		||||
    janet_stacktrace_ext(fiber, x, prefix);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_argstack(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_argstack,
 | 
			
		||||
              "(debug/arg-stack fiber)",
 | 
			
		||||
              "Gets all values currently on the fiber's argument stack. Normally, "
 | 
			
		||||
              "this should be empty unless the fiber signals while pushing arguments "
 | 
			
		||||
              "to make a function call. Returns a new array.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    JanetArray *array = janet_array(fiber->stacktop - fiber->stackstart);
 | 
			
		||||
@@ -317,7 +416,11 @@ static Janet cfun_debug_argstack(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_array(array);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_debug_step(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_debug_step,
 | 
			
		||||
              "(debug/step fiber &opt x)",
 | 
			
		||||
              "Run a fiber for one virtual instruction of the Janet machine. Can optionally "
 | 
			
		||||
              "pass in a value that will be passed as the resuming value. Returns the signal value, "
 | 
			
		||||
              "which will usually be nil, as breakpoints raise nil signals.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    Janet out = janet_wrap_nil();
 | 
			
		||||
@@ -325,85 +428,19 @@ static Janet cfun_debug_step(int32_t argc, Janet *argv) {
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg debug_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "debug/break", cfun_debug_break,
 | 
			
		||||
        JDOC("(debug/break source line col)\n\n"
 | 
			
		||||
             "Sets a breakpoint in `source` at a given line and column. "
 | 
			
		||||
             "Will throw an error if the breakpoint location "
 | 
			
		||||
             "cannot be found. For example\n\n"
 | 
			
		||||
             "\t(debug/break \"core.janet\" 10 4)\n\n"
 | 
			
		||||
             "wil set a breakpoint at line 10, 4th column of the file core.janet.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/unbreak", cfun_debug_unbreak,
 | 
			
		||||
        JDOC("(debug/unbreak source line column)\n\n"
 | 
			
		||||
             "Remove a breakpoint with a source key at a given line and column. "
 | 
			
		||||
             "Will throw an error if the breakpoint "
 | 
			
		||||
             "cannot be found.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/fbreak", cfun_debug_fbreak,
 | 
			
		||||
        JDOC("(debug/fbreak fun &opt pc)\n\n"
 | 
			
		||||
             "Set a breakpoint in a given function. pc is an optional offset, which "
 | 
			
		||||
             "is in bytecode instructions. fun is a function value. Will throw an error "
 | 
			
		||||
             "if the offset is too large or negative.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/unfbreak", cfun_debug_unfbreak,
 | 
			
		||||
        JDOC("(debug/unfbreak fun &opt pc)\n\n"
 | 
			
		||||
             "Unset a breakpoint set with debug/fbreak.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/arg-stack", cfun_debug_argstack,
 | 
			
		||||
        JDOC("(debug/arg-stack fiber)\n\n"
 | 
			
		||||
             "Gets all values currently on the fiber's argument stack. Normally, "
 | 
			
		||||
             "this should be empty unless the fiber signals while pushing arguments "
 | 
			
		||||
             "to make a function call. Returns a new array.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/stack", cfun_debug_stack,
 | 
			
		||||
        JDOC("(debug/stack fib)\n\n"
 | 
			
		||||
             "Gets information about the stack as an array of tables. Each table "
 | 
			
		||||
             "in the array contains information about a stack frame. The top-most, current "
 | 
			
		||||
             "stack frame is the first table in the array, and the bottom-most stack frame "
 | 
			
		||||
             "is the last value. Each stack frame contains some of the following attributes:\n\n"
 | 
			
		||||
             "* :c - true if the stack frame is a c function invocation\n\n"
 | 
			
		||||
             "* :column - the current source column of the stack frame\n\n"
 | 
			
		||||
             "* :function - the function that the stack frame represents\n\n"
 | 
			
		||||
             "* :line - the current source line of the stack frame\n\n"
 | 
			
		||||
             "* :name - the human-friendly name of the function\n\n"
 | 
			
		||||
             "* :pc - integer indicating the location of the program counter\n\n"
 | 
			
		||||
             "* :source - string with the file path or other identifier for the source code\n\n"
 | 
			
		||||
             "* :slots - array of all values in each slot\n\n"
 | 
			
		||||
             "* :tail - boolean indicating a tail call")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/stacktrace", cfun_debug_stacktrace,
 | 
			
		||||
        JDOC("(debug/stacktrace fiber &opt err)\n\n"
 | 
			
		||||
             "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, will skipp the error line. Returns the fiber.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/lineage", cfun_debug_lineage,
 | 
			
		||||
        JDOC("(debug/lineage fib)\n\n"
 | 
			
		||||
             "Returns an array of all child fibers from a root fiber. This function "
 | 
			
		||||
             "is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
 | 
			
		||||
             "the fiber handling the error can see which fiber raised the signal. This function should "
 | 
			
		||||
             "be used mostly for debugging purposes.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "debug/step", cfun_debug_step,
 | 
			
		||||
        JDOC("(debug/step fiber &opt x)\n\n"
 | 
			
		||||
             "Run a fiber for one virtual instruction of the Janet machine. Can optionally "
 | 
			
		||||
             "pass in a value that will be passed as the resuming value. Returns the signal value, "
 | 
			
		||||
             "which will usually be nil, as breakpoints raise nil signals.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_debug(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, debug_cfuns);
 | 
			
		||||
    JanetRegExt debug_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("debug/break", cfun_debug_break),
 | 
			
		||||
        JANET_CORE_REG("debug/unbreak", cfun_debug_unbreak),
 | 
			
		||||
        JANET_CORE_REG("debug/fbreak", cfun_debug_fbreak),
 | 
			
		||||
        JANET_CORE_REG("debug/unfbreak", cfun_debug_unfbreak),
 | 
			
		||||
        JANET_CORE_REG("debug/arg-stack", cfun_debug_argstack),
 | 
			
		||||
        JANET_CORE_REG("debug/stack", cfun_debug_stack),
 | 
			
		||||
        JANET_CORE_REG("debug/stacktrace", cfun_debug_stacktrace),
 | 
			
		||||
        JANET_CORE_REG("debug/lineage", cfun_debug_lineage),
 | 
			
		||||
        JANET_CORE_REG("debug/step", cfun_debug_step),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, debug_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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) 2021 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2974
									
								
								src/core/ev.c
									
									
									
									
									
								
							
							
						
						
									
										2974
									
								
								src/core/ev.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 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,13 +37,31 @@
 | 
			
		||||
# endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
#define _DARWIN_C_SOURCE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for sched.h for cpu count */
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(WIN32) || defined(_WIN32)
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for realpath on linux */
 | 
			
		||||
#if !defined(_XOPEN_SOURCE) && (defined(__linux__) || defined(__EMSCRIPTEN__))
 | 
			
		||||
#define _XOPEN_SOURCE 500
 | 
			
		||||
/* needed for inet_pton and InitializeSRWLock */
 | 
			
		||||
#ifdef __MINGW32__
 | 
			
		||||
#define _WIN32_WINNT _WIN32_WINNT_VISTA
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for realpath on linux, as well as pthread rwlocks. */
 | 
			
		||||
#ifndef _XOPEN_SOURCE
 | 
			
		||||
#define _XOPEN_SOURCE 600
 | 
			
		||||
#endif
 | 
			
		||||
#if _XOPEN_SOURCE < 600
 | 
			
		||||
#undef _XOPEN_SOURCE
 | 
			
		||||
#define _XOPEN_SOURCE 600
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for timegm and other extensions when building with -std=c99.
 | 
			
		||||
@@ -52,4 +71,11 @@
 | 
			
		||||
#define _NETBSD_SOURCE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for several things when building with -std=c99. */
 | 
			
		||||
#if !__BSD_VISIBLE && (defined(__DragonFly__) || defined(__FreeBSD__))
 | 
			
		||||
#define __BSD_VISIBLE 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _FILE_OFFSET_BITS 64
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1899
									
								
								src/core/ffi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1899
									
								
								src/core/ffi.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										249
									
								
								src/core/fiber.c
									
									
									
									
									
								
							
							
						
						
									
										249
									
								
								src/core/fiber.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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);
 | 
			
		||||
@@ -57,7 +59,7 @@ static JanetFiber *fiber_alloc(int32_t capacity) {
 | 
			
		||||
    if (NULL == data) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_next_collection += sizeof(Janet) * capacity;
 | 
			
		||||
    janet_vm.next_collection += sizeof(Janet) * capacity;
 | 
			
		||||
    fiber->data = data;
 | 
			
		||||
    return fiber;
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -121,7 +123,7 @@ void janet_fiber_setcapacity(JanetFiber *fiber, int32_t n) {
 | 
			
		||||
    }
 | 
			
		||||
    fiber->data = newData;
 | 
			
		||||
    fiber->capacity = n;
 | 
			
		||||
    janet_vm_next_collection += sizeof(Janet) * diff;
 | 
			
		||||
    janet_vm.next_collection += sizeof(Janet) * diff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Grow fiber if needed */
 | 
			
		||||
@@ -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));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -255,7 +257,7 @@ static void janet_env_detach(JanetFuncEnv *env) {
 | 
			
		||||
        int32_t len = env->length;
 | 
			
		||||
        size_t s = sizeof(Janet) * (size_t) len;
 | 
			
		||||
        Janet *vmem = janet_malloc(s);
 | 
			
		||||
        janet_vm_next_collection += (uint32_t) s;
 | 
			
		||||
        janet_vm.next_collection += (uint32_t) s;
 | 
			
		||||
        if (NULL == vmem) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
@@ -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 {
 | 
			
		||||
@@ -442,16 +444,19 @@ JanetFiberStatus janet_fiber_status(JanetFiber *f) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetFiber *janet_current_fiber(void) {
 | 
			
		||||
    return janet_vm_fiber;
 | 
			
		||||
    return janet_vm.fiber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetFiber *janet_root_fiber(void) {
 | 
			
		||||
    return janet_vm_root_fiber;
 | 
			
		||||
    return janet_vm.root_fiber;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* CFuns */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_getenv(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_getenv,
 | 
			
		||||
              "(fiber/getenv fiber)",
 | 
			
		||||
              "Gets the environment for a fiber. Returns nil if no such table is "
 | 
			
		||||
              "set yet.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    return fiber->env ?
 | 
			
		||||
@@ -459,7 +464,10 @@ static Janet cfun_fiber_getenv(int32_t argc, Janet *argv) {
 | 
			
		||||
           janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_setenv(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_setenv,
 | 
			
		||||
              "(fiber/setenv fiber table)",
 | 
			
		||||
              "Sets the environment table for a fiber. Set to nil to remove the current "
 | 
			
		||||
              "environment.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    if (janet_checktype(argv[1], JANET_NIL)) {
 | 
			
		||||
@@ -470,15 +478,44 @@ static Janet cfun_fiber_setenv(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_new,
 | 
			
		||||
              "(fiber/new func &opt sigmask env)",
 | 
			
		||||
              "Create a new fiber with function body func. Can optionally "
 | 
			
		||||
              "take a set of signals `sigmask` to capture from child fibers, "
 | 
			
		||||
              "and an environment table `env`. The mask is specified as a keyword where each character "
 | 
			
		||||
              "is used to indicate a signal to block. If the ev module is enabled, and "
 | 
			
		||||
              "this fiber is used as an argument to `ev/go`, these \"blocked\" signals "
 | 
			
		||||
              "will result in messages being sent to the supervisor channel. "
 | 
			
		||||
              "The default sigmask is :y. "
 | 
			
		||||
              "For example,\n\n"
 | 
			
		||||
              "    (fiber/new myfun :e123)\n\n"
 | 
			
		||||
              "blocks error signals and user signals 1, 2 and 3. The signals are "
 | 
			
		||||
              "as follows:\n\n"
 | 
			
		||||
              "* :a - block all signals\n"
 | 
			
		||||
              "* :d - block debug signals\n"
 | 
			
		||||
              "* :e - block error signals\n"
 | 
			
		||||
              "* :t - block termination signals: error + user[0-4]\n"
 | 
			
		||||
              "* :u - block user signals\n"
 | 
			
		||||
              "* :y - block yield signals\n"
 | 
			
		||||
              "* :w - block await signals (user9)\n"
 | 
			
		||||
              "* :r - block interrupt signals (user8)\n"
 | 
			
		||||
              "* :0-9 - block a specific user signal\n\n"
 | 
			
		||||
              "The sigmask argument also can take environment flags. If any mutually "
 | 
			
		||||
              "exclusive flags are present, the last flag takes precedence.\n\n"
 | 
			
		||||
              "* :i - inherit the environment from the current fiber\n"
 | 
			
		||||
              "* :p - the environment table's prototype is the current environment table") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetFunction *func = janet_getfunction(argv, 0);
 | 
			
		||||
    JanetFiber *fiber;
 | 
			
		||||
    if (func->def->min_arity > 1) {
 | 
			
		||||
        janet_panicf("fiber function must accept 0 or 1 arguments");
 | 
			
		||||
    }
 | 
			
		||||
    fiber = janet_fiber(func, 64, func->def->min_arity, NULL);
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
    janet_assert(fiber != NULL, "bad fiber arity check");
 | 
			
		||||
    if (argc == 3 && !janet_checktype(argv[2], JANET_NIL)) {
 | 
			
		||||
        fiber->env = janet_gettable(argv, 2);
 | 
			
		||||
    }
 | 
			
		||||
    if (argc >= 2) {
 | 
			
		||||
        int32_t i;
 | 
			
		||||
        JanetByteView view = janet_getbytes(argv, 1);
 | 
			
		||||
        fiber->flags = JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
 | 
			
		||||
@@ -489,7 +526,7 @@ static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
            } else {
 | 
			
		||||
                switch (view.bytes[i]) {
 | 
			
		||||
                    default:
 | 
			
		||||
                        janet_panicf("invalid flag %c, expected a, t, d, e, u, y, i, or p", view.bytes[i]);
 | 
			
		||||
                        janet_panicf("invalid flag %c, expected a, t, d, e, u, y, w, r, i, or p", view.bytes[i]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'a':
 | 
			
		||||
                        fiber->flags |=
 | 
			
		||||
@@ -519,18 +556,24 @@ static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
                    case 'y':
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_MASK_YIELD;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'w':
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_MASK_USER9;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'r':
 | 
			
		||||
                        fiber->flags |= JANET_FIBER_MASK_USER8;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'i':
 | 
			
		||||
                        if (!janet_vm_fiber->env) {
 | 
			
		||||
                            janet_vm_fiber->env = janet_table(0);
 | 
			
		||||
                        if (!janet_vm.fiber->env) {
 | 
			
		||||
                            janet_vm.fiber->env = janet_table(0);
 | 
			
		||||
                        }
 | 
			
		||||
                        fiber->env = janet_vm_fiber->env;
 | 
			
		||||
                        fiber->env = janet_vm.fiber->env;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'p':
 | 
			
		||||
                        if (!janet_vm_fiber->env) {
 | 
			
		||||
                            janet_vm_fiber->env = janet_table(0);
 | 
			
		||||
                        if (!janet_vm.fiber->env) {
 | 
			
		||||
                            janet_vm.fiber->env = janet_table(0);
 | 
			
		||||
                        }
 | 
			
		||||
                        fiber->env = janet_table(0);
 | 
			
		||||
                        fiber->env->proto = janet_vm_fiber->env;
 | 
			
		||||
                        fiber->env->proto = janet_vm.fiber->env;
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -539,32 +582,55 @@ static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_fiber(fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_status(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_status,
 | 
			
		||||
              "(fiber/status fib)",
 | 
			
		||||
              "Get the status of a fiber. The status will be one of:\n\n"
 | 
			
		||||
              "* :dead - the fiber has finished\n"
 | 
			
		||||
              "* :error - the fiber has errored out\n"
 | 
			
		||||
              "* :debug - the fiber is suspended in debug mode\n"
 | 
			
		||||
              "* :pending - the fiber has been yielded\n"
 | 
			
		||||
              "* :user(0-7) - the fiber is suspended by a user signal\n"
 | 
			
		||||
              "* :interrupted - the fiber was interrupted\n"
 | 
			
		||||
              "* :suspended - the fiber is waiting to be resumed by the scheduler\n"
 | 
			
		||||
              "* :alive - the fiber is currently running and cannot be resumed\n"
 | 
			
		||||
              "* :new - the fiber has just been created and not yet run") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    uint32_t s = janet_fiber_status(fiber);
 | 
			
		||||
    return janet_ckeywordv(janet_status_names[s]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_current(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_current,
 | 
			
		||||
              "(fiber/current)",
 | 
			
		||||
              "Returns the currently running fiber.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    return janet_wrap_fiber(janet_vm_fiber);
 | 
			
		||||
    return janet_wrap_fiber(janet_vm.fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_root(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_root,
 | 
			
		||||
              "(fiber/root)",
 | 
			
		||||
              "Returns the current root fiber. The root fiber is the oldest ancestor "
 | 
			
		||||
              "that does not have a parent.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    return janet_wrap_fiber(janet_vm_root_fiber);
 | 
			
		||||
    return janet_wrap_fiber(janet_vm.root_fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_maxstack(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_maxstack,
 | 
			
		||||
              "(fiber/maxstack fib)",
 | 
			
		||||
              "Gets the maximum stack size in janet values allowed for a fiber. While memory for "
 | 
			
		||||
              "the fiber's stack is not allocated up front, the fiber will not allocated more "
 | 
			
		||||
              "than this amount and will throw a stack-overflow error if more memory is needed. ") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    return janet_wrap_integer(fiber->maxstack);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_setmaxstack(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_setmaxstack,
 | 
			
		||||
              "(fiber/setmaxstack fib maxstack)",
 | 
			
		||||
              "Sets the maximum stack size in janet values for a fiber. By default, the "
 | 
			
		||||
              "maximum stack size is usually 8192.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    int32_t maxs = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -575,9 +641,7 @@ static Janet cfun_fiber_setmaxstack(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_can_resume(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
int janet_fiber_can_resume(JanetFiber *fiber) {
 | 
			
		||||
    JanetFiberStatus s = janet_fiber_status(fiber);
 | 
			
		||||
    int isFinished = s == JANET_STATUS_DEAD ||
 | 
			
		||||
                     s == JANET_STATUS_ERROR ||
 | 
			
		||||
@@ -586,104 +650,39 @@ static Janet cfun_fiber_can_resume(int32_t argc, Janet *argv) {
 | 
			
		||||
                     s == JANET_STATUS_USER2 ||
 | 
			
		||||
                     s == JANET_STATUS_USER3 ||
 | 
			
		||||
                     s == JANET_STATUS_USER4;
 | 
			
		||||
    return janet_wrap_boolean(!isFinished);
 | 
			
		||||
    return !isFinished;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_fiber_last_value(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_fiber_can_resume,
 | 
			
		||||
              "(fiber/can-resume? fiber)",
 | 
			
		||||
              "Check if a fiber is finished and cannot be resumed.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFiber *fiber = janet_getfiber(argv, 0);
 | 
			
		||||
    return janet_wrap_boolean(janet_fiber_can_resume(fiber));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_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);
 | 
			
		||||
    return fiber->last_value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg fiber_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/new", cfun_fiber_new,
 | 
			
		||||
        JDOC("(fiber/new func &opt sigmask)\n\n"
 | 
			
		||||
             "Create a new fiber with function body func. Can optionally "
 | 
			
		||||
             "take a set of signals to block from the current parent fiber "
 | 
			
		||||
             "when called. The mask is specified as a keyword where each character "
 | 
			
		||||
             "is used to indicate a signal to block. If the ev module is enabled, and "
 | 
			
		||||
             "this fiber is used as an argument to `ev/go`, these \"blocked\" signals "
 | 
			
		||||
             "will result in messages being sent to the supervisor channel. "
 | 
			
		||||
             "The default sigmask is :y. "
 | 
			
		||||
             "For example,\n\n"
 | 
			
		||||
             "    (fiber/new myfun :e123)\n\n"
 | 
			
		||||
             "blocks error signals and user signals 1, 2 and 3. The signals are "
 | 
			
		||||
             "as follows:\n\n"
 | 
			
		||||
             "* :a - block all signals\n"
 | 
			
		||||
             "* :d - block debug signals\n"
 | 
			
		||||
             "* :e - block error signals\n"
 | 
			
		||||
             "* :t - block termination signals: error + user[0-4]\n"
 | 
			
		||||
             "* :u - block user signals\n"
 | 
			
		||||
             "* :y - block yield signals\n"
 | 
			
		||||
             "* :0-9 - block a specific user signal\n\n"
 | 
			
		||||
             "The sigmask argument also can take environment flags. If any mutually "
 | 
			
		||||
             "exclusive flags are present, the last flag takes precedence.\n\n"
 | 
			
		||||
             "* :i - inherit the environment from the current fiber\n"
 | 
			
		||||
             "* :p - the environment table's prototype is the current environment table")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/status", cfun_fiber_status,
 | 
			
		||||
        JDOC("(fiber/status fib)\n\n"
 | 
			
		||||
             "Get the status of a fiber. The status will be one of:\n\n"
 | 
			
		||||
             "* :dead - the fiber has finished\n"
 | 
			
		||||
             "* :error - the fiber has errored out\n"
 | 
			
		||||
             "* :debug - the fiber is suspended in debug mode\n"
 | 
			
		||||
             "* :pending - the fiber has been yielded\n"
 | 
			
		||||
             "* :user(0-9) - the fiber is suspended by a user signal\n"
 | 
			
		||||
             "* :alive - the fiber is currently running and cannot be resumed\n"
 | 
			
		||||
             "* :new - the fiber has just been created and not yet run")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/root", cfun_fiber_root,
 | 
			
		||||
        JDOC("(fiber/root)\n\n"
 | 
			
		||||
             "Returns the current root fiber. The root fiber is the oldest ancestor "
 | 
			
		||||
             "that does not have a parent.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/current", cfun_fiber_current,
 | 
			
		||||
        JDOC("(fiber/current)\n\n"
 | 
			
		||||
             "Returns the currently running fiber.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/maxstack", cfun_fiber_maxstack,
 | 
			
		||||
        JDOC("(fiber/maxstack fib)\n\n"
 | 
			
		||||
             "Gets the maximum stack size in janet values allowed for a fiber. While memory for "
 | 
			
		||||
             "the fiber's stack is not allocated up front, the fiber will not allocated more "
 | 
			
		||||
             "than this amount and will throw a stack-overflow error if more memory is needed. ")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/setmaxstack", cfun_fiber_setmaxstack,
 | 
			
		||||
        JDOC("(fiber/setmaxstack fib maxstack)\n\n"
 | 
			
		||||
             "Sets the maximum stack size in janet values for a fiber. By default, the "
 | 
			
		||||
             "maximum stack size is usually 8192.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/getenv", cfun_fiber_getenv,
 | 
			
		||||
        JDOC("(fiber/getenv fiber)\n\n"
 | 
			
		||||
             "Gets the environment for a fiber. Returns nil if no such table is "
 | 
			
		||||
             "set yet.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/setenv", cfun_fiber_setenv,
 | 
			
		||||
        JDOC("(fiber/setenv fiber table)\n\n"
 | 
			
		||||
             "Sets the environment table for a fiber. Set to nil to remove the current "
 | 
			
		||||
             "environment.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/can-resume?", cfun_fiber_can_resume,
 | 
			
		||||
        JDOC("(fiber/can-resume? fiber)\n\n"
 | 
			
		||||
             "Check if a fiber is finished and cannot be resumed.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "fiber/last-value", cfun_fiber_last_value,
 | 
			
		||||
        JDOC("(fiber/last-value\n\n"
 | 
			
		||||
             "Get the last value returned or signaled from the fiber.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_fiber(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, fiber_cfuns);
 | 
			
		||||
    JanetRegExt fiber_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("fiber/new", cfun_fiber_new),
 | 
			
		||||
        JANET_CORE_REG("fiber/status", cfun_fiber_status),
 | 
			
		||||
        JANET_CORE_REG("fiber/root", cfun_fiber_root),
 | 
			
		||||
        JANET_CORE_REG("fiber/current", cfun_fiber_current),
 | 
			
		||||
        JANET_CORE_REG("fiber/maxstack", cfun_fiber_maxstack),
 | 
			
		||||
        JANET_CORE_REG("fiber/setmaxstack", cfun_fiber_setmaxstack),
 | 
			
		||||
        JANET_CORE_REG("fiber/getenv", cfun_fiber_getenv),
 | 
			
		||||
        JANET_CORE_REG("fiber/setenv", cfun_fiber_setenv),
 | 
			
		||||
        JANET_CORE_REG("fiber/can-resume?", cfun_fiber_can_resume),
 | 
			
		||||
        JANET_CORE_REG("fiber/last-value", cfun_fiber_last_value),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, fiber_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -47,7 +47,6 @@
 | 
			
		||||
#define JANET_FIBER_MASK_USER 0x3FF0
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_STATUS_MASK 0x3F0000
 | 
			
		||||
#define JANET_FIBER_FLAG_SCHEDULED 0x800000
 | 
			
		||||
#define JANET_FIBER_RESUME_SIGNAL 0x400000
 | 
			
		||||
#define JANET_FIBER_STATUS_OFFSET 16
 | 
			
		||||
 | 
			
		||||
@@ -57,7 +56,12 @@
 | 
			
		||||
#define JANET_FIBER_DID_LONGJUMP     0x8000000
 | 
			
		||||
#define JANET_FIBER_FLAG_MASK        0xF000000
 | 
			
		||||
 | 
			
		||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_CANCELED 0x10000
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000
 | 
			
		||||
#define JANET_FIBER_FLAG_ROOT 0x40000
 | 
			
		||||
#define JANET_FIBER_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 have 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
 | 
			
		||||
							
								
								
									
										377
									
								
								src/core/gc.c
									
									
									
									
									
								
							
							
						
						
									
										377
									
								
								src/core/gc.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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,28 +31,6 @@
 | 
			
		||||
#include "vector.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct JanetScratch {
 | 
			
		||||
    JanetScratchFinalizer finalize;
 | 
			
		||||
    long long mem[]; /* for proper alignment */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* GC State */
 | 
			
		||||
JANET_THREAD_LOCAL void *janet_vm_blocks;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_gc_interval;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_next_collection;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_block_count;
 | 
			
		||||
JANET_THREAD_LOCAL int janet_vm_gc_suspend = 0;
 | 
			
		||||
 | 
			
		||||
/* Roots */
 | 
			
		||||
JANET_THREAD_LOCAL Janet *janet_vm_roots;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_root_count;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_vm_root_capacity;
 | 
			
		||||
 | 
			
		||||
/* Scratch Memory */
 | 
			
		||||
JANET_THREAD_LOCAL JanetScratch **janet_scratch_mem;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_scratch_cap;
 | 
			
		||||
JANET_THREAD_LOCAL size_t janet_scratch_len;
 | 
			
		||||
 | 
			
		||||
/* Helpers for marking the various gc types */
 | 
			
		||||
static void janet_mark_funcenv(JanetFuncEnv *env);
 | 
			
		||||
static void janet_mark_funcdef(JanetFuncDef *def);
 | 
			
		||||
@@ -72,7 +50,7 @@ static JANET_THREAD_LOCAL size_t orig_rootcount;
 | 
			
		||||
 | 
			
		||||
/* Hint to the GC that we may need to collect */
 | 
			
		||||
void janet_gcpressure(size_t s) {
 | 
			
		||||
    janet_vm_next_collection += s;
 | 
			
		||||
    janet_vm.next_collection += s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Mark a value */
 | 
			
		||||
@@ -127,6 +105,14 @@ static void janet_mark_buffer(JanetBuffer *buffer) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_abstract(void *adata) {
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Check if abstract type is a threaded abstract type. If it is, marking means
 | 
			
		||||
     * updating the threaded_abstract table. */
 | 
			
		||||
    if ((janet_abstract_head(adata)->gc.flags & JANET_MEM_TYPEBITS) == JANET_MEMORY_THREADED_ABSTRACT) {
 | 
			
		||||
        janet_table_put(&janet_vm.threaded_abstracts, janet_wrap_abstract(adata), janet_wrap_true());
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (janet_gc_reachable(janet_abstract_head(adata)))
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(janet_abstract_head(adata));
 | 
			
		||||
@@ -137,6 +123,8 @@ static void janet_mark_abstract(void *adata) {
 | 
			
		||||
 | 
			
		||||
/* Mark a bunch of items in memory */
 | 
			
		||||
static void janet_mark_many(const Janet *values, int32_t n) {
 | 
			
		||||
    if (values == NULL)
 | 
			
		||||
        return;
 | 
			
		||||
    const Janet *end = values + n;
 | 
			
		||||
    while (values < end) {
 | 
			
		||||
        janet_mark(*values);
 | 
			
		||||
@@ -144,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;
 | 
			
		||||
@@ -158,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) {
 | 
			
		||||
@@ -166,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;
 | 
			
		||||
@@ -174,10 +190,13 @@ recur: /* Manual tail recursion */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_struct(const JanetKV *st) {
 | 
			
		||||
recur:
 | 
			
		||||
    if (janet_gc_reachable(janet_struct_head(st)))
 | 
			
		||||
        return;
 | 
			
		||||
    janet_gc_mark(janet_struct_head(st));
 | 
			
		||||
    janet_mark_kvs(st, janet_struct_capacity(st));
 | 
			
		||||
    st = janet_struct_proto(st);
 | 
			
		||||
    if (st) goto recur;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_mark_tuple(const Janet *tuple) {
 | 
			
		||||
@@ -218,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) {
 | 
			
		||||
@@ -271,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 */
 | 
			
		||||
@@ -290,19 +321,34 @@ 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;
 | 
			
		||||
        case JANET_MEMORY_ABSTRACT: {
 | 
			
		||||
            JanetAbstractHead *head = (JanetAbstractHead *)mem;
 | 
			
		||||
            if (head->type->gcperthread) {
 | 
			
		||||
                janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
 | 
			
		||||
            }
 | 
			
		||||
            if (head->type->gc) {
 | 
			
		||||
                janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
 | 
			
		||||
            }
 | 
			
		||||
@@ -323,34 +369,162 @@ static void janet_deinit_block(JanetGCObject *mem) {
 | 
			
		||||
            janet_free(def->bytecode);
 | 
			
		||||
            janet_free(def->sourcemap);
 | 
			
		||||
            janet_free(def->closure_bitset);
 | 
			
		||||
            janet_free(def->symbolmap);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check that a value x has been visited in the mark phase */
 | 
			
		||||
static int janet_check_liveref(Janet x) {
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        default:
 | 
			
		||||
            return 1;
 | 
			
		||||
        case JANET_ARRAY:
 | 
			
		||||
        case JANET_TABLE:
 | 
			
		||||
        case JANET_FUNCTION:
 | 
			
		||||
        case JANET_BUFFER:
 | 
			
		||||
        case JANET_FIBER:
 | 
			
		||||
            return janet_gc_reachable(janet_unwrap_pointer(x));
 | 
			
		||||
        case JANET_STRING:
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
        case JANET_KEYWORD:
 | 
			
		||||
            return janet_gc_reachable(janet_string_head(janet_unwrap_string(x)));
 | 
			
		||||
        case JANET_ABSTRACT:
 | 
			
		||||
            return janet_gc_reachable(janet_abstract_head(janet_unwrap_abstract(x)));
 | 
			
		||||
        case JANET_TUPLE:
 | 
			
		||||
            return janet_gc_reachable(janet_tuple_head(janet_unwrap_tuple(x)));
 | 
			
		||||
        case JANET_STRUCT:
 | 
			
		||||
            return janet_gc_reachable(janet_struct_head(janet_unwrap_struct(x)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Iterate over all allocated memory, and free memory that is not
 | 
			
		||||
 * marked as reachable. Flip the gc color flag for next sweep. */
 | 
			
		||||
void janet_sweep() {
 | 
			
		||||
    JanetGCObject *previous = NULL;
 | 
			
		||||
    JanetGCObject *current = janet_vm_blocks;
 | 
			
		||||
    JanetGCObject *current = janet_vm.weak_blocks;
 | 
			
		||||
    JanetGCObject *next;
 | 
			
		||||
 | 
			
		||||
    /* Sweep weak heap to drop weak refs */
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        next = current->next;
 | 
			
		||||
        next = current->data.next;
 | 
			
		||||
        if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
 | 
			
		||||
            /* Check for dead references */
 | 
			
		||||
            enum JanetMemoryType type = janet_gc_type(current);
 | 
			
		||||
            if (type == JANET_MEMORY_ARRAY_WEAK) {
 | 
			
		||||
                JanetArray *array = (JanetArray *) current;
 | 
			
		||||
                for (uint32_t i = 0; i < (uint32_t) array->count; i++) {
 | 
			
		||||
                    if (!janet_check_liveref(array->data[i])) {
 | 
			
		||||
                        array->data[i] = janet_wrap_nil();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                JanetTable *table = (JanetTable *) current;
 | 
			
		||||
                int check_values = (type == JANET_MEMORY_TABLE_WEAKV) || (type == JANET_MEMORY_TABLE_WEAKKV);
 | 
			
		||||
                int check_keys = (type == JANET_MEMORY_TABLE_WEAKK) || (type == JANET_MEMORY_TABLE_WEAKKV);
 | 
			
		||||
                JanetKV *end = table->data + table->capacity;
 | 
			
		||||
                JanetKV *kvs = table->data;
 | 
			
		||||
                while (kvs < end) {
 | 
			
		||||
                    int drop = 0;
 | 
			
		||||
                    if (check_keys && !janet_check_liveref(kvs->key)) drop = 1;
 | 
			
		||||
                    if (check_values && !janet_check_liveref(kvs->value)) drop = 1;
 | 
			
		||||
                    if (drop) {
 | 
			
		||||
                        /* Inlined from janet_table_remove without search */
 | 
			
		||||
                        table->count--;
 | 
			
		||||
                        table->deleted++;
 | 
			
		||||
                        kvs->key = janet_wrap_nil();
 | 
			
		||||
                        kvs->value = janet_wrap_false();
 | 
			
		||||
                    }
 | 
			
		||||
                    kvs++;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Sweep weak heap to free blocks */
 | 
			
		||||
    previous = NULL;
 | 
			
		||||
    current = janet_vm.weak_blocks;
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        next = current->data.next;
 | 
			
		||||
        if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
 | 
			
		||||
            previous = current;
 | 
			
		||||
            current->flags &= ~JANET_MEM_REACHABLE;
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_vm_block_count--;
 | 
			
		||||
            janet_vm.block_count--;
 | 
			
		||||
            janet_deinit_block(current);
 | 
			
		||||
            if (NULL != previous) {
 | 
			
		||||
                previous->next = next;
 | 
			
		||||
                previous->data.next = next;
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_vm_blocks = next;
 | 
			
		||||
                janet_vm.weak_blocks = next;
 | 
			
		||||
            }
 | 
			
		||||
            janet_free(current);
 | 
			
		||||
        }
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Sweep main heap to free blocks */
 | 
			
		||||
    previous = NULL;
 | 
			
		||||
    current = janet_vm.blocks;
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        next = current->data.next;
 | 
			
		||||
        if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
 | 
			
		||||
            previous = current;
 | 
			
		||||
            current->flags &= ~JANET_MEM_REACHABLE;
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_vm.block_count--;
 | 
			
		||||
            janet_deinit_block(current);
 | 
			
		||||
            if (NULL != previous) {
 | 
			
		||||
                previous->data.next = next;
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_vm.blocks = next;
 | 
			
		||||
            }
 | 
			
		||||
            janet_free(current);
 | 
			
		||||
        }
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Sweep threaded abstract types for references to decrement */
 | 
			
		||||
    JanetKV *items = janet_vm.threaded_abstracts.data;
 | 
			
		||||
    for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
 | 
			
		||||
        if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
 | 
			
		||||
 | 
			
		||||
            /* If item was not visited during the mark phase, then this
 | 
			
		||||
             * abstract type isn't present in the heap and needs its refcount
 | 
			
		||||
             * decremented, and shouuld be removed from table. If the refcount is
 | 
			
		||||
             * then 0, the item will be collected. This ensures that only one interpreter
 | 
			
		||||
             * will clean up the threaded abstract. */
 | 
			
		||||
 | 
			
		||||
            /* If not visited... */
 | 
			
		||||
            if (!janet_truthy(items[i].value)) {
 | 
			
		||||
                void *abst = janet_unwrap_abstract(items[i].key);
 | 
			
		||||
                JanetAbstractHead *head = janet_abstract_head(abst);
 | 
			
		||||
                if (head->type->gcperthread) {
 | 
			
		||||
                    janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
 | 
			
		||||
                }
 | 
			
		||||
                if (0 == janet_abstract_decref(abst)) {
 | 
			
		||||
                    /* Run finalizer */
 | 
			
		||||
                    if (head->type->gc) {
 | 
			
		||||
                        janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
 | 
			
		||||
                    }
 | 
			
		||||
                    /* 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 */
 | 
			
		||||
            items[i].value = janet_wrap_false();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Allocate some memory that is tracked for garbage collection */
 | 
			
		||||
@@ -358,7 +532,7 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
 | 
			
		||||
    JanetGCObject *mem;
 | 
			
		||||
 | 
			
		||||
    /* Make sure everything is inited */
 | 
			
		||||
    janet_assert(NULL != janet_vm_cache, "please initialize janet before use");
 | 
			
		||||
    janet_assert(NULL != janet_vm.cache, "please initialize janet before use");
 | 
			
		||||
    mem = janet_malloc(size);
 | 
			
		||||
 | 
			
		||||
    /* Check for bad malloc */
 | 
			
		||||
@@ -370,10 +544,17 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
 | 
			
		||||
    mem->flags = type;
 | 
			
		||||
 | 
			
		||||
    /* Prepend block to heap list */
 | 
			
		||||
    janet_vm_next_collection += size;
 | 
			
		||||
    mem->next = janet_vm_blocks;
 | 
			
		||||
    janet_vm_blocks = mem;
 | 
			
		||||
    janet_vm_block_count++;
 | 
			
		||||
    janet_vm.next_collection += size;
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
@@ -387,10 +568,10 @@ static void free_one_scratch(JanetScratch *s) {
 | 
			
		||||
 | 
			
		||||
/* Free all allocated scratch memory */
 | 
			
		||||
static void janet_free_all_scratch(void) {
 | 
			
		||||
    for (size_t i = 0; i < janet_scratch_len; i++) {
 | 
			
		||||
        free_one_scratch(janet_scratch_mem[i]);
 | 
			
		||||
    for (size_t i = 0; i < janet_vm.scratch_len; i++) {
 | 
			
		||||
        free_one_scratch(janet_vm.scratch_mem[i]);
 | 
			
		||||
    }
 | 
			
		||||
    janet_scratch_len = 0;
 | 
			
		||||
    janet_vm.scratch_len = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetScratch *janet_mem2scratch(void *mem) {
 | 
			
		||||
@@ -401,29 +582,31 @@ static JanetScratch *janet_mem2scratch(void *mem) {
 | 
			
		||||
/* Run garbage collection */
 | 
			
		||||
void janet_collect(void) {
 | 
			
		||||
    uint32_t i;
 | 
			
		||||
    if (janet_vm_gc_suspend) return;
 | 
			
		||||
    if (janet_vm.gc_suspend) return;
 | 
			
		||||
    depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    /* Try and prevent many major collections back to back.
 | 
			
		||||
     * A full collection will take O(janet_vm_block_count) time.
 | 
			
		||||
    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
 | 
			
		||||
     * heuristic for automatically changing the gc interval */
 | 
			
		||||
    if (janet_vm_block_count * 8 > janet_vm_gc_interval) {
 | 
			
		||||
        janet_vm_gc_interval = janet_vm_block_count * sizeof(JanetGCObject);
 | 
			
		||||
    if (janet_vm.block_count * 8 > janet_vm.gc_interval) {
 | 
			
		||||
        janet_vm.gc_interval = janet_vm.block_count * sizeof(JanetGCObject);
 | 
			
		||||
    }
 | 
			
		||||
    orig_rootcount = janet_vm_root_count;
 | 
			
		||||
    orig_rootcount = janet_vm.root_count;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_ev_mark();
 | 
			
		||||
#endif
 | 
			
		||||
    janet_mark_fiber(janet_vm_root_fiber);
 | 
			
		||||
    janet_mark_fiber(janet_vm.root_fiber);
 | 
			
		||||
    for (i = 0; i < orig_rootcount; i++)
 | 
			
		||||
        janet_mark(janet_vm_roots[i]);
 | 
			
		||||
    while (orig_rootcount < janet_vm_root_count) {
 | 
			
		||||
        Janet x = janet_vm_roots[--janet_vm_root_count];
 | 
			
		||||
        janet_mark(janet_vm.roots[i]);
 | 
			
		||||
    while (orig_rootcount < janet_vm.root_count) {
 | 
			
		||||
        Janet x = janet_vm.roots[--janet_vm.root_count];
 | 
			
		||||
        janet_mark(x);
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm.gc_mark_phase = 0;
 | 
			
		||||
    janet_sweep();
 | 
			
		||||
    janet_vm_next_collection = 0;
 | 
			
		||||
    janet_vm.next_collection = 0;
 | 
			
		||||
    janet_free_all_scratch();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -431,17 +614,17 @@ void janet_collect(void) {
 | 
			
		||||
 * and all of its children. If gcroot is called on a value n times, unroot
 | 
			
		||||
 * must also be called n times to remove it as a gc root. */
 | 
			
		||||
void janet_gcroot(Janet root) {
 | 
			
		||||
    size_t newcount = janet_vm_root_count + 1;
 | 
			
		||||
    if (newcount > janet_vm_root_capacity) {
 | 
			
		||||
    size_t newcount = janet_vm.root_count + 1;
 | 
			
		||||
    if (newcount > janet_vm.root_capacity) {
 | 
			
		||||
        size_t newcap = 2 * newcount;
 | 
			
		||||
        janet_vm_roots = janet_realloc(janet_vm_roots, sizeof(Janet) * newcap);
 | 
			
		||||
        if (NULL == janet_vm_roots) {
 | 
			
		||||
        janet_vm.roots = janet_realloc(janet_vm.roots, sizeof(Janet) * newcap);
 | 
			
		||||
        if (NULL == janet_vm.roots) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        janet_vm_root_capacity = newcap;
 | 
			
		||||
        janet_vm.root_capacity = newcap;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_roots[janet_vm_root_count] = root;
 | 
			
		||||
    janet_vm_root_count = newcount;
 | 
			
		||||
    janet_vm.roots[janet_vm.root_count] = root;
 | 
			
		||||
    janet_vm.root_count = newcount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Identity equality for GC purposes */
 | 
			
		||||
@@ -462,11 +645,11 @@ static int janet_gc_idequals(Janet lhs, Janet rhs) {
 | 
			
		||||
/* Remove a root value from the GC. This allows the gc to potentially reclaim
 | 
			
		||||
 * a value and all its children. */
 | 
			
		||||
int janet_gcunroot(Janet root) {
 | 
			
		||||
    Janet *vtop = janet_vm_roots + janet_vm_root_count;
 | 
			
		||||
    Janet *vtop = janet_vm.roots + janet_vm.root_count;
 | 
			
		||||
    /* Search from top to bottom as access is most likely LIFO */
 | 
			
		||||
    for (Janet *v = janet_vm_roots; v < vtop; v++) {
 | 
			
		||||
    for (Janet *v = janet_vm.roots; v < vtop; v++) {
 | 
			
		||||
        if (janet_gc_idequals(root, *v)) {
 | 
			
		||||
            *v = janet_vm_roots[--janet_vm_root_count];
 | 
			
		||||
            *v = janet_vm.roots[--janet_vm.root_count];
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -475,12 +658,12 @@ int janet_gcunroot(Janet root) {
 | 
			
		||||
 | 
			
		||||
/* Remove a root value from the GC. This sets the effective reference count to 0. */
 | 
			
		||||
int janet_gcunrootall(Janet root) {
 | 
			
		||||
    Janet *vtop = janet_vm_roots + janet_vm_root_count;
 | 
			
		||||
    Janet *vtop = janet_vm.roots + janet_vm.root_count;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    /* Search from top to bottom as access is most likely LIFO */
 | 
			
		||||
    for (Janet *v = janet_vm_roots; v < vtop; v++) {
 | 
			
		||||
    for (Janet *v = janet_vm.roots; v < vtop; v++) {
 | 
			
		||||
        if (janet_gc_idequals(root, *v)) {
 | 
			
		||||
            *v = janet_vm_roots[--janet_vm_root_count];
 | 
			
		||||
            *v = janet_vm.roots[--janet_vm.root_count];
 | 
			
		||||
            vtop--;
 | 
			
		||||
            ret = 1;
 | 
			
		||||
        }
 | 
			
		||||
@@ -490,27 +673,47 @@ int janet_gcunrootall(Janet root) {
 | 
			
		||||
 | 
			
		||||
/* Free all allocated memory */
 | 
			
		||||
void janet_clear_memory(void) {
 | 
			
		||||
    JanetGCObject *current = janet_vm_blocks;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    JanetKV *items = janet_vm.threaded_abstracts.data;
 | 
			
		||||
    for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
 | 
			
		||||
        if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
 | 
			
		||||
            void *abst = janet_unwrap_abstract(items[i].key);
 | 
			
		||||
            JanetAbstractHead *head = janet_abstract_head(abst);
 | 
			
		||||
            if (head->type->gcperthread) {
 | 
			
		||||
                janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
 | 
			
		||||
            }
 | 
			
		||||
            if (0 == janet_abstract_decref(abst)) {
 | 
			
		||||
                if (head->type->gc) {
 | 
			
		||||
                    janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
 | 
			
		||||
                }
 | 
			
		||||
                janet_free(janet_abstract_head(abst));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    JanetGCObject *current = janet_vm.blocks;
 | 
			
		||||
    while (NULL != current) {
 | 
			
		||||
        janet_deinit_block(current);
 | 
			
		||||
        JanetGCObject *next = current->next;
 | 
			
		||||
        JanetGCObject *next = current->data.next;
 | 
			
		||||
        janet_free(current);
 | 
			
		||||
        current = next;
 | 
			
		||||
    }
 | 
			
		||||
    janet_vm_blocks = NULL;
 | 
			
		||||
    janet_vm.blocks = NULL;
 | 
			
		||||
    janet_free_all_scratch();
 | 
			
		||||
    janet_free(janet_scratch_mem);
 | 
			
		||||
    janet_free(janet_vm.scratch_mem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Primitives for suspending GC. */
 | 
			
		||||
int janet_gclock(void) {
 | 
			
		||||
    return janet_vm_gc_suspend++;
 | 
			
		||||
    return janet_vm.gc_suspend++;
 | 
			
		||||
}
 | 
			
		||||
void janet_gcunlock(int handle) {
 | 
			
		||||
    janet_vm_gc_suspend = handle;
 | 
			
		||||
    janet_vm.gc_suspend = handle;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Scratch memory API */
 | 
			
		||||
/* 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);
 | 
			
		||||
@@ -518,16 +721,16 @@ void *janet_smalloc(size_t size) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    s->finalize = NULL;
 | 
			
		||||
    if (janet_scratch_len == janet_scratch_cap) {
 | 
			
		||||
        size_t newcap = 2 * janet_scratch_cap + 2;
 | 
			
		||||
        JanetScratch **newmem = (JanetScratch **) janet_realloc(janet_scratch_mem, newcap * sizeof(JanetScratch));
 | 
			
		||||
    if (janet_vm.scratch_len == janet_vm.scratch_cap) {
 | 
			
		||||
        size_t newcap = 2 * janet_vm.scratch_cap + 2;
 | 
			
		||||
        JanetScratch **newmem = (JanetScratch **) janet_realloc(janet_vm.scratch_mem, newcap * sizeof(JanetScratch));
 | 
			
		||||
        if (NULL == newmem) {
 | 
			
		||||
            JANET_OUT_OF_MEMORY;
 | 
			
		||||
        }
 | 
			
		||||
        janet_scratch_cap = newcap;
 | 
			
		||||
        janet_scratch_mem = newmem;
 | 
			
		||||
        janet_vm.scratch_cap = newcap;
 | 
			
		||||
        janet_vm.scratch_mem = newmem;
 | 
			
		||||
    }
 | 
			
		||||
    janet_scratch_mem[janet_scratch_len++] = s;
 | 
			
		||||
    janet_vm.scratch_mem[janet_vm.scratch_len++] = s;
 | 
			
		||||
    return (char *)(s->mem);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -544,14 +747,14 @@ void *janet_scalloc(size_t nmemb, size_t size) {
 | 
			
		||||
void *janet_srealloc(void *mem, size_t size) {
 | 
			
		||||
    if (NULL == mem) return janet_smalloc(size);
 | 
			
		||||
    JanetScratch *s = janet_mem2scratch(mem);
 | 
			
		||||
    if (janet_scratch_len) {
 | 
			
		||||
        for (size_t i = janet_scratch_len - 1; ; i--) {
 | 
			
		||||
            if (janet_scratch_mem[i] == s) {
 | 
			
		||||
    if (janet_vm.scratch_len) {
 | 
			
		||||
        for (size_t i = janet_vm.scratch_len - 1; ; i--) {
 | 
			
		||||
            if (janet_vm.scratch_mem[i] == s) {
 | 
			
		||||
                JanetScratch *news = janet_realloc(s, size + sizeof(JanetScratch));
 | 
			
		||||
                if (NULL == news) {
 | 
			
		||||
                    JANET_OUT_OF_MEMORY;
 | 
			
		||||
                }
 | 
			
		||||
                janet_scratch_mem[i] = news;
 | 
			
		||||
                janet_vm.scratch_mem[i] = news;
 | 
			
		||||
                return (char *)(news->mem);
 | 
			
		||||
            }
 | 
			
		||||
            if (i == 0) break;
 | 
			
		||||
@@ -568,10 +771,10 @@ void janet_sfinalizer(void *mem, JanetScratchFinalizer finalizer) {
 | 
			
		||||
void janet_sfree(void *mem) {
 | 
			
		||||
    if (NULL == mem) return;
 | 
			
		||||
    JanetScratch *s = janet_mem2scratch(mem);
 | 
			
		||||
    if (janet_scratch_len) {
 | 
			
		||||
        for (size_t i = janet_scratch_len - 1; ; i--) {
 | 
			
		||||
            if (janet_scratch_mem[i] == s) {
 | 
			
		||||
                janet_scratch_mem[i] = janet_scratch_mem[--janet_scratch_len];
 | 
			
		||||
    if (janet_vm.scratch_len) {
 | 
			
		||||
        for (size_t i = janet_vm.scratch_len - 1; ; i--) {
 | 
			
		||||
            if (janet_vm.scratch_mem[i] == s) {
 | 
			
		||||
                janet_vm.scratch_mem[i] = janet_vm.scratch_mem[--janet_vm.scratch_len];
 | 
			
		||||
                free_one_scratch(s);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -55,11 +55,16 @@ enum JanetMemoryType {
 | 
			
		||||
    JANET_MEMORY_FUNCTION,
 | 
			
		||||
    JANET_MEMORY_ABSTRACT,
 | 
			
		||||
    JANET_MEMORY_FUNCENV,
 | 
			
		||||
    JANET_MEMORY_FUNCDEF
 | 
			
		||||
    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 calk janet_alloc, initialize the memory,
 | 
			
		||||
 * and then call when janet_enablegc when it is initailize and reachable by the gc (on the JANET stack) */
 | 
			
		||||
/* To allocate collectable memory, one must call janet_alloc, initialize the memory,
 | 
			
		||||
 * 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) 2021 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -193,16 +189,106 @@ Janet janet_wrap_u64(uint64_t x) {
 | 
			
		||||
    return janet_wrap_abstract(box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_s64_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_it_s64_new,
 | 
			
		||||
              "(int/s64 value)",
 | 
			
		||||
              "Create a boxed signed 64 bit integer from a string value or a number.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_s64(janet_unwrap_s64(argv[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_it_u64_new(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_it_u64_new,
 | 
			
		||||
              "(int/u64 value)",
 | 
			
		||||
              "Create a boxed unsigned 64 bit integer from a string value 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 int64.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (janet_type(argv[0]) == JANET_ABSTRACT) {
 | 
			
		||||
        void *abst = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
 | 
			
		||||
        if (janet_abstract_type(abst) == &janet_s64_type) {
 | 
			
		||||
            int64_t value = *((int64_t *)abst);
 | 
			
		||||
            if (value > JANET_INTMAX_INT64) {
 | 
			
		||||
                janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
 | 
			
		||||
            }
 | 
			
		||||
            if (value < -JANET_INTMAX_INT64) {
 | 
			
		||||
                janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
 | 
			
		||||
            }
 | 
			
		||||
            return janet_wrap_number((double)value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (janet_abstract_type(abst) == &janet_u64_type) {
 | 
			
		||||
            uint64_t value = *((uint64_t *)abst);
 | 
			
		||||
            if (value > JANET_INTMAX_INT64) {
 | 
			
		||||
                janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return janet_wrap_number((double)value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    janet_panicf("expected int/u64 or int/s64, got %q", argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_to_bytes,
 | 
			
		||||
              "(int/to-bytes value &opt endianness buffer)",
 | 
			
		||||
              "Write the bytes of an `int/s64` or `int/u64` into a buffer.\n"
 | 
			
		||||
              "The `buffer` parameter specifies an existing buffer to write to, if unset a new buffer will be created.\n"
 | 
			
		||||
              "Returns the modified buffer.\n"
 | 
			
		||||
              "The `endianness` parameter indicates the byte order:\n"
 | 
			
		||||
              "- `nil` (unset): system byte order\n"
 | 
			
		||||
              "- `:le`: little-endian, least significant byte first\n"
 | 
			
		||||
              "- `:be`: big-endian, most significant byte first\n") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    if (janet_is_int(argv[0]) == JANET_INT_NONE) {
 | 
			
		||||
        janet_panicf("int/to-bytes: expected an int/s64 or int/u64, got %q", argv[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int reverse = 0;
 | 
			
		||||
    if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) {
 | 
			
		||||
        JanetKeyword endianness_kw = janet_getkeyword(argv, 1);
 | 
			
		||||
        if (!janet_cstrcmp(endianness_kw, "le")) {
 | 
			
		||||
#if JANET_BIG_ENDIAN
 | 
			
		||||
            reverse = 1;
 | 
			
		||||
#endif
 | 
			
		||||
        } else if (!janet_cstrcmp(endianness_kw, "be")) {
 | 
			
		||||
#if JANET_LITTLE_ENDIAN
 | 
			
		||||
            reverse = 1;
 | 
			
		||||
#endif
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_panicf("int/to-bytes: expected endianness :le, :be or nil, got %v", argv[1]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JanetBuffer *buffer = NULL;
 | 
			
		||||
    if (argc > 2 && !janet_checktype(argv[2], JANET_NIL)) {
 | 
			
		||||
        if (!janet_checktype(argv[2], JANET_BUFFER)) {
 | 
			
		||||
            janet_panicf("int/to-bytes: expected buffer or nil, got %q", argv[2]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        buffer = janet_unwrap_buffer(argv[2]);
 | 
			
		||||
        janet_buffer_extra(buffer, 8);
 | 
			
		||||
    } else {
 | 
			
		||||
        buffer = janet_buffer(8);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t *bytes = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
    if (reverse) {
 | 
			
		||||
        for (int i = 0; i < 8; ++i) {
 | 
			
		||||
            buffer->data[buffer->count + 7 - i] = bytes[i];
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        memcpy(buffer->data + buffer->count, bytes, 8);
 | 
			
		||||
    }
 | 
			
		||||
    buffer->count += 8;
 | 
			
		||||
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Code to support polymorphic comparison.
 | 
			
		||||
 * int/u64 and int/s64 support a "compare" method that allows
 | 
			
		||||
@@ -217,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)) {
 | 
			
		||||
@@ -233,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)) {
 | 
			
		||||
@@ -249,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:
 | 
			
		||||
@@ -265,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);
 | 
			
		||||
@@ -284,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:
 | 
			
		||||
@@ -300,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);
 | 
			
		||||
@@ -317,25 +403,52 @@ static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * In C, signed arithmetic overflow is undefined behvior
 | 
			
		||||
 * but unsigned arithmetic overflow is twos complement
 | 
			
		||||
 *
 | 
			
		||||
 * Reference:
 | 
			
		||||
 * https://en.cppreference.com/w/cpp/language/ub
 | 
			
		||||
 * http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
 | 
			
		||||
 *
 | 
			
		||||
 * This means OPMETHOD & OPMETHODINVERT must always use
 | 
			
		||||
 * unsigned arithmetic internally, regardless of the true type.
 | 
			
		||||
 * This will not affect the end result (property of twos complement).
 | 
			
		||||
 */
 | 
			
		||||
#define OPMETHOD(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_arity(argc, 2, -1); \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[0]); \
 | 
			
		||||
    for (int32_t i = 1; i < argc; i++) \
 | 
			
		||||
        *box oper##= janet_unwrap_##type(argv[i]); \
 | 
			
		||||
        /* This avoids undefined behavior. See above for why. */ \
 | 
			
		||||
        *box = (T) ((uint64_t) (*box)) oper ((uint64_t) janet_unwrap_##type(argv[i])); \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define OPMETHODINVERT(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
static Janet cfun_it_##type##_##name##i(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_fixarity(argc, 2); \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[1]); \
 | 
			
		||||
    *box oper##= janet_unwrap_##type(argv[0]); \
 | 
			
		||||
    /* This avoids undefined behavior. See above for why. */ \
 | 
			
		||||
    *box = (T) ((uint64_t) *box) oper ((uint64_t) janet_unwrap_##type(argv[0])); \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define UNARYMETHOD(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_fixarity(argc, 1); \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = oper(janet_unwrap_##type(argv[0])); \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
#define DIVZERO(name) DIVZERO_##name
 | 
			
		||||
#define DIVZERO_div janet_panic("division by zero")
 | 
			
		||||
#define DIVZERO_rem janet_panic("division by zero")
 | 
			
		||||
#define DIVZERO_mod return janet_wrap_abstract(box)
 | 
			
		||||
 | 
			
		||||
#define DIVMETHOD(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_arity(argc, 2, -1);                       \
 | 
			
		||||
@@ -343,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); \
 | 
			
		||||
} \
 | 
			
		||||
@@ -367,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; \
 | 
			
		||||
    } \
 | 
			
		||||
@@ -375,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, >>)
 | 
			
		||||
 | 
			
		||||
@@ -428,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},
 | 
			
		||||
@@ -438,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}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -464,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}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -505,23 +665,16 @@ static int it_u64_get(void *p, Janet key, Janet *out) {
 | 
			
		||||
    return janet_getmethod(janet_unwrap_keyword(key), it_u64_methods, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg it_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "int/s64", cfun_it_s64_new,
 | 
			
		||||
        JDOC("(int/s64 value)\n\n"
 | 
			
		||||
             "Create a boxed signed 64 bit integer from a string value.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "int/u64", cfun_it_u64_new,
 | 
			
		||||
        JDOC("(int/u64 value)\n\n"
 | 
			
		||||
             "Create a boxed unsigned 64 bit integer from a string value.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_inttypes(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, it_cfuns);
 | 
			
		||||
    JanetRegExt it_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("int/s64", cfun_it_s64_new),
 | 
			
		||||
        JANET_CORE_REG("int/u64", cfun_it_u64_new),
 | 
			
		||||
        JANET_CORE_REG("int/to-number", cfun_to_number),
 | 
			
		||||
        JANET_CORE_REG("int/to-bytes", cfun_to_bytes),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, it_cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_s64_type);
 | 
			
		||||
    janet_register_abstract_type(&janet_u64_type);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										489
									
								
								src/core/io.c
									
									
									
									
									
								
							
							
						
						
									
										489
									
								
								src/core/io.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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':
 | 
			
		||||
@@ -112,49 +122,36 @@ static void *makef(FILE *f, int32_t flags) {
 | 
			
		||||
    return iof;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Open a process */
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    const uint8_t *fname = janet_getstring(argv, 0);
 | 
			
		||||
    const uint8_t *fmode = NULL;
 | 
			
		||||
    int32_t flags;
 | 
			
		||||
    if (argc == 2) {
 | 
			
		||||
        fmode = janet_getkeyword(argv, 1);
 | 
			
		||||
        flags = JANET_FILE_PIPED | checkflags(fmode);
 | 
			
		||||
        if (flags & (JANET_FILE_UPDATE | JANET_FILE_BINARY | JANET_FILE_APPEND)) {
 | 
			
		||||
            janet_panicf("invalid popen file mode :%S, expected :r or :w", fmode);
 | 
			
		||||
        }
 | 
			
		||||
        fmode = (const uint8_t *)((fmode[0] == 'r') ? "r" : "w");
 | 
			
		||||
    } else {
 | 
			
		||||
        fmode = (const uint8_t *)"r";
 | 
			
		||||
        flags = JANET_FILE_PIPED | JANET_FILE_READ;
 | 
			
		||||
    }
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#define popen _popen
 | 
			
		||||
#endif
 | 
			
		||||
    FILE *f = popen((const char *)fname, (const char *)fmode);
 | 
			
		||||
    if (!f) {
 | 
			
		||||
        if (flags & JANET_FILE_NONIL)
 | 
			
		||||
            janet_panicf("failed to popen %s: %s", fname, strerror(errno));
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    return janet_makefile(f, flags);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_temp(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_temp,
 | 
			
		||||
              "(file/temp)",
 | 
			
		||||
              "Open an anonymous temporary file that is removed on close. "
 | 
			
		||||
              "Raises an error on failure.") {
 | 
			
		||||
    janet_sandbox_assert(JANET_SANDBOX_FS_TEMP);
 | 
			
		||||
    (void)argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    // XXX use mkostemp when we can to avoid CLOEXEC race.
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_fopen(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
JANET_CORE_FN(cfun_io_fopen,
 | 
			
		||||
              "(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 "
 | 
			
		||||
              "cannot be opened, returns nil, otherwise returns the new file handle. "
 | 
			
		||||
              "Mode flags:\n\n"
 | 
			
		||||
              "* r - allow reading from the file\n\n"
 | 
			
		||||
              "* w - allow writing to the file\n\n"
 | 
			
		||||
              "* a - append to the file\n\n"
 | 
			
		||||
              "Following one of the initial flags, 0 or more of the following flags can be appended:\n\n"
 | 
			
		||||
              "* b - open the file in binary mode (rather than text mode)\n\n"
 | 
			
		||||
              "* + - append to the file instead of overwriting it\n\n"
 | 
			
		||||
              "* n - error if the file cannot be opened instead of returning nil\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;
 | 
			
		||||
@@ -163,11 +160,29 @@ static Janet cfun_io_fopen(int32_t argc, Janet *argv) {
 | 
			
		||||
        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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -184,7 +199,16 @@ static void read_chunk(JanetFile *iof, JanetBuffer *buffer, int32_t nBytesMax) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Read a certain number of bytes into memory */
 | 
			
		||||
static Janet cfun_io_fread(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fread,
 | 
			
		||||
              "(file/read f what &opt buf)",
 | 
			
		||||
              "Read a number of bytes from a file `f` into a buffer. A buffer `buf` can "
 | 
			
		||||
              "be provided as an optional third argument, otherwise a new buffer "
 | 
			
		||||
              "is created. `what` can either be an integer or a keyword. Returns the "
 | 
			
		||||
              "buffer with file contents. "
 | 
			
		||||
              "Values for `what`:\n\n"
 | 
			
		||||
              "* :all - read the whole file\n\n"
 | 
			
		||||
              "* :line - read up to and including the next newline character\n\n"
 | 
			
		||||
              "* n (integer) - read up to n bytes from the file") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED) janet_panic("file is closed");
 | 
			
		||||
@@ -224,7 +248,10 @@ static Janet cfun_io_fread(int32_t argc, Janet *argv) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write bytes to a file */
 | 
			
		||||
static Janet cfun_io_fwrite(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fwrite,
 | 
			
		||||
              "(file/write f bytes)",
 | 
			
		||||
              "Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
 | 
			
		||||
              "file.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
@@ -246,21 +273,27 @@ static Janet cfun_io_fwrite(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Flush the bytes in the file */
 | 
			
		||||
static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
static void io_assert_writeable(JanetFile *iof) {
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
        janet_panic("file is closed");
 | 
			
		||||
    if (!(iof->flags & (JANET_FILE_WRITE | JANET_FILE_APPEND | JANET_FILE_UPDATE)))
 | 
			
		||||
        janet_panic("file is not writeable");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Flush the bytes in the file */
 | 
			
		||||
JANET_CORE_FN(cfun_io_fflush,
 | 
			
		||||
              "(file/flush f)",
 | 
			
		||||
              "Flush any buffered bytes to the file system. In most files, writes are "
 | 
			
		||||
              "buffered for efficiency reasons. Returns the file handle.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    io_assert_writeable(iof);
 | 
			
		||||
    if (fflush(iof->file))
 | 
			
		||||
        janet_panic("could not flush file");
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#define pclose _pclose
 | 
			
		||||
#define WEXITSTATUS(x) x
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -268,15 +301,9 @@ static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
 | 
			
		||||
int janet_file_close(JanetFile *file) {
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
    if (!(file->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
        if (file->flags & JANET_FILE_PIPED) {
 | 
			
		||||
            ret = pclose(file->file);
 | 
			
		||||
        } else
 | 
			
		||||
#endif
 | 
			
		||||
        {
 | 
			
		||||
            ret = fclose(file->file);
 | 
			
		||||
        }
 | 
			
		||||
        ret = fclose(file->file);
 | 
			
		||||
        file->flags |= JANET_FILE_CLOSED;
 | 
			
		||||
        file->file = NULL; /* NULL dereference is easier to debug then other problems */
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
@@ -291,39 +318,40 @@ static int cfun_io_gc(void *p, size_t len) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Close a file */
 | 
			
		||||
static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fclose,
 | 
			
		||||
              "(file/close f)",
 | 
			
		||||
              "Close a file and release all related resources. When you are "
 | 
			
		||||
              "done reading a file, close it to prevent a resource leak and let "
 | 
			
		||||
              "other processes read the file.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
    if (iof->flags & (JANET_FILE_NOT_CLOSEABLE))
 | 
			
		||||
        janet_panic("file not closable");
 | 
			
		||||
    if (iof->flags & JANET_FILE_PIPED) {
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
        int status = pclose(iof->file);
 | 
			
		||||
        iof->flags |= JANET_FILE_CLOSED;
 | 
			
		||||
        if (status == -1) janet_panic("could not close file");
 | 
			
		||||
        return janet_wrap_integer(WEXITSTATUS(status));
 | 
			
		||||
#else
 | 
			
		||||
        return janet_wrap_nil();
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        if (fclose(iof->file)) {
 | 
			
		||||
            iof->flags |= JANET_FILE_NOT_CLOSEABLE;
 | 
			
		||||
            janet_panic("could not close file");
 | 
			
		||||
        }
 | 
			
		||||
        iof->flags |= JANET_FILE_CLOSED;
 | 
			
		||||
    if (fclose(iof->file)) {
 | 
			
		||||
        iof->flags |= JANET_FILE_NOT_CLOSEABLE;
 | 
			
		||||
        janet_panic("could not close file");
 | 
			
		||||
    }
 | 
			
		||||
    iof->flags |= JANET_FILE_CLOSED;
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Seek a file */
 | 
			
		||||
static Janet cfun_io_fseek(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_fseek,
 | 
			
		||||
              "(file/seek f &opt whence n)",
 | 
			
		||||
              "Jump to a relative location in the file `f`. `whence` must be one of:\n\n"
 | 
			
		||||
              "* :cur - jump relative to the current file location\n\n"
 | 
			
		||||
              "* :set - jump relative to the beginning of the file\n\n"
 | 
			
		||||
              "* :end - jump relative to the end of the file\n\n"
 | 
			
		||||
              "By default, `whence` is :cur. Optionally a value `n` may be passed "
 | 
			
		||||
              "for the relative number of bytes to seek in the file. `n` may be a real "
 | 
			
		||||
              "number to handle large files of more than 4GB. Returns the file handle.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
 | 
			
		||||
    if (iof->flags & JANET_FILE_CLOSED)
 | 
			
		||||
        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);
 | 
			
		||||
@@ -337,18 +365,31 @@ static Janet cfun_io_fseek(int32_t argc, Janet *argv) {
 | 
			
		||||
            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}
 | 
			
		||||
};
 | 
			
		||||
@@ -434,6 +475,19 @@ static Janet cfun_io_print_impl_x(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
                janet_buffer_push_u8(buf, '\n');
 | 
			
		||||
            return janet_wrap_nil();
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FUNCTION: {
 | 
			
		||||
            /* Special case function */
 | 
			
		||||
            JanetFunction *fun = janet_unwrap_function(x);
 | 
			
		||||
            JanetBuffer *buf = janet_buffer(0);
 | 
			
		||||
            for (int32_t i = offset; i < argc; ++i) {
 | 
			
		||||
                janet_to_string_b(buf, argv[i]);
 | 
			
		||||
            }
 | 
			
		||||
            if (newline)
 | 
			
		||||
                janet_buffer_push_u8(buf, '\n');
 | 
			
		||||
            Janet args[1] = { janet_wrap_buffer(buf) };
 | 
			
		||||
            janet_call(fun, 1, args);
 | 
			
		||||
            return janet_wrap_nil();
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_NIL:
 | 
			
		||||
            f = dflt_file;
 | 
			
		||||
            if (f == NULL) janet_panic("cannot print to nil");
 | 
			
		||||
@@ -443,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -473,35 +528,53 @@ 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);
 | 
			
		||||
    return cfun_io_print_impl_x(argc, argv, newline, dflt_file, 0, x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_print(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_print,
 | 
			
		||||
              "(print & xs)",
 | 
			
		||||
              "Print values to the console (standard out). Value are converted "
 | 
			
		||||
              "to strings if they are not already. After printing all values, a "
 | 
			
		||||
              "newline character is printed. Use the value of `(dyn :out stdout)` to determine "
 | 
			
		||||
              "what to push characters to. Expects `(dyn :out stdout)` to be either a core/file or "
 | 
			
		||||
              "a buffer. Returns nil.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 1, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_prin(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_prin,
 | 
			
		||||
              "(prin & xs)",
 | 
			
		||||
              "Same as `print`, but does not add trailing newline.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 0, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eprint(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprint,
 | 
			
		||||
              "(eprint & xs)",
 | 
			
		||||
              "Same as `print`, but uses `(dyn :err stderr)` instead of `(dyn :out stdout)`.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 1, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eprin(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprin,
 | 
			
		||||
              "(eprin & xs)",
 | 
			
		||||
              "Same as `prin`, but uses `(dyn :err stderr)` instead of `(dyn :out stdout)`.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 0, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_xprint(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_xprint,
 | 
			
		||||
              "(xprint to & xs)",
 | 
			
		||||
              "Print to a file or other value explicitly (no dynamic bindings) with a trailing "
 | 
			
		||||
              "newline character. The value to print "
 | 
			
		||||
              "to is the first argument, and is otherwise the same as `print`. Returns nil.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    return cfun_io_print_impl_x(argc, argv, 1, NULL, 1, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_xprin(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_xprin,
 | 
			
		||||
              "(xprin to & xs)",
 | 
			
		||||
              "Print to a file or other value explicitly (no dynamic bindings). The value to print "
 | 
			
		||||
              "to is the first argument, and is otherwise the same as `prin`. Returns nil.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    return cfun_io_print_impl_x(argc, argv, 0, NULL, 1, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
@@ -520,6 +593,16 @@ static Janet cfun_io_printf_impl_x(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
            if (newline) janet_buffer_push_u8(buf, '\n');
 | 
			
		||||
            return janet_wrap_nil();
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FUNCTION: {
 | 
			
		||||
            /* Special case function */
 | 
			
		||||
            JanetFunction *fun = janet_unwrap_function(x);
 | 
			
		||||
            JanetBuffer *buf = janet_buffer(0);
 | 
			
		||||
            janet_buffer_format(buf, fmt, offset, argc, argv);
 | 
			
		||||
            if (newline) janet_buffer_push_u8(buf, '\n');
 | 
			
		||||
            Janet args[1] = { janet_wrap_buffer(buf) };
 | 
			
		||||
            janet_call(fun, 1, args);
 | 
			
		||||
            return janet_wrap_nil();
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_NIL:
 | 
			
		||||
            f = dflt_file;
 | 
			
		||||
            if (f == NULL) janet_panic("cannot print to nil");
 | 
			
		||||
@@ -529,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -557,28 +644,40 @@ static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_printf(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_printf,
 | 
			
		||||
              "(printf fmt & xs)",
 | 
			
		||||
              "Prints output formatted as if with `(string/format fmt ;xs)` to `(dyn :out stdout)` with a trailing newline.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 1, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_prinf(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_prinf,
 | 
			
		||||
              "(prinf fmt & xs)",
 | 
			
		||||
              "Like `printf` but with no trailing newline.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 0, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eprintf(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprintf,
 | 
			
		||||
              "(eprintf fmt & xs)",
 | 
			
		||||
              "Prints output formatted as if with `(string/format fmt ;xs)` to `(dyn :err stderr)` with a trailing newline.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 1, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eprinf(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprinf,
 | 
			
		||||
              "(eprinf fmt & xs)",
 | 
			
		||||
              "Like `eprintf` but with no trailing newline.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 0, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_xprintf(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_xprintf,
 | 
			
		||||
              "(xprintf to fmt & xs)",
 | 
			
		||||
              "Like `printf` but prints to an explicit file or value `to`. Returns nil.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    return cfun_io_printf_impl_x(argc, argv, 1, NULL, 1, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_xprinf(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_xprinf,
 | 
			
		||||
              "(xprinf to fmt & xs)",
 | 
			
		||||
              "Like `prinf` but prints to an explicit file or value `to`. Returns nil.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    return cfun_io_printf_impl_x(argc, argv, 0, NULL, 1, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
@@ -601,14 +700,18 @@ static void janet_flusher(const char *name, FILE *dflt_file) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_flush(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_flush,
 | 
			
		||||
              "(flush)",
 | 
			
		||||
              "Flush `(dyn :out stdout)` if it is a file, otherwise do nothing.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_flusher("out", stdout);
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_io_eflush(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_io_eflush,
 | 
			
		||||
              "(eflush)",
 | 
			
		||||
              "Flush `(dyn :err stderr)` if it is a file, otherwise do nothing.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_flusher("err", stderr);
 | 
			
		||||
@@ -637,12 +740,23 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
 | 
			
		||||
                if (janet_abstract_type(abstract) != &janet_file_type)
 | 
			
		||||
                    break;
 | 
			
		||||
                JanetFile *iofile = abstract;
 | 
			
		||||
                io_assert_writeable(iofile);
 | 
			
		||||
                f = iofile->file;
 | 
			
		||||
            }
 | 
			
		||||
            fwrite(buffer.data, buffer.count, 1, f);
 | 
			
		||||
            janet_buffer_deinit(&buffer);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FUNCTION: {
 | 
			
		||||
            JanetFunction *fun = janet_unwrap_function(x);
 | 
			
		||||
            int32_t len = 0;
 | 
			
		||||
            while (format[len]) len++;
 | 
			
		||||
            JanetBuffer *buf = janet_buffer(len);
 | 
			
		||||
            janet_formatbv(buf, format, args);
 | 
			
		||||
            Janet args[1] = { janet_wrap_buffer(buf) };
 | 
			
		||||
            janet_call(fun, 1, args);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_BUFFER:
 | 
			
		||||
            janet_formatbv(janet_unwrap_buffer(x), format, args);
 | 
			
		||||
            break;
 | 
			
		||||
@@ -651,179 +765,23 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg io_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "print", cfun_io_print,
 | 
			
		||||
        JDOC("(print & xs)\n\n"
 | 
			
		||||
             "Print values to the console (standard out). Value are converted "
 | 
			
		||||
             "to strings if they are not already. After printing all values, a "
 | 
			
		||||
             "newline character is printed. Use the value of (dyn :out stdout) to determine "
 | 
			
		||||
             "what to push characters to. Expects (dyn :out stdout) to be either a core/file or "
 | 
			
		||||
             "a buffer. Returns nil.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "prin", cfun_io_prin,
 | 
			
		||||
        JDOC("(prin & xs)\n\n"
 | 
			
		||||
             "Same as print, but does not add trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "printf", cfun_io_printf,
 | 
			
		||||
        JDOC("(printf fmt & xs)\n\n"
 | 
			
		||||
             "Prints output formatted as if with (string/format fmt ;xs) to (dyn :out stdout) with a trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "prinf", cfun_io_prinf,
 | 
			
		||||
        JDOC("(prinf fmt & xs)\n\n"
 | 
			
		||||
             "Like printf but with no trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eprin", cfun_io_eprin,
 | 
			
		||||
        JDOC("(eprin & xs)\n\n"
 | 
			
		||||
             "Same as prin, but uses (dyn :err stderr) instead of (dyn :out stdout).")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eprint", cfun_io_eprint,
 | 
			
		||||
        JDOC("(eprint & xs)\n\n"
 | 
			
		||||
             "Same as print, but uses (dyn :err stderr) instead of (dyn :out stdout).")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eprintf", cfun_io_eprintf,
 | 
			
		||||
        JDOC("(eprintf fmt & xs)\n\n"
 | 
			
		||||
             "Prints output formatted as if with (string/format fmt ;xs) to (dyn :err stderr) with a trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eprinf", cfun_io_eprinf,
 | 
			
		||||
        JDOC("(eprinf fmt & xs)\n\n"
 | 
			
		||||
             "Like eprintf but with no trailing newline.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "xprint", cfun_io_xprint,
 | 
			
		||||
        JDOC("(xprint to & xs)\n\n"
 | 
			
		||||
             "Print to a file or other value explicitly (no dynamic bindings) with a trailing "
 | 
			
		||||
             "newline character. The value to print "
 | 
			
		||||
             "to is the first argument, and is otherwise the same as print. Returns nil.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "xprin", cfun_io_xprin,
 | 
			
		||||
        JDOC("(xprin to & xs)\n\n"
 | 
			
		||||
             "Print to a file or other value explicitly (no dynamic bindings). The value to print "
 | 
			
		||||
             "to is the first argument, and is otherwise the same as prin. Returns nil.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "xprintf", cfun_io_xprintf,
 | 
			
		||||
        JDOC("(xprint to fmt & xs)\n\n"
 | 
			
		||||
             "Like printf but prints to an explicit file or value to. Returns nil.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "xprinf", cfun_io_xprinf,
 | 
			
		||||
        JDOC("(xprin to fmt & xs)\n\n"
 | 
			
		||||
             "Like prinf but prints to an explicit file or value to. Returns nil.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "flush", cfun_io_flush,
 | 
			
		||||
        JDOC("(flush)\n\n"
 | 
			
		||||
             "Flush (dyn :out stdout) if it is a file, otherwise do nothing.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "eflush", cfun_io_eflush,
 | 
			
		||||
        JDOC("(eflush)\n\n"
 | 
			
		||||
             "Flush (dyn :err stderr) if it is a file, otherwise do nothing.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/temp", cfun_io_temp,
 | 
			
		||||
        JDOC("(file/temp)\n\n"
 | 
			
		||||
             "Open an anonymous temporary file that is removed on close. "
 | 
			
		||||
             "Raises an error on failure.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/open", cfun_io_fopen,
 | 
			
		||||
        JDOC("(file/open path &opt mode)\n\n"
 | 
			
		||||
             "Open a file. `path` is an absolute or relative path, and "
 | 
			
		||||
             "`mode` is a set of flags indicating the mode to open the file in. "
 | 
			
		||||
             "`mode` is a keyword where each character represents a flag. If the file "
 | 
			
		||||
             "cannot be opened, returns nil, otherwise returns the new file handle. "
 | 
			
		||||
             "Mode flags:\n\n"
 | 
			
		||||
             "* r - allow reading from the file\n\n"
 | 
			
		||||
             "* w - allow writing to the file\n\n"
 | 
			
		||||
             "* a - append to the file\n\n"
 | 
			
		||||
             "Following one of the initial flags, 0 or more of the following flags can be appended:\n\n"
 | 
			
		||||
             "* b - open the file in binary mode (rather than text mode)\n\n"
 | 
			
		||||
             "* + - append to the file instead of overwriting it\n\n"
 | 
			
		||||
             "* n - error if the file cannot be opened instead of returning nil")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/close", cfun_io_fclose,
 | 
			
		||||
        JDOC("(file/close f)\n\n"
 | 
			
		||||
             "Close a file and release all related resources. When you are "
 | 
			
		||||
             "done reading a file, close it to prevent a resource leak and let "
 | 
			
		||||
             "other processes read the file. If the file is the result of a file/popen "
 | 
			
		||||
             "call, close waits for and returns the process exit status.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/read", cfun_io_fread,
 | 
			
		||||
        JDOC("(file/read f what &opt buf)\n\n"
 | 
			
		||||
             "Read a number of bytes from a file `f` into a buffer. A buffer `buf` can "
 | 
			
		||||
             "be provided as an optional third argument, otherwise a new buffer "
 | 
			
		||||
             "is created. `what` can either be an integer or a keyword. Returns the "
 | 
			
		||||
             "buffer with file contents. "
 | 
			
		||||
             "Values for `what`:\n\n"
 | 
			
		||||
             "* :all - read the whole file\n\n"
 | 
			
		||||
             "* :line - read up to and including the next newline character\n\n"
 | 
			
		||||
             "* n (integer) - read up to n bytes from the file")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/write", cfun_io_fwrite,
 | 
			
		||||
        JDOC("(file/write f bytes)\n\n"
 | 
			
		||||
             "Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
 | 
			
		||||
             "file.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/flush", cfun_io_fflush,
 | 
			
		||||
        JDOC("(file/flush f)\n\n"
 | 
			
		||||
             "Flush any buffered bytes to the file system. In most files, writes are "
 | 
			
		||||
             "buffered for efficiency reasons. Returns the file handle.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "file/seek", cfun_io_fseek,
 | 
			
		||||
        JDOC("(file/seek f &opt whence n)\n\n"
 | 
			
		||||
             "Jump to a relative location in the file `f`. `whence` must be one of:\n\n"
 | 
			
		||||
             "* :cur - jump relative to the current file location\n\n"
 | 
			
		||||
             "* :set - jump relative to the beginning of the file\n\n"
 | 
			
		||||
             "* :end - jump relative to the end of the file\n\n"
 | 
			
		||||
             "By default, `whence` is :cur. Optionally a value `n` may be passed "
 | 
			
		||||
             "for the relative number of bytes to seek in the file. `n` may be a real "
 | 
			
		||||
             "number to handle large files of more than 4GB. Returns the file handle.")
 | 
			
		||||
    },
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
    {
 | 
			
		||||
        "file/popen", cfun_io_popen,
 | 
			
		||||
        JDOC("(file/popen command &opt mode) (DEPRECATED for os/spawn)\n\n"
 | 
			
		||||
             "Open a file that is backed by a process. The file must be opened in either "
 | 
			
		||||
             "the :r (read) or the :w (write) mode. In :r mode, the stdout of the "
 | 
			
		||||
             "process can be read from the file. In :w mode, the stdin of the process "
 | 
			
		||||
             "can be written to. Returns the new file.")
 | 
			
		||||
    },
 | 
			
		||||
#endif
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* C API */
 | 
			
		||||
 | 
			
		||||
JanetFile *janet_getjfile(const Janet *argv, int32_t n) {
 | 
			
		||||
    return janet_getabstract(argv, n, &janet_file_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) {
 | 
			
		||||
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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -831,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;
 | 
			
		||||
@@ -839,20 +797,45 @@ FILE *janet_unwrapfile(Janet j, int *flags) {
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_io(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, io_cfuns);
 | 
			
		||||
    JanetRegExt io_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("print", cfun_io_print),
 | 
			
		||||
        JANET_CORE_REG("prin", cfun_io_prin),
 | 
			
		||||
        JANET_CORE_REG("printf", cfun_io_printf),
 | 
			
		||||
        JANET_CORE_REG("prinf", cfun_io_prinf),
 | 
			
		||||
        JANET_CORE_REG("eprin", cfun_io_eprin),
 | 
			
		||||
        JANET_CORE_REG("eprint", cfun_io_eprint),
 | 
			
		||||
        JANET_CORE_REG("eprintf", cfun_io_eprintf),
 | 
			
		||||
        JANET_CORE_REG("eprinf", cfun_io_eprinf),
 | 
			
		||||
        JANET_CORE_REG("xprint", cfun_io_xprint),
 | 
			
		||||
        JANET_CORE_REG("xprin", cfun_io_xprin),
 | 
			
		||||
        JANET_CORE_REG("xprintf", cfun_io_xprintf),
 | 
			
		||||
        JANET_CORE_REG("xprinf", cfun_io_xprinf),
 | 
			
		||||
        JANET_CORE_REG("flush", cfun_io_flush),
 | 
			
		||||
        JANET_CORE_REG("eflush", cfun_io_eflush),
 | 
			
		||||
        JANET_CORE_REG("file/temp", cfun_io_temp),
 | 
			
		||||
        JANET_CORE_REG("file/open", cfun_io_fopen),
 | 
			
		||||
        JANET_CORE_REG("file/close", cfun_io_fclose),
 | 
			
		||||
        JANET_CORE_REG("file/read", cfun_io_fread),
 | 
			
		||||
        JANET_CORE_REG("file/write", cfun_io_fwrite),
 | 
			
		||||
        JANET_CORE_REG("file/flush", cfun_io_fflush),
 | 
			
		||||
        JANET_CORE_REG("file/seek", cfun_io_fseek),
 | 
			
		||||
        JANET_CORE_REG("file/tell", cfun_io_ftell),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, io_cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_file_type);
 | 
			
		||||
    int default_flags = JANET_FILE_NOT_CLOSEABLE | JANET_FILE_SERIALIZABLE;
 | 
			
		||||
    /* stdout */
 | 
			
		||||
    janet_core_def(env, "stdout",
 | 
			
		||||
    JANET_CORE_DEF(env, "stdout",
 | 
			
		||||
                   janet_makefile(stdout, JANET_FILE_APPEND | default_flags),
 | 
			
		||||
                   JDOC("The standard output file."));
 | 
			
		||||
                   "The standard output file.");
 | 
			
		||||
    /* stderr */
 | 
			
		||||
    janet_core_def(env, "stderr",
 | 
			
		||||
    JANET_CORE_DEF(env, "stderr",
 | 
			
		||||
                   janet_makefile(stderr, JANET_FILE_APPEND | default_flags),
 | 
			
		||||
                   JDOC("The standard error file."));
 | 
			
		||||
                   "The standard error file.");
 | 
			
		||||
    /* stdin */
 | 
			
		||||
    janet_core_def(env, "stdin",
 | 
			
		||||
    JANET_CORE_DEF(env, "stdin",
 | 
			
		||||
                   janet_makefile(stdin, JANET_FILE_READ | default_flags),
 | 
			
		||||
                   JDOC("The standard input file."));
 | 
			
		||||
                   "The standard input file.");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										431
									
								
								src/core/marsh.c
									
									
									
									
									
								
							
							
						
						
									
										431
									
								
								src/core/marsh.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -37,6 +37,7 @@ typedef struct {
 | 
			
		||||
    JanetFuncEnv **seen_envs;
 | 
			
		||||
    JanetFuncDef **seen_defs;
 | 
			
		||||
    int32_t nextid;
 | 
			
		||||
    int maybe_cycles;
 | 
			
		||||
} MarshalState;
 | 
			
		||||
 | 
			
		||||
/* Lead bytes in marshaling protocol */
 | 
			
		||||
@@ -63,7 +64,19 @@ enum {
 | 
			
		||||
    LB_FUNCENV_REF, /* 219 */
 | 
			
		||||
    LB_FUNCDEF_REF, /* 220 */
 | 
			
		||||
    LB_UNSAFE_CFUNCTION, /* 221 */
 | 
			
		||||
    LB_UNSAFE_POINTER /* 222 */
 | 
			
		||||
    LB_UNSAFE_POINTER, /* 222 */
 | 
			
		||||
    LB_STRUCT_PROTO, /* 223 */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    LB_THREADED_ABSTRACT, /* 224 */
 | 
			
		||||
    LB_POINTER_BUFFER, /* 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 */
 | 
			
		||||
@@ -148,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) {
 | 
			
		||||
@@ -175,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;
 | 
			
		||||
@@ -187,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;
 | 
			
		||||
@@ -236,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);
 | 
			
		||||
@@ -247,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)
 | 
			
		||||
@@ -254,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);
 | 
			
		||||
@@ -265,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) {
 | 
			
		||||
@@ -307,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);
 | 
			
		||||
@@ -325,6 +368,7 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
 | 
			
		||||
    }
 | 
			
		||||
    if (fiber->child)
 | 
			
		||||
        marshal_one(st, janet_wrap_fiber(fiber->child), flags + 1);
 | 
			
		||||
    marshal_one(st, fiber->last_value, flags + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_marshal_size(JanetMarshalContext *ctx, size_t value) {
 | 
			
		||||
@@ -341,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);
 | 
			
		||||
@@ -357,26 +410,52 @@ void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) {
 | 
			
		||||
    marshal_one(st, x, ctx->flags + 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_MARSHAL_DEBUG
 | 
			
		||||
#define MARK_SEEN() \
 | 
			
		||||
    do { if (st->maybe_cycles) { \
 | 
			
		||||
        Janet _check = janet_table_get(&st->seen, x); \
 | 
			
		||||
        if (!janet_checktype(_check, JANET_NIL)) janet_eprintf("double MARK_SEEN on %v\n", x); \
 | 
			
		||||
        janet_eprintf("made reference %d (%t) to %v\n", st->nextid, x, x); \
 | 
			
		||||
        janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); \
 | 
			
		||||
    } } while (0)
 | 
			
		||||
#else
 | 
			
		||||
#define MARK_SEEN() \
 | 
			
		||||
    do { if (st->maybe_cycles) { \
 | 
			
		||||
        janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); \
 | 
			
		||||
    } } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void janet_marshal_abstract(JanetMarshalContext *ctx, void *abstract) {
 | 
			
		||||
    MarshalState *st = (MarshalState *)(ctx->m_state);
 | 
			
		||||
    janet_table_put(&st->seen,
 | 
			
		||||
                    janet_wrap_abstract(abstract),
 | 
			
		||||
                    janet_wrap_integer(st->nextid++));
 | 
			
		||||
    Janet x = janet_wrap_abstract(abstract);
 | 
			
		||||
    MARK_SEEN();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MARK_SEEN() \
 | 
			
		||||
    janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++))
 | 
			
		||||
 | 
			
		||||
static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
    void *abstract = janet_unwrap_abstract(x);
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Threaded abstract types get passed through as pointers in the unsafe mode */
 | 
			
		||||
    if ((flags & JANET_MARSHAL_UNSAFE) &&
 | 
			
		||||
            (JANET_MEMORY_THREADED_ABSTRACT == (janet_abstract_head(abstract)->gc.flags & JANET_MEM_TYPEBITS))) {
 | 
			
		||||
 | 
			
		||||
        /* Increment refcount before sending message. This prevents a "death in transit" problem
 | 
			
		||||
         * where a message is garbage collected while in transit between two threads - i.e., the sending threads
 | 
			
		||||
         * loses the reference and runs a garbage collection before the receiving thread gets the message. */
 | 
			
		||||
        janet_abstract_incref(abstract);
 | 
			
		||||
        pushbyte(st, LB_THREADED_ABSTRACT);
 | 
			
		||||
        pushbytes(st, (uint8_t *) &abstract, sizeof(abstract));
 | 
			
		||||
        MARK_SEEN();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    const JanetAbstractType *at = janet_abstract_type(abstract);
 | 
			
		||||
    if (at->marshal) {
 | 
			
		||||
        pushbyte(st, LB_ABSTRACT);
 | 
			
		||||
        marshal_one(st, janet_csymbolv(at->name), flags + 1);
 | 
			
		||||
        JanetMarshalContext context = {st, NULL, flags, NULL, at};
 | 
			
		||||
        JanetMarshalContext context = {st, NULL, flags + 1, NULL, at};
 | 
			
		||||
        at->marshal(abstract, &context);
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_panicf("try to marshal unregistered abstract type, cannot marshal %p", x);
 | 
			
		||||
        janet_panicf("cannot marshal %p", x);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -408,11 +487,14 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
 | 
			
		||||
    /* Check reference and registry value */
 | 
			
		||||
    {
 | 
			
		||||
        Janet check = janet_table_get(&st->seen, x);
 | 
			
		||||
        if (janet_checkint(check)) {
 | 
			
		||||
            pushbyte(st, LB_REFERENCE);
 | 
			
		||||
            pushint(st, janet_unwrap_integer(check));
 | 
			
		||||
            return;
 | 
			
		||||
        Janet check;
 | 
			
		||||
        if (st->maybe_cycles) {
 | 
			
		||||
            check = janet_table_get(&st->seen, x);
 | 
			
		||||
            if (janet_checkint(check)) {
 | 
			
		||||
                pushbyte(st, LB_REFERENCE);
 | 
			
		||||
                pushint(st, janet_unwrap_integer(check));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (st->rreg) {
 | 
			
		||||
            check = janet_table_get(st->rreg, x);
 | 
			
		||||
@@ -475,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);
 | 
			
		||||
@@ -484,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);
 | 
			
		||||
@@ -507,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);
 | 
			
		||||
@@ -523,8 +625,10 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
            int32_t count;
 | 
			
		||||
            const JanetKV *struct_ = janet_unwrap_struct(x);
 | 
			
		||||
            count = janet_struct_length(struct_);
 | 
			
		||||
            pushbyte(st, LB_STRUCT);
 | 
			
		||||
            pushbyte(st, janet_struct_proto(struct_) ? LB_STRUCT_PROTO : LB_STRUCT);
 | 
			
		||||
            pushint(st, count);
 | 
			
		||||
            if (janet_struct_proto(struct_))
 | 
			
		||||
                marshal_one(st, janet_wrap_struct(janet_struct_proto(struct_)), flags + 1);
 | 
			
		||||
            for (int32_t i = 0; i < janet_struct_capacity(struct_); i++) {
 | 
			
		||||
                if (janet_checktype(struct_[i].key, JANET_NIL))
 | 
			
		||||
                    continue;
 | 
			
		||||
@@ -542,9 +646,9 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
        case JANET_FUNCTION: {
 | 
			
		||||
            pushbyte(st, LB_FUNCTION);
 | 
			
		||||
            JanetFunction *func = janet_unwrap_function(x);
 | 
			
		||||
            pushint(st, func->def->environments_length);
 | 
			
		||||
            /* Mark seen before reading def */
 | 
			
		||||
            MARK_SEEN();
 | 
			
		||||
            pushint(st, func->def->environments_length);
 | 
			
		||||
            marshal_one_def(st, func->def, flags);
 | 
			
		||||
            for (int32_t i = 0; i < func->def->environments_length; i++)
 | 
			
		||||
                marshal_one_env(st, func->envs[i], flags + 1);
 | 
			
		||||
@@ -568,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:
 | 
			
		||||
@@ -591,6 +694,7 @@ void janet_marshal(
 | 
			
		||||
    st.seen_defs = NULL;
 | 
			
		||||
    st.seen_envs = NULL;
 | 
			
		||||
    st.rreg = rreg;
 | 
			
		||||
    st.maybe_cycles = !(flags & JANET_MARSHAL_NO_CYCLES);
 | 
			
		||||
    janet_table_init(&st.seen, 0);
 | 
			
		||||
    marshal_one(&st, x, flags);
 | 
			
		||||
    janet_table_deinit(&st.seen);
 | 
			
		||||
@@ -675,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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -729,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;
 | 
			
		||||
@@ -795,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 */
 | 
			
		||||
@@ -802,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);
 | 
			
		||||
@@ -817,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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -845,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) {
 | 
			
		||||
@@ -935,10 +1078,13 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
    fiber->data = NULL;
 | 
			
		||||
    fiber->child = NULL;
 | 
			
		||||
    fiber->env = NULL;
 | 
			
		||||
    fiber->last_value = janet_wrap_nil();
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    fiber->waiting = NULL;
 | 
			
		||||
    fiber->sched_id = 0;
 | 
			
		||||
    fiber->supervisor_channel = NULL;
 | 
			
		||||
    fiber->ev_state = NULL;
 | 
			
		||||
    fiber->ev_callback = NULL;
 | 
			
		||||
    fiber->ev_stream = NULL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /* Push fiber to seen stack */
 | 
			
		||||
@@ -987,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;
 | 
			
		||||
 | 
			
		||||
@@ -1033,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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1042,10 +1188,13 @@ static const uint8_t *unmarshal_one_fiber(
 | 
			
		||||
        Janet fiberv;
 | 
			
		||||
        fiber_flags &= ~JANET_FIBER_FLAG_HASCHILD;
 | 
			
		||||
        data = unmarshal_one(st, data, &fiberv, flags + 1);
 | 
			
		||||
        janet_asserttype(fiberv, JANET_FIBER);
 | 
			
		||||
        janet_asserttype(fiberv, JANET_FIBER, st);
 | 
			
		||||
        fiber->child = janet_unwrap_fiber(fiberv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Get the fiber last value */
 | 
			
		||||
    data = unmarshal_one(st, data, &fiber->last_value, flags + 1);
 | 
			
		||||
 | 
			
		||||
    /* We have valid fiber, finally construct remaining fields. */
 | 
			
		||||
    fiber->frame = frame;
 | 
			
		||||
    fiber->flags = fiber_flags;
 | 
			
		||||
@@ -1083,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);
 | 
			
		||||
@@ -1103,32 +1264,49 @@ Janet janet_unmarshal_janet(JanetMarshalContext *ctx) {
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
 | 
			
		||||
void janet_unmarshal_abstract_reuse(JanetMarshalContext *ctx, void *p) {
 | 
			
		||||
    UnmarshalState *st = (UnmarshalState *)(ctx->u_state);
 | 
			
		||||
    if (ctx->at == NULL) {
 | 
			
		||||
        janet_panicf("janet_unmarshal_abstract called more than once");
 | 
			
		||||
    }
 | 
			
		||||
    void *p = janet_abstract(ctx->at, size);
 | 
			
		||||
    janet_v_push(st->lookup, janet_wrap_abstract(p));
 | 
			
		||||
    ctx->at = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
 | 
			
		||||
    void *p = janet_abstract(ctx->at, size);
 | 
			
		||||
    janet_unmarshal_abstract_reuse(ctx, p);
 | 
			
		||||
    return p;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    const JanetAbstractType *at = janet_get_abstract_type(key);
 | 
			
		||||
    if (at == NULL) goto oops;
 | 
			
		||||
    if (at == NULL) janet_panic("unknown abstract type");
 | 
			
		||||
    if (at->unmarshal) {
 | 
			
		||||
        JanetMarshalContext context = {NULL, st, flags, data, at};
 | 
			
		||||
        *out = janet_wrap_abstract(at->unmarshal(&context));
 | 
			
		||||
        void *abst = at->unmarshal(&context);
 | 
			
		||||
        janet_assert(abst != NULL, "null pointer abstract");
 | 
			
		||||
        *out = janet_wrap_abstract(abst);
 | 
			
		||||
        if (context.at != NULL) {
 | 
			
		||||
            janet_panicf("janet_unmarshal_abstract not called");
 | 
			
		||||
            janet_panic("janet_unmarshal_abstract not called");
 | 
			
		||||
        }
 | 
			
		||||
        return context.data;
 | 
			
		||||
    }
 | 
			
		||||
oops:
 | 
			
		||||
    janet_panic("invalid abstract type");
 | 
			
		||||
    janet_panic("invalid abstract type - no unmarshal function pointer");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const uint8_t *unmarshal_one(
 | 
			
		||||
@@ -1223,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1233,18 +1411,19 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            data++;
 | 
			
		||||
            int32_t len = readnat(st, &data);
 | 
			
		||||
            if (len > 255) {
 | 
			
		||||
                janet_panicf("invalid function");
 | 
			
		||||
                janet_panicf("invalid function - too many environments (%d)", len);
 | 
			
		||||
            }
 | 
			
		||||
            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);
 | 
			
		||||
            if (def->environments_length != len) {
 | 
			
		||||
                janet_panicf("invalid function");
 | 
			
		||||
            }
 | 
			
		||||
            func->def = def;
 | 
			
		||||
            for (int32_t i = 0; i < def->environments_length; i++) {
 | 
			
		||||
            for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
                data = unmarshal_one_env(st, data, &(func->envs[i]), flags + 1);
 | 
			
		||||
            }
 | 
			
		||||
            return data;
 | 
			
		||||
@@ -1255,10 +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++;
 | 
			
		||||
@@ -1267,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);
 | 
			
		||||
@@ -1286,9 +1473,15 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
                }
 | 
			
		||||
                *out = janet_wrap_tuple(janet_tuple_end(tup));
 | 
			
		||||
                janet_v_push(st->lookup, *out);
 | 
			
		||||
            } else if (lead == LB_STRUCT) {
 | 
			
		||||
            } else if (lead == LB_STRUCT || lead == LB_STRUCT_PROTO) {
 | 
			
		||||
                /* Struct */
 | 
			
		||||
                JanetKV *struct_ = janet_struct_begin(len);
 | 
			
		||||
                if (lead == LB_STRUCT_PROTO) {
 | 
			
		||||
                    Janet proto;
 | 
			
		||||
                    data = unmarshal_one(st, data, &proto, flags + 1);
 | 
			
		||||
                    janet_asserttype(proto, JANET_STRUCT, st);
 | 
			
		||||
                    janet_struct_proto(struct_) = janet_unwrap_struct(proto);
 | 
			
		||||
                }
 | 
			
		||||
                for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
                    Janet key, value;
 | 
			
		||||
                    data = unmarshal_one(st, data, &key, flags + 1);
 | 
			
		||||
@@ -1303,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++) {
 | 
			
		||||
@@ -1339,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++;
 | 
			
		||||
@@ -1357,6 +1582,42 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
        case LB_THREADED_ABSTRACT: {
 | 
			
		||||
            MARSH_EOS(st, data + sizeof(void *));
 | 
			
		||||
            data++;
 | 
			
		||||
            if (!(flags & JANET_MARSHAL_UNSAFE)) {
 | 
			
		||||
                janet_panicf("unsafe flag not given, "
 | 
			
		||||
                             "will not unmarshal threaded abstract pointer at index %d",
 | 
			
		||||
                             (int)(data - st->start));
 | 
			
		||||
            }
 | 
			
		||||
            union {
 | 
			
		||||
                void *ptr;
 | 
			
		||||
                uint8_t bytes[sizeof(void *)];
 | 
			
		||||
            } u;
 | 
			
		||||
            memcpy(u.bytes, data, sizeof(void *));
 | 
			
		||||
            data += sizeof(void *);
 | 
			
		||||
 | 
			
		||||
            if (flags & JANET_MARSHAL_DECREF) {
 | 
			
		||||
                /* Decrement immediately and don't bother putting into heap */
 | 
			
		||||
                janet_abstract_decref(u.ptr);
 | 
			
		||||
                *out = janet_wrap_nil();
 | 
			
		||||
            } else {
 | 
			
		||||
                *out = janet_wrap_abstract(u.ptr);
 | 
			
		||||
                Janet check = janet_table_get(&janet_vm.threaded_abstracts, *out);
 | 
			
		||||
                if (janet_checktype(check, JANET_NIL)) {
 | 
			
		||||
                    /* Transfers reference from threaded channel buffer to current heap */
 | 
			
		||||
                    janet_table_put(&janet_vm.threaded_abstracts, *out, janet_wrap_false());
 | 
			
		||||
                } else {
 | 
			
		||||
                    /* Heap reference already accounted for, remove threaded channel reference. */
 | 
			
		||||
                    janet_abstract_decref(u.ptr);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            janet_v_push(st->lookup, *out);
 | 
			
		||||
            return data;
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        default: {
 | 
			
		||||
            janet_panicf("unknown byte %x at index %d",
 | 
			
		||||
                         *data,
 | 
			
		||||
@@ -1364,7 +1625,6 @@ static const uint8_t *unmarshal_one(
 | 
			
		||||
            return NULL;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#undef EXTRA
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_unmarshal(
 | 
			
		||||
@@ -1391,16 +1651,28 @@ Janet janet_unmarshal(
 | 
			
		||||
 | 
			
		||||
/* C functions */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_env_lookup(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_env_lookup,
 | 
			
		||||
              "(env-lookup env)",
 | 
			
		||||
              "Creates a forward lookup table for unmarshalling from an environment. "
 | 
			
		||||
              "To create a reverse lookup table, use the invert function to swap keys "
 | 
			
		||||
              "and values in the returned table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTable *env = janet_gettable(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_env_lookup(env));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_marshal(int32_t argc, Janet *argv) {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
JANET_CORE_FN(cfun_marshal,
 | 
			
		||||
              "(marshal x &opt reverse-lookup buffer no-cycles)",
 | 
			
		||||
              "Marshal a value into a buffer and return the buffer. The buffer "
 | 
			
		||||
              "can then later be unmarshalled to reconstruct the initial value. "
 | 
			
		||||
              "Optionally, one can pass in a reverse lookup table to not marshal "
 | 
			
		||||
              "aliased values that are found in the table. Then a forward "
 | 
			
		||||
              "lookup table can be used to recover the original value when "
 | 
			
		||||
              "unmarshalling.") {
 | 
			
		||||
    janet_arity(argc, 1, 4);
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
    JanetTable *rreg = NULL;
 | 
			
		||||
    uint32_t flags = 0;
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        rreg = janet_gettable(argv, 1);
 | 
			
		||||
    }
 | 
			
		||||
@@ -1409,11 +1681,18 @@ static Janet cfun_marshal(int32_t argc, Janet *argv) {
 | 
			
		||||
    } else {
 | 
			
		||||
        buffer = janet_buffer(10);
 | 
			
		||||
    }
 | 
			
		||||
    janet_marshal(buffer, argv[0], rreg, 0);
 | 
			
		||||
    if (argc > 3 && janet_truthy(argv[3])) {
 | 
			
		||||
        flags |= JANET_MARSHAL_NO_CYCLES;
 | 
			
		||||
    }
 | 
			
		||||
    janet_marshal(buffer, argv[0], rreg, flags);
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_unmarshal(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_unmarshal,
 | 
			
		||||
              "(unmarshal buffer &opt lookup)",
 | 
			
		||||
              "Unmarshal a value from a buffer. An optional lookup table "
 | 
			
		||||
              "can be provided to allow for aliases to be resolved. Returns the value "
 | 
			
		||||
              "unmarshalled from the buffer.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetTable *reg = NULL;
 | 
			
		||||
@@ -1423,35 +1702,13 @@ static Janet cfun_unmarshal(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_unmarshal(view.bytes, (size_t) view.len, 0, reg, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg marsh_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "marshal", cfun_marshal,
 | 
			
		||||
        JDOC("(marshal x &opt reverse-lookup buffer)\n\n"
 | 
			
		||||
             "Marshal a value into a buffer and return the buffer. The buffer "
 | 
			
		||||
             "can then later be unmarshalled to reconstruct the initial value. "
 | 
			
		||||
             "Optionally, one can pass in a reverse lookup table to not marshal "
 | 
			
		||||
             "aliased values that are found in the table. Then a forward "
 | 
			
		||||
             "lookup table can be used to recover the original value when "
 | 
			
		||||
             "unmarshalling.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "unmarshal", cfun_unmarshal,
 | 
			
		||||
        JDOC("(unmarshal buffer &opt lookup)\n\n"
 | 
			
		||||
             "Unmarshal a value from a buffer. An optional lookup table "
 | 
			
		||||
             "can be provided to allow for aliases to be resolved. Returns the value "
 | 
			
		||||
             "unmarshalled from the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "env-lookup", cfun_env_lookup,
 | 
			
		||||
        JDOC("(env-lookup env)\n\n"
 | 
			
		||||
             "Creates a forward lookup table for unmarshalling from an environment. "
 | 
			
		||||
             "To create a reverse lookup table, use the invert function to swap keys "
 | 
			
		||||
             "and values in the returned table.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Module entry point */
 | 
			
		||||
void janet_lib_marsh(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, marsh_cfuns);
 | 
			
		||||
    JanetRegExt marsh_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("marshal", cfun_marshal),
 | 
			
		||||
        JANET_CORE_REG("unmarshal", cfun_unmarshal),
 | 
			
		||||
        JANET_CORE_REG("env-lookup", cfun_env_lookup),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, marsh_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										486
									
								
								src/core/math.c
									
									
									
									
									
								
							
							
						
						
									
										486
									
								
								src/core/math.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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,13 +23,12 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
 | 
			
		||||
static JANET_THREAD_LOCAL JanetRNG janet_vm_rng = {0, 0, 0, 0, 0};
 | 
			
		||||
 | 
			
		||||
static int janet_rng_get(void *p, Janet key, Janet *out);
 | 
			
		||||
static Janet janet_rng_next(void *p, Janet key);
 | 
			
		||||
 | 
			
		||||
@@ -69,7 +68,7 @@ const JanetAbstractType janet_rng_type = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JanetRNG *janet_default_rng(void) {
 | 
			
		||||
    return &janet_vm_rng;
 | 
			
		||||
    return &janet_vm.rng;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_rng_seed(JanetRNG *rng, uint32_t seed) {
 | 
			
		||||
@@ -86,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;
 | 
			
		||||
@@ -118,7 +117,12 @@ double janet_rng_double(JanetRNG *rng) {
 | 
			
		||||
    return ldexp((double)(big >> (64 - 52)), -52);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_rng_make(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_rng_make,
 | 
			
		||||
              "(math/rng &opt seed)",
 | 
			
		||||
              "Creates a 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."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_arity(argc, 0, 1);
 | 
			
		||||
    JanetRNG *rng = janet_abstract(&janet_rng_type, sizeof(JanetRNG));
 | 
			
		||||
    if (argc == 1) {
 | 
			
		||||
@@ -135,13 +139,20 @@ static Janet cfun_rng_make(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_abstract(rng);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_rng_uniform(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_rng_uniform,
 | 
			
		||||
              "(math/rng-uniform rng)",
 | 
			
		||||
              "Extract a random number in the range [0, 1) from the RNG."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type);
 | 
			
		||||
    return janet_wrap_number(janet_rng_double(rng));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_rng_int(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_rng_int,
 | 
			
		||||
              "(math/rng-int rng &opt max)",
 | 
			
		||||
              "Extract a random integer in the range [0, max) for max > 0 from the RNG.  "
 | 
			
		||||
              "If max is 0, return 0.  If no max is given, the default is 2^31 - 1."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type);
 | 
			
		||||
    if (argc == 1) {
 | 
			
		||||
@@ -169,7 +180,11 @@ static void rng_get_4bytes(JanetRNG *rng, uint8_t *buf) {
 | 
			
		||||
    buf[3] = (word >> 24) & 0xFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_rng_buffer(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_rng_buffer,
 | 
			
		||||
              "(math/rng-buffer rng n &opt buf)",
 | 
			
		||||
              "Get n random bytes and put them in a buffer. Creates a new buffer if no buffer is "
 | 
			
		||||
              "provided, otherwise appends to the given buffer. Returns the buffer."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetRNG *rng = janet_getabstract(argv, 0, &janet_rng_type);
 | 
			
		||||
    int32_t n = janet_getnat(argv, 1);
 | 
			
		||||
@@ -214,314 +229,219 @@ static Janet janet_rng_next(void *p, Janet key) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Get a random number */
 | 
			
		||||
static Janet janet_rand(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_rand,
 | 
			
		||||
              "(math/random)",
 | 
			
		||||
              "Returns a uniformly distributed random number between 0 and 1.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    return janet_wrap_number(janet_rng_double(&janet_vm_rng));
 | 
			
		||||
    return janet_wrap_number(janet_rng_double(&janet_vm.rng));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Seed the random number generator */
 | 
			
		||||
static Janet janet_srand(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_srand,
 | 
			
		||||
              "(math/seedrandom seed)",
 | 
			
		||||
              "Set the seed for the random number generator. `seed` should be "
 | 
			
		||||
              "an integer or a buffer."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (janet_checkint(argv[0])) {
 | 
			
		||||
        uint32_t seed = (uint32_t)(janet_getinteger(argv, 0));
 | 
			
		||||
        janet_rng_seed(&janet_vm_rng, seed);
 | 
			
		||||
        janet_rng_seed(&janet_vm.rng, seed);
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetByteView bytes = janet_getbytes(argv, 0);
 | 
			
		||||
        janet_rng_longseed(&janet_vm_rng, bytes.bytes, bytes.len);
 | 
			
		||||
        janet_rng_longseed(&janet_vm.rng, bytes.bytes, bytes.len);
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define JANET_DEFINE_MATHOP(name, fop)\
 | 
			
		||||
static Janet janet_##name(int32_t argc, Janet *argv) {\
 | 
			
		||||
#define JANET_DEFINE_NAMED_MATHOP(janet_name, fop, doc)\
 | 
			
		||||
JANET_CORE_FN(janet_##fop, "(math/" janet_name " x)", doc) {\
 | 
			
		||||
    janet_fixarity(argc, 1); \
 | 
			
		||||
    double x = janet_getnumber(argv, 0); \
 | 
			
		||||
    return janet_wrap_number(fop(x)); \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_DEFINE_MATHOP(acos, acos)
 | 
			
		||||
JANET_DEFINE_MATHOP(asin, asin)
 | 
			
		||||
JANET_DEFINE_MATHOP(atan, atan)
 | 
			
		||||
JANET_DEFINE_MATHOP(cos, cos)
 | 
			
		||||
JANET_DEFINE_MATHOP(cosh, cosh)
 | 
			
		||||
JANET_DEFINE_MATHOP(acosh, acosh)
 | 
			
		||||
JANET_DEFINE_MATHOP(sin, sin)
 | 
			
		||||
JANET_DEFINE_MATHOP(sinh, sinh)
 | 
			
		||||
JANET_DEFINE_MATHOP(asinh, asinh)
 | 
			
		||||
JANET_DEFINE_MATHOP(tan, tan)
 | 
			
		||||
JANET_DEFINE_MATHOP(tanh, tanh)
 | 
			
		||||
JANET_DEFINE_MATHOP(atanh, atanh)
 | 
			
		||||
JANET_DEFINE_MATHOP(exp, exp)
 | 
			
		||||
JANET_DEFINE_MATHOP(exp2, exp2)
 | 
			
		||||
JANET_DEFINE_MATHOP(expm1, expm1)
 | 
			
		||||
JANET_DEFINE_MATHOP(log, log)
 | 
			
		||||
JANET_DEFINE_MATHOP(log10, log10)
 | 
			
		||||
JANET_DEFINE_MATHOP(log2, log2)
 | 
			
		||||
JANET_DEFINE_MATHOP(sqrt, sqrt)
 | 
			
		||||
JANET_DEFINE_MATHOP(cbrt, cbrt)
 | 
			
		||||
JANET_DEFINE_MATHOP(ceil, ceil)
 | 
			
		||||
JANET_DEFINE_MATHOP(fabs, fabs)
 | 
			
		||||
JANET_DEFINE_MATHOP(floor, floor)
 | 
			
		||||
JANET_DEFINE_MATHOP(trunc, trunc)
 | 
			
		||||
JANET_DEFINE_MATHOP(round, round)
 | 
			
		||||
JANET_DEFINE_MATHOP(gamma, lgamma)
 | 
			
		||||
JANET_DEFINE_MATHOP(log1p, log1p)
 | 
			
		||||
JANET_DEFINE_MATHOP(erf, erf)
 | 
			
		||||
JANET_DEFINE_MATHOP(erfc, erfc)
 | 
			
		||||
#define JANET_DEFINE_MATHOP(fop, doc) JANET_DEFINE_NAMED_MATHOP(#fop, fop, doc)
 | 
			
		||||
 | 
			
		||||
#define JANET_DEFINE_MATH2OP(name, fop)\
 | 
			
		||||
static Janet janet_##name(int32_t argc, Janet *argv) {\
 | 
			
		||||
JANET_DEFINE_MATHOP(acos, "Returns the arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asin, "Returns the arcsin of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atan, "Returns the arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cos, "Returns the cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cosh, "Returns the hyperbolic cosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(acosh, "Returns the hyperbolic arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sin, "Returns the sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sinh, "Returns the hyperbolic sine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asinh, "Returns the hyperbolic arcsine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tan, "Returns the tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(tanh, "Returns the hyperbolic tangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atanh, "Returns the hyperbolic arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp, "Returns e to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(exp2, "Returns 2 to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(expm1, "Returns e to the power of x minus 1.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log, "Returns the natural logarithm of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log10, "Returns the log base 10 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log2, "Returns the log base 2 of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(sqrt, "Returns the square root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cbrt, "Returns the cube root of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(ceil, "Returns the smallest integer value number that is not less than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(floor, "Returns the largest integer value number that is not greater than x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(trunc, "Returns the integer between x and 0 nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(round, "Returns the integer nearest to x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(log1p, "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
 | 
			
		||||
JANET_DEFINE_MATHOP(erf, "Returns the error function of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(erfc, "Returns the complementary error function of x.")
 | 
			
		||||
JANET_DEFINE_NAMED_MATHOP("log-gamma", lgamma, "Returns log-gamma(x).")
 | 
			
		||||
JANET_DEFINE_NAMED_MATHOP("abs", fabs, "Return the absolute value of x.")
 | 
			
		||||
JANET_DEFINE_NAMED_MATHOP("gamma", tgamma, "Returns gamma(x).")
 | 
			
		||||
 | 
			
		||||
#define JANET_DEFINE_MATH2OP(name, fop, signature, doc)\
 | 
			
		||||
JANET_CORE_FN(janet_##name, signature, doc) {\
 | 
			
		||||
    janet_fixarity(argc, 2); \
 | 
			
		||||
    double lhs = janet_getnumber(argv, 0); \
 | 
			
		||||
    double rhs = janet_getnumber(argv, 1); \
 | 
			
		||||
    return janet_wrap_number(fop(lhs, rhs)); \
 | 
			
		||||
}\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_DEFINE_MATH2OP(atan2, atan2)
 | 
			
		||||
JANET_DEFINE_MATH2OP(pow, pow)
 | 
			
		||||
JANET_DEFINE_MATH2OP(hypot, hypot)
 | 
			
		||||
JANET_DEFINE_MATH2OP(nextafter, nextafter)
 | 
			
		||||
JANET_DEFINE_MATH2OP(atan2, atan2, "(math/atan2 y x)", "Returns the arctangent of y/x. Works even when x is 0.")
 | 
			
		||||
JANET_DEFINE_MATH2OP(pow, pow, "(math/pow a x)", "Returns a to the power of x.")
 | 
			
		||||
JANET_DEFINE_MATH2OP(hypot, hypot, "(math/hypot a b)", "Returns c from the equation c^2 = a^2 + b^2.")
 | 
			
		||||
JANET_DEFINE_MATH2OP(nextafter, nextafter,  "(math/next x y)", "Returns the next representable floating point value after x in the direction of y.")
 | 
			
		||||
 | 
			
		||||
static Janet janet_not(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(janet_not, "(not x)", "Returns the boolean inverse of x.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_boolean(!janet_truthy(argv[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg math_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "not", janet_not,
 | 
			
		||||
        JDOC("(not x)\n\nReturns the boolean inverse of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/random", janet_rand,
 | 
			
		||||
        JDOC("(math/random)\n\n"
 | 
			
		||||
             "Returns a uniformly distributed random number between 0 and 1.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/seedrandom", janet_srand,
 | 
			
		||||
        JDOC("(math/seedrandom seed)\n\n"
 | 
			
		||||
             "Set the seed for the random number generator. seed should be "
 | 
			
		||||
             "an integer or a buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/cos", janet_cos,
 | 
			
		||||
        JDOC("(math/cos x)\n\n"
 | 
			
		||||
             "Returns the cosine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/sin", janet_sin,
 | 
			
		||||
        JDOC("(math/sin x)\n\n"
 | 
			
		||||
             "Returns the sine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/tan", janet_tan,
 | 
			
		||||
        JDOC("(math/tan x)\n\n"
 | 
			
		||||
             "Returns the tangent of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/acos", janet_acos,
 | 
			
		||||
        JDOC("(math/acos x)\n\n"
 | 
			
		||||
             "Returns the arccosine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/asin", janet_asin,
 | 
			
		||||
        JDOC("(math/asin x)\n\n"
 | 
			
		||||
             "Returns the arcsine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/atan", janet_atan,
 | 
			
		||||
        JDOC("(math/atan x)\n\n"
 | 
			
		||||
             "Returns the arctangent of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/exp", janet_exp,
 | 
			
		||||
        JDOC("(math/exp x)\n\n"
 | 
			
		||||
             "Returns e to the power of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/log", janet_log,
 | 
			
		||||
        JDOC("(math/log x)\n\n"
 | 
			
		||||
             "Returns log base natural number of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/log10", janet_log10,
 | 
			
		||||
        JDOC("(math/log10 x)\n\n"
 | 
			
		||||
             "Returns log base 10 of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/log2", janet_log2,
 | 
			
		||||
        JDOC("(math/log2 x)\n\n"
 | 
			
		||||
             "Returns log base 2 of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/sqrt", janet_sqrt,
 | 
			
		||||
        JDOC("(math/sqrt x)\n\n"
 | 
			
		||||
             "Returns the square root of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/cbrt", janet_cbrt,
 | 
			
		||||
        JDOC("(math/cbrt x)\n\n"
 | 
			
		||||
             "Returns the cube root of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/floor", janet_floor,
 | 
			
		||||
        JDOC("(math/floor x)\n\n"
 | 
			
		||||
             "Returns the largest integer value number that is not greater than x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/ceil", janet_ceil,
 | 
			
		||||
        JDOC("(math/ceil x)\n\n"
 | 
			
		||||
             "Returns the smallest integer value number that is not less than x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/pow", janet_pow,
 | 
			
		||||
        JDOC("(math/pow a x)\n\n"
 | 
			
		||||
             "Return a to the power of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/abs", janet_fabs,
 | 
			
		||||
        JDOC("(math/abs x)\n\n"
 | 
			
		||||
             "Return the absolute value of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/sinh", janet_sinh,
 | 
			
		||||
        JDOC("(math/sinh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic sine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/cosh", janet_cosh,
 | 
			
		||||
        JDOC("(math/cosh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic cosine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/tanh", janet_tanh,
 | 
			
		||||
        JDOC("(math/tanh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic tangent of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/atanh", janet_atanh,
 | 
			
		||||
        JDOC("(math/atanh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic arctangent of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/asinh", janet_asinh,
 | 
			
		||||
        JDOC("(math/asinh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic arcsine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/acosh", janet_acosh,
 | 
			
		||||
        JDOC("(math/acosh x)\n\n"
 | 
			
		||||
             "Return the hyperbolic arccosine of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/atan2", janet_atan2,
 | 
			
		||||
        JDOC("(math/atan2 y x)\n\n"
 | 
			
		||||
             "Return the arctangent of y/x. Works even when x is 0.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/rng", cfun_rng_make,
 | 
			
		||||
        JDOC("(math/rng &opt seed)\n\n"
 | 
			
		||||
             "Creates a Psuedo-Random number generator, with an optional seed. "
 | 
			
		||||
             "The seed should be an unsigned 32 bit integer or a buffer. "
 | 
			
		||||
             "Do not use this for cryptography. Returns a core/rng abstract type.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/rng-uniform", cfun_rng_uniform,
 | 
			
		||||
        JDOC("(math/rng-seed rng seed)\n\n"
 | 
			
		||||
             "Extract a random number in the range [0, 1) from the RNG.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/rng-int", cfun_rng_int,
 | 
			
		||||
        JDOC("(math/rng-int rng &opt max)\n\n"
 | 
			
		||||
             "Extract a random random integer in the range [0, max] from the RNG. If "
 | 
			
		||||
             "no max is given, the default is 2^31 - 1.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/rng-buffer", cfun_rng_buffer,
 | 
			
		||||
        JDOC("(math/rng-buffer rng n &opt buf)\n\n"
 | 
			
		||||
             "Get n random bytes and put them in a buffer. Creates a new buffer if no buffer is "
 | 
			
		||||
             "provided, otherwise appends to the given buffer. Returns the buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/hypot", janet_hypot,
 | 
			
		||||
        JDOC("(math/hypot a b)\n\n"
 | 
			
		||||
             "Returns the c from the equation c^2 = a^2 + b^2")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/exp2", janet_exp2,
 | 
			
		||||
        JDOC("(math/exp2 x)\n\n"
 | 
			
		||||
             "Returns 2 to the power of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/log1p", janet_log1p,
 | 
			
		||||
        JDOC("(math/log1p x)\n\n"
 | 
			
		||||
             "Returns (log base e of x) + 1 more accurately than (+ (math/log x) 1)")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/gamma", janet_gamma,
 | 
			
		||||
        JDOC("(math/gamma x)\n\n"
 | 
			
		||||
             "Returns gamma(x).")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/erfc", janet_erfc,
 | 
			
		||||
        JDOC("(math/erfc x)\n\n"
 | 
			
		||||
             "Returns the complementary error function of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/erf", janet_erf,
 | 
			
		||||
        JDOC("(math/erf x)\n\n"
 | 
			
		||||
             "Returns the error function of x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/expm1", janet_expm1,
 | 
			
		||||
        JDOC("(math/expm1 x)\n\n"
 | 
			
		||||
             "Returns e to the power of x minus 1.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/trunc", janet_trunc,
 | 
			
		||||
        JDOC("(math/trunc x)\n\n"
 | 
			
		||||
             "Returns the integer between x and 0 nearest to x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/round", janet_round,
 | 
			
		||||
        JDOC("(math/round x)\n\n"
 | 
			
		||||
             "Returns the integer nearest to x.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "math/next", janet_nextafter,
 | 
			
		||||
        JDOC("(math/next x y)\n\n"
 | 
			
		||||
             "Returns the next representable floating point value after x in the direction of y.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
static double janet_gcd(double x, double y) {
 | 
			
		||||
    if (isnan(x) || isnan(y)) {
 | 
			
		||||
#ifdef NAN
 | 
			
		||||
        return NAN;
 | 
			
		||||
#else
 | 
			
		||||
        return 0.0 / 0.0;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
    if (isinf(x) || isinf(y)) return INFINITY;
 | 
			
		||||
    while (y != 0) {
 | 
			
		||||
        double temp = y;
 | 
			
		||||
        y = fmod(x, y);
 | 
			
		||||
        x = temp;
 | 
			
		||||
    }
 | 
			
		||||
    return x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static double janet_lcm(double x, double y) {
 | 
			
		||||
    return (x / janet_gcd(x, y)) * y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_gcd, "(math/gcd x y)",
 | 
			
		||||
              "Returns the greatest common divisor between x and y.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    double x = janet_getnumber(argv, 0);
 | 
			
		||||
    double y = janet_getnumber(argv, 1);
 | 
			
		||||
    return janet_wrap_number(janet_gcd(x, y));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_lcm, "(math/lcm x y)",
 | 
			
		||||
              "Returns the least common multiple of x and y.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    double x = janet_getnumber(argv, 0);
 | 
			
		||||
    double y = janet_getnumber(argv, 1);
 | 
			
		||||
    return janet_wrap_number(janet_lcm(x, y));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, math_cfuns);
 | 
			
		||||
    JanetRegExt math_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("not", janet_not),
 | 
			
		||||
        JANET_CORE_REG("math/random", janet_rand),
 | 
			
		||||
        JANET_CORE_REG("math/seedrandom", janet_srand),
 | 
			
		||||
        JANET_CORE_REG("math/cos", janet_cos),
 | 
			
		||||
        JANET_CORE_REG("math/sin", janet_sin),
 | 
			
		||||
        JANET_CORE_REG("math/tan", janet_tan),
 | 
			
		||||
        JANET_CORE_REG("math/acos", janet_acos),
 | 
			
		||||
        JANET_CORE_REG("math/asin", janet_asin),
 | 
			
		||||
        JANET_CORE_REG("math/atan", janet_atan),
 | 
			
		||||
        JANET_CORE_REG("math/exp", janet_exp),
 | 
			
		||||
        JANET_CORE_REG("math/log", janet_log),
 | 
			
		||||
        JANET_CORE_REG("math/log10", janet_log10),
 | 
			
		||||
        JANET_CORE_REG("math/log2", janet_log2),
 | 
			
		||||
        JANET_CORE_REG("math/sqrt", janet_sqrt),
 | 
			
		||||
        JANET_CORE_REG("math/cbrt", janet_cbrt),
 | 
			
		||||
        JANET_CORE_REG("math/floor", janet_floor),
 | 
			
		||||
        JANET_CORE_REG("math/ceil", janet_ceil),
 | 
			
		||||
        JANET_CORE_REG("math/pow", janet_pow),
 | 
			
		||||
        JANET_CORE_REG("math/abs", janet_fabs),
 | 
			
		||||
        JANET_CORE_REG("math/sinh", janet_sinh),
 | 
			
		||||
        JANET_CORE_REG("math/cosh", janet_cosh),
 | 
			
		||||
        JANET_CORE_REG("math/tanh", janet_tanh),
 | 
			
		||||
        JANET_CORE_REG("math/atanh", janet_atanh),
 | 
			
		||||
        JANET_CORE_REG("math/asinh", janet_asinh),
 | 
			
		||||
        JANET_CORE_REG("math/acosh", janet_acosh),
 | 
			
		||||
        JANET_CORE_REG("math/atan2", janet_atan2),
 | 
			
		||||
        JANET_CORE_REG("math/rng", cfun_rng_make),
 | 
			
		||||
        JANET_CORE_REG("math/rng-uniform", cfun_rng_uniform),
 | 
			
		||||
        JANET_CORE_REG("math/rng-int", cfun_rng_int),
 | 
			
		||||
        JANET_CORE_REG("math/rng-buffer", cfun_rng_buffer),
 | 
			
		||||
        JANET_CORE_REG("math/hypot", janet_hypot),
 | 
			
		||||
        JANET_CORE_REG("math/exp2", janet_exp2),
 | 
			
		||||
        JANET_CORE_REG("math/log1p", janet_log1p),
 | 
			
		||||
        JANET_CORE_REG("math/gamma", janet_tgamma),
 | 
			
		||||
        JANET_CORE_REG("math/log-gamma", janet_lgamma),
 | 
			
		||||
        JANET_CORE_REG("math/erfc", janet_erfc),
 | 
			
		||||
        JANET_CORE_REG("math/erf", janet_erf),
 | 
			
		||||
        JANET_CORE_REG("math/expm1", janet_expm1),
 | 
			
		||||
        JANET_CORE_REG("math/trunc", janet_trunc),
 | 
			
		||||
        JANET_CORE_REG("math/round", janet_round),
 | 
			
		||||
        JANET_CORE_REG("math/next", janet_nextafter),
 | 
			
		||||
        JANET_CORE_REG("math/gcd", janet_cfun_gcd),
 | 
			
		||||
        JANET_CORE_REG("math/lcm", janet_cfun_lcm),
 | 
			
		||||
        JANET_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);
 | 
			
		||||
    janet_register_abstract_type(&janet_rng_type);
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
    janet_def(env, "math/pi", janet_wrap_number(3.1415926535897931),
 | 
			
		||||
              JDOC("The value pi."));
 | 
			
		||||
    janet_def(env, "math/e", janet_wrap_number(2.7182818284590451),
 | 
			
		||||
              JDOC("The base of the natural log."));
 | 
			
		||||
    janet_def(env, "math/inf", janet_wrap_number(INFINITY),
 | 
			
		||||
              JDOC("The number representing positive infinity"));
 | 
			
		||||
    janet_def(env, "math/-inf", janet_wrap_number(-INFINITY),
 | 
			
		||||
              JDOC("The number representing negative infinity"));
 | 
			
		||||
    janet_def(env, "math/int32-min", janet_wrap_number(INT32_MIN),
 | 
			
		||||
              JDOC("The minimum contiguous integer representable by a 32 bit signed integer"));
 | 
			
		||||
    janet_def(env, "math/int32-max", janet_wrap_number(INT32_MAX),
 | 
			
		||||
              JDOC("The maximum contiguous integer represtenable by a 32 bit signed integer"));
 | 
			
		||||
    janet_def(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
 | 
			
		||||
              JDOC("The minimum contiguous integer representable by a double (2^53)"));
 | 
			
		||||
    janet_def(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
 | 
			
		||||
              JDOC("The maximum contiguous integer represtenable by a double (-(2^53))"));
 | 
			
		||||
    JANET_CORE_DEF(env, "math/pi", janet_wrap_number(3.1415926535897931),
 | 
			
		||||
                   "The value pi.");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/e", janet_wrap_number(2.7182818284590451),
 | 
			
		||||
                   "The base of the natural log.");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/inf", janet_wrap_number(INFINITY),
 | 
			
		||||
                   "The number representing positive infinity");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/-inf", janet_wrap_number(-INFINITY),
 | 
			
		||||
                   "The number representing negative infinity");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int32-min", janet_wrap_number(INT32_MIN),
 | 
			
		||||
                   "The minimum contiguous integer representable by a 32 bit signed integer");
 | 
			
		||||
    JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX),
 | 
			
		||||
                   "The maximum contiguous integer 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 representable by a double (-(2^53))");
 | 
			
		||||
#ifdef NAN
 | 
			
		||||
    janet_def(env, "math/nan", janet_wrap_number(NAN),
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#else
 | 
			
		||||
    janet_def(env, "math/nan", janet_wrap_number(0.0 / 0.0),
 | 
			
		||||
    JANET_CORE_DEF(env, "math/nan", janet_wrap_number(0.0 / 0.0), "Not a number (IEEE-754 NaN)");
 | 
			
		||||
#endif
 | 
			
		||||
              JDOC("Not a number (IEEE-754 NaN)"));
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										788
									
								
								src/core/net.c
									
									
									
									
									
								
							
							
						
						
									
										788
									
								
								src/core/net.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1552
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										1552
									
								
								src/core/os.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										335
									
								
								src/core/parse.c
									
									
									
									
									
								
							
							
						
						
									
										335
									
								
								src/core/parse.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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
 | 
			
		||||
@@ -51,15 +51,15 @@ static const uint32_t symchars[8] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Check if a character is a valid symbol character
 | 
			
		||||
 * symbol chars are A-Z, a-z, 0-9, or one of !$&*+-./:<=>@\^_~| */
 | 
			
		||||
static int is_symbol_char(uint8_t c) {
 | 
			
		||||
 * symbol chars are A-Z, a-z, 0-9, or one of !$&*+-./:<=>@\^_| */
 | 
			
		||||
int janet_is_symbol_char(uint8_t c) {
 | 
			
		||||
    return symchars[c >> 5] & ((uint32_t)1 << (c & 0x1F));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Validate some utf8. Useful for identifiers. Only validates
 | 
			
		||||
 * the encoding, does not check for valid code points (they
 | 
			
		||||
 * are less well defined than the encoding). */
 | 
			
		||||
static int valid_utf8(const uint8_t *str, int32_t len) {
 | 
			
		||||
int janet_valid_utf8(const uint8_t *str, int32_t len) {
 | 
			
		||||
    int32_t i = 0;
 | 
			
		||||
    int32_t j;
 | 
			
		||||
    while (i < len) {
 | 
			
		||||
@@ -206,6 +206,37 @@ static void popstate(JanetParser *p, Janet val) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void delim_error(JanetParser *parser, size_t stack_index, char c, const char *msg) {
 | 
			
		||||
    JanetParseState *s = parser->states + stack_index;
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(40);
 | 
			
		||||
    if (msg) {
 | 
			
		||||
        janet_buffer_push_cstring(buffer, msg);
 | 
			
		||||
    }
 | 
			
		||||
    if (c) {
 | 
			
		||||
        janet_buffer_push_u8(buffer, c);
 | 
			
		||||
    }
 | 
			
		||||
    if (stack_index > 0) {
 | 
			
		||||
        janet_buffer_push_cstring(buffer, ", ");
 | 
			
		||||
        if (s->flags & PFLAG_PARENS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '(');
 | 
			
		||||
        } else if (s->flags & PFLAG_SQRBRACKETS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '[');
 | 
			
		||||
        } else if (s->flags & PFLAG_CURLYBRACKETS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '{');
 | 
			
		||||
        } else if (s->flags & PFLAG_STRING) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '"');
 | 
			
		||||
        } else if (s->flags & PFLAG_LONGSTRING) {
 | 
			
		||||
            int32_t i;
 | 
			
		||||
            for (i = 0; i < s->argn; i++) {
 | 
			
		||||
                janet_buffer_push_u8(buffer, '`');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        janet_formatb(buffer, " opened at line %d, column %d", (int32_t) s->line, (int32_t) s->column);
 | 
			
		||||
    }
 | 
			
		||||
    parser->error = (const char *) janet_string(buffer->data, buffer->count);
 | 
			
		||||
    parser->flag |= JANET_PARSER_GENERATED_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int checkescape(uint8_t c) {
 | 
			
		||||
    switch (c) {
 | 
			
		||||
        default:
 | 
			
		||||
@@ -228,6 +259,14 @@ static int checkescape(uint8_t c) {
 | 
			
		||||
            return '\f';
 | 
			
		||||
        case 'v':
 | 
			
		||||
            return '\v';
 | 
			
		||||
        case 'a':
 | 
			
		||||
            return '\a';
 | 
			
		||||
        case 'b':
 | 
			
		||||
            return '\b';
 | 
			
		||||
        case '\'':
 | 
			
		||||
            return '\'';
 | 
			
		||||
        case '?':
 | 
			
		||||
            return '?';
 | 
			
		||||
        case 'e':
 | 
			
		||||
            return 27;
 | 
			
		||||
        case '"':
 | 
			
		||||
@@ -324,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') {
 | 
			
		||||
@@ -335,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--;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -411,7 +451,7 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
    Janet ret;
 | 
			
		||||
    double numval;
 | 
			
		||||
    int32_t blen;
 | 
			
		||||
    if (is_symbol_char(c)) {
 | 
			
		||||
    if (janet_is_symbol_char(c)) {
 | 
			
		||||
        push_buf(p, (uint8_t) c);
 | 
			
		||||
        if (c > 127) state->argn = 1; /* Use to indicate non ascii */
 | 
			
		||||
        return 1;
 | 
			
		||||
@@ -422,14 +462,19 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
    int start_num = start_dig || p->buf[0] == '-' || p->buf[0] == '+' || p->buf[0] == '.';
 | 
			
		||||
    if (p->buf[0] == ':') {
 | 
			
		||||
        /* Don't do full utf-8 check unless we have seen non ascii characters. */
 | 
			
		||||
        int valid = (!state->argn) || valid_utf8(p->buf + 1, blen - 1);
 | 
			
		||||
        int valid = (!state->argn) || janet_valid_utf8(p->buf + 1, blen - 1);
 | 
			
		||||
        if (!valid) {
 | 
			
		||||
            p->error = "invalid utf-8 in keyword";
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
        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)) {
 | 
			
		||||
@@ -442,7 +487,7 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Don't do full utf-8 check unless we have seen non ascii characters. */
 | 
			
		||||
            int valid = (!state->argn) || valid_utf8(p->buf, blen);
 | 
			
		||||
            int valid = (!state->argn) || janet_valid_utf8(p->buf, blen);
 | 
			
		||||
            if (!valid) {
 | 
			
		||||
                p->error = "invalid utf-8 in symbol";
 | 
			
		||||
                return 0;
 | 
			
		||||
@@ -582,7 +627,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
    switch (c) {
 | 
			
		||||
        default:
 | 
			
		||||
            if (is_whitespace(c)) return 1;
 | 
			
		||||
            if (!is_symbol_char(c)) {
 | 
			
		||||
            if (!janet_is_symbol_char(c)) {
 | 
			
		||||
                p->error = "unexpected character";
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
@@ -612,7 +657,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
        case '}': {
 | 
			
		||||
            Janet ds;
 | 
			
		||||
            if (p->statecount == 1) {
 | 
			
		||||
                p->error = "unexpected delimiter";
 | 
			
		||||
                delim_error(p, 0, c, "unexpected closing delimiter ");
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
            if ((c == ')' && (state->flags & PFLAG_PARENS)) ||
 | 
			
		||||
@@ -633,7 +678,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
                    ds = close_struct(p, state);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                p->error = "mismatched delimiter";
 | 
			
		||||
                delim_error(p, p->statecount - 1, c, "mismatched delimiter ");
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
            popstate(p, ds);
 | 
			
		||||
@@ -684,26 +729,7 @@ void janet_parser_eof(JanetParser *parser) {
 | 
			
		||||
    size_t oldline = parser->line;
 | 
			
		||||
    janet_parser_consume(parser, '\n');
 | 
			
		||||
    if (parser->statecount > 1) {
 | 
			
		||||
        JanetParseState *s = parser->states + (parser->statecount - 1);
 | 
			
		||||
        JanetBuffer *buffer = janet_buffer(40);
 | 
			
		||||
        janet_buffer_push_cstring(buffer, "unexpected end of source, ");
 | 
			
		||||
        if (s->flags & PFLAG_PARENS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '(');
 | 
			
		||||
        } else if (s->flags & PFLAG_SQRBRACKETS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '[');
 | 
			
		||||
        } else if (s->flags & PFLAG_CURLYBRACKETS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '{');
 | 
			
		||||
        } else if (s->flags & PFLAG_STRING) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '"');
 | 
			
		||||
        } else if (s->flags & PFLAG_LONGSTRING) {
 | 
			
		||||
            int32_t i;
 | 
			
		||||
            for (i = 0; i < s->argn; i++) {
 | 
			
		||||
                janet_buffer_push_u8(buffer, '`');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
 | 
			
		||||
        parser->error = (const char *) janet_string(buffer->data, buffer->count);
 | 
			
		||||
        parser->flag |= JANET_PARSER_GENERATED_ERROR;
 | 
			
		||||
        delim_error(parser, parser->statecount - 1, 0, "unexpected end of source");
 | 
			
		||||
    }
 | 
			
		||||
    parser->line = oldline;
 | 
			
		||||
    parser->column = oldcolumn;
 | 
			
		||||
@@ -746,6 +772,7 @@ Janet janet_parser_produce(JanetParser *parser) {
 | 
			
		||||
    }
 | 
			
		||||
    parser->pending--;
 | 
			
		||||
    parser->argcount--;
 | 
			
		||||
    parser->states[0].argn--;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -759,6 +786,7 @@ Janet janet_parser_produce_wrapped(JanetParser *parser) {
 | 
			
		||||
    }
 | 
			
		||||
    parser->pending--;
 | 
			
		||||
    parser->argcount--;
 | 
			
		||||
    parser->states[0].argn--;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -878,7 +906,10 @@ const JanetAbstractType janet_parser_type = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* C Function parser */
 | 
			
		||||
static Janet cfun_parse_parser(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_parser,
 | 
			
		||||
              "(parser/new)",
 | 
			
		||||
              "Creates and returns a new parser object. Parsers are state machines "
 | 
			
		||||
              "that can receive bytes and generate a stream of values.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    JanetParser *p = janet_abstract(&janet_parser_type, sizeof(JanetParser));
 | 
			
		||||
@@ -886,7 +917,11 @@ static Janet cfun_parse_parser(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_abstract(p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_consume(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_consume,
 | 
			
		||||
              "(parser/consume parser bytes &opt index)",
 | 
			
		||||
              "Input bytes into the parser and parse them. Will not throw errors "
 | 
			
		||||
              "if there is a parse error. Starts at the byte index given by `index`. Returns "
 | 
			
		||||
              "the number of bytes read.") {
 | 
			
		||||
    janet_arity(argc, 2, 3);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -911,14 +946,20 @@ static Janet cfun_parse_consume(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_integer(i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_eof(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_eof,
 | 
			
		||||
              "(parser/eof parser)",
 | 
			
		||||
              "Indicate to the parser that the end of file was reached. This puts the parser in the :dead state.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    janet_parser_eof(p);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_insert,
 | 
			
		||||
              "(parser/insert parser value)",
 | 
			
		||||
              "Insert a value into the parser. This means that the parser state can be manipulated "
 | 
			
		||||
              "in between chunks of bytes. This would allow a user to add extra elements to arrays "
 | 
			
		||||
              "and tuples, for example. Returns the parser.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    JanetParseState *s = p->states + p->statecount - 1;
 | 
			
		||||
@@ -957,13 +998,17 @@ static Janet cfun_parse_insert(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_has_more(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_has_more,
 | 
			
		||||
              "(parser/has-more parser)",
 | 
			
		||||
              "Check if the parser has more values in the value queue.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    return janet_wrap_boolean(janet_parser_has_more(p));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_byte(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_byte,
 | 
			
		||||
              "(parser/byte parser b)",
 | 
			
		||||
              "Input a single byte `b` into the parser byte stream. Returns the parser.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    int32_t i = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -971,7 +1016,13 @@ static Janet cfun_parse_byte(int32_t argc, Janet *argv) {
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_status(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_status,
 | 
			
		||||
              "(parser/status parser)",
 | 
			
		||||
              "Gets the current status of the parser state machine. The status will "
 | 
			
		||||
              "be one of:\n\n"
 | 
			
		||||
              "* :pending - a value is being parsed.\n\n"
 | 
			
		||||
              "* :error - a parsing error was encountered.\n\n"
 | 
			
		||||
              "* :root - the parser can either read more values or safely terminate.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    const char *stat = NULL;
 | 
			
		||||
@@ -992,7 +1043,12 @@ static Janet cfun_parse_status(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_ckeywordv(stat);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_error(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_error,
 | 
			
		||||
              "(parser/error parser)",
 | 
			
		||||
              "If the parser is in the error state, returns the message associated with "
 | 
			
		||||
              "that error. Otherwise, returns nil. Also flushes the parser state and parser "
 | 
			
		||||
              "queue, so be sure to handle everything in the queue before calling "
 | 
			
		||||
              "`parser/error`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    const char *err = janet_parser_error(p);
 | 
			
		||||
@@ -1004,7 +1060,13 @@ static Janet cfun_parse_error(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_produce(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_produce,
 | 
			
		||||
              "(parser/produce parser &opt wrap)",
 | 
			
		||||
              "Dequeue the next value in the parse queue. Will return nil if "
 | 
			
		||||
              "no parsed values are in the queue, otherwise will dequeue the "
 | 
			
		||||
              "next value. If `wrap` is truthy, will return a 1-element tuple that "
 | 
			
		||||
              "wraps the result. This tuple can be used for source-mapping "
 | 
			
		||||
              "purposes.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    if (argc == 2 && janet_truthy(argv[1])) {
 | 
			
		||||
@@ -1014,14 +1076,22 @@ static Janet cfun_parse_produce(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_flush(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_flush,
 | 
			
		||||
              "(parser/flush parser)",
 | 
			
		||||
              "Clears the parser state and parse queue. Can be used to reset the parser "
 | 
			
		||||
              "if an error was encountered. Does not reset the line and column counter, so "
 | 
			
		||||
              "to begin parsing in a new context, create a new parser.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    janet_parser_flush(p);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_where(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_where,
 | 
			
		||||
              "(parser/where parser &opt line col)",
 | 
			
		||||
              "Returns the current line number and column of the parser's internal state. If line is "
 | 
			
		||||
              "provided, the current line number of the parser is first set to that value. If column is "
 | 
			
		||||
              "also provided, the current column number of the parser is also first set to that value.") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
@@ -1051,8 +1121,9 @@ static Janet janet_wrap_parse_state(JanetParseState *s, Janet *args,
 | 
			
		||||
 | 
			
		||||
    if (s->flags & PFLAG_CONTAINER) {
 | 
			
		||||
        JanetArray *container_args = janet_array(s->argn);
 | 
			
		||||
        container_args->count = s->argn;
 | 
			
		||||
        safe_memcpy(container_args->data, args, sizeof(args[0])*s->argn);
 | 
			
		||||
        for (int32_t i = 0; i < s->argn; i++) {
 | 
			
		||||
            janet_array_push(container_args, args[i]);
 | 
			
		||||
        }
 | 
			
		||||
        janet_table_put(state, janet_ckeywordv("args"),
 | 
			
		||||
                        janet_wrap_array(container_args));
 | 
			
		||||
    }
 | 
			
		||||
@@ -1137,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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1147,11 +1219,15 @@ static Janet parser_state_frames(const JanetParser *p) {
 | 
			
		||||
    JanetArray *states = janet_array(count);
 | 
			
		||||
    states->count = count;
 | 
			
		||||
    uint8_t *buf = p->buf;
 | 
			
		||||
    Janet *args = p->args;
 | 
			
		||||
    /* Iterate arg stack backwards */
 | 
			
		||||
    Janet *args = p->argcount ? p->args + p->argcount : p->args; /* avoid ptr arithmetic on NULL */
 | 
			
		||||
    for (int32_t i = count - 1; i >= 0; --i) {
 | 
			
		||||
        JanetParseState *s = p->states + i;
 | 
			
		||||
        /* avoid ptr arithmetic on args if NULL */
 | 
			
		||||
        if ((s->flags & PFLAG_CONTAINER) && s->argn) {
 | 
			
		||||
            args -= s->argn;
 | 
			
		||||
        }
 | 
			
		||||
        states->data[i] = janet_wrap_parse_state(s, args, buf, (uint32_t) p->bufcount);
 | 
			
		||||
        args -= s->argn;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_array(states);
 | 
			
		||||
}
 | 
			
		||||
@@ -1162,7 +1238,16 @@ static const struct ParserStateGetter parser_state_getters[] = {
 | 
			
		||||
    {NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_state(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_state,
 | 
			
		||||
              "(parser/state parser &opt key)",
 | 
			
		||||
              "Returns a representation of the internal state of the parser. If a key is passed, "
 | 
			
		||||
              "only that information about the state is returned. Allowed keys are:\n\n"
 | 
			
		||||
              "* :delimiters - Each byte in the string represents a nested data structure. For example, "
 | 
			
		||||
              "if the parser state is '([\"', then the parser is in the middle of parsing a "
 | 
			
		||||
              "string inside of square brackets inside parentheses. Can be used to augment a REPL prompt.\n\n"
 | 
			
		||||
              "* :frames - Each table in the array represents a 'frame' in the parser state. Frames "
 | 
			
		||||
              "contain information about the start of the expression being parsed as well as the "
 | 
			
		||||
              "type of that expression and some type-specific information.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    const uint8_t *key = NULL;
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
@@ -1190,7 +1275,11 @@ static Janet cfun_parse_state(int32_t argc, Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_parse_clone(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_parse_clone,
 | 
			
		||||
              "(parser/clone p)",
 | 
			
		||||
              "Creates a deep clone of a parser that is identical to the input parser. "
 | 
			
		||||
              "This cloned parser can be used to continue parsing from a good checkpoint "
 | 
			
		||||
              "if parsing later fails. Returns a new parser.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *src = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    JanetParser *dest = janet_abstract(&janet_parser_type, sizeof(JanetParser));
 | 
			
		||||
@@ -1225,105 +1314,23 @@ static Janet parsernext(void *p, Janet key) {
 | 
			
		||||
    return janet_nextmethod(parser_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg parse_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "parser/new", cfun_parse_parser,
 | 
			
		||||
        JDOC("(parser/new)\n\n"
 | 
			
		||||
             "Creates and returns a new parser object. Parsers are state machines "
 | 
			
		||||
             "that can receive bytes, and generate a stream of values.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/clone", cfun_parse_clone,
 | 
			
		||||
        JDOC("(parser/clone p)\n\n"
 | 
			
		||||
             "Creates a deep clone of a parser that is identical to the input parser. "
 | 
			
		||||
             "This cloned parser can be used to continue parsing from a good checkpoint "
 | 
			
		||||
             "if parsing later fails. Returns a new parser.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/has-more", cfun_parse_has_more,
 | 
			
		||||
        JDOC("(parser/has-more parser)\n\n"
 | 
			
		||||
             "Check if the parser has more values in the value queue.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/produce", cfun_parse_produce,
 | 
			
		||||
        JDOC("(parser/produce parser &opt wrap)\n\n"
 | 
			
		||||
             "Dequeue the next value in the parse queue. Will return nil if "
 | 
			
		||||
             "no parsed values are in the queue, otherwise will dequeue the "
 | 
			
		||||
             "next value. If `wrap` is truthy, will return a 1-element tuple that "
 | 
			
		||||
             "wraps the result. This tuple can be used for source-mapping "
 | 
			
		||||
             "purposes.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/consume", cfun_parse_consume,
 | 
			
		||||
        JDOC("(parser/consume parser bytes &opt index)\n\n"
 | 
			
		||||
             "Input bytes into the parser and parse them. Will not throw errors "
 | 
			
		||||
             "if there is a parse error. Starts at the byte index given by index. Returns "
 | 
			
		||||
             "the number of bytes read.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/byte", cfun_parse_byte,
 | 
			
		||||
        JDOC("(parser/byte parser b)\n\n"
 | 
			
		||||
             "Input a single byte into the parser byte stream. Returns the parser.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/error", cfun_parse_error,
 | 
			
		||||
        JDOC("(parser/error parser)\n\n"
 | 
			
		||||
             "If the parser is in the error state, returns the message associated with "
 | 
			
		||||
             "that error. Otherwise, returns nil. Also flushes the parser state and parser "
 | 
			
		||||
             "queue, so be sure to handle everything in the queue before calling "
 | 
			
		||||
             "parser/error.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/status", cfun_parse_status,
 | 
			
		||||
        JDOC("(parser/status parser)\n\n"
 | 
			
		||||
             "Gets the current status of the parser state machine. The status will "
 | 
			
		||||
             "be one of:\n\n"
 | 
			
		||||
             "* :pending - a value is being parsed.\n\n"
 | 
			
		||||
             "* :error - a parsing error was encountered.\n\n"
 | 
			
		||||
             "* :root - the parser can either read more values or safely terminate.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/flush", cfun_parse_flush,
 | 
			
		||||
        JDOC("(parser/flush parser)\n\n"
 | 
			
		||||
             "Clears the parser state and parse queue. Can be used to reset the parser "
 | 
			
		||||
             "if an error was encountered. Does not reset the line and column counter, so "
 | 
			
		||||
             "to begin parsing in a new context, create a new parser.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/state", cfun_parse_state,
 | 
			
		||||
        JDOC("(parser/state parser &opt key)\n\n"
 | 
			
		||||
             "Returns a representation of the internal state of the parser. If a key is passed, "
 | 
			
		||||
             "only that information about the state is returned. Allowed keys are:\n\n"
 | 
			
		||||
             "* :delimiters - Each byte in the string represents a nested data structure. For example, "
 | 
			
		||||
             "if the parser state is '([\"', then the parser is in the middle of parsing a "
 | 
			
		||||
             "string inside of square brackets inside parentheses. Can be used to augment a REPL prompt.\n\n"
 | 
			
		||||
             "* :frames - Each table in the array represents a 'frame' in the parser state. Frames "
 | 
			
		||||
             "contain information about the start of the expression being parsed as well as the "
 | 
			
		||||
             "type of that expression and some type-specific information.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/where", cfun_parse_where,
 | 
			
		||||
        JDOC("(parser/where parser &opt line col)\n\n"
 | 
			
		||||
             "Returns the current line number and column of the parser's internal state. If line is "
 | 
			
		||||
             "provided, the current line number of the parser is first set to that value. If column is "
 | 
			
		||||
             "also provided, the current column number of the parser is also first set to that value.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/eof", cfun_parse_eof,
 | 
			
		||||
        JDOC("(parser/eof parser)\n\n"
 | 
			
		||||
             "Indicate that the end of file was reached to the parser. This puts the parser in the :dead state.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "parser/insert", cfun_parse_insert,
 | 
			
		||||
        JDOC("(parser/insert parser value)\n\n"
 | 
			
		||||
             "Insert a value into the parser. This means that the parser state can be manipulated "
 | 
			
		||||
             "in between chunks of bytes. This would allow a user to add extra elements to arrays "
 | 
			
		||||
             "and tuples, for example. Returns the parser.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Load the library */
 | 
			
		||||
void janet_lib_parse(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, parse_cfuns);
 | 
			
		||||
    JanetRegExt parse_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("parser/new", cfun_parse_parser),
 | 
			
		||||
        JANET_CORE_REG("parser/clone", cfun_parse_clone),
 | 
			
		||||
        JANET_CORE_REG("parser/has-more", cfun_parse_has_more),
 | 
			
		||||
        JANET_CORE_REG("parser/produce", cfun_parse_produce),
 | 
			
		||||
        JANET_CORE_REG("parser/consume", cfun_parse_consume),
 | 
			
		||||
        JANET_CORE_REG("parser/byte", cfun_parse_byte),
 | 
			
		||||
        JANET_CORE_REG("parser/error", cfun_parse_error),
 | 
			
		||||
        JANET_CORE_REG("parser/status", cfun_parse_status),
 | 
			
		||||
        JANET_CORE_REG("parser/flush", cfun_parse_flush),
 | 
			
		||||
        JANET_CORE_REG("parser/state", cfun_parse_state),
 | 
			
		||||
        JANET_CORE_REG("parser/where", cfun_parse_where),
 | 
			
		||||
        JANET_CORE_REG("parser/eof", cfun_parse_eof),
 | 
			
		||||
        JANET_CORE_REG("parser/insert", cfun_parse_insert),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, parse_cfuns);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										421
									
								
								src/core/peg.c
									
									
									
									
									
								
							
							
						
						
									
										421
									
								
								src/core/peg.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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;
 | 
			
		||||
@@ -211,9 +215,10 @@ tail:
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_SET: {
 | 
			
		||||
            if (text >= s->text_end) return NULL;
 | 
			
		||||
            uint32_t word = rule[1 + (text[0] >> 5)];
 | 
			
		||||
            uint32_t mask = (uint32_t)1 << (text[0] & 0x1F);
 | 
			
		||||
            return (text < s->text_end && (word & mask))
 | 
			
		||||
            return (word & mask)
 | 
			
		||||
                   ? text + 1
 | 
			
		||||
                   : NULL;
 | 
			
		||||
        }
 | 
			
		||||
@@ -260,30 +265,52 @@ tail:
 | 
			
		||||
            goto tail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_IF:
 | 
			
		||||
        case RULE_IFNOT: {
 | 
			
		||||
        case RULE_IF: {
 | 
			
		||||
            const uint32_t *rule_a = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_b = s->bytecode + rule[2];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, rule_a, text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (rule[0] == RULE_IF ? !result : !!result) return NULL;
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            rule = rule_b;
 | 
			
		||||
            goto tail;
 | 
			
		||||
        }
 | 
			
		||||
        case RULE_IFNOT: {
 | 
			
		||||
            const uint32_t *rule_a = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_b = s->bytecode + rule[2];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            CapState cs = cap_save(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, rule_a, text);
 | 
			
		||||
            if (!!result) {
 | 
			
		||||
                up1(s);
 | 
			
		||||
                return NULL;
 | 
			
		||||
            } else {
 | 
			
		||||
                cap_load(s, cs);
 | 
			
		||||
                up1(s);
 | 
			
		||||
                rule = rule_b;
 | 
			
		||||
                goto tail;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_NOT: {
 | 
			
		||||
            const uint32_t *rule_a = s->bytecode + rule[1];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            CapState cs = cap_save(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, rule_a, text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            return (result) ? NULL : text;
 | 
			
		||||
            if (result) {
 | 
			
		||||
                up1(s);
 | 
			
		||||
                return NULL;
 | 
			
		||||
            } else {
 | 
			
		||||
                cap_load(s, cs);
 | 
			
		||||
                up1(s);
 | 
			
		||||
                return text;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_THRU:
 | 
			
		||||
        case RULE_TO: {
 | 
			
		||||
            const uint32_t *rule_a = s->bytecode + rule[1];
 | 
			
		||||
            const uint8_t *next_text;
 | 
			
		||||
            const uint8_t *next_text = NULL;
 | 
			
		||||
            CapState cs = cap_save(s);
 | 
			
		||||
            down1(s);
 | 
			
		||||
            while (text <= s->text_end) {
 | 
			
		||||
@@ -293,6 +320,7 @@ tail:
 | 
			
		||||
                    if (rule[0] == RULE_TO) cap_load(s, cs2);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                cap_load(s, cs2);
 | 
			
		||||
                text++;
 | 
			
		||||
            }
 | 
			
		||||
            up1(s);
 | 
			
		||||
@@ -314,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;
 | 
			
		||||
                }
 | 
			
		||||
@@ -387,6 +415,25 @@ tail:
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_CAPTURE_NUM: {
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            /* check number parsing */
 | 
			
		||||
            double x = 0.0;
 | 
			
		||||
            int32_t base = (int32_t) rule[2];
 | 
			
		||||
            if (janet_scan_number_base(text, (int32_t)(result - text), base, &x)) return NULL;
 | 
			
		||||
            /* Specialized pushcap - avoid intermediate string creation */
 | 
			
		||||
            if (!s->has_backref && s->mode == PEG_MODE_ACCUMULATE) {
 | 
			
		||||
                janet_buffer_push_bytes(s->scratch, text, (int32_t)(result - text));
 | 
			
		||||
            } else {
 | 
			
		||||
                uint32_t tag = rule[3];
 | 
			
		||||
                pushcap(s, janet_wrap_number(x), tag);
 | 
			
		||||
            }
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_ACCUMULATE: {
 | 
			
		||||
            uint32_t tag = rule[2];
 | 
			
		||||
            int oldmode = s->mode;
 | 
			
		||||
@@ -418,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;
 | 
			
		||||
@@ -439,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];
 | 
			
		||||
@@ -558,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 {
 | 
			
		||||
@@ -952,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) {
 | 
			
		||||
@@ -975,6 +1160,34 @@ 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);
 | 
			
		||||
    uint32_t base = 0;
 | 
			
		||||
    if (argc >= 2) {
 | 
			
		||||
        if (!janet_checktype(argv[1], JANET_NIL)) {
 | 
			
		||||
            if (!janet_checkint(argv[1])) goto error;
 | 
			
		||||
            base = (uint32_t) janet_unwrap_integer(argv[1]);
 | 
			
		||||
            if (base < 2 || base > 36) goto error;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
 | 
			
		||||
    uint32_t rule = peg_compile1(b, argv[0]);
 | 
			
		||||
    emit_3(r, RULE_CAPTURE_NUM, rule, base, tag);
 | 
			
		||||
    return;
 | 
			
		||||
error:
 | 
			
		||||
    peg_panicf(b, "expected integer between 2 and 36, got %v", argv[1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void spec_reference(Builder *b, int32_t argc, const Janet *argv) {
 | 
			
		||||
    peg_arity(b, argc, 1, 2);
 | 
			
		||||
    Reserve r = reserve(b, 3);
 | 
			
		||||
@@ -1038,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
 | 
			
		||||
@@ -1118,6 +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},
 | 
			
		||||
@@ -1127,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},
 | 
			
		||||
@@ -1198,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);
 | 
			
		||||
@@ -1214,6 +1464,23 @@ 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));
 | 
			
		||||
            new_grammar->proto = grammar;
 | 
			
		||||
            b->grammar = grammar = new_grammar;
 | 
			
		||||
            /* Run the main rule */
 | 
			
		||||
            Janet main_rule = janet_table_rawget(grammar, janet_ckeywordv("main"));
 | 
			
		||||
            if (janet_checktype(main_rule, JANET_NIL))
 | 
			
		||||
                peg_panic(b, "grammar requires :main rule");
 | 
			
		||||
            rule = peg_compile1(b, main_rule);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_STRUCT: {
 | 
			
		||||
            /* Build grammar table */
 | 
			
		||||
            const JanetKV *st = janet_unwrap_struct(peg);
 | 
			
		||||
@@ -1349,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;
 | 
			
		||||
@@ -1419,6 +1686,12 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
 | 
			
		||||
                if (rule[1] >= clen) goto bad;
 | 
			
		||||
                i += 3;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_CAPTURE_NUM:
 | 
			
		||||
                /* [rule, base, tag] */
 | 
			
		||||
                if (rule[1] >= blen) goto bad;
 | 
			
		||||
                op_flags[rule[1]] |= 0x01;
 | 
			
		||||
                i += 4;
 | 
			
		||||
                break;
 | 
			
		||||
            case RULE_ACCUMULATE:
 | 
			
		||||
            case RULE_GROUP:
 | 
			
		||||
            case RULE_CAPTURE:
 | 
			
		||||
@@ -1436,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:
 | 
			
		||||
@@ -1447,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1541,7 +1831,11 @@ static JanetPeg *compile_peg(Janet x) {
 | 
			
		||||
 * C Functions
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_compile(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_compile,
 | 
			
		||||
              "(peg/compile peg)",
 | 
			
		||||
              "Compiles a peg source data structure into a <core/peg>. This will speed up matching "
 | 
			
		||||
              "if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to supplement "
 | 
			
		||||
              "the grammar of the peg for otherwise undefined peg keywords.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetPeg *peg = compile_peg(argv[0]);
 | 
			
		||||
    return janet_wrap_abstract(peg);
 | 
			
		||||
@@ -1552,7 +1846,7 @@ typedef struct {
 | 
			
		||||
    JanetPeg *peg;
 | 
			
		||||
    PegState s;
 | 
			
		||||
    JanetByteView bytes;
 | 
			
		||||
    JanetByteView repl;
 | 
			
		||||
    Janet subst;
 | 
			
		||||
    int32_t start;
 | 
			
		||||
} PegCall;
 | 
			
		||||
 | 
			
		||||
@@ -1560,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]);
 | 
			
		||||
@@ -1568,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);
 | 
			
		||||
@@ -1585,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);
 | 
			
		||||
@@ -1599,18 +1894,25 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void peg_call_reset(PegCall *c) {
 | 
			
		||||
    c->s.depth = JANET_RECURSION_GUARD;
 | 
			
		||||
    c->s.captures->count = 0;
 | 
			
		||||
    c->s.tagged_captures->count = 0;
 | 
			
		||||
    c->s.scratch->count = 0;
 | 
			
		||||
    c->s.tags->count = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_match(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_match,
 | 
			
		||||
              "(peg/match peg text &opt start & args)",
 | 
			
		||||
              "Match a Parsing Expression Grammar to a byte string and return an array of captured values. "
 | 
			
		||||
              "Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + c.start);
 | 
			
		||||
    return result ? janet_wrap_array(c.s.captures) : janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_find(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_find,
 | 
			
		||||
              "(peg/find peg text &opt start & args)",
 | 
			
		||||
              "Find first index where the peg matches in text. Returns an integer, or nil if not found.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    for (int32_t i = c.start; i < c.bytes.len; i++) {
 | 
			
		||||
        peg_call_reset(&c);
 | 
			
		||||
@@ -1620,7 +1922,9 @@ static Janet cfun_peg_find(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_find_all(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_find_all,
 | 
			
		||||
              "(peg/find-all peg text &opt start & args)",
 | 
			
		||||
              "Find all indexes where the peg matches in text. Returns an array of integers.") {
 | 
			
		||||
    PegCall c = peg_cfun_init(argc, argv, 0);
 | 
			
		||||
    JanetArray *ret = janet_array(0);
 | 
			
		||||
    for (int32_t i = c.start; i < c.bytes.len; i++) {
 | 
			
		||||
@@ -1644,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;
 | 
			
		||||
@@ -1659,11 +1964,22 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
 | 
			
		||||
    return janet_wrap_buffer(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_replace_all(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace_all,
 | 
			
		||||
              "(peg/replace-all peg 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet cfun_peg_replace(int32_t argc, Janet *argv) {
 | 
			
		||||
JANET_CORE_FN(cfun_peg_replace,
 | 
			
		||||
              "(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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1688,47 +2004,18 @@ static Janet peg_next(void *p, Janet key) {
 | 
			
		||||
    return janet_nextmethod(peg_methods, key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const JanetReg peg_cfuns[] = {
 | 
			
		||||
    {
 | 
			
		||||
        "peg/compile", cfun_peg_compile,
 | 
			
		||||
        JDOC("(peg/compile peg)\n\n"
 | 
			
		||||
             "Compiles a peg source data structure into a <core/peg>. This will speed up matching "
 | 
			
		||||
             "if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to suppliment "
 | 
			
		||||
             "the grammar of the peg for otherwise undefined peg keywords.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/match", cfun_peg_match,
 | 
			
		||||
        JDOC("(peg/match peg text &opt start & args)\n\n"
 | 
			
		||||
             "Match a Parsing Expression Grammar to a byte string and return an array of captured values. "
 | 
			
		||||
             "Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/find", cfun_peg_find,
 | 
			
		||||
        JDOC("(peg/find peg text &opt start & args)\n\n"
 | 
			
		||||
             "Find first index where the peg matches in text. Returns an integer, or nil if not found.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/find-all", cfun_peg_find_all,
 | 
			
		||||
        JDOC("(peg/find-all peg text &opt start & args)\n\n"
 | 
			
		||||
             "Find all indexes where the peg matches in text. Returns an array of integers.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/replace", cfun_peg_replace,
 | 
			
		||||
        JDOC("(peg/replace peg repl text &opt start & args)\n\n"
 | 
			
		||||
             "Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. "
 | 
			
		||||
             "If no matches are found, returns the input string in a new buffer.")
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "peg/replace-all", cfun_peg_replace_all,
 | 
			
		||||
        JDOC("(peg/replace-all peg repl text &opt start & args)\n\n"
 | 
			
		||||
             "Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.")
 | 
			
		||||
    },
 | 
			
		||||
    {NULL, NULL, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Load the peg module */
 | 
			
		||||
void janet_lib_peg(JanetTable *env) {
 | 
			
		||||
    janet_core_cfuns(env, NULL, peg_cfuns);
 | 
			
		||||
    JanetRegExt cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("peg/compile", cfun_peg_compile),
 | 
			
		||||
        JANET_CORE_REG("peg/match", cfun_peg_match),
 | 
			
		||||
        JANET_CORE_REG("peg/find", cfun_peg_find),
 | 
			
		||||
        JANET_CORE_REG("peg/find-all", cfun_peg_find_all),
 | 
			
		||||
        JANET_CORE_REG("peg/replace", cfun_peg_replace),
 | 
			
		||||
        JANET_CORE_REG("peg/replace-all", cfun_peg_replace_all),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, cfuns);
 | 
			
		||||
    janet_register_abstract_type(&janet_peg_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										181
									
								
								src/core/pp.c
									
									
									
									
									
								
							
							
						
						
									
										181
									
								
								src/core/pp.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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;
 | 
			
		||||
@@ -227,12 +239,14 @@ void janet_to_string_b(JanetBuffer *buffer, Janet x) {
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
        case JANET_CFUNCTION: {
 | 
			
		||||
            Janet check = janet_table_get(janet_vm_registry, x);
 | 
			
		||||
            if (janet_checktype(check, JANET_SYMBOL)) {
 | 
			
		||||
            JanetCFunRegistry *reg = janet_registry_get(janet_unwrap_cfunction(x));
 | 
			
		||||
            if (NULL != reg) {
 | 
			
		||||
                janet_buffer_push_cstring(buffer, "<cfunction ");
 | 
			
		||||
                janet_buffer_push_bytes(buffer,
 | 
			
		||||
                                        janet_unwrap_symbol(check),
 | 
			
		||||
                                        janet_string_length(janet_unwrap_symbol(check)));
 | 
			
		||||
                if (NULL != reg->name_prefix) {
 | 
			
		||||
                    janet_buffer_push_cstring(buffer, reg->name_prefix);
 | 
			
		||||
                    janet_buffer_push_u8(buffer, '/');
 | 
			
		||||
                }
 | 
			
		||||
                janet_buffer_push_cstring(buffer, reg->name);
 | 
			
		||||
                janet_buffer_push_u8(buffer, '>');
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
@@ -241,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 ");
 | 
			
		||||
@@ -259,21 +277,13 @@ void janet_to_string_b(JanetBuffer *buffer, Janet x) {
 | 
			
		||||
 | 
			
		||||
/* See parse.c for full table */
 | 
			
		||||
 | 
			
		||||
static const uint32_t pp_symchars[8] = {
 | 
			
		||||
    0x00000000, 0xf7ffec72, 0xc7ffffff, 0x07fffffe,
 | 
			
		||||
    0x00000000, 0x00000000, 0x00000000, 0x00000000
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int pp_is_symbol_char(uint8_t c) {
 | 
			
		||||
    return pp_symchars[c >> 5] & ((uint32_t)1 << (c & 0x1F));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if a symbol or keyword contains no symbol characters */
 | 
			
		||||
static int contains_bad_chars(const uint8_t *sym, int issym) {
 | 
			
		||||
    int32_t len = janet_string_length(sym);
 | 
			
		||||
    if (len && issym && sym[0] >= '0' && sym[0] <= '9') return 1;
 | 
			
		||||
    if (!janet_valid_utf8(sym, len)) return 1;
 | 
			
		||||
    for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
        if (!pp_is_symbol_char(sym[i])) return 1;
 | 
			
		||||
        if (!janet_is_symbol_char(sym[i])) return 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -369,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:
 | 
			
		||||
@@ -568,12 +580,12 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
 | 
			
		||||
        case JANET_STRUCT:
 | 
			
		||||
        case JANET_TABLE: {
 | 
			
		||||
            int istable = janet_checktype(x, JANET_TABLE);
 | 
			
		||||
            janet_buffer_push_cstring(S->buffer, istable ? "@" : "{");
 | 
			
		||||
 | 
			
		||||
            /* For object-like tables, print class name */
 | 
			
		||||
            if (istable) {
 | 
			
		||||
                JanetTable *t = janet_unwrap_table(x);
 | 
			
		||||
                JanetTable *proto = t->proto;
 | 
			
		||||
                janet_buffer_push_cstring(S->buffer, "@");
 | 
			
		||||
                if (NULL != proto) {
 | 
			
		||||
                    Janet name = janet_table_get(proto, janet_ckeywordv("_name"));
 | 
			
		||||
                    const uint8_t *n;
 | 
			
		||||
@@ -588,8 +600,25 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                janet_buffer_push_cstring(S->buffer, "{");
 | 
			
		||||
            } else {
 | 
			
		||||
                JanetStruct st = janet_unwrap_struct(x);
 | 
			
		||||
                JanetStruct proto = janet_struct_proto(st);
 | 
			
		||||
                if (NULL != proto) {
 | 
			
		||||
                    Janet name = janet_struct_get(proto, janet_ckeywordv("_name"));
 | 
			
		||||
                    const uint8_t *n;
 | 
			
		||||
                    int32_t len;
 | 
			
		||||
                    if (janet_bytes_view(name, &n, &len)) {
 | 
			
		||||
                        if (S->flags & JANET_PRETTY_COLOR) {
 | 
			
		||||
                            janet_buffer_push_cstring(S->buffer, janet_class_color);
 | 
			
		||||
                        }
 | 
			
		||||
                        janet_buffer_push_bytes(S->buffer, n, len);
 | 
			
		||||
                        if (S->flags & JANET_PRETTY_COLOR) {
 | 
			
		||||
                            janet_buffer_push_cstring(S->buffer, "\x1B[0m");
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            janet_buffer_push_cstring(S->buffer, "{");
 | 
			
		||||
 | 
			
		||||
            S->depth--;
 | 
			
		||||
            S->indent += 2;
 | 
			
		||||
@@ -625,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;
 | 
			
		||||
@@ -724,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]);
 | 
			
		||||
        }
 | 
			
		||||
@@ -739,20 +768,48 @@ static void pushtypes(JanetBuffer *buffer, int types) {
 | 
			
		||||
 | 
			
		||||
#define MAX_ITEM  256
 | 
			
		||||
#define FMT_FLAGS "-+ #0"
 | 
			
		||||
#define FMT_REPLACE_INTTYPES "diouxX"
 | 
			
		||||
#define MAX_FORMAT 32
 | 
			
		||||
 | 
			
		||||
struct FmtMapping {
 | 
			
		||||
    char c;
 | 
			
		||||
    const char *mapping;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Janet uses fixed width integer types for most things, so map
 | 
			
		||||
 * format specifiers to these fixed sizes */
 | 
			
		||||
static const struct FmtMapping format_mappings[] = {
 | 
			
		||||
    {'D', PRId64},
 | 
			
		||||
    {'I', PRIi64},
 | 
			
		||||
    {'d', PRId64},
 | 
			
		||||
    {'i', PRIi64},
 | 
			
		||||
    {'o', PRIo64},
 | 
			
		||||
    {'u', PRIu64},
 | 
			
		||||
    {'x', PRIx64},
 | 
			
		||||
    {'X', PRIX64},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char *get_fmt_mapping(char c) {
 | 
			
		||||
    for (size_t i = 0; i < (sizeof(format_mappings) / sizeof(struct FmtMapping)); i++) {
 | 
			
		||||
        if (format_mappings[i].c == c)
 | 
			
		||||
            return format_mappings[i].mapping;
 | 
			
		||||
    }
 | 
			
		||||
    janet_assert(0, "bad format mapping");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *scanformat(
 | 
			
		||||
    const char *strfrmt,
 | 
			
		||||
    char *form,
 | 
			
		||||
    char width[3],
 | 
			
		||||
    char precision[3]) {
 | 
			
		||||
    const char *p = strfrmt;
 | 
			
		||||
 | 
			
		||||
    /* Parse strfrmt */
 | 
			
		||||
    memset(width, '\0', 3);
 | 
			
		||||
    memset(precision, '\0', 3);
 | 
			
		||||
    while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL)
 | 
			
		||||
        p++; /* skip flags */
 | 
			
		||||
    if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS) / sizeof(char))
 | 
			
		||||
        janet_panic("invalid format (repeated flags)");
 | 
			
		||||
    if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS)) janet_panic("invalid format (repeated flags)");
 | 
			
		||||
    if (isdigit((int)(*p)))
 | 
			
		||||
        width[0] = *p++; /* skip width */
 | 
			
		||||
    if (isdigit((int)(*p)))
 | 
			
		||||
@@ -766,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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -789,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;
 | 
			
		||||
                }
 | 
			
		||||
@@ -842,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;
 | 
			
		||||
                }
 | 
			
		||||
@@ -881,7 +962,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (nb >= MAX_ITEM)
 | 
			
		||||
                janet_panicf("format buffer overflow", form);
 | 
			
		||||
                janet_panic("format buffer overflow");
 | 
			
		||||
            if (nb > 0)
 | 
			
		||||
                janet_buffer_push_bytes(b, (uint8_t *) item, nb);
 | 
			
		||||
        }
 | 
			
		||||
@@ -951,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;
 | 
			
		||||
                }
 | 
			
		||||
@@ -972,18 +1060,11 @@ void janet_buffer_format(
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 's': {
 | 
			
		||||
                    const uint8_t *s = janet_getstring(argv, arg);
 | 
			
		||||
                    int32_t l = janet_string_length(s);
 | 
			
		||||
                    const char *s = janet_getcbytes(argv, arg);
 | 
			
		||||
                    if (form[2] == '\0')
 | 
			
		||||
                        janet_buffer_push_bytes(b, s, l);
 | 
			
		||||
                        janet_buffer_push_cstring(b, s);
 | 
			
		||||
                    else {
 | 
			
		||||
                        if (l != (int32_t) strlen((const char *) s))
 | 
			
		||||
                            janet_panic("string contains zeros");
 | 
			
		||||
                        if (!strchr(form, '.') && l >= 100) {
 | 
			
		||||
                            janet_panic("no precision and string is too long to be formatted");
 | 
			
		||||
                        } else {
 | 
			
		||||
                            nb = snprintf(item, MAX_ITEM, form, s);
 | 
			
		||||
                        }
 | 
			
		||||
                        nb = snprintf(item, MAX_ITEM, form, s);
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
@@ -1033,7 +1114,7 @@ void janet_buffer_format(
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (nb >= MAX_ITEM)
 | 
			
		||||
                janet_panicf("format buffer overflow", form);
 | 
			
		||||
                janet_panic("format buffer overflow");
 | 
			
		||||
            if (nb > 0)
 | 
			
		||||
                janet_buffer_push_bytes(b, (uint8_t *) item, nb);
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 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) 2021 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) 2021 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,47 +23,60 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Run a string */
 | 
			
		||||
/* Run a string of code. The return value is a set of error flags, JANET_DO_ERROR_RUNTIME, JANET_DO_ERROR_COMPILE, and JANET_DOR_ERROR_PARSE if
 | 
			
		||||
 * any errors were encountered in those phases. More information is printed to stderr. */
 | 
			
		||||
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) {
 | 
			
		||||
                    janet_stacktrace(fiber, ret);
 | 
			
		||||
                    errflags |= 0x01;
 | 
			
		||||
                    janet_stacktrace_ext(fiber, ret, "");
 | 
			
		||||
                    errflags |= JANET_DO_ERROR_RUNTIME;
 | 
			
		||||
                    done = 1;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                ret = janet_wrap_string(cres.error);
 | 
			
		||||
                if (cres.macrofiber) {
 | 
			
		||||
                    janet_eprintf("compile error in %s: ", sourcePath);
 | 
			
		||||
                    janet_stacktrace(cres.macrofiber, ret);
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf("compile error in %s: %s\n", sourcePath,
 | 
			
		||||
                                  (const char *)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;
 | 
			
		||||
                }
 | 
			
		||||
                errflags |= 0x02;
 | 
			
		||||
                if (cres.macrofiber) {
 | 
			
		||||
                    janet_eprintf("%s:%d:%d: compile error", sourcePath,
 | 
			
		||||
                                  line, col);
 | 
			
		||||
                    janet_stacktrace_ext(cres.macrofiber, ret, "");
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
 | 
			
		||||
                                  line, col, (const char *)cres.error);
 | 
			
		||||
                }
 | 
			
		||||
                errflags |= JANET_DO_ERROR_COMPILE;
 | 
			
		||||
                done = 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -71,24 +84,26 @@ 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);
 | 
			
		||||
                errflags |= 0x04;
 | 
			
		||||
                const char *e = janet_parser_error(parser);
 | 
			
		||||
                errflags |= JANET_DO_ERROR_PARSE;
 | 
			
		||||
                ret = janet_cstringv(e);
 | 
			
		||||
                janet_eprintf("parse error in %s: %s\n", sourcePath, e);
 | 
			
		||||
                int32_t line = (int32_t) parser->line;
 | 
			
		||||
                int32_t col = (int32_t) parser->column;
 | 
			
		||||
                janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
 | 
			
		||||
                done = 1;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -96,8 +111,21 @@ 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) {
 | 
			
		||||
        if (fiber) {
 | 
			
		||||
            janet_gcroot(janet_wrap_fiber(fiber));
 | 
			
		||||
        }
 | 
			
		||||
        janet_loop();
 | 
			
		||||
        if (fiber) {
 | 
			
		||||
            janet_gcunroot(janet_wrap_fiber(fiber));
 | 
			
		||||
            ret = fiber->last_value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (out) *out = ret;
 | 
			
		||||
    return errflags;
 | 
			
		||||
}
 | 
			
		||||
@@ -108,3 +136,19 @@ int janet_dostring(JanetTable *env, const char *str, const char *sourcePath, Jan
 | 
			
		||||
    return janet_dobytes(env, (const uint8_t *)str, len, sourcePath, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Run a fiber to completion (use event loop if enabled). Return the status. */
 | 
			
		||||
int janet_loop_fiber(JanetFiber *fiber) {
 | 
			
		||||
    int status;
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    janet_schedule(fiber, janet_wrap_nil());
 | 
			
		||||
    janet_loop();
 | 
			
		||||
    status = janet_fiber_status(fiber);
 | 
			
		||||
#else
 | 
			
		||||
    Janet out;
 | 
			
		||||
    status = janet_continue(fiber, janet_wrap_nil(), &out);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
 | 
			
		||||
        janet_stacktrace_ext(fiber, out, "");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return status;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user