mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 09:33:02 +00:00 
			
		
		
		
	Compare commits
	
		
			337 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					894cd0e022 | ||
| 
						 | 
					db2c63fffc | ||
| 
						 | 
					60e0f32f1a | ||
| 
						 | 
					e731996a68 | ||
| 
						 | 
					2f69cd4209 | ||
| 
						 | 
					fd59de25c5 | ||
| 
						 | 
					af12c3d41a | ||
| 
						 | 
					54b52bbeb5 | ||
| 
						 | 
					1174c68d9a | ||
| 
						 | 
					448ea7167f | ||
| 
						 | 
					6b27008c99 | ||
| 
						 | 
					725c785882 | ||
| 
						 | 
					ab068cff67 | ||
| 
						 | 
					9dc03adfda | ||
| 
						 | 
					49f9e4eddf | ||
| 
						 | 
					43c47ac44c | ||
| 
						 | 
					1cebe64664 | ||
| 
						 | 
					f33c381043 | ||
| 
						 | 
					3479841c77 | ||
| 
						 | 
					6a899968a9 | ||
| 
						 | 
					bb8405a36e | ||
| 
						 | 
					c7bc711f63 | ||
| 
						 | 
					e326071c35 | ||
| 
						 | 
					ad6a669381 | ||
| 
						 | 
					e4c9dafc9a | ||
| 
						 | 
					dfc0aefd87 | ||
| 
						 | 
					356b39c6f5 | ||
| 
						 | 
					8da7bb6b68 | ||
| 
						 | 
					9341081a4d | ||
| 
						 | 
					324a086eb4 | ||
| 
						 | 
					ed595f52c2 | ||
| 
						 | 
					64ad0023bb | ||
| 
						 | 
					fe5f661d15 | ||
| 
						 | 
					ff26e3a8ba | ||
| 
						 | 
					14657a762c | ||
| 
						 | 
					4754fa3902 | ||
| 
						 | 
					f302f87337 | ||
| 
						 | 
					94dbcde292 | ||
| 
						 | 
					4336a174b1 | ||
| 
						 | 
					0adb13ed71 | ||
| 
						 | 
					03ba1f7021 | ||
| 
						 | 
					1f7f20788c | ||
| 
						 | 
					c59dd29190 | ||
| 
						 | 
					99f63a41a3 | ||
| 
						 | 
					a575f5df36 | ||
| 
						 | 
					0817e627ee | ||
| 
						 | 
					14d90239a7 | ||
| 
						 | 
					f5d11dc656 | ||
| 
						 | 
					6dcf5bf077 | ||
| 
						 | 
					ac2082e9b3 | ||
| 
						 | 
					dbac495bee | ||
| 
						 | 
					fe5ccb163e | ||
| 
						 | 
					1aea5ee007 | ||
| 
						 | 
					13cd9f8067 | ||
| 
						 | 
					34496ecaf0 | ||
| 
						 | 
					c043b1d949 | ||
| 
						 | 
					9a6d2a7b32 | ||
| 
						 | 
					f8a9efa8e4 | ||
| 
						 | 
					5b2169e0d1 | ||
| 
						 | 
					2c927ea768 | ||
| 
						 | 
					f4bbcdcbc8 | ||
| 
						 | 
					79c375b1af | ||
| 
						 | 
					f443a3b3a1 | ||
| 
						 | 
					684d2d63f4 | ||
| 
						 | 
					1900d8f843 | ||
| 
						 | 
					3c2af95d21 | ||
| 
						 | 
					b35414ea0f | ||
| 
						 | 
					fb5b056f7b | ||
| 
						 | 
					7248c1dfdb | ||
| 
						 | 
					4c7ea9e893 | ||
| 
						 | 
					c7801ce277 | ||
| 
						 | 
					f741a8e3ff | ||
| 
						 | 
					6a92e8b609 | ||
| 
						 | 
					9da91a8217 | ||
| 
						 | 
					69853c8e5c | ||
| 
						 | 
					1f41b6c138 | ||
| 
						 | 
					e001efa9fd | ||
| 
						 | 
					435e64d4cf | ||
| 
						 | 
					f296c8f5fb | ||
| 
						 | 
					8d0e6ed32f | ||
| 
						 | 
					b6a36afffe | ||
| 
						 | 
					e422abc269 | ||
| 
						 | 
					221d71d07b | ||
| 
						 | 
					9f35f0837e | ||
| 
						 | 
					515891b035 | ||
| 
						 | 
					94a506876f | ||
| 
						 | 
					9bde57854a | ||
| 
						 | 
					f456369941 | ||
| 
						 | 
					8f0a1ffe5d | ||
| 
						 | 
					e4bafc621a | ||
| 
						 | 
					cfa39ab3b0 | ||
| 
						 | 
					47e91bfd89 | ||
| 
						 | 
					eecc388ebd | ||
| 
						 | 
					0a15a5ee56 | ||
| 
						 | 
					cfaae47cea | ||
| 
						 | 
					c1a0352592 | ||
| 
						 | 
					965f45aa3f | ||
| 
						 | 
					6ea27fe836 | ||
| 
						 | 
					0dccc22b38 | ||
| 
						 | 
					cbe833962b | ||
| 
						 | 
					b5720f6f10 | ||
| 
						 | 
					56b4e0b0ec | ||
| 
						 | 
					e316ccb1e0 | ||
| 
						 | 
					a6f93efd39 | ||
| 
						 | 
					20511cf608 | ||
| 
						 | 
					1a1dd39367 | ||
| 
						 | 
					589981bdcb | ||
| 
						 | 
					89546776b2 | ||
| 
						 | 
					f0d7b3cd12 | ||
| 
						 | 
					e37be627e0 | ||
| 
						 | 
					d803561582 | ||
| 
						 | 
					a1aab4008f | ||
| 
						 | 
					a1172529bf | ||
| 
						 | 
					1d905bf07f | ||
| 
						 | 
					eed678a14b | ||
| 
						 | 
					b1bdffbc34 | ||
| 
						 | 
					cff718f37d | ||
| 
						 | 
					40e9430278 | ||
| 
						 | 
					62fc55fc74 | ||
| 
						 | 
					80729353c8 | ||
| 
						 | 
					105ba5e124 | ||
| 
						 | 
					ad1b50d1f5 | ||
| 
						 | 
					1905437abe | ||
| 
						 | 
					87fc339c45 | ||
| 
						 | 
					3af7d61d3e | ||
| 
						 | 
					a45ef7a856 | ||
| 
						 | 
					299998055d | ||
| 
						 | 
					c9586d39ed | ||
| 
						 | 
					2e9f67f4e4 | ||
| 
						 | 
					e318170fea | ||
| 
						 | 
					73c4289792 | ||
| 
						 | 
					ea45d7ee47 | ||
| 
						 | 
					6d970725e7 | ||
| 
						 | 
					458c2c6d88 | ||
| 
						 | 
					0cc53a8964 | ||
| 
						 | 
					0bc96304a9 | ||
| 
						 | 
					c75b088ff8 | ||
| 
						 | 
					181f0341f5 | ||
| 
						 | 
					33bb08d53b | ||
| 
						 | 
					6d188f6e44 | ||
| 
						 | 
					c3648331f1 | ||
| 
						 | 
					a5b66029d3 | ||
| 
						 | 
					49bfe80191 | ||
| 
						 | 
					a5def77bfe | ||
| 
						 | 
					9ecb5b4791 | ||
| 
						 | 
					1cc48a370a | ||
| 
						 | 
					f1ec8d1e11 | ||
| 
						 | 
					55c34cd84f | ||
| 
						 | 
					aca52d1e36 | ||
| 
						 | 
					6f90df26a5 | ||
| 
						 | 
					9d9cb378ff | ||
| 
						 | 
					f92aac14aa | ||
| 
						 | 
					3f27d78ab5 | ||
| 
						 | 
					282d1ba22f | ||
| 
						 | 
					94c19575b1 | ||
| 
						 | 
					e3e485285b | ||
| 
						 | 
					986e36720e | ||
| 
						 | 
					74348ab6c2 | ||
| 
						 | 
					8d1ad99f42 | ||
| 
						 | 
					e69bbff195 | ||
| 
						 | 
					c9f33bbde0 | ||
| 
						 | 
					9c9f9d4fa6 | ||
| 
						 | 
					2f64a6b0cb | ||
| 
						 | 
					dfa78ad3c6 | ||
| 
						 | 
					677ae46f0c | ||
| 
						 | 
					6ada2a458f | ||
| 
						 | 
					8145f3b68d | ||
| 
						 | 
					48289acee6 | ||
| 
						 | 
					e5a989c6f9 | ||
| 
						 | 
					4c56704935 | ||
| 
						 | 
					9cda44f443 | ||
| 
						 | 
					431451bac2 | ||
| 
						 | 
					395ca7feea | ||
| 
						 | 
					e0b7533c39 | ||
| 
						 | 
					5b2a402930 | ||
| 
						 | 
					85129a1873 | ||
| 
						 | 
					487d333024 | ||
| 
						 | 
					fe7d35171f | ||
| 
						 | 
					b3aed13567 | ||
| 
						 | 
					a9d4d2bfa3 | ||
| 
						 | 
					1ff521683f | ||
| 
						 | 
					0395a03b6b | ||
| 
						 | 
					7fda7709ff | ||
| 
						 | 
					65a9200cff | ||
| 
						 | 
					473eec26c1 | ||
| 
						 | 
					9fa945ad93 | ||
| 
						 | 
					a895219d2f | ||
| 
						 | 
					427f7c362e | ||
| 
						 | 
					73f5c41fae | ||
| 
						 | 
					b4ec168401 | ||
| 
						 | 
					726d35c766 | ||
| 
						 | 
					6db796e10c | ||
| 
						 | 
					c38d9134cd | ||
| 
						 | 
					471204b163 | ||
| 
						 | 
					7f23bfa66d | ||
| 
						 | 
					9287b26042 | ||
| 
						 | 
					e22936fbf8 | ||
| 
						 | 
					04ace9fc16 | ||
| 
						 | 
					8466b333fb | ||
| 
						 | 
					96602612ba | ||
| 
						 | 
					690b98bff9 | ||
| 
						 | 
					8329131bfe | ||
| 
						 | 
					9986aab326 | ||
| 
						 | 
					0b105bc535 | ||
| 
						 | 
					51ac9c9506 | ||
| 
						 | 
					0310176696 | ||
| 
						 | 
					84a7a2bc3e | ||
| 
						 | 
					1e66a7e555 | ||
| 
						 | 
					2bffb9d682 | ||
| 
						 | 
					811125a760 | ||
| 
						 | 
					0dd91082a1 | ||
| 
						 | 
					c80587868e | ||
| 
						 | 
					8c52dc86c7 | ||
| 
						 | 
					be24592bc3 | ||
| 
						 | 
					0d1a5c621d | ||
| 
						 | 
					8a3eff3b65 | ||
| 
						 | 
					b1050b884d | ||
| 
						 | 
					181d883a1d | ||
| 
						 | 
					e01b65fd3d | ||
| 
						 | 
					bbd74b5ae2 | ||
| 
						 | 
					d5a5c49357 | ||
| 
						 | 
					a964b164a6 | ||
| 
						 | 
					1aac0489d7 | ||
| 
						 | 
					e474755887 | ||
| 
						 | 
					bf9a60f70d | ||
| 
						 | 
					a2ba0913d3 | ||
| 
						 | 
					f74df41fff | ||
| 
						 | 
					2a950e4ce9 | ||
| 
						 | 
					f05e5f908e | ||
| 
						 | 
					43139b43b1 | ||
| 
						 | 
					5811b47aad | ||
| 
						 | 
					54e3db4d8c | ||
| 
						 | 
					7491421c31 | ||
| 
						 | 
					9d0da74347 | ||
| 
						 | 
					e9870b293f | ||
| 
						 | 
					ab910d060b | ||
| 
						 | 
					b60ef68ac6 | ||
| 
						 | 
					c9986936ed | ||
| 
						 | 
					d77be46644 | ||
| 
						 | 
					3715d7a184 | ||
| 
						 | 
					1c96c7163a | ||
| 
						 | 
					9f733b25db | ||
| 
						 | 
					1419a33b64 | ||
| 
						 | 
					f270739f9f | ||
| 
						 | 
					e51a391286 | ||
| 
						 | 
					c815185574 | ||
| 
						 | 
					8045e29a52 | ||
| 
						 | 
					bbb3e16fd1 | ||
| 
						 | 
					3cd1657387 | ||
| 
						 | 
					d7ea122cf7 | ||
| 
						 | 
					6aea7c7f70 | ||
| 
						 | 
					56ba1d9cd3 | ||
| 
						 | 
					408b03ae0d | ||
| 
						 | 
					d94fd746af | ||
| 
						 | 
					dbd1316d1e | ||
| 
						 | 
					75845c0283 | ||
| 
						 | 
					88db9751d7 | ||
| 
						 | 
					6f645c4cb7 | ||
| 
						 | 
					4e31d85349 | ||
| 
						 | 
					de542a81c0 | ||
| 
						 | 
					461576e7a2 | ||
| 
						 | 
					21bd62b1ce | ||
| 
						 | 
					838cd1157c | ||
| 
						 | 
					2f068b91d8 | ||
| 
						 | 
					aba87bf1bd | ||
| 
						 | 
					e64da8ede4 | ||
| 
						 | 
					a9f38dfce4 | ||
| 
						 | 
					a097537a03 | ||
| 
						 | 
					66e0b53cf6 | ||
| 
						 | 
					06f2e81dd5 | ||
| 
						 | 
					40ae2e812f | ||
| 
						 | 
					06f613e40b | ||
| 
						 | 
					61c8c1e8d2 | ||
| 
						 | 
					ee924ee310 | ||
| 
						 | 
					fad0ce3ced | ||
| 
						 | 
					d396180939 | ||
| 
						 | 
					0d089abe67 | ||
| 
						 | 
					ed5c1dfc3c | ||
| 
						 | 
					6b949a7375 | ||
| 
						 | 
					3028e2908f | ||
| 
						 | 
					578803b01f | ||
| 
						 | 
					46738825c0 | ||
| 
						 | 
					56357699cb | ||
| 
						 | 
					fe8e718183 | ||
| 
						 | 
					1eb34989d4 | ||
| 
						 | 
					2f3b4c8bfb | ||
| 
						 | 
					6412768000 | ||
| 
						 | 
					82688b9a44 | ||
| 
						 | 
					651e12cfe4 | ||
| 
						 | 
					4118d581af | ||
| 
						 | 
					62608bec03 | ||
| 
						 | 
					71cffc973d | ||
| 
						 | 
					a8e49d084b | ||
| 
						 | 
					db631097b1 | ||
| 
						 | 
					0d31674166 | ||
| 
						 | 
					cb5af974a4 | ||
| 
						 | 
					f2f421a0a2 | ||
| 
						 | 
					413c46e2ee | ||
| 
						 | 
					3b412d51f0 | ||
| 
						 | 
					4931e2aee2 | ||
| 
						 | 
					ffadf673cf | ||
| 
						 | 
					5b5a7e5a24 | ||
| 
						 | 
					ab53208f47 | ||
| 
						 | 
					7c407705e8 | ||
| 
						 | 
					60378ff941 | ||
| 
						 | 
					30a0c77d19 | ||
| 
						 | 
					07ec89276b | ||
| 
						 | 
					a37dc1af9d | ||
| 
						 | 
					03458df140 | ||
| 
						 | 
					164eb9659e | ||
| 
						 | 
					99cfbaa63b | ||
| 
						 | 
					8d8a6534e3 | ||
| 
						 | 
					938c5013c9 | ||
| 
						 | 
					ea9d5ec793 | ||
| 
						 | 
					ec65f038a8 | ||
| 
						 | 
					199ec36d40 | ||
| 
						 | 
					1326ded048 | ||
| 
						 | 
					8347439644 | ||
| 
						 | 
					cddc2a8280 | ||
| 
						 | 
					97a8938407 | ||
| 
						 | 
					939d1dcae9 | ||
| 
						 | 
					9d5cc5c11f | ||
| 
						 | 
					d998f24d26 | ||
| 
						 | 
					d543f8857b | ||
| 
						 | 
					c48a942d22 | ||
| 
						 | 
					e1602618c3 | ||
| 
						 | 
					36be240623 | ||
| 
						 | 
					04e499c97f | ||
| 
						 | 
					f586a8a9dc | ||
| 
						 | 
					5112ed77d6 | ||
| 
						 | 
					bf29a54272 | ||
| 
						 | 
					6d9286a202 | ||
| 
						 | 
					92fdd07ca3 | ||
| 
						 | 
					1c937ad960 | ||
| 
						 | 
					f9891a5c04 | ||
| 
						 | 
					e8ad311d84 | ||
| 
						 | 
					545c09e202 | 
@@ -13,7 +13,7 @@ tasks:
 | 
			
		||||
    gmake test-install
 | 
			
		||||
- meson_min: |
 | 
			
		||||
    cd janet
 | 
			
		||||
    meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dtyped_array=false -Dreduced_os=true
 | 
			
		||||
    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: |
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -5,9 +5,14 @@ on:
 | 
			
		||||
    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:
 | 
			
		||||
@@ -35,6 +40,8 @@ jobs:
 | 
			
		||||
            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:
 | 
			
		||||
@@ -50,5 +57,6 @@ jobs:
 | 
			
		||||
        with:
 | 
			
		||||
          draft: true
 | 
			
		||||
          files: |
 | 
			
		||||
            ./dist/*.zip
 | 
			
		||||
            ./*.zip
 | 
			
		||||
            ./*.msi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							@@ -2,6 +2,9 @@ name: Test
 | 
			
		||||
 | 
			
		||||
on: [push, pull_request]
 | 
			
		||||
 | 
			
		||||
permissions:
 | 
			
		||||
  contents: read
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
 | 
			
		||||
  test-posix:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -34,6 +34,7 @@ local
 | 
			
		||||
 | 
			
		||||
# Common test files I use.
 | 
			
		||||
temp.janet
 | 
			
		||||
temp*.janet
 | 
			
		||||
scratch.janet
 | 
			
		||||
 | 
			
		||||
# Emscripten
 | 
			
		||||
@@ -67,10 +68,13 @@ tags
 | 
			
		||||
vgcore.*
 | 
			
		||||
*.out.*
 | 
			
		||||
 | 
			
		||||
# Wix artifacts
 | 
			
		||||
# WiX artifacts
 | 
			
		||||
*.msi
 | 
			
		||||
*.wixpdb
 | 
			
		||||
 | 
			
		||||
# Makefile config
 | 
			
		||||
/config.mk
 | 
			
		||||
 | 
			
		||||
# Created by https://www.gitignore.io/api/c
 | 
			
		||||
 | 
			
		||||
### C ###
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										79
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,8 +1,85 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
All notable changes to this project will be documented in this file.
 | 
			
		||||
 | 
			
		||||
## 1.25.1 - 2022-10-29
 | 
			
		||||
- Add `memcmp` function to core library.
 | 
			
		||||
- Fix bug in `os/open` with `:rw` permissions not correct on Linux.
 | 
			
		||||
- Support config.mk for more easily configuring the Makefile.
 | 
			
		||||
 | 
			
		||||
## 1.25.0 - 2022-10-10
 | 
			
		||||
- Windows FFI fixes.
 | 
			
		||||
- Fix PEG `if-not` combinator with captures in the condition
 | 
			
		||||
- Fix bug with `os/date` with nil first argument
 | 
			
		||||
- Fix bug with `net/accept` on Linux that could leak file descriptors to subprocesses
 | 
			
		||||
- Reduce number of hash collisions from pointer hashing
 | 
			
		||||
- Add optional parameter to `marshal` to skip cycle checking code
 | 
			
		||||
 | 
			
		||||
## 1.24.1 - 2022-08-24
 | 
			
		||||
- Fix FFI bug on Linux/Posix
 | 
			
		||||
- Improve parse error messages for bad delimiters.
 | 
			
		||||
- Add optional `name` parameter to the `short-fn` macro.
 | 
			
		||||
 | 
			
		||||
## 1.24.0 - 2022-08-14
 | 
			
		||||
- Add FFI support to 64-bit windows compiled with MSVC
 | 
			
		||||
- Don't process shared object names passed to dlopen.
 | 
			
		||||
- Add better support for windows console in the default shell.c for auto-completion and
 | 
			
		||||
  other shell-like input features.
 | 
			
		||||
- Improve default error message from `assert`.
 | 
			
		||||
- Add the `tabseq` macro for simpler table comprehensions.
 | 
			
		||||
- Allow setting `(dyn :task-id)` in fibers to improve context in supervisor messages. Prior to
 | 
			
		||||
  this change, supervisor messages over threaded channels would be from ambiguous threads/fibers.
 | 
			
		||||
 | 
			
		||||
## 1.23.0 - 2022-06-20
 | 
			
		||||
- Add experimental `ffi/` module for interfacing with dynamic libraries and raw function pointers. Only available
 | 
			
		||||
  on 64 bit linux, mac, and bsd systems.
 | 
			
		||||
- Allow using `&named` in function prototypes for named arguments. This is a more ergonomic
 | 
			
		||||
  variant of `&keys` that isn't as redundant, more self documenting, and allows extension to
 | 
			
		||||
  things like default arguments.
 | 
			
		||||
- Add `delay` macro for lazy evaluate-and-save thunks.
 | 
			
		||||
- Remove pthread.h from janet.h for easier includes.
 | 
			
		||||
- Add `debugger` - an easy to use debugger function that just takes a fiber.
 | 
			
		||||
- `dofile` will now start a debugger on errors if the environment it is passed has `:debug` set.
 | 
			
		||||
- Add `debugger-on-status` function, which can be passed to `run-context` to start a debugger on
 | 
			
		||||
  abnormal fiber signals.
 | 
			
		||||
- Allow running scripts with the `-d` flag to use the built-in debugger on errors and breakpoints.
 | 
			
		||||
- Add mutexes (locks) and reader-writer locks to ev module for thread coordination.
 | 
			
		||||
- Add `parse-all` as a generalization of the `parse` function.
 | 
			
		||||
- Add `os/cpu-count` to get the number of available processors on a machine
 | 
			
		||||
 | 
			
		||||
## 1.22.0 - 2022-05-09
 | 
			
		||||
- Prohibit negative size argument to `table/new`.
 | 
			
		||||
- Add `module/value`.
 | 
			
		||||
- Remove `file/popen`. Use `os/spawn` with the `:pipe` options instead.
 | 
			
		||||
- Fix bug in peg `thru` and `to` combinators.
 | 
			
		||||
- Fix printing issue in `doc` macro.
 | 
			
		||||
- Numerous updates to function docstrings
 | 
			
		||||
- Add `defdyn` aliases for various dynamic bindings used in core.
 | 
			
		||||
- Install `janet.h` symlink to make Janet native libraries and applications
 | 
			
		||||
  easier to build without `jpm`.
 | 
			
		||||
 | 
			
		||||
## 1.21.2 - 2022-04-01
 | 
			
		||||
- C functions `janet_dobytes` and `janet_dostring` will now enter the event loop if it is enabled.
 | 
			
		||||
- Fix hashing regression - hash of negative 0 must be the same as positive 0 since they are equal.
 | 
			
		||||
- The `flycheck` function no longer pollutes the module/cache
 | 
			
		||||
- Fix quasiquote bug in compiler
 | 
			
		||||
- Disallow use of `cancel` and `resume` on fibers scheduled or created with `ev/go`, as well as the root
 | 
			
		||||
  fiber.
 | 
			
		||||
 | 
			
		||||
## 1.20.0 - 2022-1-27
 | 
			
		||||
- Add `:missing-symbol` hook to `compile` that will act as a catch-all macro for undefined symbols.
 | 
			
		||||
- Add `:redef` dynamic binding that will allow users to redefine top-level bindings with late binding. This
 | 
			
		||||
  is intended for development use.
 | 
			
		||||
- Fix a bug with reading from a stream returned by `os/open` on Windows and Linux.
 | 
			
		||||
- Add `:ppc64` as a detectable OS type.
 | 
			
		||||
- Add `& more` support for destructuring in the match macro.
 | 
			
		||||
- Add `& more` support for destructuring in all binding forms (`def`).
 | 
			
		||||
 | 
			
		||||
## 1.19.2 - 2021-12-06
 | 
			
		||||
- Fix bug with missing status lines in some stack traces.
 | 
			
		||||
- Update hash function to have better statistical properties.
 | 
			
		||||
 | 
			
		||||
## 1.19.1 - 2021-12-04
 | 
			
		||||
- Add an optional `prefix` paramater to `debug/stacktrace` to allow printing prettier error messages.
 | 
			
		||||
- 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.
 | 
			
		||||
 
 | 
			
		||||
@@ -43,7 +43,7 @@ For changes to the VM and Core code, you will probably need to know C. Janet is
 | 
			
		||||
a subset of C99 that works with Microsoft Visual C++. This means most of C99 but with the following
 | 
			
		||||
omissions.
 | 
			
		||||
 | 
			
		||||
* No `restrict` 
 | 
			
		||||
* No `restrict`
 | 
			
		||||
* Certain functions in the standard library are not always available
 | 
			
		||||
 | 
			
		||||
In practice, this means programming for both MSVC on one hand and everything else on the other.
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										42
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								Makefile
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2021 Calvin Rose
 | 
			
		||||
# Copyright (c) 2022 Calvin Rose
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -21,9 +21,10 @@
 | 
			
		||||
################################
 | 
			
		||||
##### Set global variables #####
 | 
			
		||||
################################
 | 
			
		||||
 | 
			
		||||
sinclude config.mk
 | 
			
		||||
PREFIX?=/usr/local
 | 
			
		||||
 | 
			
		||||
JANETCONF_HEADER?=src/conf/janetconf.h
 | 
			
		||||
INCLUDEDIR?=$(PREFIX)/include
 | 
			
		||||
BINDIR?=$(PREFIX)/bin
 | 
			
		||||
LIBDIR?=$(PREFIX)/lib
 | 
			
		||||
@@ -58,6 +59,7 @@ 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
 | 
			
		||||
@@ -82,7 +84,7 @@ all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
 | 
			
		||||
##### Name Files #####
 | 
			
		||||
######################
 | 
			
		||||
 | 
			
		||||
JANET_HEADERS=src/include/janet.h src/conf/janetconf.h
 | 
			
		||||
JANET_HEADERS=src/include/janet.h $(JANETCONF_HEADER)
 | 
			
		||||
 | 
			
		||||
JANET_LOCAL_HEADERS=src/core/features.h \
 | 
			
		||||
					src/core/util.h \
 | 
			
		||||
@@ -107,6 +109,7 @@ 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/gc.c \
 | 
			
		||||
				   src/core/inttypes.c \
 | 
			
		||||
@@ -165,21 +168,25 @@ build/c/janet.c: build/janet_boot src/boot/boot.janet
 | 
			
		||||
##### Amalgamation #####
 | 
			
		||||
########################
 | 
			
		||||
 | 
			
		||||
SONAME=libjanet.so.1.19
 | 
			
		||||
ifeq ($(UNAME), Darwin)
 | 
			
		||||
SONAME=libjanet.1.25.dylib
 | 
			
		||||
else
 | 
			
		||||
SONAME=libjanet.so.1.25
 | 
			
		||||
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)
 | 
			
		||||
	./$(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
 | 
			
		||||
@@ -238,6 +245,7 @@ build/janet-%.tar.gz: $(JANET_TARGET) \
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/lib/
 | 
			
		||||
	cp $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/man/man1/
 | 
			
		||||
	cp janet.1 build/$(JANET_DIST_DIR)/man/man1/janet.1
 | 
			
		||||
	mkdir -p build/$(JANET_DIST_DIR)/src/
 | 
			
		||||
	cp build/c/janet.c build/c/shell.c build/$(JANET_DIST_DIR)/src/
 | 
			
		||||
	cp CONTRIBUTING.md LICENSE README.md build/$(JANET_DIST_DIR)/
 | 
			
		||||
@@ -276,17 +284,24 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
 | 
			
		||||
	cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
 | 
			
		||||
	ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd
 | 
			
		||||
	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)
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	cp janet.1 '$(DESTDIR)$(JANET_MANPATH)'
 | 
			
		||||
	mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
 | 
			
		||||
	cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || true
 | 
			
		||||
	[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
 | 
			
		||||
 | 
			
		||||
install-jpm-git: $(JANET_TARGET)
 | 
			
		||||
	mkdir -p build
 | 
			
		||||
@@ -303,6 +318,7 @@ install-jpm-git: $(JANET_TARGET)
 | 
			
		||||
uninstall:
 | 
			
		||||
	-rm '$(DESTDIR)$(BINDIR)/janet'
 | 
			
		||||
	-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'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
[](https://gitter.im/janet-language/community)
 | 
			
		||||
 
 | 
			
		||||
[](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">
 | 
			
		||||
@@ -89,6 +89,8 @@ 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`.
 | 
			
		||||
@@ -103,6 +105,8 @@ cd somewhere/my/projects/janet
 | 
			
		||||
make CC=gcc-x86
 | 
			
		||||
make test
 | 
			
		||||
make repl
 | 
			
		||||
make install
 | 
			
		||||
make install-jpm-git
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### FreeBSD
 | 
			
		||||
@@ -116,6 +120,8 @@ cd somewhere/my/projects/janet
 | 
			
		||||
gmake
 | 
			
		||||
gmake test
 | 
			
		||||
gmake repl
 | 
			
		||||
gmake install
 | 
			
		||||
gmake install-jpm-git
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### NetBSD
 | 
			
		||||
@@ -284,7 +290,7 @@ Usually, one of a few reasons:
 | 
			
		||||
  Janet does not run on the JVM, and has a more primitive garbage collector.
 | 
			
		||||
- We want to keep the Janet core small. With Lisps, usually a feature can be added as a library
 | 
			
		||||
  without feeling "bolted on", especially when compared to ALGOL like languages. Adding features
 | 
			
		||||
  to the core also makes it a bit more difficult keep Janet maximally portable.
 | 
			
		||||
  to the core also makes it a bit more difficult to keep Janet maximally portable.
 | 
			
		||||
 | 
			
		||||
### Why is my terminal spitting out junk when I run the REPL?
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
							
								
								
									
										71
									
								
								examples/ffi/gtk.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								examples/ffi/gtk.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
# :lazy true needed for jpm quickbin
 | 
			
		||||
# lazily loads library on first function use
 | 
			
		||||
# so the `main` function
 | 
			
		||||
# can be marshalled.
 | 
			
		||||
(ffi/context "/usr/lib/libgtk-3.so" :lazy true)
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-application-new :ptr
 | 
			
		||||
  "Add docstrings as needed."
 | 
			
		||||
  [title :string flags :uint])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  g-signal-connect-data :ulong
 | 
			
		||||
  [a :ptr b :ptr c :ptr d :ptr e :ptr f :int])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  g-application-run :int
 | 
			
		||||
  [app :ptr argc :int argv :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-application-window-new :ptr
 | 
			
		||||
  [a :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-button-new-with-label :ptr
 | 
			
		||||
  [a :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-container-add :void
 | 
			
		||||
  [a :ptr b :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-widget-show-all :void
 | 
			
		||||
  [a :ptr])
 | 
			
		||||
 | 
			
		||||
(ffi/defbind
 | 
			
		||||
  gtk-button-set-label :void
 | 
			
		||||
  [a :ptr b :ptr])
 | 
			
		||||
 | 
			
		||||
(def cb (delay (ffi/trampoline :default)))
 | 
			
		||||
 | 
			
		||||
(defn ffi/array
 | 
			
		||||
  ``Convert a janet array to a buffer that can be passed to FFI functions.
 | 
			
		||||
  For example, to create an array of type `char *` (array of c strings), one
 | 
			
		||||
  could use `(ffi/array ["hello" "world"] :ptr)`. One needs to be careful that
 | 
			
		||||
  array elements are not garbage collected though - the GC can't follow references
 | 
			
		||||
  inside an arbitrary byte buffer.``
 | 
			
		||||
  [arr ctype &opt buf]
 | 
			
		||||
  (default buf @"")
 | 
			
		||||
  (each el arr
 | 
			
		||||
    (ffi/write ctype el buf))
 | 
			
		||||
  buf)
 | 
			
		||||
 | 
			
		||||
(defn on-active
 | 
			
		||||
  [app]
 | 
			
		||||
  (def window (gtk-application-window-new app))
 | 
			
		||||
  (def btn (gtk-button-new-with-label "Click Me!"))
 | 
			
		||||
  (g-signal-connect-data btn "clicked" (cb)
 | 
			
		||||
                         (fn [btn] (gtk-button-set-label btn "Hello World"))
 | 
			
		||||
                         nil 1)
 | 
			
		||||
  (gtk-container-add window btn)
 | 
			
		||||
  (gtk-widget-show-all window))
 | 
			
		||||
 | 
			
		||||
(defn main
 | 
			
		||||
  [&]
 | 
			
		||||
  (def app (gtk-application-new "org.janet-lang.example.HelloApp" 0))
 | 
			
		||||
  (g-signal-connect-data app "activate" (cb) on-active nil 1)
 | 
			
		||||
  # manually build an array with ffi/write
 | 
			
		||||
  # - we are responsible for preventing gc when the arg array is used
 | 
			
		||||
  (def argv (ffi/array (dyn *args*) :string))
 | 
			
		||||
  (g-application-run app (length (dyn *args*)) argv))
 | 
			
		||||
							
								
								
									
										208
									
								
								examples/ffi/so.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								examples/ffi/so.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,208 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#define EXPORTER __declspec(dllexport)
 | 
			
		||||
#else
 | 
			
		||||
#define EXPORTER
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Structs */
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int a, b;
 | 
			
		||||
    float c, d;
 | 
			
		||||
} Split;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    float c, d;
 | 
			
		||||
    int a, b;
 | 
			
		||||
} SplitFlip;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int u, v, w, x, y, z;
 | 
			
		||||
} SixInts;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int a;
 | 
			
		||||
    int b;
 | 
			
		||||
} intint;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int a;
 | 
			
		||||
    int b;
 | 
			
		||||
    int c;
 | 
			
		||||
} intintint;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    int64_t a;
 | 
			
		||||
    int64_t b;
 | 
			
		||||
    int64_t c;
 | 
			
		||||
} big;
 | 
			
		||||
 | 
			
		||||
/* Functions */
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int int_fn(int a, int b) {
 | 
			
		||||
    return (a << 2) + b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double my_fn(int64_t a, int64_t b, const char *x) {
 | 
			
		||||
    return (double)(a + b) + 0.5 + strlen(x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_fn(double x, double y, double z) {
 | 
			
		||||
    return (x + y) * z * 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_many(double x, double y, double z, double w, double a, double b) {
 | 
			
		||||
    return x + y + z + w + a + b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_lots(
 | 
			
		||||
    double a,
 | 
			
		||||
    double b,
 | 
			
		||||
    double c,
 | 
			
		||||
    double d,
 | 
			
		||||
    double e,
 | 
			
		||||
    double f,
 | 
			
		||||
    double g,
 | 
			
		||||
    double h,
 | 
			
		||||
    double i,
 | 
			
		||||
    double j) {
 | 
			
		||||
    return i + j;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double double_lots_2(
 | 
			
		||||
    double a,
 | 
			
		||||
    double b,
 | 
			
		||||
    double c,
 | 
			
		||||
    double d,
 | 
			
		||||
    double e,
 | 
			
		||||
    double f,
 | 
			
		||||
    double g,
 | 
			
		||||
    double h,
 | 
			
		||||
    double i,
 | 
			
		||||
    double j) {
 | 
			
		||||
    return a +
 | 
			
		||||
           10.0 * b +
 | 
			
		||||
           100.0 * c +
 | 
			
		||||
           1000.0 * d +
 | 
			
		||||
           10000.0 * e +
 | 
			
		||||
           100000.0 * f +
 | 
			
		||||
           1000000.0 * g +
 | 
			
		||||
           10000000.0 * h +
 | 
			
		||||
           100000000.0 * i +
 | 
			
		||||
           1000000000.0 * j;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
double float_fn(float x, float y, float z) {
 | 
			
		||||
    return (x + y) * z;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int intint_fn(double x, intint ii) {
 | 
			
		||||
    printf("double: %g\n", x);
 | 
			
		||||
    return ii.a + ii.b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int intintint_fn(double x, intintint iii) {
 | 
			
		||||
    printf("double: %g\n", x);
 | 
			
		||||
    return iii.a + iii.b + iii.c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
intint return_struct(int i) {
 | 
			
		||||
    intint ret;
 | 
			
		||||
    ret.a = i;
 | 
			
		||||
    ret.b = i * i;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
big struct_big(int i, double d) {
 | 
			
		||||
    big ret;
 | 
			
		||||
    ret.a = i;
 | 
			
		||||
    ret.b = (int64_t) d;
 | 
			
		||||
    ret.c = ret.a + ret.b + 1000;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
void void_fn(void) {
 | 
			
		||||
    printf("void fn ran\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
void void_fn_2(double y) {
 | 
			
		||||
    printf("y = %f\n", y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
void void_ret_fn(int x) {
 | 
			
		||||
    printf("void fn ran: %d\n", x);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int intintint_fn_2(intintint iii, int i) {
 | 
			
		||||
    fprintf(stderr, "iii.a = %d, iii.b = %d, iii.c = %d, i = %d\n", iii.a, iii.b, iii.c, i);
 | 
			
		||||
    return i * (iii.a + iii.b + iii.c);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
float split_fn(Split s) {
 | 
			
		||||
    return s.a * s.c + s.b * s.d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
float split_flip_fn(SplitFlip s) {
 | 
			
		||||
    return s.a * s.c + s.b * s.d;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
Split split_ret_fn(int x, float y) {
 | 
			
		||||
    Split ret;
 | 
			
		||||
    ret.a = x;
 | 
			
		||||
    ret.b = x;
 | 
			
		||||
    ret.c = y;
 | 
			
		||||
    ret.d = y;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
SplitFlip split_flip_ret_fn(int x, float y) {
 | 
			
		||||
    SplitFlip ret;
 | 
			
		||||
    ret.a = x;
 | 
			
		||||
    ret.b = x;
 | 
			
		||||
    ret.c = y;
 | 
			
		||||
    ret.d = y;
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
SixInts sixints_fn(void) {
 | 
			
		||||
    return (SixInts) {
 | 
			
		||||
        6666, 1111, 2222, 3333, 4444, 5555
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int sixints_fn_2(int x, SixInts s) {
 | 
			
		||||
    return x + s.u + s.v + s.w + s.x + s.y + s.z;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EXPORTER
 | 
			
		||||
int sixints_fn_3(SixInts s, int x) {
 | 
			
		||||
    return x + s.u + s.v + s.w + s.x + s.y + s.z;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										134
									
								
								examples/ffi/test.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								examples/ffi/test.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
#
 | 
			
		||||
# Simple FFI test script that tests against a simple shared object
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(def is-windows (= :windows (os/which)))
 | 
			
		||||
(def ffi/loc (string "examples/ffi/so." (if is-windows "dll" "so")))
 | 
			
		||||
(def ffi/source-loc "examples/ffi/so.c")
 | 
			
		||||
 | 
			
		||||
(if is-windows
 | 
			
		||||
  (os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
 | 
			
		||||
  (os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px))
 | 
			
		||||
 | 
			
		||||
(ffi/context ffi/loc)
 | 
			
		||||
 | 
			
		||||
(def intintint (ffi/struct :int :int :int))
 | 
			
		||||
(def big (ffi/struct :s64 :s64 :s64))
 | 
			
		||||
(def split (ffi/struct :int :int :float :float))
 | 
			
		||||
(def split-flip (ffi/struct :float :float :int :int))
 | 
			
		||||
(def six-ints (ffi/struct :int :int :int :int :int :int))
 | 
			
		||||
 | 
			
		||||
(ffi/defbind int-fn :int [a :int b :int])
 | 
			
		||||
(ffi/defbind double-fn :double [a :double b :double c :double])
 | 
			
		||||
(ffi/defbind double-many :double
 | 
			
		||||
  [x :double y :double z :double w :double a :double b :double])
 | 
			
		||||
(ffi/defbind double-lots :double
 | 
			
		||||
  [a :double b :double c :double d :double e :double f :double g :double h :double i :double j :double])
 | 
			
		||||
(ffi/defbind float-fn :double
 | 
			
		||||
  [x :float y :float z :float])
 | 
			
		||||
(ffi/defbind intint-fn :int
 | 
			
		||||
  [x :double ii [:int :int]])
 | 
			
		||||
(ffi/defbind return-struct [:int :int]
 | 
			
		||||
  [i :int])
 | 
			
		||||
(ffi/defbind intintint-fn :int
 | 
			
		||||
  [x :double iii intintint])
 | 
			
		||||
(ffi/defbind struct-big big
 | 
			
		||||
  [i :int d :double])
 | 
			
		||||
(ffi/defbind void-fn :void [])
 | 
			
		||||
(ffi/defbind double-lots-2 :double
 | 
			
		||||
  [a :double
 | 
			
		||||
   b :double
 | 
			
		||||
   c :double
 | 
			
		||||
   d :double
 | 
			
		||||
   e :double
 | 
			
		||||
   f :double
 | 
			
		||||
   g :double
 | 
			
		||||
   h :double
 | 
			
		||||
   i :double
 | 
			
		||||
   j :double])
 | 
			
		||||
(ffi/defbind void-fn-2 :void [y :double])
 | 
			
		||||
(ffi/defbind intintint-fn-2 :int [iii intintint i :int])
 | 
			
		||||
(ffi/defbind split-fn :float [s split])
 | 
			
		||||
(ffi/defbind split-flip-fn :float [s split-flip])
 | 
			
		||||
(ffi/defbind split-ret-fn split [x :int y :float])
 | 
			
		||||
(ffi/defbind split-flip-ret-fn split-flip [x :int y :float])
 | 
			
		||||
(ffi/defbind sixints-fn six-ints [])
 | 
			
		||||
(ffi/defbind sixints-fn-2 :int [x :int s six-ints])
 | 
			
		||||
(ffi/defbind sixints-fn-3 :int [s six-ints x :int])
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Struct reading and writing
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(defn check-round-trip
 | 
			
		||||
  [t value]
 | 
			
		||||
  (def buf (ffi/write t value))
 | 
			
		||||
  (def same-value (ffi/read t buf))
 | 
			
		||||
  (assert (deep= value same-value)
 | 
			
		||||
          (string/format "round trip %j (got %j)" value same-value)))
 | 
			
		||||
 | 
			
		||||
(check-round-trip :bool true)
 | 
			
		||||
(check-round-trip :bool false)
 | 
			
		||||
(check-round-trip :void nil)
 | 
			
		||||
(check-round-trip :void nil)
 | 
			
		||||
(check-round-trip :s8 10)
 | 
			
		||||
(check-round-trip :s8 0)
 | 
			
		||||
(check-round-trip :s8 -10)
 | 
			
		||||
(check-round-trip :u8 10)
 | 
			
		||||
(check-round-trip :u8 0)
 | 
			
		||||
(check-round-trip :s16 10)
 | 
			
		||||
(check-round-trip :s16 0)
 | 
			
		||||
(check-round-trip :s16 -12312)
 | 
			
		||||
(check-round-trip :u16 10)
 | 
			
		||||
(check-round-trip :u16 0)
 | 
			
		||||
(check-round-trip :u32 0)
 | 
			
		||||
(check-round-trip :u32 10)
 | 
			
		||||
(check-round-trip :u32 0xFFFF7777)
 | 
			
		||||
(check-round-trip :s32 0x7FFF7777)
 | 
			
		||||
(check-round-trip :s32 0)
 | 
			
		||||
(check-round-trip :s32 -1234567)
 | 
			
		||||
 | 
			
		||||
(def s (ffi/struct :s8 :s8 :s8 :float))
 | 
			
		||||
(check-round-trip s [1 3 5 123.5])
 | 
			
		||||
(check-round-trip s [-1 -3 -5 -123.5])
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Call functions
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
(tracev (sixints-fn))
 | 
			
		||||
(tracev (sixints-fn-2 100 [1 2 3 4 5 6]))
 | 
			
		||||
(tracev (sixints-fn-3 [1 2 3 4 5 6] 200))
 | 
			
		||||
(tracev (split-ret-fn 10 12))
 | 
			
		||||
(tracev (split-flip-ret-fn 10 12))
 | 
			
		||||
(tracev (split-flip-ret-fn 12 10))
 | 
			
		||||
(tracev (intintint-fn-2 [10 20 30] 3))
 | 
			
		||||
(tracev (split-fn [5 6 1.2 3.4]))
 | 
			
		||||
(tracev (void-fn-2 10.3))
 | 
			
		||||
(tracev (double-many 1 2 3 4 5 6))
 | 
			
		||||
(tracev (string/format "%.17g" (double-many 1 2 3 4 5 6)))
 | 
			
		||||
(tracev (type (double-many 1 2 3 4 5 6)))
 | 
			
		||||
(tracev (double-lots-2 0 1 2 3 4 5 6 7 8 9))
 | 
			
		||||
(tracev (void-fn))
 | 
			
		||||
(tracev (int-fn 10 20))
 | 
			
		||||
(tracev (double-fn 1.5 2.5 3.5))
 | 
			
		||||
(tracev (double-lots 1 2 3 4 5 6 7 8 9 10))
 | 
			
		||||
(tracev (float-fn 8 4 17))
 | 
			
		||||
(tracev (intint-fn 123.456 [10 20]))
 | 
			
		||||
(tracev (intintint-fn 123.456 [10 20 30]))
 | 
			
		||||
(tracev (return-struct 42))
 | 
			
		||||
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
 | 
			
		||||
(tracev (struct-big 11 99.5))
 | 
			
		||||
 | 
			
		||||
(assert (= [10 10 12 12] (split-ret-fn 10 12)))
 | 
			
		||||
(assert (= [12 12 10 10] (split-flip-ret-fn 10 12)))
 | 
			
		||||
(assert (= 183 (intintint-fn-2 [10 20 31] 3)))
 | 
			
		||||
(assert (= 264 (math/round (* 10 (split-fn [5 6 1.2 3.4])))))
 | 
			
		||||
(assert (= 9876543210 (double-lots-2 0 1 2 3 4 5 6 7 8 9)))
 | 
			
		||||
(assert (= 60 (int-fn 10 20)))
 | 
			
		||||
(assert (= 42 (double-fn 1.5 2.5 3.5)))
 | 
			
		||||
(assert (= 21 (math/round (double-many 1 2 3 4 5 6.01))))
 | 
			
		||||
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
 | 
			
		||||
(assert (= 204 (float-fn 8 4 17)))
 | 
			
		||||
 | 
			
		||||
(print "Done.")
 | 
			
		||||
							
								
								
									
										7
									
								
								examples/ffi/win32.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								examples/ffi/win32.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
(ffi/context "user32.dll")
 | 
			
		||||
 | 
			
		||||
(ffi/defbind MessageBoxA :int
 | 
			
		||||
  [w :ptr text :string cap :string typ :int])
 | 
			
		||||
 | 
			
		||||
(MessageBoxA nil "Hello, World!" "Test" 0)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								janet.1
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								janet.1
									
									
									
									
									
								
							@@ -164,10 +164,15 @@ Execute a string of Janet source. Source code is executed in the order it is enc
 | 
			
		||||
arguments are executed before later ones.
 | 
			
		||||
 | 
			
		||||
.TP
 | 
			
		||||
.BR \-E\ code arguments
 | 
			
		||||
.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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								meson.build
									
									
									
									
									
								
							@@ -20,7 +20,7 @@
 | 
			
		||||
 | 
			
		||||
project('janet', 'c',
 | 
			
		||||
  default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
 | 
			
		||||
  version : '1.19.1')
 | 
			
		||||
  version : '1.25.1')
 | 
			
		||||
 | 
			
		||||
# Global settings
 | 
			
		||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
 | 
			
		||||
@@ -76,6 +76,7 @@ conf.set('JANET_SIMPLE_GETLINE', get_option('simple_getline'))
 | 
			
		||||
conf.set('JANET_EV_NO_EPOLL', not get_option('epoll'))
 | 
			
		||||
conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
 | 
			
		||||
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
 | 
			
		||||
conf.set('JANET_NO_FFI', not get_option('ffi'))
 | 
			
		||||
if get_option('os_name') != ''
 | 
			
		||||
  conf.set('JANET_OS_NAME', get_option('os_name'))
 | 
			
		||||
endif
 | 
			
		||||
@@ -116,6 +117,7 @@ core_src = [
 | 
			
		||||
  'src/core/debug.c',
 | 
			
		||||
  'src/core/emit.c',
 | 
			
		||||
  'src/core/ev.c',
 | 
			
		||||
  'src/core/ffi.c',
 | 
			
		||||
  'src/core/fiber.c',
 | 
			
		||||
  'src/core/gc.c',
 | 
			
		||||
  'src/core/inttypes.c',
 | 
			
		||||
@@ -263,3 +265,9 @@ patched_janet = custom_target('patched-janeth',
 | 
			
		||||
  build_by_default : true,
 | 
			
		||||
  output : ['janet.h'],
 | 
			
		||||
  command : [janet_nativeclient, '@INPUT@', '@OUTPUT@'])
 | 
			
		||||
 | 
			
		||||
# Create a version of the janet.h header that matches what jpm often expects
 | 
			
		||||
if meson.version().version_compare('>=0.61')
 | 
			
		||||
  install_symlink('janet.h', pointing_to: 'janet/janet.h', install_dir: get_option('includedir'))
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ option('simple_getline', type : 'boolean', value : false)
 | 
			
		||||
option('epoll', type : 'boolean', value : false)
 | 
			
		||||
option('kqueue', type : 'boolean', value : false)
 | 
			
		||||
option('interpreter_interrupt', type : 'boolean', value : false)
 | 
			
		||||
option('ffi', type : 'boolean', value : true)
 | 
			
		||||
 | 
			
		||||
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
 | 
			
		||||
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1268
									
								
								src/boot/boot.janet
									
									
									
									
									
								
							
							
						
						
									
										1268
									
								
								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) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,10 @@
 | 
			
		||||
#define JANETCONF_H
 | 
			
		||||
 | 
			
		||||
#define JANET_VERSION_MAJOR 1
 | 
			
		||||
#define JANET_VERSION_MINOR 19
 | 
			
		||||
#define JANET_VERSION_MINOR 25
 | 
			
		||||
#define JANET_VERSION_PATCH 1
 | 
			
		||||
#define JANET_VERSION_EXTRA ""
 | 
			
		||||
#define JANET_VERSION "1.19.1"
 | 
			
		||||
#define JANET_VERSION "1.25.1"
 | 
			
		||||
 | 
			
		||||
/* #define JANET_BUILD "local" */
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -23,14 +23,16 @@
 | 
			
		||||
#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
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Create new userdata */
 | 
			
		||||
void *janet_abstract_begin(const JanetAbstractType *atype, size_t size) {
 | 
			
		||||
@@ -85,6 +87,14 @@ void *janet_abstract_threaded(const JanetAbstractType *atype, size_t size) {
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
 | 
			
		||||
size_t janet_os_mutex_size(void) {
 | 
			
		||||
    return sizeof(CRITICAL_SECTION);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t janet_os_rwlock_size(void) {
 | 
			
		||||
    return sizeof(void *);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_incref(JanetAbstractHead *ab) {
 | 
			
		||||
    return InterlockedIncrement(&ab->gc.data.refcount);
 | 
			
		||||
}
 | 
			
		||||
@@ -106,11 +116,45 @@ void janet_os_mutex_lock(JanetOSMutex *mutex) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
 | 
			
		||||
    /* error handling? May want to keep counter */
 | 
			
		||||
    LeaveCriticalSection((CRITICAL_SECTION *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {
 | 
			
		||||
    InitializeSRWLock((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_deinit(JanetOSRWLock *rwlock) {
 | 
			
		||||
    /* no op? */
 | 
			
		||||
    (void) rwlock;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_rlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    AcquireSRWLockShared((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_wlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    AcquireSRWLockExclusive((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_runlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    ReleaseSRWLockShared((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
 | 
			
		||||
    ReleaseSRWLockExclusive((PSRWLOCK) rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
size_t janet_os_mutex_size(void) {
 | 
			
		||||
    return sizeof(pthread_mutex_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t janet_os_rwlock_size(void) {
 | 
			
		||||
    return sizeof(pthread_rwlock_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int32_t janet_incref(JanetAbstractHead *ab) {
 | 
			
		||||
    return __atomic_add_fetch(&ab->gc.data.refcount, 1, __ATOMIC_RELAXED);
 | 
			
		||||
}
 | 
			
		||||
@@ -120,19 +164,47 @@ static int32_t janet_decref(JanetAbstractHead *ab) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_init(mutex, NULL);
 | 
			
		||||
    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(mutex);
 | 
			
		||||
    pthread_mutex_destroy((pthread_mutex_t *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_lock(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_lock(mutex);
 | 
			
		||||
    pthread_mutex_lock((pthread_mutex_t *) mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
 | 
			
		||||
    pthread_mutex_unlock(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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -125,7 +125,7 @@ Janet janet_array_peek(JanetArray *array) {
 | 
			
		||||
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.") {
 | 
			
		||||
              "`(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);
 | 
			
		||||
@@ -136,7 +136,7 @@ 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++) {
 | 
			
		||||
@@ -194,7 +194,7 @@ JANET_CORE_FN(cfun_array_push,
 | 
			
		||||
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. "
 | 
			
		||||
              "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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -553,6 +553,10 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("vararg"));
 | 
			
		||||
    if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
 | 
			
		||||
 | 
			
		||||
    /* Check structarg */
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("structarg"));
 | 
			
		||||
    if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
 | 
			
		||||
 | 
			
		||||
    /* Check source */
 | 
			
		||||
    x = janet_get1(s, janet_ckeywordv("source"));
 | 
			
		||||
    if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x);
 | 
			
		||||
@@ -884,6 +888,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,6 +941,7 @@ 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("constants"), janet_disasm_constants(def));
 | 
			
		||||
@@ -986,6 +995,7 @@ JANET_CORE_FN(cfun_disasm,
 | 
			
		||||
        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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -164,7 +164,7 @@ void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x) {
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_new,
 | 
			
		||||
              "(buffer/new capacity)",
 | 
			
		||||
              "Creates a new, empty buffer with enough backing memory for capacity bytes. "
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -174,16 +174,17 @@ JANET_CORE_FN(cfun_buffer_new,
 | 
			
		||||
 | 
			
		||||
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. "
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -312,7 +313,7 @@ JANET_CORE_FN(cfun_buffer_clear,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_popn,
 | 
			
		||||
              "(buffer/popn buffer n)",
 | 
			
		||||
              "Removes the last n bytes from the buffer. Returns the modified buffer.") {
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -327,9 +328,9 @@ JANET_CORE_FN(cfun_buffer_popn,
 | 
			
		||||
 | 
			
		||||
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, "
 | 
			
		||||
              "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. "
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -399,9 +400,9 @@ JANET_CORE_FN(cfun_buffer_bittoggle,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_buffer_blit,
 | 
			
		||||
              "(buffer/blit dest src &opt dest-start src-start src-end)",
 | 
			
		||||
              "Insert the contents of src into dest. Can optionally take indices that "
 | 
			
		||||
              "indicate which part of src to copy into which part of dest. Indices can be "
 | 
			
		||||
              "negative to index from the end of src or dest. Returns dest.") {
 | 
			
		||||
              "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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -260,11 +260,27 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int64_t janet_getinteger64(const Janet *argv, int32_t n) {
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
    return janet_unwrap_s64(argv[n]);
 | 
			
		||||
#else
 | 
			
		||||
    Janet x = argv[n];
 | 
			
		||||
    if (!janet_checkint64(x)) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected 64 bit signed integer, got %v", n, x);
 | 
			
		||||
    }
 | 
			
		||||
    return (int64_t) janet_unwrap_number(x);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t janet_getuinteger64(const Janet *argv, int32_t n) {
 | 
			
		||||
#ifdef JANET_INT_TYPES
 | 
			
		||||
    return janet_unwrap_u64(argv[n]);
 | 
			
		||||
#else
 | 
			
		||||
    Janet x = argv[n];
 | 
			
		||||
    if (!janet_checkint64(x)) {
 | 
			
		||||
        janet_panicf("bad slot #%d, expected 64 bit unsigned integer, got %v", n, x);
 | 
			
		||||
    }
 | 
			
		||||
    return (uint64_t) janet_unwrap_number(x);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t janet_getsize(const Janet *argv, int32_t n) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -197,6 +197,39 @@ void janetc_popscope_keepslot(JanetCompiler *c, JanetSlot retslot) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int lookup_missing(
 | 
			
		||||
    JanetCompiler *c,
 | 
			
		||||
    const uint8_t *sym,
 | 
			
		||||
    JanetFunction *handler,
 | 
			
		||||
    JanetBinding *out) {
 | 
			
		||||
    int32_t minar = handler->def->min_arity;
 | 
			
		||||
    int32_t maxar = handler->def->max_arity;
 | 
			
		||||
    if (minar > 1 || maxar < 1) {
 | 
			
		||||
        janetc_error(c, janet_cstring("missing symbol lookup handler must take 1 argument"));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    Janet args[1] = { janet_wrap_symbol(sym) };
 | 
			
		||||
    JanetFiber *fiberp = janet_fiber(handler, 64, 1, args);
 | 
			
		||||
    if (NULL == fiberp) {
 | 
			
		||||
        janetc_error(c, janet_cstring("failed to call missing symbol lookup handler"));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    fiberp->env = c->env;
 | 
			
		||||
    int lock = janet_gclock();
 | 
			
		||||
    Janet tempOut;
 | 
			
		||||
    JanetSignal status = janet_continue(fiberp, janet_wrap_nil(), &tempOut);
 | 
			
		||||
    janet_gcunlock(lock);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK) {
 | 
			
		||||
        janetc_error(c, janet_formatc("(lookup) %V", tempOut));
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Convert return value as entry. */
 | 
			
		||||
    /* Alternative could use janet_resolve_ext(c->env, sym) to read result from environment. */
 | 
			
		||||
    *out = janet_binding_from_entry(tempOut);
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Allow searching for symbols. Return information about the symbol */
 | 
			
		||||
JanetSlot janetc_resolve(
 | 
			
		||||
    JanetCompiler *c,
 | 
			
		||||
@@ -230,6 +263,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 +287,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;
 | 
			
		||||
@@ -651,7 +705,7 @@ static int macroexpand1(
 | 
			
		||||
    }
 | 
			
		||||
    Janet macroval;
 | 
			
		||||
    JanetBindingType btype = janet_resolve(c->env, name, ¯oval);
 | 
			
		||||
    if (btype != JANET_BINDING_MACRO ||
 | 
			
		||||
    if (!(btype == JANET_BINDING_MACRO || btype == JANET_BINDING_DYNAMIC_MACRO) ||
 | 
			
		||||
            !janet_checktype(macroval, JANET_FUNCTION))
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
@@ -942,7 +996,7 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* C Function for compiling */
 | 
			
		||||
JANET_CORE_FN(cfun,
 | 
			
		||||
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 "
 | 
			
		||||
@@ -958,7 +1012,14 @@ JANET_CORE_FN(cfun,
 | 
			
		||||
    }
 | 
			
		||||
    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 {
 | 
			
		||||
            janet_panic_type(x, 2, JANET_TFLAG_STRING | JANET_TFLAG_KEYWORD);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    JanetArray *lints = (argc >= 4) ? janet_getarray(argv, 3) : NULL;
 | 
			
		||||
    JanetCompileResult res = janet_compile_lint(argv[0], env, source, lints);
 | 
			
		||||
@@ -982,7 +1043,7 @@ JANET_CORE_FN(cfun,
 | 
			
		||||
 | 
			
		||||
void janet_lib_compile(JanetTable *env) {
 | 
			
		||||
    JanetRegExt cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("compile", cfun),
 | 
			
		||||
        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) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -42,51 +42,6 @@ extern size_t janet_core_image_size;
 | 
			
		||||
#define JDOC(x) NULL
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
 | 
			
		||||
 * with native code. */
 | 
			
		||||
#if defined(JANET_NO_DYNAMIC_MODULES)
 | 
			
		||||
typedef int Clib;
 | 
			
		||||
#define load_clib(name) ((void) name, 0)
 | 
			
		||||
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
 | 
			
		||||
#define error_clib() "dynamic libraries not supported"
 | 
			
		||||
#elif defined(JANET_WINDOWS)
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
typedef HINSTANCE Clib;
 | 
			
		||||
#define load_clib(name) LoadLibrary((name))
 | 
			
		||||
#define symbol_clib(lib, sym) GetProcAddress((lib), (sym))
 | 
			
		||||
static char error_clib_buf[256];
 | 
			
		||||
static char *error_clib(void) {
 | 
			
		||||
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 | 
			
		||||
                   NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | 
			
		||||
                   error_clib_buf, sizeof(error_clib_buf), NULL);
 | 
			
		||||
    error_clib_buf[strlen(error_clib_buf) - 1] = '\0';
 | 
			
		||||
    return error_clib_buf;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
typedef void *Clib;
 | 
			
		||||
#define load_clib(name) dlopen((name), RTLD_NOW)
 | 
			
		||||
#define symbol_clib(lib, sym) dlsym((lib), (sym))
 | 
			
		||||
#define error_clib() dlerror()
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static char *get_processed_name(const char *name) {
 | 
			
		||||
    if (name[0] == '.') return (char *) name;
 | 
			
		||||
    const char *c;
 | 
			
		||||
    for (c = name; *c; c++) {
 | 
			
		||||
        if (*c == '/') return (char *) name;
 | 
			
		||||
    }
 | 
			
		||||
    size_t l = (size_t)(c - name);
 | 
			
		||||
    char *ret = janet_malloc(l + 3);
 | 
			
		||||
    if (NULL == ret) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    ret[0] = '.';
 | 
			
		||||
    ret[1] = '/';
 | 
			
		||||
    memcpy(ret + 2, name, l + 1);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetModule janet_native(const char *name, const uint8_t **error) {
 | 
			
		||||
    char *processed_name = get_processed_name(name);
 | 
			
		||||
    Clib lib = load_clib(processed_name);
 | 
			
		||||
@@ -339,7 +294,10 @@ JANET_CORE_FN(janet_core_native,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_describe,
 | 
			
		||||
              "(describe x)",
 | 
			
		||||
              "Returns a string that is a human-readable description of a value x.") {
 | 
			
		||||
              "Returns a string that is a human-readable description of `x`. "
 | 
			
		||||
              "For recursive data structures, the string returned contains a "
 | 
			
		||||
              "pointer value from which the identity of `x` "
 | 
			
		||||
              "can be determined.") {
 | 
			
		||||
    JanetBuffer *b = janet_buffer(0);
 | 
			
		||||
    for (int32_t i = 0; i < argc; ++i)
 | 
			
		||||
        janet_description_b(b, argv[i]);
 | 
			
		||||
@@ -656,27 +614,39 @@ JANET_CORE_FN(janet_core_signal,
 | 
			
		||||
              "(signal what x)",
 | 
			
		||||
              "Raise a signal with payload x. ") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    int sig;
 | 
			
		||||
    Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
 | 
			
		||||
    if (janet_checkint(argv[0])) {
 | 
			
		||||
        int32_t s = janet_unwrap_integer(argv[0]);
 | 
			
		||||
        if (s < 0 || s > 9) {
 | 
			
		||||
            janet_panicf("expected user signal between 0 and 9, got %d", s);
 | 
			
		||||
        }
 | 
			
		||||
        sig = JANET_SIGNAL_USER0 + s;
 | 
			
		||||
        janet_signalv(JANET_SIGNAL_USER0 + s, payload);
 | 
			
		||||
    } else {
 | 
			
		||||
        JanetKeyword kw = janet_getkeyword(argv, 0);
 | 
			
		||||
        if (!janet_cstrcmp(kw, "yield")) {
 | 
			
		||||
            sig = JANET_SIGNAL_YIELD;
 | 
			
		||||
        } else if (!janet_cstrcmp(kw, "error")) {
 | 
			
		||||
            sig = JANET_SIGNAL_ERROR;
 | 
			
		||||
        } else if (!janet_cstrcmp(kw, "debug")) {
 | 
			
		||||
            sig = JANET_SIGNAL_DEBUG;
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_panicf("unknown signal, expected :yield, :error, or :debug, got %v", argv[0]);
 | 
			
		||||
        for (unsigned i = 0; i < sizeof(janet_signal_names) / sizeof(char *); i++) {
 | 
			
		||||
            if (!janet_cstrcmp(kw, janet_signal_names[i])) {
 | 
			
		||||
                janet_signalv((JanetSignal) i, payload);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
 | 
			
		||||
    janet_signalv(sig, payload);
 | 
			
		||||
    janet_panicf("unknown signal %v", argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_core_memcmp,
 | 
			
		||||
              "(memcmp a b &opt len offset-a offset-b)",
 | 
			
		||||
              "Compare memory. Takes to byte sequences `a` and `b`, and "
 | 
			
		||||
              "return 0 if they have identical contents, a negative integer if a is less than b, "
 | 
			
		||||
              "and a positive integer if a is greather than b. Optionally take a length and offsets "
 | 
			
		||||
              "to compare slices of the bytes sequences.") {
 | 
			
		||||
    janet_arity(argc, 2, 5);
 | 
			
		||||
    JanetByteView a = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView b = janet_getbytes(argv, 1);
 | 
			
		||||
    int32_t len = janet_optnat(argv, argc, 2, a.len < b.len ? a.len : b.len);
 | 
			
		||||
    int32_t offset_a = janet_optnat(argv, argc, 3, 0);
 | 
			
		||||
    int32_t offset_b = janet_optnat(argv, argc, 4, 0);
 | 
			
		||||
    if (offset_a + len > a.len) janet_panicf("invalid offset-a: %d", offset_a);
 | 
			
		||||
    if (offset_b + len > b.len) janet_panicf("invalid offset-b: %d", offset_b);
 | 
			
		||||
    return janet_wrap_integer(memcmp(a.bytes + offset_a, b.bytes + offset_b, (size_t) len));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
@@ -980,6 +950,7 @@ static void janet_load_libs(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("nat?", janet_core_check_nat),
 | 
			
		||||
        JANET_CORE_REG("slice", janet_core_slice),
 | 
			
		||||
        JANET_CORE_REG("signal", janet_core_signal),
 | 
			
		||||
        JANET_CORE_REG("memcmp", janet_core_memcmp),
 | 
			
		||||
        JANET_CORE_REG("getproto", janet_core_getproto),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
@@ -1013,6 +984,9 @@ static void janet_load_libs(JanetTable *env) {
 | 
			
		||||
#ifdef JANET_NET
 | 
			
		||||
    janet_lib_net(env);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI
 | 
			
		||||
    janet_lib_ffi(env);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BOOTSTRAP
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -97,7 +97,8 @@ void janet_debug_find(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_stacktrace(JanetFiber *fiber, Janet err) {
 | 
			
		||||
    janet_stacktrace_ext(fiber, err, NULL);
 | 
			
		||||
    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
 | 
			
		||||
@@ -129,9 +130,8 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
 | 
			
		||||
            /* Print prelude to stack frame */
 | 
			
		||||
            if (!wrote_error) {
 | 
			
		||||
                JanetFiberStatus status = janet_fiber_status(fiber);
 | 
			
		||||
                const char *override_prefix = prefix ? prefix : "";
 | 
			
		||||
                janet_eprintf("%s%s: %s\n",
 | 
			
		||||
                              override_prefix,
 | 
			
		||||
                              prefix,
 | 
			
		||||
                              janet_status_names[status],
 | 
			
		||||
                              errstr);
 | 
			
		||||
                wrote_error = 1;
 | 
			
		||||
@@ -340,9 +340,9 @@ JANET_CORE_FN(cfun_debug_stack,
 | 
			
		||||
              "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"
 | 
			
		||||
              "* :source-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"
 | 
			
		||||
              "* :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"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										219
									
								
								src/core/ev.c
									
									
									
									
									
								
							
							
						
						
									
										219
									
								
								src/core/ev.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -79,7 +79,11 @@ typedef struct {
 | 
			
		||||
    int32_t limit;
 | 
			
		||||
    int closed;
 | 
			
		||||
    int is_threaded;
 | 
			
		||||
    JanetOSMutex lock;
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    CRITICAL_SECTION lock;
 | 
			
		||||
#else
 | 
			
		||||
    pthread_mutex_t lock;
 | 
			
		||||
#endif
 | 
			
		||||
} JanetChannel;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -464,9 +468,10 @@ const JanetAbstractType janet_stream_type = {
 | 
			
		||||
 | 
			
		||||
/* Register a fiber to resume with value */
 | 
			
		||||
void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig) {
 | 
			
		||||
    if (fiber->flags & JANET_FIBER_FLAG_CANCELED) return;
 | 
			
		||||
    if (fiber->gc.flags & JANET_FIBER_EV_FLAG_CANCELED) return;
 | 
			
		||||
    fiber->gc.flags |= JANET_FIBER_FLAG_ROOT;
 | 
			
		||||
    JanetTask t = { fiber, value, sig, ++fiber->sched_id };
 | 
			
		||||
    if (sig == JANET_SIGNAL_ERROR) fiber->flags |= JANET_FIBER_FLAG_CANCELED;
 | 
			
		||||
    if (sig == JANET_SIGNAL_ERROR) fiber->gc.flags |= JANET_FIBER_EV_FLAG_CANCELED;
 | 
			
		||||
    janet_q_push(&janet_vm.spawn, &t, sizeof(t));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -530,10 +535,15 @@ static int janet_channel_push(JanetChannel *channel, Janet x, int mode);
 | 
			
		||||
static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice);
 | 
			
		||||
 | 
			
		||||
static Janet make_supervisor_event(const char *name, JanetFiber *fiber, int threaded) {
 | 
			
		||||
    Janet tup[2];
 | 
			
		||||
    Janet tup[3];
 | 
			
		||||
    tup[0] = janet_ckeywordv(name);
 | 
			
		||||
    tup[1] = threaded ? fiber->last_value : janet_wrap_fiber(fiber) ;
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_n(tup, 2));
 | 
			
		||||
    if (fiber->env != NULL) {
 | 
			
		||||
        tup[2] = janet_table_get(fiber->env, janet_ckeywordv("task-id"));
 | 
			
		||||
    } else {
 | 
			
		||||
        tup[2] = janet_wrap_nil();
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_tuple(janet_tuple_n(tup, 3));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Common init code */
 | 
			
		||||
@@ -642,7 +652,7 @@ static void janet_chan_init(JanetChannel *chan, int32_t limit, int threaded) {
 | 
			
		||||
    janet_q_init(&chan->items);
 | 
			
		||||
    janet_q_init(&chan->read_pending);
 | 
			
		||||
    janet_q_init(&chan->write_pending);
 | 
			
		||||
    janet_os_mutex_init(&chan->lock);
 | 
			
		||||
    janet_os_mutex_init((JanetOSMutex *) &chan->lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_chan_deinit(JanetChannel *chan) {
 | 
			
		||||
@@ -655,17 +665,17 @@ static void janet_chan_deinit(JanetChannel *chan) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    janet_q_deinit(&chan->items);
 | 
			
		||||
    janet_os_mutex_deinit(&chan->lock);
 | 
			
		||||
    janet_os_mutex_deinit((JanetOSMutex *) &chan->lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_chan_lock(JanetChannel *chan) {
 | 
			
		||||
    if (!janet_chan_is_threaded(chan)) return;
 | 
			
		||||
    janet_os_mutex_lock(&chan->lock);
 | 
			
		||||
    janet_os_mutex_lock((JanetOSMutex *) &chan->lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void janet_chan_unlock(JanetChannel *chan) {
 | 
			
		||||
    if (!janet_chan_is_threaded(chan)) return;
 | 
			
		||||
    janet_os_mutex_unlock(&chan->lock);
 | 
			
		||||
    janet_os_mutex_unlock((JanetOSMutex *) &chan->lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -744,7 +754,6 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
 | 
			
		||||
    int mode = msg.tag;
 | 
			
		||||
    JanetChannel *channel = (JanetChannel *) msg.argp;
 | 
			
		||||
    Janet x = msg.argj;
 | 
			
		||||
    janet_ev_dec_refcount();
 | 
			
		||||
    if (fiber->sched_id == sched_id) {
 | 
			
		||||
        if (mode == JANET_CP_MODE_CHOICE_READ) {
 | 
			
		||||
            janet_assert(!janet_chan_unpack(channel, &x, 0), "packing error");
 | 
			
		||||
@@ -837,7 +846,6 @@ static int janet_channel_push(JanetChannel *channel, Janet x, int mode) {
 | 
			
		||||
            pending.mode = mode ? JANET_CP_MODE_CHOICE_WRITE : JANET_CP_MODE_WRITE;
 | 
			
		||||
            janet_q_push(&channel->write_pending, &pending, sizeof(pending));
 | 
			
		||||
            janet_chan_unlock(channel);
 | 
			
		||||
            janet_ev_inc_refcount();
 | 
			
		||||
            if (is_threaded) {
 | 
			
		||||
                janet_gcroot(janet_wrap_fiber(pending.fiber));
 | 
			
		||||
            }
 | 
			
		||||
@@ -855,7 +863,6 @@ static int janet_channel_push(JanetChannel *channel, Janet x, int mode) {
 | 
			
		||||
            msg.argj = x;
 | 
			
		||||
            janet_ev_post_event(vm, janet_thread_chan_cb, msg);
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_ev_dec_refcount();
 | 
			
		||||
            if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
 | 
			
		||||
                janet_schedule(reader.fiber, make_read_result(channel, x));
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -888,7 +895,6 @@ static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice)
 | 
			
		||||
        pending.mode = is_choice ? JANET_CP_MODE_CHOICE_READ : JANET_CP_MODE_READ;
 | 
			
		||||
        janet_q_push(&channel->read_pending, &pending, sizeof(pending));
 | 
			
		||||
        janet_chan_unlock(channel);
 | 
			
		||||
        janet_ev_inc_refcount();
 | 
			
		||||
        if (is_threaded) {
 | 
			
		||||
            janet_gcroot(janet_wrap_fiber(pending.fiber));
 | 
			
		||||
        }
 | 
			
		||||
@@ -907,7 +913,6 @@ static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice)
 | 
			
		||||
            msg.argj = janet_wrap_nil();
 | 
			
		||||
            janet_ev_post_event(vm, janet_thread_chan_cb, msg);
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_ev_dec_refcount();
 | 
			
		||||
            if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) {
 | 
			
		||||
                janet_schedule(writer.fiber, make_write_result(channel));
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -979,23 +984,30 @@ JANET_CORE_FN(cfun_channel_choice,
 | 
			
		||||
            JanetChannel *chan = janet_getchannel(data, 0);
 | 
			
		||||
            janet_chan_lock(chan);
 | 
			
		||||
            if (chan->closed) {
 | 
			
		||||
                janet_chan_unlock(chan);
 | 
			
		||||
                return make_close_result(chan);
 | 
			
		||||
            }
 | 
			
		||||
            if (janet_q_count(&chan->items) < chan->limit) {
 | 
			
		||||
                janet_chan_unlock(chan);
 | 
			
		||||
                janet_channel_push(chan, data[1], 1);
 | 
			
		||||
                return make_write_result(chan);
 | 
			
		||||
            }
 | 
			
		||||
            janet_chan_unlock(chan);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Read */
 | 
			
		||||
            JanetChannel *chan = janet_getchannel(argv, i);
 | 
			
		||||
            janet_chan_lock(chan);
 | 
			
		||||
            if (chan->closed) {
 | 
			
		||||
                janet_chan_unlock(chan);
 | 
			
		||||
                return make_close_result(chan);
 | 
			
		||||
            }
 | 
			
		||||
            if (chan->items.head != chan->items.tail) {
 | 
			
		||||
                Janet item;
 | 
			
		||||
                janet_chan_unlock(chan);
 | 
			
		||||
                janet_channel_pop(chan, &item, 1);
 | 
			
		||||
                return make_read_result(chan, item);
 | 
			
		||||
            }
 | 
			
		||||
            janet_chan_unlock(chan);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1004,13 +1016,11 @@ JANET_CORE_FN(cfun_channel_choice,
 | 
			
		||||
        if (janet_indexed_view(argv[i], &data, &len) && len == 2) {
 | 
			
		||||
            /* Write */
 | 
			
		||||
            JanetChannel *chan = janet_getchannel(data, 0);
 | 
			
		||||
            if (chan->closed) continue;
 | 
			
		||||
            janet_channel_push(chan, data[1], 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            /* Read */
 | 
			
		||||
            Janet item;
 | 
			
		||||
            JanetChannel *chan = janet_getchannel(argv, i);
 | 
			
		||||
            if (chan->closed) continue;
 | 
			
		||||
            janet_channel_pop(chan, &item, 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -1111,7 +1121,6 @@ JANET_CORE_FN(cfun_channel_close,
 | 
			
		||||
                msg.argj = janet_wrap_nil();
 | 
			
		||||
                janet_ev_post_event(vm, janet_thread_chan_cb, msg);
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_ev_dec_refcount();
 | 
			
		||||
                if (writer.mode == JANET_CP_MODE_CHOICE_WRITE) {
 | 
			
		||||
                    janet_schedule(writer.fiber, janet_wrap_nil());
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -1131,7 +1140,6 @@ JANET_CORE_FN(cfun_channel_close,
 | 
			
		||||
                msg.argj = janet_wrap_nil();
 | 
			
		||||
                janet_ev_post_event(vm, janet_thread_chan_cb, msg);
 | 
			
		||||
            } else {
 | 
			
		||||
                janet_ev_dec_refcount();
 | 
			
		||||
                if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
 | 
			
		||||
                    janet_schedule(reader.fiber, janet_wrap_nil());
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -1228,22 +1236,27 @@ JanetFiber *janet_loop1(void) {
 | 
			
		||||
    while (janet_vm.spawn.head != janet_vm.spawn.tail) {
 | 
			
		||||
        JanetTask task = {NULL, janet_wrap_nil(), JANET_SIGNAL_OK, 0};
 | 
			
		||||
        janet_q_pop(&janet_vm.spawn, &task, sizeof(task));
 | 
			
		||||
        task.fiber->flags &= ~JANET_FIBER_FLAG_CANCELED;
 | 
			
		||||
        if (task.fiber->gc.flags & JANET_FIBER_EV_FLAG_SUSPENDED) janet_ev_dec_refcount();
 | 
			
		||||
        task.fiber->gc.flags &= ~(JANET_FIBER_EV_FLAG_CANCELED | JANET_FIBER_EV_FLAG_SUSPENDED);
 | 
			
		||||
        if (task.expected_sched_id != task.fiber->sched_id) continue;
 | 
			
		||||
        Janet res;
 | 
			
		||||
        JanetSignal sig = janet_continue_signal(task.fiber, task.value, &res, task.sig);
 | 
			
		||||
        void *sv = task.fiber->supervisor_channel;
 | 
			
		||||
        int is_suspended = sig == JANET_SIGNAL_EVENT || sig == JANET_SIGNAL_YIELD || sig == JANET_SIGNAL_INTERRUPT;
 | 
			
		||||
        if (is_suspended) {
 | 
			
		||||
            task.fiber->gc.flags |= JANET_FIBER_EV_FLAG_SUSPENDED;
 | 
			
		||||
            janet_ev_inc_refcount();
 | 
			
		||||
        }
 | 
			
		||||
        if (NULL == sv) {
 | 
			
		||||
            if (!is_suspended) {
 | 
			
		||||
                janet_stacktrace(task.fiber, res);
 | 
			
		||||
                janet_stacktrace_ext(task.fiber, res, "");
 | 
			
		||||
            }
 | 
			
		||||
        } else if (sig == JANET_SIGNAL_OK || (task.fiber->flags & (1 << sig))) {
 | 
			
		||||
            JanetChannel *chan = janet_channel_unwrap(sv);
 | 
			
		||||
            janet_channel_push(chan, make_supervisor_event(janet_signal_names[sig],
 | 
			
		||||
                               task.fiber, chan->is_threaded), 2);
 | 
			
		||||
        } else if (!is_suspended) {
 | 
			
		||||
            janet_stacktrace(task.fiber, res);
 | 
			
		||||
            janet_stacktrace_ext(task.fiber, res, "");
 | 
			
		||||
        }
 | 
			
		||||
        if (sig == JANET_SIGNAL_INTERRUPT) {
 | 
			
		||||
            /* On interrupts, return the interrupted fiber immediately */
 | 
			
		||||
@@ -1257,8 +1270,25 @@ JanetFiber *janet_loop1(void) {
 | 
			
		||||
        memset(&to, 0, sizeof(to));
 | 
			
		||||
        int has_timeout;
 | 
			
		||||
        /* Drop timeouts that are no longer needed */
 | 
			
		||||
        while ((has_timeout = peek_timeout(&to)) && (to.curr_fiber == NULL) && to.fiber->sched_id != to.sched_id) {
 | 
			
		||||
            pop_timeout(0);
 | 
			
		||||
        while ((has_timeout = peek_timeout(&to))) {
 | 
			
		||||
            if (to.curr_fiber != NULL) {
 | 
			
		||||
                JanetFiberStatus s = janet_fiber_status(to.curr_fiber);
 | 
			
		||||
                int is_finished = (s == JANET_STATUS_DEAD ||
 | 
			
		||||
                                   s == JANET_STATUS_ERROR ||
 | 
			
		||||
                                   s == JANET_STATUS_USER0 ||
 | 
			
		||||
                                   s == JANET_STATUS_USER1 ||
 | 
			
		||||
                                   s == JANET_STATUS_USER2 ||
 | 
			
		||||
                                   s == JANET_STATUS_USER3 ||
 | 
			
		||||
                                   s == JANET_STATUS_USER4);
 | 
			
		||||
                if (is_finished) {
 | 
			
		||||
                    pop_timeout(0);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            } else if (to.fiber->sched_id != to.sched_id) {
 | 
			
		||||
                pop_timeout(0);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        /* Run polling implementation only if pending timeouts or pending events */
 | 
			
		||||
        if (janet_vm.tq_count || janet_vm.listener_count || janet_vm.extra_listeners) {
 | 
			
		||||
@@ -1431,7 +1461,7 @@ static void janet_epoll_sync_callback(JanetEVGenericMessage msg) {
 | 
			
		||||
    JanetAsyncStatus status2 = JANET_ASYNC_STATUS_NOT_DONE;
 | 
			
		||||
    if (state->stream->_mask & JANET_ASYNC_LISTEN_WRITE)
 | 
			
		||||
        status1 = state->machine(state, JANET_ASYNC_EVENT_WRITE);
 | 
			
		||||
    if (state->stream->_mask & JANET_ASYNC_LISTEN_WRITE)
 | 
			
		||||
    if (state->stream->_mask & JANET_ASYNC_LISTEN_READ)
 | 
			
		||||
        status2 = state->machine(state, JANET_ASYNC_EVENT_READ);
 | 
			
		||||
    if (status1 == JANET_ASYNC_STATUS_DONE ||
 | 
			
		||||
            status2 == JANET_ASYNC_STATUS_DONE) {
 | 
			
		||||
@@ -1611,9 +1641,6 @@ JanetTimestamp to_interval(const JanetTimestamp ts) {
 | 
			
		||||
}
 | 
			
		||||
#define JANET_KQUEUE_INTERVAL(timestamp) (to_interval((timestamp - ts_now())))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* TODO: make this available be we using kqueue or epoll, instead of
 | 
			
		||||
 * redefinining it for kqueue and epoll separately? */
 | 
			
		||||
static JanetTimestamp ts_now(void) {
 | 
			
		||||
    struct timespec now;
 | 
			
		||||
    janet_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &now), "failed to get time");
 | 
			
		||||
@@ -2216,6 +2243,10 @@ JanetAsyncStatus ev_machine_read(JanetListenerState *s, JanetAsyncEvent event) {
 | 
			
		||||
            } else
 | 
			
		||||
#endif
 | 
			
		||||
            {
 | 
			
		||||
                /* Some handles (not all) read from the offset in lopOverlapped
 | 
			
		||||
                 * if its not set before calling `ReadFile` these streams will always read from offset 0 */
 | 
			
		||||
                state->overlapped.Offset = (DWORD) state->bytes_read;
 | 
			
		||||
 | 
			
		||||
                status = ReadFile(s->stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped);
 | 
			
		||||
                if (!status && (ERROR_IO_PENDING != WSAGetLastError())) {
 | 
			
		||||
                    if (WSAGetLastError() == ERROR_BROKEN_PIPE) {
 | 
			
		||||
@@ -2656,9 +2687,10 @@ error:
 | 
			
		||||
/* C functions */
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_ev_go,
 | 
			
		||||
              "(ev/go fiber &opt value supervisor)",
 | 
			
		||||
              "Put a fiber on the event loop to be resumed later. Optionally pass "
 | 
			
		||||
              "a value to resume with, otherwise resumes with nil. Returns the fiber. "
 | 
			
		||||
              "(ev/go fiber-or-fun &opt value supervisor)",
 | 
			
		||||
              "Put a fiber on the event loop to be resumed later. If a function is used, it is wrapped"
 | 
			
		||||
              "with `fiber/new` first. "
 | 
			
		||||
              "Optionally pass a value to resume with, otherwise resumes with nil. Returns the fiber. "
 | 
			
		||||
              "An optional `core/channel` can be provided as a supervisor. When various "
 | 
			
		||||
              "events occur in the newly scheduled fiber, an event will be pushed to the supervisor. "
 | 
			
		||||
              "If not provided, the new fiber will inherit the current supervisor.") {
 | 
			
		||||
@@ -2693,6 +2725,8 @@ JANET_CORE_FN(cfun_ev_go,
 | 
			
		||||
    return janet_wrap_fiber(fiber);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define JANET_THREAD_SUPERVISOR_FLAG 0x100
 | 
			
		||||
 | 
			
		||||
/* For ev/thread - Run an interpreter in the new thread. */
 | 
			
		||||
static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
 | 
			
		||||
    JanetBuffer *buffer = (JanetBuffer *) args.argp;
 | 
			
		||||
@@ -2715,7 +2749,7 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Get supervsior */
 | 
			
		||||
        if (flags & 0x8) {
 | 
			
		||||
        if (flags & JANET_THREAD_SUPERVISOR_FLAG) {
 | 
			
		||||
            Janet sup =
 | 
			
		||||
                janet_unmarshal(nextbytes, endbytes - nextbytes,
 | 
			
		||||
                                JANET_MARSHAL_UNSAFE, NULL, &nextbytes);
 | 
			
		||||
@@ -2767,6 +2801,10 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
 | 
			
		||||
        } else {
 | 
			
		||||
            fiber = janet_unwrap_fiber(fiberv);
 | 
			
		||||
        }
 | 
			
		||||
        if (flags & 0x8) {
 | 
			
		||||
            if (NULL == fiber->env) fiber->env = janet_table(0);
 | 
			
		||||
            janet_table_put(fiber->env, janet_ckeywordv("task-id"), value);
 | 
			
		||||
        }
 | 
			
		||||
        fiber->supervisor_channel = janet_vm.user;
 | 
			
		||||
        janet_schedule(fiber, value);
 | 
			
		||||
        janet_loop();
 | 
			
		||||
@@ -2811,6 +2849,7 @@ JANET_CORE_FN(cfun_ev_thread,
 | 
			
		||||
              "If you want to run the thread without waiting for a result, pass the `:n` flag to return nil immediately. "
 | 
			
		||||
              "Otherwise, returns nil. Available flags:\n\n"
 | 
			
		||||
              "* `:n` - return immediately\n"
 | 
			
		||||
              "* `:t` - set the task-id of the new thread to value. The task-id is passed in messages to the supervisor channel.\n"
 | 
			
		||||
              "* `:a` - don't copy abstract registry to new thread (performance optimization)\n"
 | 
			
		||||
              "* `:c` - don't copy cfunction registry to new thread (performance optimization)") {
 | 
			
		||||
    janet_arity(argc, 1, 4);
 | 
			
		||||
@@ -2818,10 +2857,10 @@ JANET_CORE_FN(cfun_ev_thread,
 | 
			
		||||
    if (!janet_checktype(argv[0], JANET_FUNCTION)) janet_getfiber(argv, 0);
 | 
			
		||||
    uint64_t flags = 0;
 | 
			
		||||
    if (argc >= 3) {
 | 
			
		||||
        flags = janet_getflags(argv, 2, "nac");
 | 
			
		||||
        flags = janet_getflags(argv, 2, "nact");
 | 
			
		||||
    }
 | 
			
		||||
    void *supervisor = janet_optabstract(argv, argc, 3, &janet_channel_type, janet_vm.root_fiber->supervisor_channel);
 | 
			
		||||
    if (NULL != supervisor) flags |= 0x8;
 | 
			
		||||
    if (NULL != supervisor) flags |= JANET_THREAD_SUPERVISOR_FLAG;
 | 
			
		||||
 | 
			
		||||
    /* Marshal arguments for the new thread. */
 | 
			
		||||
    JanetBuffer *buffer = janet_malloc(sizeof(JanetBuffer));
 | 
			
		||||
@@ -2832,7 +2871,7 @@ JANET_CORE_FN(cfun_ev_thread,
 | 
			
		||||
    if (!(flags & 0x2)) {
 | 
			
		||||
        janet_marshal(buffer, janet_wrap_table(janet_vm.abstract_registry), NULL, JANET_MARSHAL_UNSAFE);
 | 
			
		||||
    }
 | 
			
		||||
    if (flags & 0x8) {
 | 
			
		||||
    if (flags & JANET_THREAD_SUPERVISOR_FLAG) {
 | 
			
		||||
        janet_marshal(buffer, janet_wrap_abstract(supervisor), NULL, JANET_MARSHAL_UNSAFE);
 | 
			
		||||
    }
 | 
			
		||||
    if (!(flags & 0x4)) {
 | 
			
		||||
@@ -2991,6 +3030,106 @@ JANET_CORE_FN(janet_cfun_stream_write,
 | 
			
		||||
    janet_await();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int mutexgc(void *p, size_t size) {
 | 
			
		||||
    (void) size;
 | 
			
		||||
    janet_os_mutex_deinit(p);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_mutex_type = {
 | 
			
		||||
    "core/lock",
 | 
			
		||||
    mutexgc,
 | 
			
		||||
    JANET_ATEND_GC
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_mutex,
 | 
			
		||||
              "(ev/lock)",
 | 
			
		||||
              "Create a new lock to coordinate threads.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    void *mutex = janet_abstract_threaded(&janet_mutex_type, janet_os_mutex_size());
 | 
			
		||||
    janet_os_mutex_init(mutex);
 | 
			
		||||
    return janet_wrap_abstract(mutex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_mutex_acquire,
 | 
			
		||||
              "(ev/acquire-lock lock)",
 | 
			
		||||
              "Acquire a lock such that this operating system thread is the only thread with access to this resource."
 | 
			
		||||
              " This will block this entire thread until the lock becomes available, and will not yield to other fibers "
 | 
			
		||||
              "on this system thread.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    void *mutex = janet_getabstract(argv, 0, &janet_mutex_type);
 | 
			
		||||
    janet_os_mutex_lock(mutex);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_mutex_release,
 | 
			
		||||
              "(ev/release-lock lock)",
 | 
			
		||||
              "Release a lock such that other threads may acquire it.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    void *mutex = janet_getabstract(argv, 0, &janet_mutex_type);
 | 
			
		||||
    janet_os_mutex_unlock(mutex);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rwlockgc(void *p, size_t size) {
 | 
			
		||||
    (void) size;
 | 
			
		||||
    janet_os_rwlock_deinit(p);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const JanetAbstractType janet_rwlock_type = {
 | 
			
		||||
    "core/rwlock",
 | 
			
		||||
    rwlockgc,
 | 
			
		||||
    JANET_ATEND_GC
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_rwlock,
 | 
			
		||||
              "(ev/rwlock)",
 | 
			
		||||
              "Create a new read-write lock to coordinate threads.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    void *rwlock = janet_abstract_threaded(&janet_rwlock_type, janet_os_rwlock_size());
 | 
			
		||||
    janet_os_rwlock_init(rwlock);
 | 
			
		||||
    return janet_wrap_abstract(rwlock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_rwlock_read_lock,
 | 
			
		||||
              "(ev/acquire-rlock rwlock)",
 | 
			
		||||
              "Acquire a read lock an a read-write lock.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    void *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
 | 
			
		||||
    janet_os_rwlock_rlock(rwlock);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_rwlock_write_lock,
 | 
			
		||||
              "(ev/acquire-wlock rwlock)",
 | 
			
		||||
              "Acquire a write lock on a read-write lock.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    void *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
 | 
			
		||||
    janet_os_rwlock_wlock(rwlock);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_rwlock_read_release,
 | 
			
		||||
              "(ev/release-rlock rwlock)",
 | 
			
		||||
              "Release a read lock on a read-write lock") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    void *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
 | 
			
		||||
    janet_os_rwlock_runlock(rwlock);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(janet_cfun_rwlock_write_release,
 | 
			
		||||
              "(ev/release-wlock rwlock)",
 | 
			
		||||
              "Release a write lock on a read-write lock") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    void *rwlock = janet_getabstract(argv, 0, &janet_rwlock_type);
 | 
			
		||||
    janet_os_rwlock_wunlock(rwlock);
 | 
			
		||||
    return argv[0];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_lib_ev(JanetTable *env) {
 | 
			
		||||
    JanetRegExt ev_cfuns_ext[] = {
 | 
			
		||||
        JANET_CORE_REG("ev/give", cfun_channel_push),
 | 
			
		||||
@@ -3013,12 +3152,22 @@ void janet_lib_ev(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("ev/read", janet_cfun_stream_read),
 | 
			
		||||
        JANET_CORE_REG("ev/chunk", janet_cfun_stream_chunk),
 | 
			
		||||
        JANET_CORE_REG("ev/write", janet_cfun_stream_write),
 | 
			
		||||
        JANET_CORE_REG("ev/lock", janet_cfun_mutex),
 | 
			
		||||
        JANET_CORE_REG("ev/acquire-lock", janet_cfun_mutex_acquire),
 | 
			
		||||
        JANET_CORE_REG("ev/release-lock", janet_cfun_mutex_release),
 | 
			
		||||
        JANET_CORE_REG("ev/rwlock", janet_cfun_rwlock),
 | 
			
		||||
        JANET_CORE_REG("ev/acquire-rlock", janet_cfun_rwlock_read_lock),
 | 
			
		||||
        JANET_CORE_REG("ev/acquire-wlock", janet_cfun_rwlock_write_lock),
 | 
			
		||||
        JANET_CORE_REG("ev/release-rlock", janet_cfun_rwlock_read_release),
 | 
			
		||||
        JANET_CORE_REG("ev/release-wlock", janet_cfun_rwlock_write_release),
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, ev_cfuns_ext);
 | 
			
		||||
    janet_register_abstract_type(&janet_stream_type);
 | 
			
		||||
    janet_register_abstract_type(&janet_channel_type);
 | 
			
		||||
    janet_register_abstract_type(&janet_mutex_type);
 | 
			
		||||
    janet_register_abstract_type(&janet_rwlock_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -36,13 +36,22 @@
 | 
			
		||||
# endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for sched.h for cpu count */
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(WIN32) || defined(_WIN32)
 | 
			
		||||
#define WIN32_LEAN_AND_MEAN
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for realpath on linux */
 | 
			
		||||
#if !defined(_XOPEN_SOURCE) && (defined(__linux__) || defined(__EMSCRIPTEN__))
 | 
			
		||||
#define _XOPEN_SOURCE 500
 | 
			
		||||
/* Needed for realpath on linux, as well as pthread rwlocks. */
 | 
			
		||||
#ifndef _XOPEN_SOURCE
 | 
			
		||||
#define _XOPEN_SOURCE 600
 | 
			
		||||
#endif
 | 
			
		||||
#if _XOPEN_SOURCE < 600
 | 
			
		||||
#undef _XOPEN_SOURCE
 | 
			
		||||
#define _XOPEN_SOURCE 600
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Needed for timegm and other extensions when building with -std=c99.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1372
									
								
								src/core/ffi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1372
									
								
								src/core/ffi.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -48,7 +48,6 @@
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_STATUS_MASK 0x3F0000
 | 
			
		||||
#define JANET_FIBER_RESUME_SIGNAL 0x400000
 | 
			
		||||
#define JANET_FIBER_FLAG_CANCELED 0x400000
 | 
			
		||||
#define JANET_FIBER_STATUS_OFFSET 16
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_BREAKPOINT       0x1000000
 | 
			
		||||
@@ -57,6 +56,10 @@
 | 
			
		||||
#define JANET_FIBER_DID_LONGJUMP     0x8000000
 | 
			
		||||
#define JANET_FIBER_FLAG_MASK        0xF000000
 | 
			
		||||
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_CANCELED 0x10000
 | 
			
		||||
#define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000
 | 
			
		||||
#define JANET_FIBER_FLAG_ROOT 0x40000
 | 
			
		||||
 | 
			
		||||
#define janet_fiber_set_status(f, s) do {\
 | 
			
		||||
    (f)->flags &= ~JANET_FIBER_STATUS_MASK;\
 | 
			
		||||
    (f)->flags |= (s) << JANET_FIBER_STATUS_OFFSET;\
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose & contributors
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose & contributors
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -207,6 +207,92 @@ JANET_CORE_FN(cfun_it_u64_new,
 | 
			
		||||
    return janet_wrap_u64(janet_unwrap_u64(argv[0]));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_to_number,
 | 
			
		||||
              "(int/to-number value)",
 | 
			
		||||
              "Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int32.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    if (janet_type(argv[0]) == JANET_ABSTRACT) {
 | 
			
		||||
        void *abst = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
 | 
			
		||||
        if (janet_abstract_type(abst) == &janet_s64_type) {
 | 
			
		||||
            int64_t value = *((int64_t *)abst);
 | 
			
		||||
            if (value > JANET_INTMAX_INT64) {
 | 
			
		||||
                janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
 | 
			
		||||
            }
 | 
			
		||||
            if (value < -JANET_INTMAX_INT64) {
 | 
			
		||||
                janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
 | 
			
		||||
            }
 | 
			
		||||
            return janet_wrap_number((double)value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (janet_abstract_type(abst) == &janet_u64_type) {
 | 
			
		||||
            uint64_t value = *((uint64_t *)abst);
 | 
			
		||||
            if (value > JANET_INTMAX_INT64) {
 | 
			
		||||
                janet_panicf("cannot convert %q to a number, must be in the range [%q, %q]", argv[0], janet_wrap_number(JANET_INTMIN_DOUBLE), janet_wrap_number(JANET_INTMAX_DOUBLE));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return janet_wrap_number((double)value);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    janet_panicf("expected int/u64 or int/s64, got %q", argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_to_bytes,
 | 
			
		||||
              "(int/to-bytes value &opt endianness buffer)",
 | 
			
		||||
              "Write the bytes of an `int/s64` or `int/u64` into a buffer.\n"
 | 
			
		||||
              "The `buffer` parameter specifies an existing buffer to write to, if unset a new buffer will be created.\n"
 | 
			
		||||
              "Returns the modified buffer.\n"
 | 
			
		||||
              "The `endianness` paramater indicates the byte order:\n"
 | 
			
		||||
              "- `nil` (unset): system byte order\n"
 | 
			
		||||
              "- `:le`: little-endian, least significant byte first\n"
 | 
			
		||||
              "- `:be`: big-endian, most significant byte first\n") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    if (janet_is_int(argv[0]) == JANET_INT_NONE) {
 | 
			
		||||
        janet_panicf("int/to-bytes: expected an int/s64 or int/u64, got %q", argv[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int reverse = 0;
 | 
			
		||||
    if (argc > 1 && !janet_checktype(argv[1], JANET_NIL)) {
 | 
			
		||||
        JanetKeyword endianness_kw = janet_getkeyword(argv, 1);
 | 
			
		||||
        if (!janet_cstrcmp(endianness_kw, "le")) {
 | 
			
		||||
#if JANET_BIG_ENDIAN
 | 
			
		||||
            reverse = 1;
 | 
			
		||||
#endif
 | 
			
		||||
        } else if (!janet_cstrcmp(endianness_kw, "be")) {
 | 
			
		||||
#if JANET_LITTLE_ENDIAN
 | 
			
		||||
            reverse = 1;
 | 
			
		||||
#endif
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_panicf("int/to-bytes: expected endianness :le, :be or nil, got %v", argv[1]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JanetBuffer *buffer = NULL;
 | 
			
		||||
    if (argc > 2 && !janet_checktype(argv[2], JANET_NIL)) {
 | 
			
		||||
        if (!janet_checktype(argv[2], JANET_BUFFER)) {
 | 
			
		||||
            janet_panicf("int/to-bytes: expected buffer or nil, got %q", argv[2]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        buffer = janet_unwrap_buffer(argv[2]);
 | 
			
		||||
        janet_buffer_extra(buffer, 8);
 | 
			
		||||
    } else {
 | 
			
		||||
        buffer = janet_buffer(8);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t *bytes = janet_unwrap_abstract(argv[0]);
 | 
			
		||||
    if (reverse) {
 | 
			
		||||
        for (int i = 0; i < 8; ++i) {
 | 
			
		||||
            buffer->data[buffer->count + 7 - i] = bytes[i];
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        memcpy(buffer->data + buffer->count, bytes, 8);
 | 
			
		||||
    }
 | 
			
		||||
    buffer->count += 8;
 | 
			
		||||
 | 
			
		||||
    return janet_wrap_buffer(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Code to support polymorphic comparison.
 | 
			
		||||
 * int/u64 and int/s64 support a "compare" method that allows
 | 
			
		||||
@@ -321,13 +407,26 @@ static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * In C, signed arithmetic overflow is undefined behvior
 | 
			
		||||
 * but unsigned arithmetic overflow is twos complement
 | 
			
		||||
 *
 | 
			
		||||
 * Reference:
 | 
			
		||||
 * https://en.cppreference.com/w/cpp/language/ub
 | 
			
		||||
 * http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
 | 
			
		||||
 *
 | 
			
		||||
 * This means OPMETHOD & OPMETHODINVERT must always use
 | 
			
		||||
 * unsigned arithmetic internally, regardless of the true type.
 | 
			
		||||
 * This will not affect the end result (property of twos complement).
 | 
			
		||||
 */
 | 
			
		||||
#define OPMETHOD(T, type, name, oper) \
 | 
			
		||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_arity(argc, 2, -1); \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[0]); \
 | 
			
		||||
    for (int32_t i = 1; i < argc; i++) \
 | 
			
		||||
        *box oper##= janet_unwrap_##type(argv[i]); \
 | 
			
		||||
        /* This avoids undefined behavior. See above for why. */ \
 | 
			
		||||
        *box = (T) ((uint64_t) (*box)) oper ((uint64_t) janet_unwrap_##type(argv[i])); \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
@@ -336,7 +435,8 @@ static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
 | 
			
		||||
    janet_fixarity(argc, 2); \
 | 
			
		||||
    T *box = janet_abstract(&janet_##type##_type, sizeof(T)); \
 | 
			
		||||
    *box = janet_unwrap_##type(argv[1]); \
 | 
			
		||||
    *box oper##= janet_unwrap_##type(argv[0]); \
 | 
			
		||||
    /* This avoids undefined behavior. See above for why. */ \
 | 
			
		||||
    *box = (T) ((uint64_t) *box) oper ((uint64_t) janet_unwrap_##type(argv[0])); \
 | 
			
		||||
    return janet_wrap_abstract(box); \
 | 
			
		||||
} \
 | 
			
		||||
 | 
			
		||||
@@ -514,6 +614,8 @@ void janet_lib_inttypes(JanetTable *env) {
 | 
			
		||||
    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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										123
									
								
								src/core/io.c
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								src/core/io.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -112,42 +112,6 @@ static void *makef(FILE *f, int32_t flags) {
 | 
			
		||||
    return iof;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Open a process */
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
JANET_CORE_FN(cfun_io_popen,
 | 
			
		||||
              "(file/popen command &opt mode) (DEPRECATED for os/spawn)",
 | 
			
		||||
              "Open a file that is backed by a process. The file must be opened in either "
 | 
			
		||||
              "the :r (read) or the :w (write) mode. In :r mode, the stdout of the "
 | 
			
		||||
              "process can be read from the file. In :w mode, the stdin of the process "
 | 
			
		||||
              "can be written to. Returns the new file.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    const uint8_t *fname = janet_getstring(argv, 0);
 | 
			
		||||
    const uint8_t *fmode = NULL;
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_temp,
 | 
			
		||||
              "(file/temp)",
 | 
			
		||||
              "Open an anonymous temporary file that is removed on close. "
 | 
			
		||||
@@ -296,7 +260,6 @@ JANET_CORE_FN(cfun_io_fflush,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#define pclose _pclose
 | 
			
		||||
#define WEXITSTATUS(x) x
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -304,14 +267,7 @@ JANET_CORE_FN(cfun_io_fflush,
 | 
			
		||||
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;
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
@@ -331,30 +287,18 @@ JANET_CORE_FN(cfun_io_fclose,
 | 
			
		||||
              "(file/close f)",
 | 
			
		||||
              "Close a file and release all related resources. When you are "
 | 
			
		||||
              "done reading a file, close it to prevent a resource leak and let "
 | 
			
		||||
              "other processes read the file. If the file is the result of a file/popen "
 | 
			
		||||
              "call, close waits for and returns the process exit status.") {
 | 
			
		||||
              "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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -546,27 +490,27 @@ 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 "
 | 
			
		||||
              "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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_prin,
 | 
			
		||||
              "(prin & xs)",
 | 
			
		||||
              "Same as print, but does not add trailing newline.") {
 | 
			
		||||
              "Same as `print`, but does not add trailing newline.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 0, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprint,
 | 
			
		||||
              "(eprint & xs)",
 | 
			
		||||
              "Same as print, but uses (dyn :err stderr) instead of (dyn :out stdout).") {
 | 
			
		||||
              "Same as `print`, but uses `(dyn :err stderr)` instead of `(dyn :out stdout)`.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 1, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprin,
 | 
			
		||||
              "(eprin & xs)",
 | 
			
		||||
              "Same as prin, but uses (dyn :err stderr) instead of (dyn :out stdout).") {
 | 
			
		||||
              "Same as `prin`, but uses `(dyn :err stderr)` instead of `(dyn :out stdout)`.") {
 | 
			
		||||
    return cfun_io_print_impl(argc, argv, 0, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -574,7 +518,7 @@ 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.") {
 | 
			
		||||
              "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]);
 | 
			
		||||
}
 | 
			
		||||
@@ -582,7 +526,7 @@ JANET_CORE_FN(cfun_io_xprint,
 | 
			
		||||
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.") {
 | 
			
		||||
              "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]);
 | 
			
		||||
}
 | 
			
		||||
@@ -601,6 +545,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");
 | 
			
		||||
@@ -640,38 +594,38 @@ static Janet cfun_io_printf_impl(int32_t argc, Janet *argv, int newline,
 | 
			
		||||
 | 
			
		||||
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.") {
 | 
			
		||||
              "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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_prinf,
 | 
			
		||||
              "(prinf fmt & xs)",
 | 
			
		||||
              "Like printf but with no trailing newline.") {
 | 
			
		||||
              "Like `printf` but with no trailing newline.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 0, "out", stdout);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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.") {
 | 
			
		||||
              "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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_eprinf,
 | 
			
		||||
              "(eprinf fmt & xs)",
 | 
			
		||||
              "Like eprintf but with no trailing newline.") {
 | 
			
		||||
              "Like `eprintf` but with no trailing newline.") {
 | 
			
		||||
    return cfun_io_printf_impl(argc, argv, 0, "err", stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_xprintf,
 | 
			
		||||
              "(xprintf to fmt & xs)",
 | 
			
		||||
              "Like printf but prints to an explicit file or value to. Returns nil.") {
 | 
			
		||||
              "Like `printf` but prints to an explicit file or value `to`. Returns nil.") {
 | 
			
		||||
    janet_arity(argc, 2, -1);
 | 
			
		||||
    return cfun_io_printf_impl_x(argc, argv, 1, NULL, 1, argv[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_xprinf,
 | 
			
		||||
              "(xprinf to fmt & xs)",
 | 
			
		||||
              "Like prinf but prints to an explicit file or value to. Returns nil.") {
 | 
			
		||||
              "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]);
 | 
			
		||||
}
 | 
			
		||||
@@ -696,7 +650,7 @@ static void janet_flusher(const char *name, FILE *dflt_file) {
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_flush,
 | 
			
		||||
              "(flush)",
 | 
			
		||||
              "Flush (dyn :out stdout) if it is a file, otherwise do nothing.") {
 | 
			
		||||
              "Flush `(dyn :out stdout)` if it is a file, otherwise do nothing.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_flusher("out", stdout);
 | 
			
		||||
@@ -705,7 +659,7 @@ JANET_CORE_FN(cfun_io_flush,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_io_eflush,
 | 
			
		||||
              "(eflush)",
 | 
			
		||||
              "Flush (dyn :err stderr) if it is a file, otherwise do nothing.") {
 | 
			
		||||
              "Flush `(dyn :err stderr)` if it is a file, otherwise do nothing.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_flusher("err", stderr);
 | 
			
		||||
@@ -740,6 +694,16 @@ void janet_dynprintf(const char *name, FILE *dflt_file, const char *format, ...)
 | 
			
		||||
            janet_buffer_deinit(&buffer);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_FUNCTION: {
 | 
			
		||||
            JanetFunction *fun = janet_unwrap_function(x);
 | 
			
		||||
            int32_t len = 0;
 | 
			
		||||
            while (format[len]) len++;
 | 
			
		||||
            JanetBuffer *buf = janet_buffer(len);
 | 
			
		||||
            janet_formatbv(buf, format, args);
 | 
			
		||||
            Janet args[1] = { janet_wrap_buffer(buf) };
 | 
			
		||||
            janet_call(fun, 1, args);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_BUFFER:
 | 
			
		||||
            janet_formatbv(janet_unwrap_buffer(x), format, args);
 | 
			
		||||
            break;
 | 
			
		||||
@@ -802,9 +766,6 @@ void janet_lib_io(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("file/write", cfun_io_fwrite),
 | 
			
		||||
        JANET_CORE_REG("file/flush", cfun_io_fflush),
 | 
			
		||||
        JANET_CORE_REG("file/seek", cfun_io_fseek),
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
        JANET_CORE_REG("file/popen", cfun_io_popen),
 | 
			
		||||
#endif
 | 
			
		||||
        JANET_REG_END
 | 
			
		||||
    };
 | 
			
		||||
    janet_core_cfuns_ext(env, NULL, io_cfuns);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -37,6 +37,7 @@ typedef struct {
 | 
			
		||||
    JanetFuncEnv **seen_envs;
 | 
			
		||||
    JanetFuncDef **seen_defs;
 | 
			
		||||
    int32_t nextid;
 | 
			
		||||
    int maybe_cycles;
 | 
			
		||||
} MarshalState;
 | 
			
		||||
 | 
			
		||||
/* Lead bytes in marshaling protocol */
 | 
			
		||||
@@ -364,13 +365,15 @@ void janet_marshal_janet(JanetMarshalContext *ctx, Janet x) {
 | 
			
		||||
 | 
			
		||||
void janet_marshal_abstract(JanetMarshalContext *ctx, void *abstract) {
 | 
			
		||||
    MarshalState *st = (MarshalState *)(ctx->m_state);
 | 
			
		||||
    janet_table_put(&st->seen,
 | 
			
		||||
                    janet_wrap_abstract(abstract),
 | 
			
		||||
                    janet_wrap_integer(st->nextid++));
 | 
			
		||||
    if (st->maybe_cycles) {
 | 
			
		||||
        janet_table_put(&st->seen,
 | 
			
		||||
                        janet_wrap_abstract(abstract),
 | 
			
		||||
                        janet_wrap_integer(st->nextid++));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MARK_SEEN() \
 | 
			
		||||
    janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++))
 | 
			
		||||
    do { if (st->maybe_cycles) janet_table_put(&st->seen, x, janet_wrap_integer(st->nextid++)); } while (0)
 | 
			
		||||
 | 
			
		||||
static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
    void *abstract = janet_unwrap_abstract(x);
 | 
			
		||||
@@ -428,11 +431,14 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
 | 
			
		||||
 | 
			
		||||
    /* Check reference and registry value */
 | 
			
		||||
    {
 | 
			
		||||
        Janet check = janet_table_get(&st->seen, x);
 | 
			
		||||
        if (janet_checkint(check)) {
 | 
			
		||||
            pushbyte(st, LB_REFERENCE);
 | 
			
		||||
            pushint(st, janet_unwrap_integer(check));
 | 
			
		||||
            return;
 | 
			
		||||
        Janet check;
 | 
			
		||||
        if (st->maybe_cycles) {
 | 
			
		||||
            check = janet_table_get(&st->seen, x);
 | 
			
		||||
            if (janet_checkint(check)) {
 | 
			
		||||
                pushbyte(st, LB_REFERENCE);
 | 
			
		||||
                pushint(st, janet_unwrap_integer(check));
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (st->rreg) {
 | 
			
		||||
            check = janet_table_get(st->rreg, x);
 | 
			
		||||
@@ -613,6 +619,7 @@ void janet_marshal(
 | 
			
		||||
    st.seen_defs = NULL;
 | 
			
		||||
    st.seen_envs = NULL;
 | 
			
		||||
    st.rreg = rreg;
 | 
			
		||||
    st.maybe_cycles = !(flags & JANET_MARSHAL_NO_CYCLES);
 | 
			
		||||
    janet_table_init(&st.seen, 0);
 | 
			
		||||
    marshal_one(&st, x, flags);
 | 
			
		||||
    janet_table_deinit(&st.seen);
 | 
			
		||||
@@ -1471,16 +1478,17 @@ JANET_CORE_FN(cfun_env_lookup,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_marshal,
 | 
			
		||||
              "(marshal x &opt reverse-lookup buffer)",
 | 
			
		||||
              "(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, 3);
 | 
			
		||||
    janet_arity(argc, 1, 4);
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
    JanetTable *rreg = NULL;
 | 
			
		||||
    uint32_t flags = 0;
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
        rreg = janet_gettable(argv, 1);
 | 
			
		||||
    }
 | 
			
		||||
@@ -1489,7 +1497,10 @@ JANET_CORE_FN(cfun_marshal,
 | 
			
		||||
    } 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -231,7 +231,7 @@ static Janet janet_rng_next(void *p, Janet key) {
 | 
			
		||||
/* Get a random number */
 | 
			
		||||
JANET_CORE_FN(janet_rand,
 | 
			
		||||
              "(math/random)",
 | 
			
		||||
              "Returns a uniformly distributed random number between 0 and 1") {
 | 
			
		||||
              "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));
 | 
			
		||||
@@ -240,7 +240,7 @@ JANET_CORE_FN(janet_rand,
 | 
			
		||||
/* Seed the random number generator */
 | 
			
		||||
JANET_CORE_FN(janet_srand,
 | 
			
		||||
              "(math/seedrandom seed)",
 | 
			
		||||
              "Set the seed for the random number generator. seed should be "
 | 
			
		||||
              "Set the seed for the random number generator. `seed` should be "
 | 
			
		||||
              "an integer or a buffer."
 | 
			
		||||
             ) {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
@@ -261,7 +261,7 @@ JANET_CORE_FN(janet_##name, "(math/" #name " x)", doc) {\
 | 
			
		||||
    return janet_wrap_number(fop(x)); \
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_DEFINE_MATHOP(acos, acos, "Returns the arccosize of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(acos, acos, "Returns the arccosine of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(asin, asin, "Returns the arcsin of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(atan, atan, "Returns the arctangent of x.")
 | 
			
		||||
JANET_DEFINE_MATHOP(cos, cos, "Returns the cosine of x.")
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose and contributors.
 | 
			
		||||
* Copyright (c) 2022 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
 | 
			
		||||
@@ -224,7 +224,12 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
 | 
			
		||||
            janet_schedule(s->fiber, janet_wrap_nil());
 | 
			
		||||
            return JANET_ASYNC_STATUS_DONE;
 | 
			
		||||
        case JANET_ASYNC_EVENT_READ: {
 | 
			
		||||
#if defined(JANET_LINUX)
 | 
			
		||||
            JSock connfd = accept4(s->stream->handle, NULL, NULL, SOCK_CLOEXEC);
 | 
			
		||||
#else
 | 
			
		||||
            /* On BSDs, CLOEXEC should be inherited from server socket */
 | 
			
		||||
            JSock connfd = accept(s->stream->handle, NULL, NULL);
 | 
			
		||||
#endif
 | 
			
		||||
            if (JSOCKVALID(connfd)) {
 | 
			
		||||
                janet_net_socknoblock(connfd);
 | 
			
		||||
                JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
 | 
			
		||||
@@ -884,7 +889,6 @@ static JanetStream *make_stream(JSock handle, uint32_t flags) {
 | 
			
		||||
    return janet_stream((JanetHandle) handle, flags | JANET_STREAM_SOCKET, net_stream_methods);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void janet_lib_net(JanetTable *env) {
 | 
			
		||||
    JanetRegExt net_cfuns[] = {
 | 
			
		||||
        JANET_CORE_REG("net/address", cfun_net_sockaddr),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										193
									
								
								src/core/os.c
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								src/core/os.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose and contributors.
 | 
			
		||||
* Copyright (c) 2022 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
 | 
			
		||||
@@ -39,8 +39,12 @@
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_APPLE
 | 
			
		||||
#include <AvailabilityMacros.h>
 | 
			
		||||
#ifdef JANET_BSD
 | 
			
		||||
#include <sys/sysctl.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_LINUX
 | 
			
		||||
#include <sched.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
@@ -67,12 +71,6 @@ extern char **environ;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* For macos */
 | 
			
		||||
#ifdef __MACH__
 | 
			
		||||
#include <mach/clock.h>
 | 
			
		||||
#include <mach/mach.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Not POSIX, but all Unixes but Solaris have this function. */
 | 
			
		||||
#if defined(JANET_POSIX) && !defined(__sun)
 | 
			
		||||
time_t timegm(struct tm *tm);
 | 
			
		||||
@@ -183,6 +181,8 @@ JANET_CORE_FN(os_arch,
 | 
			
		||||
    return janet_ckeywordv("sparc");
 | 
			
		||||
#elif (defined(__ppc__))
 | 
			
		||||
    return janet_ckeywordv("ppc");
 | 
			
		||||
#elif (defined(__ppc64__) || defined(_ARCH_PPC64) || defined(_M_PPC))
 | 
			
		||||
    return janet_ckeywordv("ppc64");
 | 
			
		||||
#else
 | 
			
		||||
    return janet_ckeywordv("unknown");
 | 
			
		||||
#endif
 | 
			
		||||
@@ -209,6 +209,47 @@ JANET_CORE_FN(os_exit,
 | 
			
		||||
    return janet_wrap_nil();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_cpu_count,
 | 
			
		||||
              "(os/cpu-count &opt dflt)",
 | 
			
		||||
              "Get an approximate number of CPUs available on for this process to use. If "
 | 
			
		||||
              "unable to get an approximation, will return a default value dflt.") {
 | 
			
		||||
    janet_arity(argc, 0, 1);
 | 
			
		||||
    Janet dflt = argc > 0 ? argv[0] : janet_wrap_nil();
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    (void) dflt;
 | 
			
		||||
    SYSTEM_INFO info;
 | 
			
		||||
    GetSystemInfo(&info);
 | 
			
		||||
    return janet_wrap_integer(info.dwNumberOfProcessors);
 | 
			
		||||
#elif defined(JANET_LINUX)
 | 
			
		||||
    (void) dflt;
 | 
			
		||||
    cpu_set_t cs;
 | 
			
		||||
    CPU_ZERO(&cs);
 | 
			
		||||
    sched_getaffinity(0, sizeof(cs), &cs);
 | 
			
		||||
    int count = CPU_COUNT(&cs);
 | 
			
		||||
    return janet_wrap_integer(count);
 | 
			
		||||
#elif defined(JANET_BSD) && defined(HW_NCPUONLINE)
 | 
			
		||||
    (void) dflt;
 | 
			
		||||
    const int name[2] = {CTL_HW, HW_NCPUONLINE};
 | 
			
		||||
    int result = 0;
 | 
			
		||||
    size_t len = sizeof(int);
 | 
			
		||||
    if (-1 == sysctl(name, 2, &result, &len, NULL, 0)) {
 | 
			
		||||
        return dflt;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_integer(result);
 | 
			
		||||
#elif defined(JANET_BSD) && defined(HW_NCPU)
 | 
			
		||||
    (void) dflt;
 | 
			
		||||
    const int name[2] = {CTL_HW, HW_NCPU};
 | 
			
		||||
    int result = 0;
 | 
			
		||||
    size_t len = sizeof(int);
 | 
			
		||||
    if (-1 == sysctl(name, 2, &result, &len, NULL, 0)) {
 | 
			
		||||
        return dflt;
 | 
			
		||||
    }
 | 
			
		||||
    return janet_wrap_integer(result);
 | 
			
		||||
#else
 | 
			
		||||
    return dflt;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_REDUCED_OS
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_NO_PROCESSES
 | 
			
		||||
@@ -403,7 +444,7 @@ typedef struct {
 | 
			
		||||
static JanetEVGenericMessage janet_proc_wait_subr(JanetEVGenericMessage args) {
 | 
			
		||||
    JanetProc *proc = (JanetProc *) args.argp;
 | 
			
		||||
    WaitForSingleObject(proc->pHandle, INFINITE);
 | 
			
		||||
    GetExitCodeProcess(proc->pHandle, &args.argi);
 | 
			
		||||
    GetExitCodeProcess(proc->pHandle, &args.tag);
 | 
			
		||||
    return args;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -429,15 +470,7 @@ static int proc_get_status(JanetProc *proc) {
 | 
			
		||||
/* Function that is called in separate thread to wait on a pid */
 | 
			
		||||
static JanetEVGenericMessage janet_proc_wait_subr(JanetEVGenericMessage args) {
 | 
			
		||||
    JanetProc *proc = (JanetProc *) args.argp;
 | 
			
		||||
#ifdef WNOWAIT
 | 
			
		||||
    pid_t result;
 | 
			
		||||
    int status = 0;
 | 
			
		||||
    do {
 | 
			
		||||
        result = waitpid(proc->pid, &status, WNOWAIT);
 | 
			
		||||
    } while (result == -1 && errno == EINTR);
 | 
			
		||||
#else
 | 
			
		||||
    args.tag = proc_get_status(proc);
 | 
			
		||||
#endif
 | 
			
		||||
    return args;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -448,11 +481,7 @@ static void janet_proc_wait_cb(JanetEVGenericMessage args) {
 | 
			
		||||
    janet_ev_dec_refcount();
 | 
			
		||||
    JanetProc *proc = (JanetProc *) args.argp;
 | 
			
		||||
    if (NULL != proc) {
 | 
			
		||||
#ifdef WNOWAIT
 | 
			
		||||
        int status = proc_get_status(proc);
 | 
			
		||||
#else
 | 
			
		||||
        int status = args.tag;
 | 
			
		||||
#endif
 | 
			
		||||
        proc->return_code = (int32_t) status;
 | 
			
		||||
        proc->flags |= JANET_PROC_WAITED;
 | 
			
		||||
        proc->flags &= ~JANET_PROC_WAITING;
 | 
			
		||||
@@ -558,8 +587,8 @@ JANET_CORE_FN(os_proc_wait,
 | 
			
		||||
JANET_CORE_FN(os_proc_kill,
 | 
			
		||||
              "(os/proc-kill proc &opt wait)",
 | 
			
		||||
              "Kill a subprocess by sending SIGKILL to it on posix systems, or by closing the process "
 | 
			
		||||
              "handle on windows. If wait is truthy, will wait for the process to finish and "
 | 
			
		||||
              "returns the exit code. Otherwise, returns proc.") {
 | 
			
		||||
              "handle on windows. If `wait` is truthy, will wait for the process to finish and "
 | 
			
		||||
              "returns the exit code. Otherwise, returns `proc`.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetProc *proc = janet_getabstract(argv, 0, &ProcAT);
 | 
			
		||||
    if (proc->flags & JANET_PROC_WAITED) {
 | 
			
		||||
@@ -844,19 +873,19 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_spawn) {
 | 
			
		||||
        Janet maybe_stdin = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("in"));
 | 
			
		||||
        Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out"));
 | 
			
		||||
        Janet maybe_stderr = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("err"));
 | 
			
		||||
        if (janet_keyeq(maybe_stdin, "pipe")) {
 | 
			
		||||
        if (is_spawn && janet_keyeq(maybe_stdin, "pipe")) {
 | 
			
		||||
            new_in = make_pipes(&pipe_in, 1, &pipe_errflag);
 | 
			
		||||
            pipe_owner_flags |= JANET_PROC_OWNS_STDIN;
 | 
			
		||||
        } else if (!janet_checktype(maybe_stdin, JANET_NIL)) {
 | 
			
		||||
            new_in = janet_getjstream(&maybe_stdin, 0, &orig_in);
 | 
			
		||||
        }
 | 
			
		||||
        if (janet_keyeq(maybe_stdout, "pipe")) {
 | 
			
		||||
        if (is_spawn && janet_keyeq(maybe_stdout, "pipe")) {
 | 
			
		||||
            new_out = make_pipes(&pipe_out, 0, &pipe_errflag);
 | 
			
		||||
            pipe_owner_flags |= JANET_PROC_OWNS_STDOUT;
 | 
			
		||||
        } else if (!janet_checktype(maybe_stdout, JANET_NIL)) {
 | 
			
		||||
            new_out = janet_getjstream(&maybe_stdout, 0, &orig_out);
 | 
			
		||||
        }
 | 
			
		||||
        if (janet_keyeq(maybe_stderr, "pipe")) {
 | 
			
		||||
        if (is_spawn && janet_keyeq(maybe_stderr, "pipe")) {
 | 
			
		||||
            new_err = make_pipes(&pipe_err, 0, &pipe_errflag);
 | 
			
		||||
            pipe_owner_flags |= JANET_PROC_OWNS_STDERR;
 | 
			
		||||
        } else if (!janet_checktype(maybe_stderr, JANET_NIL)) {
 | 
			
		||||
@@ -1071,21 +1100,20 @@ JANET_CORE_FN(os_execute,
 | 
			
		||||
              "`env` is a table or struct mapping environment variables to values. It can also "
 | 
			
		||||
              "contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. "
 | 
			
		||||
              "These arguments should be core/file values. "
 | 
			
		||||
              "One can also pass in the :pipe keyword "
 | 
			
		||||
              "for these arguments to create files that will read (for :err and :out) or write (for :in) "
 | 
			
		||||
              "to the file descriptor of the subprocess. This is only useful in `os/spawn`, which takes "
 | 
			
		||||
              "the same parameters as `os/execute`, but will return an object that contains references to these "
 | 
			
		||||
              "files via (return-value :in), (return-value :out), and (return-value :err). "
 | 
			
		||||
              "Returns the exit status of the program.") {
 | 
			
		||||
    return os_execute_impl(argc, argv, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_spawn,
 | 
			
		||||
              "(os/spawn args &opt flags env)",
 | 
			
		||||
              "Execute a program on the system and return a handle to the process. Otherwise, the "
 | 
			
		||||
              "same arguments as os/execute. Does not wait for the process. "
 | 
			
		||||
              "The returned value has the fields :in, :out, :err, :return-code and "
 | 
			
		||||
              "the additional field :pid on unix like platforms.") {
 | 
			
		||||
              "Execute a program on the system and return a handle to the process. Otherwise, takes the "
 | 
			
		||||
              "same arguments as `os/execute`. Does not wait for the process. "
 | 
			
		||||
              "For each of the :in, :out, and :err keys to the `env` argument, one "
 | 
			
		||||
              "can also pass in the keyword `:pipe` "
 | 
			
		||||
              "to get streams for standard IO of the subprocess that can be read from and written to. "
 | 
			
		||||
              "The returned value `proc` has the fields :in, :out, :err, :return-code, and "
 | 
			
		||||
              "the additional field :pid on unix-like platforms. Use `(os/proc-wait proc)` to rejoin the "
 | 
			
		||||
              "subprocess or `(os/proc-kill proc)`.") {
 | 
			
		||||
    return os_execute_impl(argc, argv, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1125,7 +1153,7 @@ JANET_CORE_FN(os_shell,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_environ,
 | 
			
		||||
              "(os/environ)",
 | 
			
		||||
              "Get a copy of the os environment table.") {
 | 
			
		||||
              "Get a copy of the OS environment table.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    int32_t nenv = 0;
 | 
			
		||||
@@ -1206,7 +1234,7 @@ JANET_CORE_FN(os_time,
 | 
			
		||||
JANET_CORE_FN(os_clock,
 | 
			
		||||
              "(os/clock)",
 | 
			
		||||
              "Return the number of whole + fractional seconds since some fixed point in time. The clock "
 | 
			
		||||
              "is guaranteed to be non decreasing in real time.") {
 | 
			
		||||
              "is guaranteed to be non-decreasing in real time.") {
 | 
			
		||||
    janet_fixarity(argc, 0);
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    struct timespec tv;
 | 
			
		||||
@@ -1217,7 +1245,7 @@ JANET_CORE_FN(os_clock,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_sleep,
 | 
			
		||||
              "(os/sleep n)",
 | 
			
		||||
              "Suspend the program for n seconds. 'nsec' can be a real number. Returns "
 | 
			
		||||
              "Suspend the program for `n` seconds. `n` can be a real number. Returns "
 | 
			
		||||
              "nil.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    double delay = janet_getnumber(argv, 0);
 | 
			
		||||
@@ -1254,7 +1282,7 @@ JANET_CORE_FN(os_cwd,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_cryptorand,
 | 
			
		||||
              "(os/cryptorand n &opt buf)",
 | 
			
		||||
              "Get or append n bytes of good quality random data provided by the OS. Returns a new buffer or buf.") {
 | 
			
		||||
              "Get or append `n` bytes of good quality random data provided by the OS. Returns a new buffer or `buf`.") {
 | 
			
		||||
    JanetBuffer *buffer;
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    int32_t offset;
 | 
			
		||||
@@ -1296,7 +1324,7 @@ JANET_CORE_FN(os_date,
 | 
			
		||||
    time_t t;
 | 
			
		||||
    struct tm t_infos;
 | 
			
		||||
    struct tm *t_info = NULL;
 | 
			
		||||
    if (argc) {
 | 
			
		||||
    if (argc && !janet_checktype(argv[0], JANET_NIL)) {
 | 
			
		||||
        int64_t integer = janet_getinteger64(argv, 0);
 | 
			
		||||
        t = (time_t) integer;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -1305,6 +1333,7 @@ JANET_CORE_FN(os_date,
 | 
			
		||||
    if (argc >= 2 && janet_truthy(argv[1])) {
 | 
			
		||||
        /* local time */
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
        _tzset();
 | 
			
		||||
        localtime_s(&t_infos, &t);
 | 
			
		||||
        t_info = &t_infos;
 | 
			
		||||
#else
 | 
			
		||||
@@ -1391,9 +1420,9 @@ static timeint_t entry_getint(Janet env_entry, char *field) {
 | 
			
		||||
JANET_CORE_FN(os_mktime,
 | 
			
		||||
              "(os/mktime date-struct &opt local)",
 | 
			
		||||
              "Get the broken down date-struct time expressed as the number "
 | 
			
		||||
              " of seconds since January 1, 1970, the Unix epoch. "
 | 
			
		||||
              "of seconds since January 1, 1970, the Unix epoch. "
 | 
			
		||||
              "Returns a real number. "
 | 
			
		||||
              "Date is given in UTC unless local is truthy, in which case the "
 | 
			
		||||
              "Date is given in UTC unless `local` is truthy, in which case the "
 | 
			
		||||
              "date is computed for the local timezone.\n\n"
 | 
			
		||||
              "Inverse function to os/date.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
@@ -1464,7 +1493,7 @@ JANET_CORE_FN(os_link,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_symlink,
 | 
			
		||||
              "(os/symlink oldpath newpath)",
 | 
			
		||||
              "Create a symlink from oldpath to newpath, returning nil. Same as (os/link oldpath newpath true).") {
 | 
			
		||||
              "Create a symlink from oldpath to newpath, returning nil. Same as `(os/link oldpath newpath true)`.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
    (void) argc;
 | 
			
		||||
@@ -1839,10 +1868,10 @@ JANET_CORE_FN(os_stat,
 | 
			
		||||
              "* :uid - File uid\n\n"
 | 
			
		||||
              "* :gid - File gid\n\n"
 | 
			
		||||
              "* :nlink - number of links to file\n\n"
 | 
			
		||||
              "* :rdev - Real device of file. 0 on windows.\n\n"
 | 
			
		||||
              "* :rdev - Real device of file. 0 on Windows\n\n"
 | 
			
		||||
              "* :size - size of file in bytes\n\n"
 | 
			
		||||
              "* :blocks - number of blocks in file. 0 on windows\n\n"
 | 
			
		||||
              "* :blocksize - size of blocks in file. 0 on windows\n\n"
 | 
			
		||||
              "* :blocks - number of blocks in file. 0 on Windows\n\n"
 | 
			
		||||
              "* :blocksize - size of blocks in file. 0 on Windows\n\n"
 | 
			
		||||
              "* :accessed - timestamp when file last accessed\n\n"
 | 
			
		||||
              "* :changed - timestamp when file last changed (permissions changed)\n\n"
 | 
			
		||||
              "* :modified - timestamp when file last modified (content changed)\n") {
 | 
			
		||||
@@ -1857,9 +1886,9 @@ JANET_CORE_FN(os_lstat,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_chmod,
 | 
			
		||||
              "(os/chmod path mode)",
 | 
			
		||||
              "Change file permissions, where mode is a permission string as returned by "
 | 
			
		||||
              "os/perm-string, or an integer as returned by os/perm-int. "
 | 
			
		||||
              "When mode is an integer, it is interpreted as a Unix permission value, best specified in octal, like "
 | 
			
		||||
              "Change file permissions, where `mode` is a permission string as returned by "
 | 
			
		||||
              "`os/perm-string`, or an integer as returned by `os/perm-int`. "
 | 
			
		||||
              "When `mode` is an integer, it is interpreted as a Unix permission value, best specified in octal, like "
 | 
			
		||||
              "8r666 or 8r400. Windows will not differentiate between user, group, and other permissions, and thus will combine all of these permissions. Returns nil.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    const char *path = janet_getcstring(argv, 0);
 | 
			
		||||
@@ -1961,9 +1990,9 @@ JANET_CORE_FN(os_realpath,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_permission_string,
 | 
			
		||||
              "(os/perm-string int)",
 | 
			
		||||
              "Convert a Unix octal permission value from a permission integer as returned by os/stat "
 | 
			
		||||
              "Convert a Unix octal permission value from a permission integer as returned by `os/stat` "
 | 
			
		||||
              "to a human readable string, that follows the formatting "
 | 
			
		||||
              "of unix tools like ls. Returns the string as a 9 character string of r, w, x and - characters. Does not "
 | 
			
		||||
              "of Unix tools like `ls`. Returns the string as a 9-character string of r, w, x and - characters. Does not "
 | 
			
		||||
              "include the file/directory/symlink character as rendered by `ls`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return os_make_permstring(os_get_unix_mode(argv, 0));
 | 
			
		||||
@@ -1971,7 +2000,7 @@ JANET_CORE_FN(os_permission_string,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_permission_int,
 | 
			
		||||
              "(os/perm-int bytes)",
 | 
			
		||||
              "Parse a 9 character permission string and return an integer that can be used by chmod.") {
 | 
			
		||||
              "Parse a 9-character permission string and return an integer that can be used by chmod.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    return janet_wrap_integer(os_get_unix_mode(argv, 0));
 | 
			
		||||
}
 | 
			
		||||
@@ -1990,28 +2019,28 @@ static jmode_t os_optmode(int32_t argc, const Janet *argv, int32_t n, int32_t df
 | 
			
		||||
JANET_CORE_FN(os_open,
 | 
			
		||||
              "(os/open path &opt flags mode)",
 | 
			
		||||
              "Create a stream from a file, like the POSIX open system call. Returns a new stream. "
 | 
			
		||||
              "mode should be a file mode as passed to os/chmod, but only if the create flag is given. "
 | 
			
		||||
              "`mode` should be a file mode as passed to `os/chmod`, but only if the create flag is given. "
 | 
			
		||||
              "The default mode is 8r666. "
 | 
			
		||||
              "Allowed flags are as follows:\n\n"
 | 
			
		||||
              "  * :r - open this file for reading\n"
 | 
			
		||||
              "  * :w - open this file for writing\n"
 | 
			
		||||
              "  * :c - create a new file (O_CREATE)\n"
 | 
			
		||||
              "  * :e - fail if the file exists (O_EXCL)\n"
 | 
			
		||||
              "  * :t - shorten an existing file to length 0 (O_TRUNC)\n\n"
 | 
			
		||||
              "Posix only flags:\n\n"
 | 
			
		||||
              "  * :a - append to a file (O_APPEND)\n"
 | 
			
		||||
              "  * :x - O_SYNC\n"
 | 
			
		||||
              "  * :C - O_NOCTTY\n\n"
 | 
			
		||||
              "Windows only flags:\n\n"
 | 
			
		||||
              "  * :R - share reads (FILE_SHARE_READ)\n"
 | 
			
		||||
              "  * :W - share writes (FILE_SHARE_WRITE)\n"
 | 
			
		||||
              "  * :D - share deletes (FILE_SHARE_DELETE)\n"
 | 
			
		||||
              "  * :H - FILE_ATTRIBUTE_HIDDEN\n"
 | 
			
		||||
              "  * :O - FILE_ATTRIBUTE_READONLY\n"
 | 
			
		||||
              "  * :F - FILE_ATTRIBUTE_OFFLINE\n"
 | 
			
		||||
              "  * :T - FILE_ATTRIBUTE_TEMPORARY\n"
 | 
			
		||||
              "  * :d - FILE_FLAG_DELETE_ON_CLOSE\n"
 | 
			
		||||
              "  * :b - FILE_FLAG_NO_BUFFERING\n") {
 | 
			
		||||
              "  * :c - create a new file (O\\_CREATE)\n"
 | 
			
		||||
              "  * :e - fail if the file exists (O\\_EXCL)\n"
 | 
			
		||||
              "  * :t - shorten an existing file to length 0 (O\\_TRUNC)\n\n"
 | 
			
		||||
              "Posix-only flags:\n\n"
 | 
			
		||||
              "  * :a - append to a file (O\\_APPEND)\n"
 | 
			
		||||
              "  * :x - O\\_SYNC\n"
 | 
			
		||||
              "  * :C - O\\_NOCTTY\n\n"
 | 
			
		||||
              "Windows-only flags:\n\n"
 | 
			
		||||
              "  * :R - share reads (FILE\\_SHARE\\_READ)\n"
 | 
			
		||||
              "  * :W - share writes (FILE\\_SHARE\\_WRITE)\n"
 | 
			
		||||
              "  * :D - share deletes (FILE\\_SHARE\\_DELETE)\n"
 | 
			
		||||
              "  * :H - FILE\\_ATTRIBUTE\\_HIDDEN\n"
 | 
			
		||||
              "  * :O - FILE\\_ATTRIBUTE\\_READONLY\n"
 | 
			
		||||
              "  * :F - FILE\\_ATTRIBUTE\\_OFFLINE\n"
 | 
			
		||||
              "  * :T - FILE\\_ATTRIBUTE\\_TEMPORARY\n"
 | 
			
		||||
              "  * :d - FILE\\_FLAG\\_DELETE\\_ON\\_CLOSE\n"
 | 
			
		||||
              "  * :b - FILE\\_FLAG\\_NO\\_BUFFERING\n") {
 | 
			
		||||
    janet_arity(argc, 1, 3);
 | 
			
		||||
    const char *path = janet_getcstring(argv, 0);
 | 
			
		||||
    const uint8_t *opt_flags = janet_optkeyword(argv, argc, 1, (const uint8_t *) "r");
 | 
			
		||||
@@ -2108,20 +2137,18 @@ JANET_CORE_FN(os_open,
 | 
			
		||||
#ifdef JANET_LINUX
 | 
			
		||||
    open_flags |= O_CLOEXEC;
 | 
			
		||||
#endif
 | 
			
		||||
    int read_flag = 0;
 | 
			
		||||
    int write_flag = 0;
 | 
			
		||||
    for (const uint8_t *c = opt_flags; *c; c++) {
 | 
			
		||||
        switch (*c) {
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
            case 'r':
 | 
			
		||||
                open_flags = (open_flags & O_WRONLY)
 | 
			
		||||
                             ? ((open_flags & ~O_WRONLY) | O_RDWR)
 | 
			
		||||
                             : (open_flags | O_RDONLY);
 | 
			
		||||
                read_flag = 1;
 | 
			
		||||
                stream_flags |= JANET_STREAM_READABLE;
 | 
			
		||||
                break;
 | 
			
		||||
            case 'w':
 | 
			
		||||
                open_flags = (open_flags & O_RDONLY)
 | 
			
		||||
                             ? ((open_flags & ~O_RDONLY) | O_RDWR)
 | 
			
		||||
                             : (open_flags | O_WRONLY);
 | 
			
		||||
                write_flag = 1;
 | 
			
		||||
                stream_flags |= JANET_STREAM_WRITABLE;
 | 
			
		||||
                break;
 | 
			
		||||
            case 'c':
 | 
			
		||||
@@ -2145,6 +2172,15 @@ JANET_CORE_FN(os_open,
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /* If both read and write, fix up to O_RDWR */
 | 
			
		||||
    if (read_flag && !write_flag) {
 | 
			
		||||
        open_flags |= O_RDONLY;
 | 
			
		||||
    } else if (write_flag && !read_flag) {
 | 
			
		||||
        open_flags |= O_WRONLY;
 | 
			
		||||
    } else {
 | 
			
		||||
        open_flags = O_RDWR;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
        fd = open(path, open_flags, mode);
 | 
			
		||||
    } while (fd == -1 && errno == EINTR);
 | 
			
		||||
@@ -2155,7 +2191,7 @@ JANET_CORE_FN(os_open,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(os_pipe,
 | 
			
		||||
              "(os/pipe)",
 | 
			
		||||
              "Create a readable stream and a writable stream that are connected. Returns a two element "
 | 
			
		||||
              "Create a readable stream and a writable stream that are connected. Returns a two-element "
 | 
			
		||||
              "tuple where the first element is a readable stream and the second element is the writable "
 | 
			
		||||
              "stream.") {
 | 
			
		||||
    (void) argv;
 | 
			
		||||
@@ -2204,6 +2240,7 @@ void janet_lib_os(JanetTable *env) {
 | 
			
		||||
        JANET_CORE_REG("os/chmod", os_chmod),
 | 
			
		||||
        JANET_CORE_REG("os/touch", os_touch),
 | 
			
		||||
        JANET_CORE_REG("os/cd", os_cd),
 | 
			
		||||
        JANET_CORE_REG("os/cpu-count", os_cpu_count),
 | 
			
		||||
#ifndef JANET_NO_UMASK
 | 
			
		||||
        JANET_CORE_REG("os/umask", os_umask),
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -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", s->line, s->column);
 | 
			
		||||
    }
 | 
			
		||||
    parser->error = (const char *) janet_string(buffer->data, buffer->count);
 | 
			
		||||
    parser->flag |= JANET_PARSER_GENERATED_ERROR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int checkescape(uint8_t c) {
 | 
			
		||||
    switch (c) {
 | 
			
		||||
        default:
 | 
			
		||||
@@ -612,7 +643,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
        case '}': {
 | 
			
		||||
            Janet ds;
 | 
			
		||||
            if (p->statecount == 1) {
 | 
			
		||||
                p->error = "unexpected delimiter";
 | 
			
		||||
                delim_error(p, 0, c, "unexpected closing delimiter ");
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
            if ((c == ')' && (state->flags & PFLAG_PARENS)) ||
 | 
			
		||||
@@ -633,7 +664,7 @@ static int root(JanetParser *p, JanetParseState *state, uint8_t c) {
 | 
			
		||||
                    ds = close_struct(p, state);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                p->error = "mismatched delimiter";
 | 
			
		||||
                delim_error(p, p->statecount - 1, c, "mismatched delimiter ");
 | 
			
		||||
                return 1;
 | 
			
		||||
            }
 | 
			
		||||
            popstate(p, ds);
 | 
			
		||||
@@ -684,26 +715,7 @@ void janet_parser_eof(JanetParser *parser) {
 | 
			
		||||
    size_t oldline = parser->line;
 | 
			
		||||
    janet_parser_consume(parser, '\n');
 | 
			
		||||
    if (parser->statecount > 1) {
 | 
			
		||||
        JanetParseState *s = parser->states + (parser->statecount - 1);
 | 
			
		||||
        JanetBuffer *buffer = janet_buffer(40);
 | 
			
		||||
        janet_buffer_push_cstring(buffer, "unexpected end of source, ");
 | 
			
		||||
        if (s->flags & PFLAG_PARENS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '(');
 | 
			
		||||
        } else if (s->flags & PFLAG_SQRBRACKETS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '[');
 | 
			
		||||
        } else if (s->flags & PFLAG_CURLYBRACKETS) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '{');
 | 
			
		||||
        } else if (s->flags & PFLAG_STRING) {
 | 
			
		||||
            janet_buffer_push_u8(buffer, '"');
 | 
			
		||||
        } else if (s->flags & PFLAG_LONGSTRING) {
 | 
			
		||||
            int32_t i;
 | 
			
		||||
            for (i = 0; i < s->argn; i++) {
 | 
			
		||||
                janet_buffer_push_u8(buffer, '`');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
 | 
			
		||||
        parser->error = (const char *) janet_string(buffer->data, buffer->count);
 | 
			
		||||
        parser->flag |= JANET_PARSER_GENERATED_ERROR;
 | 
			
		||||
        delim_error(parser, parser->statecount - 1, 0, "unexpected end of source");
 | 
			
		||||
    }
 | 
			
		||||
    parser->line = oldline;
 | 
			
		||||
    parser->column = oldcolumn;
 | 
			
		||||
@@ -883,7 +895,7 @@ const JanetAbstractType janet_parser_type = {
 | 
			
		||||
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.") {
 | 
			
		||||
              "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));
 | 
			
		||||
@@ -894,7 +906,7 @@ JANET_CORE_FN(cfun_parse_parser,
 | 
			
		||||
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 "
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -922,7 +934,7 @@ JANET_CORE_FN(cfun_parse_consume,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_parse_eof,
 | 
			
		||||
              "(parser/eof parser)",
 | 
			
		||||
              "Indicate that the end of file was reached to the parser. This puts the parser in the :dead state.") {
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -982,7 +994,7 @@ JANET_CORE_FN(cfun_parse_has_more,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_parse_byte,
 | 
			
		||||
              "(parser/byte parser b)",
 | 
			
		||||
              "Input a single byte into the parser byte stream. Returns the parser.") {
 | 
			
		||||
              "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);
 | 
			
		||||
@@ -1022,7 +1034,7 @@ JANET_CORE_FN(cfun_parse_error,
 | 
			
		||||
              "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/error`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetParser *p = janet_getabstract(argv, 0, &janet_parser_type);
 | 
			
		||||
    const char *err = janet_parser_error(p);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -211,9 +211,10 @@ tail:
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_SET: {
 | 
			
		||||
            if (text >= s->text_end) return NULL;
 | 
			
		||||
            uint32_t word = rule[1 + (text[0] >> 5)];
 | 
			
		||||
            uint32_t mask = (uint32_t)1 << (text[0] & 0x1F);
 | 
			
		||||
            return (text < s->text_end && (word & mask))
 | 
			
		||||
            return (word & mask)
 | 
			
		||||
                   ? text + 1
 | 
			
		||||
                   : NULL;
 | 
			
		||||
        }
 | 
			
		||||
@@ -260,24 +261,46 @@ tail:
 | 
			
		||||
            goto tail;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_IF:
 | 
			
		||||
        case RULE_IFNOT: {
 | 
			
		||||
        case RULE_IF: {
 | 
			
		||||
            const uint32_t *rule_a = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_b = s->bytecode + rule[2];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, rule_a, text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            if (rule[0] == RULE_IF ? !result : !!result) return NULL;
 | 
			
		||||
            if (!result) return NULL;
 | 
			
		||||
            rule = rule_b;
 | 
			
		||||
            goto tail;
 | 
			
		||||
        }
 | 
			
		||||
        case RULE_IFNOT: {
 | 
			
		||||
            const uint32_t *rule_a = s->bytecode + rule[1];
 | 
			
		||||
            const uint32_t *rule_b = s->bytecode + rule[2];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            CapState cs = cap_save(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, rule_a, text);
 | 
			
		||||
            if (!!result) {
 | 
			
		||||
                up1(s);
 | 
			
		||||
                return NULL;
 | 
			
		||||
            } else {
 | 
			
		||||
                cap_load(s, cs);
 | 
			
		||||
                up1(s);
 | 
			
		||||
                rule = rule_b;
 | 
			
		||||
                goto tail;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_NOT: {
 | 
			
		||||
            const uint32_t *rule_a = s->bytecode + rule[1];
 | 
			
		||||
            down1(s);
 | 
			
		||||
            CapState cs = cap_save(s);
 | 
			
		||||
            const uint8_t *result = peg_rule(s, rule_a, text);
 | 
			
		||||
            up1(s);
 | 
			
		||||
            return (result) ? NULL : text;
 | 
			
		||||
            if (result) {
 | 
			
		||||
                up1(s);
 | 
			
		||||
                return NULL;
 | 
			
		||||
            } else {
 | 
			
		||||
                cap_load(s, cs);
 | 
			
		||||
                up1(s);
 | 
			
		||||
                return text;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        case RULE_THRU:
 | 
			
		||||
@@ -293,6 +316,7 @@ tail:
 | 
			
		||||
                    if (rule[0] == RULE_TO) cap_load(s, cs2);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                cap_load(s, cs2);
 | 
			
		||||
                text++;
 | 
			
		||||
            }
 | 
			
		||||
            up1(s);
 | 
			
		||||
@@ -1660,7 +1684,9 @@ 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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -762,8 +762,7 @@ static const char *scanformat(
 | 
			
		||||
    memset(precision, '\0', 3);
 | 
			
		||||
    while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL)
 | 
			
		||||
        p++; /* skip flags */
 | 
			
		||||
    if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS) / sizeof(char))
 | 
			
		||||
        janet_panic("invalid format (repeated flags)");
 | 
			
		||||
    if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS)) janet_panic("invalid format (repeated flags)");
 | 
			
		||||
    if (isdigit((int)(*p)))
 | 
			
		||||
        width[0] = *p++; /* skip width */
 | 
			
		||||
    if (isdigit((int)(*p)))
 | 
			
		||||
@@ -983,8 +982,9 @@ void janet_buffer_format(
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                case 's': {
 | 
			
		||||
                    const uint8_t *s = janet_getstring(argv, arg);
 | 
			
		||||
                    int32_t l = janet_string_length(s);
 | 
			
		||||
                    JanetByteView bytes = janet_getbytes(argv, arg);
 | 
			
		||||
                    const uint8_t *s = bytes.bytes;
 | 
			
		||||
                    int32_t l = bytes.len;
 | 
			
		||||
                    if (form[2] == '\0')
 | 
			
		||||
                        janet_buffer_push_bytes(b, s, l);
 | 
			
		||||
                    else {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -23,6 +23,7 @@
 | 
			
		||||
#ifndef JANET_AMALG
 | 
			
		||||
#include "features.h"
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include "state.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Run a string */
 | 
			
		||||
@@ -50,7 +51,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
                fiber->env = env;
 | 
			
		||||
                JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
 | 
			
		||||
                if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
 | 
			
		||||
                    janet_stacktrace(fiber, ret);
 | 
			
		||||
                    janet_stacktrace_ext(fiber, ret, "");
 | 
			
		||||
                    errflags |= 0x01;
 | 
			
		||||
                    done = 1;
 | 
			
		||||
                }
 | 
			
		||||
@@ -58,7 +59,7 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
                ret = janet_wrap_string(cres.error);
 | 
			
		||||
                if (cres.macrofiber) {
 | 
			
		||||
                    janet_eprintf("compile error in %s: ", sourcePath);
 | 
			
		||||
                    janet_stacktrace(cres.macrofiber, ret);
 | 
			
		||||
                    janet_stacktrace_ext(cres.macrofiber, ret, "");
 | 
			
		||||
                } else {
 | 
			
		||||
                    janet_eprintf("compile error in %s: %s\n", sourcePath,
 | 
			
		||||
                                  (const char *)cres.error);
 | 
			
		||||
@@ -79,9 +80,9 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
                const char *e = janet_parser_error(&parser);
 | 
			
		||||
                errflags |= 0x04;
 | 
			
		||||
                ret = janet_cstringv(e);
 | 
			
		||||
                size_t line = parser.line;
 | 
			
		||||
                size_t col = parser.column;
 | 
			
		||||
                janet_eprintf("%s:%lu:%lu: parse error: %s\n", sourcePath, line, col, 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;
 | 
			
		||||
            }
 | 
			
		||||
@@ -100,6 +101,14 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
 | 
			
		||||
    /* Clean up and return errors */
 | 
			
		||||
    janet_parser_deinit(&parser);
 | 
			
		||||
    if (where) janet_gcunroot(janet_wrap_string(where));
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
    /* Enter the event loop if we are not already in it */
 | 
			
		||||
    if (janet_vm.stackn == 0) {
 | 
			
		||||
        janet_gcroot(ret);
 | 
			
		||||
        janet_loop();
 | 
			
		||||
        janet_gcunroot(ret);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (out) *out = ret;
 | 
			
		||||
    return errflags;
 | 
			
		||||
}
 | 
			
		||||
@@ -121,7 +130,7 @@ int janet_loop_fiber(JanetFiber *fiber) {
 | 
			
		||||
    Janet out;
 | 
			
		||||
    status = janet_continue(fiber, janet_wrap_nil(), &out);
 | 
			
		||||
    if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
 | 
			
		||||
        janet_stacktrace(fiber, out);
 | 
			
		||||
        janet_stacktrace_ext(fiber, out, "");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return status;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -31,7 +31,7 @@
 | 
			
		||||
 | 
			
		||||
static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    if (argn != 1) {
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 1 argument");
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 1 argument to quote");
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    return janetc_cslot(argv[0]);
 | 
			
		||||
@@ -40,7 +40,7 @@ static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
static JanetSlot janetc_splice(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetSlot ret;
 | 
			
		||||
    if (argn != 1) {
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 1 argument");
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 1 argument to splice");
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    ret = janetc_value(opts, argv[0]);
 | 
			
		||||
@@ -62,6 +62,8 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    JanetSlot *slots = NULL;
 | 
			
		||||
    JanetFopts subopts = opts;
 | 
			
		||||
    subopts.flags &= ~JANET_FOPTS_HINT;
 | 
			
		||||
    switch (janet_type(x)) {
 | 
			
		||||
        default:
 | 
			
		||||
            return janetc_cslot(x);
 | 
			
		||||
@@ -82,7 +84,7 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for (i = 0; i < len; i++)
 | 
			
		||||
                janet_v_push(slots, quasiquote(opts, tup[i], depth - 1, level));
 | 
			
		||||
                janet_v_push(slots, quasiquote(subopts, tup[i], depth - 1, level));
 | 
			
		||||
            return qq_slots(opts, slots, (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR)
 | 
			
		||||
                            ? JOP_MAKE_BRACKET_TUPLE
 | 
			
		||||
                            : JOP_MAKE_TUPLE);
 | 
			
		||||
@@ -91,7 +93,7 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
 | 
			
		||||
            int32_t i;
 | 
			
		||||
            JanetArray *array = janet_unwrap_array(x);
 | 
			
		||||
            for (i = 0; i < array->count; i++)
 | 
			
		||||
                janet_v_push(slots, quasiquote(opts, array->data[i], depth - 1, level));
 | 
			
		||||
                janet_v_push(slots, quasiquote(subopts, array->data[i], depth - 1, level));
 | 
			
		||||
            return qq_slots(opts, slots, JOP_MAKE_ARRAY);
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_TABLE:
 | 
			
		||||
@@ -100,8 +102,8 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
 | 
			
		||||
            int32_t len, cap = 0;
 | 
			
		||||
            janet_dictionary_view(x, &kvs, &len, &cap);
 | 
			
		||||
            while ((kv = janet_dictionary_next(kvs, cap, kv))) {
 | 
			
		||||
                JanetSlot key = quasiquote(opts, kv->key, depth - 1, level);
 | 
			
		||||
                JanetSlot value =  quasiquote(opts, kv->value, depth - 1, level);
 | 
			
		||||
                JanetSlot key = quasiquote(subopts, kv->key, depth - 1, level);
 | 
			
		||||
                JanetSlot value =  quasiquote(subopts, kv->value, depth - 1, level);
 | 
			
		||||
                key.flags &= ~JANET_SLOT_SPLICED;
 | 
			
		||||
                value.flags &= ~JANET_SLOT_SPLICED;
 | 
			
		||||
                janet_v_push(slots, key);
 | 
			
		||||
@@ -115,7 +117,7 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x, int depth, int level) {
 | 
			
		||||
 | 
			
		||||
static JanetSlot janetc_quasiquote(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    if (argn != 1) {
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 1 argument");
 | 
			
		||||
        janetc_cerror(opts.compiler, "expected 1 argument to quasiquote");
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    }
 | 
			
		||||
    return quasiquote(opts, argv[0], JANET_RECURSION_GUARD, 0);
 | 
			
		||||
@@ -141,7 +143,7 @@ static int destructure(JanetCompiler *c,
 | 
			
		||||
                       JanetTable *attr) {
 | 
			
		||||
    switch (janet_type(left)) {
 | 
			
		||||
        default:
 | 
			
		||||
            janetc_cerror(c, "unexpected type in destructuring");
 | 
			
		||||
            janetc_error(c, janet_formatc("unexpected type in destruction, got %v", left));
 | 
			
		||||
            return 1;
 | 
			
		||||
        case JANET_SYMBOL:
 | 
			
		||||
            /* Leaf, assign right to left */
 | 
			
		||||
@@ -154,6 +156,67 @@ static int destructure(JanetCompiler *c,
 | 
			
		||||
            for (int32_t i = 0; i < len; i++) {
 | 
			
		||||
                JanetSlot nextright = janetc_farslot(c);
 | 
			
		||||
                Janet subval = values[i];
 | 
			
		||||
 | 
			
		||||
                if (janet_checktype(subval, JANET_SYMBOL) && !janet_cstrcmp(janet_unwrap_symbol(subval), "&")) {
 | 
			
		||||
                    if (i + 1 >= len) {
 | 
			
		||||
                        janetc_cerror(c, "expected symbol following '& in destructuring pattern");
 | 
			
		||||
                        return 1;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (i + 2 < len) {
 | 
			
		||||
                        int32_t num_extra = len - i - 1;
 | 
			
		||||
                        Janet *extra = janet_tuple_begin(num_extra);
 | 
			
		||||
                        janet_tuple_flag(extra) |= JANET_TUPLE_FLAG_BRACKETCTOR;
 | 
			
		||||
 | 
			
		||||
                        for (int32_t j = 0; j < num_extra; ++j) {
 | 
			
		||||
                            extra[j] = values[j + i + 1];
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        janetc_error(c, janet_formatc("expected a single symbol follow '& in destructuring pattern, found %q", janet_wrap_tuple(janet_tuple_end(extra))));
 | 
			
		||||
                        return 1;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                    if (!janet_checktype(values[i + 1], JANET_SYMBOL)) {
 | 
			
		||||
                        janetc_error(c, janet_formatc("expected symbol following '& in destructuring pattern, found %q", values[i + 1]));
 | 
			
		||||
                        return 1;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    JanetSlot argi = janetc_farslot(c);
 | 
			
		||||
                    JanetSlot arg  = janetc_farslot(c);
 | 
			
		||||
                    JanetSlot len  = janetc_farslot(c);
 | 
			
		||||
 | 
			
		||||
                    janetc_emit_si(c, JOP_LOAD_INTEGER, argi, i, 0);
 | 
			
		||||
                    janetc_emit_ss(c, JOP_LENGTH, len, right, 0);
 | 
			
		||||
 | 
			
		||||
                    /* loop condition - reuse arg slot for the condition result */
 | 
			
		||||
                    int32_t label_loop_start = janetc_emit_sss(c, JOP_LESS_THAN, arg, argi, len, 0);
 | 
			
		||||
                    int32_t label_loop_cond_jump = janetc_emit_si(c, JOP_JUMP_IF_NOT, arg, 0, 0);
 | 
			
		||||
 | 
			
		||||
                    /* loop body */
 | 
			
		||||
                    janetc_emit_sss(c, JOP_GET, arg, right, argi, 0);
 | 
			
		||||
                    janetc_emit_s(c, JOP_PUSH, arg, 0);
 | 
			
		||||
                    janetc_emit_ssi(c, JOP_ADD_IMMEDIATE, argi, argi, 1, 0);
 | 
			
		||||
 | 
			
		||||
                    /* loop - jump back to the start of the loop */
 | 
			
		||||
                    int32_t label_loop_loop = janet_v_count(c->buffer);
 | 
			
		||||
                    janetc_emit(c, JOP_JUMP);
 | 
			
		||||
                    int32_t label_loop_exit = janet_v_count(c->buffer);
 | 
			
		||||
 | 
			
		||||
                    c->buffer[label_loop_cond_jump] |= (label_loop_exit - label_loop_cond_jump) << 16;
 | 
			
		||||
                    c->buffer[label_loop_loop] |= (label_loop_start - label_loop_loop) << 8;
 | 
			
		||||
 | 
			
		||||
                    janetc_freeslot(c, argi);
 | 
			
		||||
                    janetc_freeslot(c, arg);
 | 
			
		||||
                    janetc_freeslot(c, len);
 | 
			
		||||
 | 
			
		||||
                    janetc_emit_s(c, JOP_MAKE_TUPLE, nextright, 1);
 | 
			
		||||
 | 
			
		||||
                    leaf(c, janet_unwrap_symbol(values[i + 1]), nextright, attr);
 | 
			
		||||
                    janetc_freeslot(c, nextright);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (i < 0x100) {
 | 
			
		||||
                    janetc_emit_ssu(c, JOP_GET_INDEX, nextright, right, (uint8_t) i, 1);
 | 
			
		||||
                } else {
 | 
			
		||||
@@ -239,11 +302,17 @@ static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv)
 | 
			
		||||
static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv) {
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    JanetTable *tab = janet_table(2);
 | 
			
		||||
    const char *binding_name = janet_type(argv[0]) == JANET_SYMBOL
 | 
			
		||||
                               ? ((const char *)janet_unwrap_symbol(argv[0]))
 | 
			
		||||
                               : "<multiple bindings>";
 | 
			
		||||
    for (i = 1; i < argn - 1; i++) {
 | 
			
		||||
        Janet attr = argv[i];
 | 
			
		||||
        switch (janet_type(attr)) {
 | 
			
		||||
            case JANET_TUPLE:
 | 
			
		||||
                janetc_cerror(c, "unexpected form - did you intend to use defn?");
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                janetc_cerror(c, "could not add metadata to binding");
 | 
			
		||||
                janetc_error(c, janet_formatc("cannot add metadata %v to binding %s", attr, binding_name));
 | 
			
		||||
                break;
 | 
			
		||||
            case JANET_KEYWORD:
 | 
			
		||||
                janet_table_put(tab, attr, janet_wrap_true());
 | 
			
		||||
@@ -298,8 +367,20 @@ static int varleaf(
 | 
			
		||||
        /* Global var, generate var */
 | 
			
		||||
        JanetSlot refslot;
 | 
			
		||||
        JanetTable *entry = janet_table_clone(reftab);
 | 
			
		||||
        JanetArray *ref = janet_array(1);
 | 
			
		||||
        janet_array_push(ref, janet_wrap_nil());
 | 
			
		||||
 | 
			
		||||
        Janet redef_kw = janet_ckeywordv("redef");
 | 
			
		||||
        int is_redef = janet_truthy(janet_table_get(c->env, redef_kw));
 | 
			
		||||
 | 
			
		||||
        JanetArray *ref;
 | 
			
		||||
        JanetBinding old_binding;
 | 
			
		||||
        if (is_redef && (old_binding = janet_resolve_ext(c->env, sym),
 | 
			
		||||
                         old_binding.type == JANET_BINDING_VAR)) {
 | 
			
		||||
            ref = janet_unwrap_array(old_binding.value);
 | 
			
		||||
        } else {
 | 
			
		||||
            ref = janet_array(1);
 | 
			
		||||
            janet_array_push(ref, janet_wrap_nil());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        janet_table_put(entry, janet_ckeywordv("ref"), janet_wrap_array(ref));
 | 
			
		||||
        janet_table_put(entry, janet_ckeywordv("source-map"),
 | 
			
		||||
                        janet_wrap_tuple(janetc_make_sourcemap(c)));
 | 
			
		||||
@@ -315,10 +396,11 @@ static int varleaf(
 | 
			
		||||
static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    Janet head;
 | 
			
		||||
    JanetTable *attr_table = handleattr(c, argn, argv);
 | 
			
		||||
    JanetSlot ret = dohead(c, opts, &head, argn, argv);
 | 
			
		||||
    if (c->result.status == JANET_COMPILE_ERROR)
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    destructure(c, argv[0], ret, varleaf, handleattr(c, argn, argv));
 | 
			
		||||
    destructure(c, argv[0], ret, varleaf, attr_table);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -331,14 +413,31 @@ static int defleaf(
 | 
			
		||||
        JanetTable *entry = janet_table_clone(tab);
 | 
			
		||||
        janet_table_put(entry, janet_ckeywordv("source-map"),
 | 
			
		||||
                        janet_wrap_tuple(janetc_make_sourcemap(c)));
 | 
			
		||||
        JanetSlot valsym = janetc_cslot(janet_ckeywordv("value"));
 | 
			
		||||
        JanetSlot tabslot = janetc_cslot(janet_wrap_table(entry));
 | 
			
		||||
 | 
			
		||||
        Janet redef_kw = janet_ckeywordv("redef");
 | 
			
		||||
        int is_redef = janet_truthy(janet_table_get(c->env, redef_kw));
 | 
			
		||||
        if (is_redef) janet_table_put(entry, redef_kw, janet_wrap_true());
 | 
			
		||||
 | 
			
		||||
        if (is_redef) {
 | 
			
		||||
            JanetBinding binding = janet_resolve_ext(c->env, sym);
 | 
			
		||||
            JanetArray *ref;
 | 
			
		||||
            if (binding.type == JANET_BINDING_DYNAMIC_DEF || binding.type == JANET_BINDING_DYNAMIC_MACRO) {
 | 
			
		||||
                ref = janet_unwrap_array(binding.value);
 | 
			
		||||
            } else {
 | 
			
		||||
                ref = janet_array(1);
 | 
			
		||||
                janet_array_push(ref, janet_wrap_nil());
 | 
			
		||||
            }
 | 
			
		||||
            janet_table_put(entry, janet_ckeywordv("ref"), janet_wrap_array(ref));
 | 
			
		||||
            JanetSlot refslot = janetc_cslot(janet_wrap_array(ref));
 | 
			
		||||
            janetc_emit_ssu(c, JOP_PUT_INDEX, refslot, s, 0, 0);
 | 
			
		||||
        } else {
 | 
			
		||||
            JanetSlot valsym = janetc_cslot(janet_ckeywordv("value"));
 | 
			
		||||
            JanetSlot tabslot = janetc_cslot(janet_wrap_table(entry));
 | 
			
		||||
            janetc_emit_sss(c, JOP_PUT, tabslot, valsym, s, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /* Add env entry to env */
 | 
			
		||||
        janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(entry));
 | 
			
		||||
 | 
			
		||||
        /* Put value in table when evaulated */
 | 
			
		||||
        janetc_emit_sss(c, JOP_PUT, tabslot, valsym, s, 0);
 | 
			
		||||
    }
 | 
			
		||||
    return namelocal(c, sym, 0, s);
 | 
			
		||||
}
 | 
			
		||||
@@ -347,10 +446,11 @@ static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    JanetCompiler *c = opts.compiler;
 | 
			
		||||
    Janet head;
 | 
			
		||||
    opts.flags &= ~JANET_FOPTS_HINT;
 | 
			
		||||
    JanetTable *attr_table = handleattr(c, argn, argv);
 | 
			
		||||
    JanetSlot ret = dohead(c, opts, &head, argn, argv);
 | 
			
		||||
    if (c->result.status == JANET_COMPILE_ERROR)
 | 
			
		||||
        return janetc_cslot(janet_wrap_nil());
 | 
			
		||||
    destructure(c, argv[0], ret, defleaf, handleattr(c, argn, argv));
 | 
			
		||||
    destructure(c, argv[0], ret, defleaf, attr_table);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -725,6 +825,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    int selfref = 0;
 | 
			
		||||
    int seenamp = 0;
 | 
			
		||||
    int seenopt = 0;
 | 
			
		||||
    int namedargs = 0;
 | 
			
		||||
 | 
			
		||||
    /* Begin function */
 | 
			
		||||
    c->scope->flags |= JANET_SCOPE_CLOSURE;
 | 
			
		||||
@@ -749,6 +850,9 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
 | 
			
		||||
    /* Keep track of destructured parameters */
 | 
			
		||||
    JanetSlot *destructed_params = NULL;
 | 
			
		||||
    JanetSlot *named_params = NULL;
 | 
			
		||||
    JanetTable *named_table = NULL;
 | 
			
		||||
    JanetSlot named_slot;
 | 
			
		||||
 | 
			
		||||
    /* Compile function parameters */
 | 
			
		||||
    params = janet_unwrap_tuple(argv[parami]);
 | 
			
		||||
@@ -756,49 +860,75 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    arity = paramcount;
 | 
			
		||||
    for (i = 0; i < paramcount; i++) {
 | 
			
		||||
        Janet param = params[i];
 | 
			
		||||
        if (janet_checktype(param, JANET_SYMBOL)) {
 | 
			
		||||
        if (namedargs) {
 | 
			
		||||
            arity--;
 | 
			
		||||
            if (!janet_checktype(param, JANET_SYMBOL)) {
 | 
			
		||||
                errmsg = "only named arguments can follow &named";
 | 
			
		||||
                goto error;
 | 
			
		||||
            }
 | 
			
		||||
            Janet key = janet_wrap_keyword(janet_unwrap_symbol(param));
 | 
			
		||||
            janet_table_put(named_table, key, param);
 | 
			
		||||
            janet_v_push(named_params, janetc_farslot(c));
 | 
			
		||||
        } else if (janet_checktype(param, JANET_SYMBOL)) {
 | 
			
		||||
            /* Check for varargs and unfixed arity */
 | 
			
		||||
            if (!janet_cstrcmp(janet_unwrap_symbol(param), "&")) {
 | 
			
		||||
                if (seenamp) {
 | 
			
		||||
                    errmsg = "& in unexpected location";
 | 
			
		||||
                    goto error;
 | 
			
		||||
                } else if (i == paramcount - 1) {
 | 
			
		||||
                    allow_extra = 1;
 | 
			
		||||
            const uint8_t *sym = janet_unwrap_symbol(param);
 | 
			
		||||
            if (sym[0] == '&') {
 | 
			
		||||
                if (!janet_cstrcmp(sym, "&")) {
 | 
			
		||||
                    if (seenamp) {
 | 
			
		||||
                        errmsg = "& in unexpected location";
 | 
			
		||||
                        goto error;
 | 
			
		||||
                    } else if (i == paramcount - 1) {
 | 
			
		||||
                        allow_extra = 1;
 | 
			
		||||
                        arity--;
 | 
			
		||||
                    } else if (i == paramcount - 2) {
 | 
			
		||||
                        vararg = 1;
 | 
			
		||||
                        arity -= 2;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        errmsg = "& in unexpected location";
 | 
			
		||||
                        goto error;
 | 
			
		||||
                    }
 | 
			
		||||
                    seenamp = 1;
 | 
			
		||||
                } else if (!janet_cstrcmp(sym, "&opt")) {
 | 
			
		||||
                    if (seenopt) {
 | 
			
		||||
                        errmsg = "only one &opt allowed";
 | 
			
		||||
                        goto error;
 | 
			
		||||
                    } else if (i == paramcount - 1) {
 | 
			
		||||
                        errmsg = "&opt cannot be last item in parameter list";
 | 
			
		||||
                        goto error;
 | 
			
		||||
                    }
 | 
			
		||||
                    min_arity = i;
 | 
			
		||||
                    arity--;
 | 
			
		||||
                } else if (i == paramcount - 2) {
 | 
			
		||||
                    vararg = 1;
 | 
			
		||||
                    arity -= 2;
 | 
			
		||||
                } else {
 | 
			
		||||
                    errmsg = "& in unexpected location";
 | 
			
		||||
                    goto error;
 | 
			
		||||
                }
 | 
			
		||||
                seenamp = 1;
 | 
			
		||||
            } else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&opt")) {
 | 
			
		||||
                if (seenopt) {
 | 
			
		||||
                    errmsg = "only one &opt allowed";
 | 
			
		||||
                    goto error;
 | 
			
		||||
                } else if (i == paramcount - 1) {
 | 
			
		||||
                    errmsg = "&opt cannot be last item in parameter list";
 | 
			
		||||
                    goto error;
 | 
			
		||||
                }
 | 
			
		||||
                min_arity = i;
 | 
			
		||||
                arity--;
 | 
			
		||||
                seenopt = 1;
 | 
			
		||||
            } else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&keys")) {
 | 
			
		||||
                if (seenamp) {
 | 
			
		||||
                    errmsg = "&keys in unexpected location";
 | 
			
		||||
                    goto error;
 | 
			
		||||
                } else if (i == paramcount - 2) {
 | 
			
		||||
                    seenopt = 1;
 | 
			
		||||
                } else if (!janet_cstrcmp(sym, "&keys")) {
 | 
			
		||||
                    if (seenamp) {
 | 
			
		||||
                        errmsg = "&keys in unexpected location";
 | 
			
		||||
                        goto error;
 | 
			
		||||
                    } else if (i == paramcount - 2) {
 | 
			
		||||
                        vararg = 1;
 | 
			
		||||
                        structarg = 1;
 | 
			
		||||
                        arity -= 2;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        errmsg = "&keys in unexpected location";
 | 
			
		||||
                        goto error;
 | 
			
		||||
                    }
 | 
			
		||||
                    seenamp = 1;
 | 
			
		||||
                } else if (!janet_cstrcmp(sym, "&named")) {
 | 
			
		||||
                    if (seenamp) {
 | 
			
		||||
                        errmsg = "&named in unexpected location";
 | 
			
		||||
                        goto error;
 | 
			
		||||
                    }
 | 
			
		||||
                    vararg = 1;
 | 
			
		||||
                    structarg = 1;
 | 
			
		||||
                    arity -= 2;
 | 
			
		||||
                    arity--;
 | 
			
		||||
                    seenamp = 1;
 | 
			
		||||
                    namedargs = 1;
 | 
			
		||||
                    named_table = janet_table(10);
 | 
			
		||||
                    named_slot = janetc_farslot(c);
 | 
			
		||||
                } else {
 | 
			
		||||
                    errmsg = "&keys in unexpected location";
 | 
			
		||||
                    goto error;
 | 
			
		||||
                    janetc_nameslot(c, sym, janetc_farslot(c));
 | 
			
		||||
                }
 | 
			
		||||
                seenamp = 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                janetc_nameslot(c, janet_unwrap_symbol(param), janetc_farslot(c));
 | 
			
		||||
                janetc_nameslot(c, sym, janetc_farslot(c));
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_v_push(destructed_params, janetc_farslot(c));
 | 
			
		||||
@@ -817,6 +947,14 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
 | 
			
		||||
    }
 | 
			
		||||
    janet_v_free(destructed_params);
 | 
			
		||||
 | 
			
		||||
    /* Compile named arguments */
 | 
			
		||||
    if (namedargs) {
 | 
			
		||||
        Janet param = janet_wrap_table(named_table);
 | 
			
		||||
        destructure(c, param, named_slot, defleaf, NULL);
 | 
			
		||||
        janetc_freeslot(c, named_slot);
 | 
			
		||||
        janet_v_free(named_params);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    max_arity = (vararg || allow_extra) ? INT32_MAX : arity;
 | 
			
		||||
    if (!seenopt) min_arity = arity;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -173,10 +173,10 @@ static int32_t kmp_next(struct kmp_state *state) {
 | 
			
		||||
JANET_CORE_FN(cfun_string_slice,
 | 
			
		||||
              "(string/slice bytes &opt start end)",
 | 
			
		||||
              "Returns a substring from a byte sequence. The substring is from "
 | 
			
		||||
              "index start inclusive to index end exclusive. All indexing "
 | 
			
		||||
              "is from 0. 'start' and 'end' can also be negative to indicate indexing "
 | 
			
		||||
              "index `start` inclusive to index `end`, exclusive. All indexing "
 | 
			
		||||
              "is from 0. `start` and `end` can also be negative to indicate indexing "
 | 
			
		||||
              "from the end of the string. Note that index -1 is synonymous with "
 | 
			
		||||
              "index (length bytes) to allow a full negative slice range. ") {
 | 
			
		||||
              "index `(length bytes)` to allow a full negative slice range. ") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_stringv(view.bytes + range.start, range.end - range.start);
 | 
			
		||||
@@ -184,7 +184,7 @@ JANET_CORE_FN(cfun_string_slice,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_symbol_slice,
 | 
			
		||||
              "(symbol/slice bytes &opt start end)",
 | 
			
		||||
              "Same a string/slice, but returns a symbol.") {
 | 
			
		||||
              "Same as string/slice, but returns a symbol.") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_symbolv(view.bytes + range.start, range.end - range.start);
 | 
			
		||||
@@ -192,7 +192,7 @@ JANET_CORE_FN(cfun_symbol_slice,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_keyword_slice,
 | 
			
		||||
              "(keyword/slice bytes &opt start end)",
 | 
			
		||||
              "Same a string/slice, but returns a keyword.") {
 | 
			
		||||
              "Same as string/slice, but returns a keyword.") {
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
    return janet_keywordv(view.bytes + range.start, range.end - range.start);
 | 
			
		||||
@@ -200,7 +200,7 @@ JANET_CORE_FN(cfun_keyword_slice,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_repeat,
 | 
			
		||||
              "(string/repeat bytes n)",
 | 
			
		||||
              "Returns a string that is n copies of bytes concatenated.") {
 | 
			
		||||
              "Returns a string that is `n` copies of `bytes` concatenated.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    int32_t rep = janet_getinteger(argv, 1);
 | 
			
		||||
@@ -282,7 +282,7 @@ JANET_CORE_FN(cfun_string_asciiupper,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_reverse,
 | 
			
		||||
              "(string/reverse str)",
 | 
			
		||||
              "Returns a string that is the reversed version of str.") {
 | 
			
		||||
              "Returns a string that is the reversed version of `str`.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetByteView view = janet_getbytes(argv, 0);
 | 
			
		||||
    uint8_t *buf = janet_string_begin(view.len);
 | 
			
		||||
@@ -308,8 +308,8 @@ static void findsetup(int32_t argc, Janet *argv, struct kmp_state *s, int32_t ex
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_find,
 | 
			
		||||
              "(string/find patt str &opt start-index)",
 | 
			
		||||
              "Searches for the first instance of pattern patt in string "
 | 
			
		||||
              "str. Returns the index of the first character in patt if found, "
 | 
			
		||||
              "Searches for the first instance of pattern `patt` in string "
 | 
			
		||||
              "`str`. Returns the index of the first character in `patt` if found, "
 | 
			
		||||
              "otherwise returns nil.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct kmp_state state;
 | 
			
		||||
@@ -323,7 +323,7 @@ JANET_CORE_FN(cfun_string_find,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_hasprefix,
 | 
			
		||||
              "(string/has-prefix? pfx str)",
 | 
			
		||||
              "Tests whether str starts with pfx.") {
 | 
			
		||||
              "Tests whether `str` starts with `pfx`.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView prefix = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView str = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -334,7 +334,7 @@ JANET_CORE_FN(cfun_string_hasprefix,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_hassuffix,
 | 
			
		||||
              "(string/has-suffix? sfx str)",
 | 
			
		||||
              "Tests whether str ends with sfx.") {
 | 
			
		||||
              "Tests whether `str` ends with `sfx`.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView suffix = janet_getbytes(argv, 0);
 | 
			
		||||
    JanetByteView str = janet_getbytes(argv, 1);
 | 
			
		||||
@@ -347,9 +347,9 @@ JANET_CORE_FN(cfun_string_hassuffix,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_findall,
 | 
			
		||||
              "(string/find-all patt str &opt start-index)",
 | 
			
		||||
              "Searches for all instances of pattern patt in string "
 | 
			
		||||
              "str. Returns an array of all indices of found patterns. Overlapping "
 | 
			
		||||
              "instances of the pattern are counted individually, meaning a byte in str "
 | 
			
		||||
              "Searches for all instances of pattern `patt` in string "
 | 
			
		||||
              "`str`. Returns an array of all indices of found patterns. Overlapping "
 | 
			
		||||
              "instances of the pattern are counted individually, meaning a byte in `str` "
 | 
			
		||||
              "may contribute to multiple found patterns.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct kmp_state state;
 | 
			
		||||
@@ -386,8 +386,8 @@ static void replacesetup(int32_t argc, Janet *argv, struct replace_state *s) {
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_replace,
 | 
			
		||||
              "(string/replace patt subst str)",
 | 
			
		||||
              "Replace the first occurrence of patt with subst in the string str. "
 | 
			
		||||
              "Will return the new string if patt is found, otherwise returns str.") {
 | 
			
		||||
              "Replace the first occurrence of `patt` with `subst` in the string `str`. "
 | 
			
		||||
              "Will return the new string if `patt` is found, otherwise returns `str`.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct replace_state s;
 | 
			
		||||
    uint8_t *buf;
 | 
			
		||||
@@ -409,9 +409,9 @@ JANET_CORE_FN(cfun_string_replace,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_replaceall,
 | 
			
		||||
              "(string/replace-all patt subst str)",
 | 
			
		||||
              "Replace all instances of patt with subst in the string str. Overlapping "
 | 
			
		||||
              "Replace all instances of `patt` with `subst` in the string `str`. Overlapping "
 | 
			
		||||
              "matches will not be counted, only the first match in such a span will be replaced. "
 | 
			
		||||
              "Will return the new string if patt is found, otherwise returns str.") {
 | 
			
		||||
              "Will return the new string if `patt` is found, otherwise returns `str`.") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    struct replace_state s;
 | 
			
		||||
    JanetBuffer b;
 | 
			
		||||
@@ -433,11 +433,11 @@ JANET_CORE_FN(cfun_string_replaceall,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_split,
 | 
			
		||||
              "(string/split delim str &opt start limit)",
 | 
			
		||||
              "Splits a string str with delimiter delim and returns an array of "
 | 
			
		||||
              "substrings. The substrings will not contain the delimiter delim. If delim "
 | 
			
		||||
              "Splits a string `str` with delimiter `delim` and returns an array of "
 | 
			
		||||
              "substrings. The substrings will not contain the delimiter `delim`. If `delim` "
 | 
			
		||||
              "is not found, the returned array will have one element. Will start searching "
 | 
			
		||||
              "for delim at the index start (if provided), and return up to a maximum "
 | 
			
		||||
              "of limit results (if provided).") {
 | 
			
		||||
              "for `delim` at the index `start` (if provided), and return up to a maximum "
 | 
			
		||||
              "of `limit` results (if provided).") {
 | 
			
		||||
    int32_t result;
 | 
			
		||||
    JanetArray *array;
 | 
			
		||||
    struct kmp_state state;
 | 
			
		||||
@@ -461,9 +461,9 @@ JANET_CORE_FN(cfun_string_split,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_checkset,
 | 
			
		||||
              "(string/check-set set str)",
 | 
			
		||||
              "Checks that the string str only contains bytes that appear in the string set. "
 | 
			
		||||
              "Returns true if all bytes in str appear in set, false if some bytes in str do "
 | 
			
		||||
              "not appear in set.") {
 | 
			
		||||
              "Checks that the string `str` only contains bytes that appear in the string `set`. "
 | 
			
		||||
              "Returns true if all bytes in `str` appear in `set`, false if some bytes in `str` do "
 | 
			
		||||
              "not appear in `set`.") {
 | 
			
		||||
    uint32_t bitset[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetByteView set = janet_getbytes(argv, 0);
 | 
			
		||||
@@ -488,7 +488,7 @@ JANET_CORE_FN(cfun_string_checkset,
 | 
			
		||||
JANET_CORE_FN(cfun_string_join,
 | 
			
		||||
              "(string/join parts &opt sep)",
 | 
			
		||||
              "Joins an array of strings into one string, optionally separated by "
 | 
			
		||||
              "a separator string sep.") {
 | 
			
		||||
              "a separator string `sep`.") {
 | 
			
		||||
    janet_arity(argc, 1, 2);
 | 
			
		||||
    JanetView parts = janet_getindexed(argv, 0);
 | 
			
		||||
    JanetByteView joiner;
 | 
			
		||||
@@ -530,7 +530,7 @@ JANET_CORE_FN(cfun_string_join,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_string_format,
 | 
			
		||||
              "(string/format format & values)",
 | 
			
		||||
              "Similar to snprintf, but specialized for operating with Janet values. Returns "
 | 
			
		||||
              "Similar to C's `snprintf`, but specialized for operating with Janet values. Returns "
 | 
			
		||||
              "a new string.") {
 | 
			
		||||
    janet_arity(argc, 1, -1);
 | 
			
		||||
    JanetBuffer *buffer = janet_buffer(0);
 | 
			
		||||
@@ -574,7 +574,7 @@ static void trim_help_args(int32_t argc, Janet *argv, JanetByteView *str, JanetB
 | 
			
		||||
JANET_CORE_FN(cfun_string_trim,
 | 
			
		||||
              "(string/trim str &opt set)",
 | 
			
		||||
              "Trim leading and trailing whitespace from a byte sequence. If the argument "
 | 
			
		||||
              "set is provided, consider only characters in set to be whitespace.") {
 | 
			
		||||
              "`set` is provided, consider only characters in `set` to be whitespace.") {
 | 
			
		||||
    JanetByteView str, set;
 | 
			
		||||
    trim_help_args(argc, argv, &str, &set);
 | 
			
		||||
    int32_t left_edge = trim_help_leftedge(str, set);
 | 
			
		||||
@@ -587,7 +587,7 @@ JANET_CORE_FN(cfun_string_trim,
 | 
			
		||||
JANET_CORE_FN(cfun_string_triml,
 | 
			
		||||
              "(string/triml str &opt set)",
 | 
			
		||||
              "Trim leading whitespace from a byte sequence. If the argument "
 | 
			
		||||
              "set is provided, consider only characters in set to be whitespace.") {
 | 
			
		||||
              "`set` is provided, consider only characters in `set` to be whitespace.") {
 | 
			
		||||
    JanetByteView str, set;
 | 
			
		||||
    trim_help_args(argc, argv, &str, &set);
 | 
			
		||||
    int32_t left_edge = trim_help_leftedge(str, set);
 | 
			
		||||
@@ -597,7 +597,7 @@ JANET_CORE_FN(cfun_string_triml,
 | 
			
		||||
JANET_CORE_FN(cfun_string_trimr,
 | 
			
		||||
              "(string/trimr str &opt set)",
 | 
			
		||||
              "Trim trailing whitespace from a byte sequence. If the argument "
 | 
			
		||||
              "set is provided, consider only characters in set to be whitespace.") {
 | 
			
		||||
              "`set` is provided, consider only characters in `set` to be whitespace.") {
 | 
			
		||||
    JanetByteView str, set;
 | 
			
		||||
    trim_help_args(argc, argv, &str, &set);
 | 
			
		||||
    int32_t right_edge = trim_help_rightedge(str, set);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -296,17 +296,17 @@ JanetTable *janet_table_proto_flatten(JanetTable *t) {
 | 
			
		||||
JANET_CORE_FN(cfun_table_new,
 | 
			
		||||
              "(table/new capacity)",
 | 
			
		||||
              "Creates a new empty table with pre-allocated memory "
 | 
			
		||||
              "for capacity entries. This means that if one knows the number of "
 | 
			
		||||
              "entries going to go in a table on creation, extra memory allocation "
 | 
			
		||||
              "for `capacity` entries. This means that if one knows the number of "
 | 
			
		||||
              "entries going into a table on creation, extra memory allocation "
 | 
			
		||||
              "can be avoided. Returns the new table.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    int32_t cap = janet_getinteger(argv, 0);
 | 
			
		||||
    int32_t cap = janet_getnat(argv, 0);
 | 
			
		||||
    return janet_wrap_table(janet_table(cap));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_getproto,
 | 
			
		||||
              "(table/getproto tab)",
 | 
			
		||||
              "Get the prototype table of a table. Returns nil if a table "
 | 
			
		||||
              "Get the prototype table of a table. Returns nil if the table "
 | 
			
		||||
              "has no prototype, otherwise returns the prototype.") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    JanetTable *t = janet_gettable(argv, 0);
 | 
			
		||||
@@ -317,7 +317,7 @@ JANET_CORE_FN(cfun_table_getproto,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_setproto,
 | 
			
		||||
              "(table/setproto tab proto)",
 | 
			
		||||
              "Set the prototype of a table. Returns the original table tab.") {
 | 
			
		||||
              "Set the prototype of a table. Returns the original table `tab`.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetTable *table = janet_gettable(argv, 0);
 | 
			
		||||
    JanetTable *proto = NULL;
 | 
			
		||||
@@ -339,8 +339,8 @@ JANET_CORE_FN(cfun_table_tostruct,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_table_rawget,
 | 
			
		||||
              "(table/rawget tab key)",
 | 
			
		||||
              "Gets a value from a table without looking at the prototype table. "
 | 
			
		||||
              "If a table tab does not contain t directly, the function will return "
 | 
			
		||||
              "Gets a value from a table `tab` without looking at the prototype table. "
 | 
			
		||||
              "If `tab` does not contain the key directly, the function will return "
 | 
			
		||||
              "nil without checking the prototype. Returns the value in the table.") {
 | 
			
		||||
    janet_fixarity(argc, 2);
 | 
			
		||||
    JanetTable *table = janet_gettable(argv, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -65,12 +65,12 @@ JANET_CORE_FN(cfun_tuple_brackets,
 | 
			
		||||
 | 
			
		||||
JANET_CORE_FN(cfun_tuple_slice,
 | 
			
		||||
              "(tuple/slice arrtup [,start=0 [,end=(length arrtup)]])",
 | 
			
		||||
              "Take a sub sequence of an array or tuple from index start "
 | 
			
		||||
              "inclusive to index end exclusive. If start or end are not provided, "
 | 
			
		||||
              "they default to 0 and the length of arrtup respectively. "
 | 
			
		||||
              "'start' and 'end' can also be negative to indicate indexing "
 | 
			
		||||
              "Take a sub-sequence of an array or tuple from index `start` "
 | 
			
		||||
              "inclusive to index `end` exclusive. If `start` or `end` are not provided, "
 | 
			
		||||
              "they default to 0 and the length of `arrtup`, respectively. "
 | 
			
		||||
              "`start` and `end` can also be negative to indicate indexing "
 | 
			
		||||
              "from the end of the input. Note that index -1 is synonymous with "
 | 
			
		||||
              "index '(length arrtup)' to allow a full negative slice range. "
 | 
			
		||||
              "index `(length arrtup)` to allow a full negative slice range. "
 | 
			
		||||
              "Returns the new tuple.") {
 | 
			
		||||
    JanetView view = janet_getindexed(argv, 0);
 | 
			
		||||
    JanetRange range = janet_getslice(argc, argv);
 | 
			
		||||
@@ -96,7 +96,7 @@ JANET_CORE_FN(cfun_tuple_type,
 | 
			
		||||
JANET_CORE_FN(cfun_tuple_sourcemap,
 | 
			
		||||
              "(tuple/sourcemap tup)",
 | 
			
		||||
              "Returns the sourcemap metadata attached to a tuple, "
 | 
			
		||||
              " which is another tuple (line, column).") {
 | 
			
		||||
              "which is another tuple (line, column).") {
 | 
			
		||||
    janet_fixarity(argc, 1);
 | 
			
		||||
    const Janet *tup = janet_gettuple(argv, 0);
 | 
			
		||||
    Janet contents[2];
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										154
									
								
								src/core/util.c
									
									
									
									
									
								
							
							
						
						
									
										154
									
								
								src/core/util.c
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -36,6 +36,17 @@
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
#ifdef JANET_DYNAMIC_MODULES
 | 
			
		||||
#include <psapi.h>
 | 
			
		||||
#pragma comment (lib, "Psapi.lib")
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_APPLE
 | 
			
		||||
#include <AvailabilityMacros.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
/* Base 64 lookup table for digits */
 | 
			
		||||
@@ -224,13 +235,17 @@ int32_t janet_string_calchash(const uint8_t *str, int32_t len) {
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
uint32_t janet_hash_mix(uint32_t input, uint32_t more) {
 | 
			
		||||
    uint32_t mix1 = (more + 0x9e3779b9 + (input << 6) + (input >> 2));
 | 
			
		||||
    return input ^ (0x9e3779b9 + (mix1 << 6) + (mix1 >> 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Computes hash of an array of values */
 | 
			
		||||
int32_t janet_array_calchash(const Janet *array, int32_t len) {
 | 
			
		||||
    const Janet *end = array + len;
 | 
			
		||||
    uint32_t hash = 0;
 | 
			
		||||
    uint32_t hash = 33;
 | 
			
		||||
    while (array < end) {
 | 
			
		||||
        uint32_t elem = janet_hash(*array++);
 | 
			
		||||
        hash ^= elem + 0x9e3779b9 + (hash << 6) + (hash >> 2);
 | 
			
		||||
        hash = janet_hash_mix(hash, janet_hash(*array++));
 | 
			
		||||
    }
 | 
			
		||||
    return (int32_t) hash;
 | 
			
		||||
}
 | 
			
		||||
@@ -238,10 +253,10 @@ int32_t janet_array_calchash(const Janet *array, int32_t len) {
 | 
			
		||||
/* Computes hash of an array of values */
 | 
			
		||||
int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len) {
 | 
			
		||||
    const JanetKV *end = kvs + len;
 | 
			
		||||
    uint32_t hash = 0;
 | 
			
		||||
    uint32_t hash = 33;
 | 
			
		||||
    while (kvs < end) {
 | 
			
		||||
        hash ^= janet_hash(kvs->key) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
 | 
			
		||||
        hash ^= janet_hash(kvs->value) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
 | 
			
		||||
        hash = janet_hash_mix(hash, janet_hash(kvs->key));
 | 
			
		||||
        hash = janet_hash_mix(hash, janet_hash(kvs->value));
 | 
			
		||||
        kvs++;
 | 
			
		||||
    }
 | 
			
		||||
    return (int32_t) hash;
 | 
			
		||||
@@ -250,6 +265,7 @@ int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len) {
 | 
			
		||||
/* Calculate next power of 2. May overflow. If n is 0,
 | 
			
		||||
 * will return 0. */
 | 
			
		||||
int32_t janet_tablen(int32_t n) {
 | 
			
		||||
    if (n < 0) return 0;
 | 
			
		||||
    n |= n >> 1;
 | 
			
		||||
    n |= n >> 2;
 | 
			
		||||
    n |= n >> 4;
 | 
			
		||||
@@ -593,10 +609,8 @@ void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetReg
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
 | 
			
		||||
    Janet ref;
 | 
			
		||||
JanetBinding janet_binding_from_entry(Janet entry) {
 | 
			
		||||
    JanetTable *entry_table;
 | 
			
		||||
    Janet entry = janet_table_get(env, janet_wrap_symbol(sym));
 | 
			
		||||
    JanetBinding binding = {
 | 
			
		||||
        JANET_BINDING_NONE,
 | 
			
		||||
        janet_wrap_nil(),
 | 
			
		||||
@@ -623,29 +637,41 @@ JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
 | 
			
		||||
        binding.deprecation = JANET_BINDING_DEP_NORMAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!janet_checktype(
 | 
			
		||||
                janet_table_get(entry_table, janet_ckeywordv("macro")),
 | 
			
		||||
                JANET_NIL)) {
 | 
			
		||||
        binding.value = janet_table_get(entry_table, janet_ckeywordv("value"));
 | 
			
		||||
        binding.type = JANET_BINDING_MACRO;
 | 
			
		||||
    int macro = janet_truthy(janet_table_get(entry_table, janet_ckeywordv("macro")));
 | 
			
		||||
    Janet value = janet_table_get(entry_table, janet_ckeywordv("value"));
 | 
			
		||||
    Janet ref = janet_table_get(entry_table, janet_ckeywordv("ref"));
 | 
			
		||||
    int ref_is_valid = janet_checktype(ref, JANET_ARRAY);
 | 
			
		||||
    int redef = ref_is_valid && janet_truthy(janet_table_get(entry_table, janet_ckeywordv("redef")));
 | 
			
		||||
 | 
			
		||||
    if (macro) {
 | 
			
		||||
        binding.value = redef ? ref : value;
 | 
			
		||||
        binding.type = redef ? JANET_BINDING_DYNAMIC_MACRO : JANET_BINDING_MACRO;
 | 
			
		||||
        return binding;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ref = janet_table_get(entry_table, janet_ckeywordv("ref"));
 | 
			
		||||
    if (janet_checktype(ref, JANET_ARRAY)) {
 | 
			
		||||
    if (ref_is_valid) {
 | 
			
		||||
        binding.value = ref;
 | 
			
		||||
        binding.type = JANET_BINDING_VAR;
 | 
			
		||||
        return binding;
 | 
			
		||||
        binding.type = redef ? JANET_BINDING_DYNAMIC_DEF : JANET_BINDING_VAR;
 | 
			
		||||
    } else {
 | 
			
		||||
        binding.value = value;
 | 
			
		||||
        binding.type = JANET_BINDING_DEF;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    binding.value = janet_table_get(entry_table, janet_ckeywordv("value"));
 | 
			
		||||
    binding.type = JANET_BINDING_DEF;
 | 
			
		||||
    return binding;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) {
 | 
			
		||||
    Janet entry = janet_table_get(env, janet_wrap_symbol(sym));
 | 
			
		||||
    return janet_binding_from_entry(entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out) {
 | 
			
		||||
    JanetBinding binding = janet_resolve_ext(env, sym);
 | 
			
		||||
    *out = binding.value;
 | 
			
		||||
    if (binding.type == JANET_BINDING_DYNAMIC_DEF || binding.type == JANET_BINDING_DYNAMIC_MACRO) {
 | 
			
		||||
        *out = janet_array_peek(janet_unwrap_array(binding.value));
 | 
			
		||||
    } else {
 | 
			
		||||
        *out = binding.value;
 | 
			
		||||
    }
 | 
			
		||||
    return binding.type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -720,6 +746,13 @@ int janet_checkint64(Janet x) {
 | 
			
		||||
    return janet_checkint64range(dval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_checkuint64(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_NUMBER))
 | 
			
		||||
        return 0;
 | 
			
		||||
    double dval = janet_unwrap_number(x);
 | 
			
		||||
    return dval >= 0 && dval <= JANET_INTMAX_DOUBLE && dval == (uint64_t) dval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int janet_checksize(Janet x) {
 | 
			
		||||
    if (!janet_checktype(x, JANET_NUMBER))
 | 
			
		||||
        return 0;
 | 
			
		||||
@@ -775,11 +808,6 @@ int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffe
 | 
			
		||||
 | 
			
		||||
/* Clock shims for various platforms */
 | 
			
		||||
#ifdef JANET_GETTIME
 | 
			
		||||
/* For macos */
 | 
			
		||||
#ifdef __MACH__
 | 
			
		||||
#include <mach/clock.h>
 | 
			
		||||
#include <mach/mach.h>
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    FILETIME ftime;
 | 
			
		||||
@@ -792,7 +820,10 @@ int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    spec->tv_nsec = wintime % 10000000LL * 100;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
#elif defined(__MACH__)
 | 
			
		||||
/* clock_gettime() wasn't available on Mac until 10.12. */
 | 
			
		||||
#elif defined(JANET_APPLE) && !defined(MAC_OS_X_VERSION_10_12)
 | 
			
		||||
#include <mach/clock.h>
 | 
			
		||||
#include <mach/mach.h>
 | 
			
		||||
int janet_gettime(struct timespec *spec) {
 | 
			
		||||
    clock_serv_t cclock;
 | 
			
		||||
    mach_timespec_t mts;
 | 
			
		||||
@@ -860,6 +891,73 @@ int janet_cryptorand(uint8_t *out, size_t n) {
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Dynamic library loading */
 | 
			
		||||
 | 
			
		||||
char *get_processed_name(const char *name) {
 | 
			
		||||
    if (name[0] == '.') return (char *) name;
 | 
			
		||||
    const char *c;
 | 
			
		||||
    for (c = name; *c; c++) {
 | 
			
		||||
        if (*c == '/') return (char *) name;
 | 
			
		||||
    }
 | 
			
		||||
    size_t l = (size_t)(c - name);
 | 
			
		||||
    char *ret = janet_malloc(l + 3);
 | 
			
		||||
    if (NULL == ret) {
 | 
			
		||||
        JANET_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
    ret[0] = '.';
 | 
			
		||||
    ret[1] = '/';
 | 
			
		||||
    memcpy(ret + 2, name, l + 1);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(JANET_WINDOWS)
 | 
			
		||||
 | 
			
		||||
static char error_clib_buf[256];
 | 
			
		||||
char *error_clib(void) {
 | 
			
		||||
    FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 | 
			
		||||
                   NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | 
			
		||||
                   error_clib_buf, sizeof(error_clib_buf), NULL);
 | 
			
		||||
    error_clib_buf[strlen(error_clib_buf) - 1] = '\0';
 | 
			
		||||
    return error_clib_buf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Clib load_clib(const char *name) {
 | 
			
		||||
    if (name == NULL) {
 | 
			
		||||
        return GetModuleHandle(NULL);
 | 
			
		||||
    } else {
 | 
			
		||||
        return LoadLibrary(name);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void free_clib(HINSTANCE clib) {
 | 
			
		||||
    if (clib != GetModuleHandle(NULL)) {
 | 
			
		||||
        FreeLibrary(clib);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void *symbol_clib(HINSTANCE clib, const char *sym) {
 | 
			
		||||
    if (clib != GetModuleHandle(NULL)) {
 | 
			
		||||
        return GetProcAddress(clib, sym);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* Look up symbols from all loaded modules */
 | 
			
		||||
        HMODULE hMods[1024];
 | 
			
		||||
        DWORD needed = 0;
 | 
			
		||||
        if (EnumProcessModules(GetCurrentProcess(), hMods, sizeof(hMods), &needed)) {
 | 
			
		||||
            needed /= sizeof(HMODULE);
 | 
			
		||||
            for (DWORD i = 0; i < needed; i++) {
 | 
			
		||||
                void *address = GetProcAddress(hMods[i], sym);
 | 
			
		||||
                if (NULL != address) {
 | 
			
		||||
                    return address;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            janet_panicf("ffi: %s", error_clib());
 | 
			
		||||
        }
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Alloc function macro fills */
 | 
			
		||||
void *(janet_malloc)(size_t size) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -31,6 +31,14 @@
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifndef JANET_WINDOWS
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(JANET_REDUCED_OS) || !defined(JANET_SINGLE_THREADED)
 | 
			
		||||
#include <time.h>
 | 
			
		||||
@@ -56,6 +64,7 @@
 | 
			
		||||
} while (0)
 | 
			
		||||
 | 
			
		||||
/* Utils */
 | 
			
		||||
uint32_t janet_hash_mix(uint32_t input, uint32_t more);
 | 
			
		||||
#define janet_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1))
 | 
			
		||||
int janet_valid_utf8(const uint8_t *str, int32_t len);
 | 
			
		||||
int janet_is_symbol_char(uint8_t c);
 | 
			
		||||
@@ -83,6 +92,7 @@ void janet_buffer_format(
 | 
			
		||||
    int32_t argc,
 | 
			
		||||
    Janet *argv);
 | 
			
		||||
Janet janet_next_impl(Janet ds, Janet key, int is_interpreter);
 | 
			
		||||
JanetBinding janet_binding_from_entry(Janet entry);
 | 
			
		||||
 | 
			
		||||
/* Registry functions */
 | 
			
		||||
void janet_registry_put(
 | 
			
		||||
@@ -119,6 +129,31 @@ int janet_gettime(struct timespec *spec);
 | 
			
		||||
#define strdup(x) _strdup(x)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Use LoadLibrary on windows or dlopen on posix to load dynamic libaries
 | 
			
		||||
 * with native code. */
 | 
			
		||||
#if defined(JANET_NO_DYNAMIC_MODULES)
 | 
			
		||||
typedef int Clib;
 | 
			
		||||
#define load_clib(name) ((void) name, 0)
 | 
			
		||||
#define symbol_clib(lib, sym) ((void) lib, (void) sym, NULL)
 | 
			
		||||
#define error_clib() "dynamic libraries not supported"
 | 
			
		||||
#define free_clib(c) ((void) (c), 0)
 | 
			
		||||
#elif defined(JANET_WINDOWS)
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
typedef HINSTANCE Clib;
 | 
			
		||||
void *symbol_clib(Clib clib, const char *sym);
 | 
			
		||||
void free_clib(Clib clib);
 | 
			
		||||
Clib load_clib(const char *name);
 | 
			
		||||
char *error_clib(void);
 | 
			
		||||
#else
 | 
			
		||||
#include <dlfcn.h>
 | 
			
		||||
typedef void *Clib;
 | 
			
		||||
#define load_clib(name) dlopen((name), RTLD_NOW)
 | 
			
		||||
#define free_clib(lib) dlclose((lib))
 | 
			
		||||
#define symbol_clib(lib, sym) dlsym((lib), (sym))
 | 
			
		||||
#define error_clib() dlerror()
 | 
			
		||||
#endif
 | 
			
		||||
char *get_processed_name(const char *name);
 | 
			
		||||
 | 
			
		||||
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
 | 
			
		||||
 | 
			
		||||
/* Initialize builtin libraries */
 | 
			
		||||
@@ -157,5 +192,8 @@ void janet_lib_ev(JanetTable *env);
 | 
			
		||||
void janet_ev_mark(void);
 | 
			
		||||
int janet_make_pipe(JanetHandle handles[2], int mode);
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef JANET_FFI
 | 
			
		||||
void janet_lib_ffi(JanetTable *env);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -295,6 +295,15 @@ int janet_equals(Janet x, Janet y) {
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint64_t murmur64(uint64_t h) {
 | 
			
		||||
    h ^= h >> 33;
 | 
			
		||||
    h *= 0xff51afd7ed558ccdUL;
 | 
			
		||||
    h ^= h >> 33;
 | 
			
		||||
    h *= 0xc4ceb9fe1a85ec53UL;
 | 
			
		||||
    h ^= h >> 33;
 | 
			
		||||
    return h;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Computes a hash value for a function */
 | 
			
		||||
int32_t janet_hash(Janet x) {
 | 
			
		||||
    int32_t hash = 0;
 | 
			
		||||
@@ -322,9 +331,11 @@ int32_t janet_hash(Janet x) {
 | 
			
		||||
                uint64_t u;
 | 
			
		||||
            } as;
 | 
			
		||||
            as.d = janet_unwrap_number(x);
 | 
			
		||||
            as.d += 0.0; /* normalize negative 0 */
 | 
			
		||||
            uint32_t lo = (uint32_t)(as.u & 0xFFFFFFFF);
 | 
			
		||||
            uint32_t hi = (uint32_t)(as.u >> 32);
 | 
			
		||||
            hash = (int32_t)(hi ^ (lo >> 3));
 | 
			
		||||
            uint32_t hilo = (hi ^ lo) * 2654435769u;
 | 
			
		||||
            hash = (int32_t)((hilo << 16) | (hilo >> 16));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case JANET_ABSTRACT: {
 | 
			
		||||
@@ -338,15 +349,14 @@ int32_t janet_hash(Janet x) {
 | 
			
		||||
        /* fallthrough */
 | 
			
		||||
        default:
 | 
			
		||||
            if (sizeof(double) == sizeof(void *)) {
 | 
			
		||||
                /* Assuming 8 byte pointer */
 | 
			
		||||
                uint64_t i = janet_u64(x);
 | 
			
		||||
                uint32_t lo = (uint32_t)(i & 0xFFFFFFFF);
 | 
			
		||||
                uint32_t hi = (uint32_t)(i >> 32);
 | 
			
		||||
                hash = (int32_t)(hi ^ (lo >> 3));
 | 
			
		||||
                /* Assuming 8 byte pointer (8 byte aligned) */
 | 
			
		||||
                uint64_t i = murmur64(janet_u64(x));
 | 
			
		||||
                hash = (int32_t)(i >> 32);
 | 
			
		||||
            } else {
 | 
			
		||||
                /* Assuming 4 byte pointer (or smaller) */
 | 
			
		||||
                hash = (int32_t)((char *)janet_unwrap_pointer(x) - (char *)0);
 | 
			
		||||
                hash >>= 2;
 | 
			
		||||
                uintptr_t diff = (uintptr_t) janet_unwrap_pointer(x);
 | 
			
		||||
                uint32_t hilo = (uint32_t) diff * 2654435769u;
 | 
			
		||||
                hash = (int32_t)((hilo << 16) | (hilo >> 16));
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -220,14 +220,14 @@
 | 
			
		||||
/* Trace a function call */
 | 
			
		||||
static void vm_do_trace(JanetFunction *func, int32_t argc, const Janet *argv) {
 | 
			
		||||
    if (func->def->name) {
 | 
			
		||||
        janet_printf("trace (%S", func->def->name);
 | 
			
		||||
        janet_eprintf("trace (%S", func->def->name);
 | 
			
		||||
    } else {
 | 
			
		||||
        janet_printf("trace (%p", janet_wrap_function(func));
 | 
			
		||||
        janet_eprintf("trace (%p", janet_wrap_function(func));
 | 
			
		||||
    }
 | 
			
		||||
    for (int32_t i = 0; i < argc; i++) {
 | 
			
		||||
        janet_printf(" %p", argv[i]);
 | 
			
		||||
        janet_eprintf(" %p", argv[i]);
 | 
			
		||||
    }
 | 
			
		||||
    janet_printf(")\n");
 | 
			
		||||
    janet_eprintf(")\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Invoke a method once we have looked it up */
 | 
			
		||||
@@ -315,7 +315,7 @@ static Janet janet_binop_call(const char *lmethod, const char *rmethod, Janet lh
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Forward declaration */
 | 
			
		||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out);
 | 
			
		||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out, int is_cancel);
 | 
			
		||||
static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *out);
 | 
			
		||||
 | 
			
		||||
/* Interpreter main loop */
 | 
			
		||||
@@ -1056,7 +1056,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        vm_maybe_auto_suspend(1);
 | 
			
		||||
        vm_assert_type(stack[B], JANET_FIBER);
 | 
			
		||||
        JanetFiber *child = janet_unwrap_fiber(stack[B]);
 | 
			
		||||
        if (janet_check_can_resume(child, &retreg)) {
 | 
			
		||||
        if (janet_check_can_resume(child, &retreg, 0)) {
 | 
			
		||||
            vm_commit();
 | 
			
		||||
            janet_panicv(retreg);
 | 
			
		||||
        }
 | 
			
		||||
@@ -1096,7 +1096,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
 | 
			
		||||
        Janet retreg;
 | 
			
		||||
        vm_assert_type(stack[B], JANET_FIBER);
 | 
			
		||||
        JanetFiber *child = janet_unwrap_fiber(stack[B]);
 | 
			
		||||
        if (janet_check_can_resume(child, &retreg)) {
 | 
			
		||||
        if (janet_check_can_resume(child, &retreg, 1)) {
 | 
			
		||||
            vm_commit();
 | 
			
		||||
            janet_panicv(retreg);
 | 
			
		||||
        }
 | 
			
		||||
@@ -1285,6 +1285,12 @@ JanetSignal janet_step(JanetFiber *fiber, Janet in, Janet *out) {
 | 
			
		||||
    return signal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Janet void_cfunction(int32_t argc, Janet *argv) {
 | 
			
		||||
    (void) argc;
 | 
			
		||||
    (void) argv;
 | 
			
		||||
    janet_panic("placeholder");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
 | 
			
		||||
    /* Check entry conditions */
 | 
			
		||||
    if (!janet_vm.fiber)
 | 
			
		||||
@@ -1292,9 +1298,17 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
 | 
			
		||||
    if (janet_vm.stackn >= JANET_RECURSION_GUARD)
 | 
			
		||||
        janet_panic("C stack recursed too deeply");
 | 
			
		||||
 | 
			
		||||
    /* Dirty stack */
 | 
			
		||||
    int32_t dirty_stack = janet_vm.fiber->stacktop - janet_vm.fiber->stackstart;
 | 
			
		||||
    if (dirty_stack) {
 | 
			
		||||
        janet_fiber_cframe(janet_vm.fiber, void_cfunction);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Tracing */
 | 
			
		||||
    if (fun->gc.flags & JANET_FUNCFLAG_TRACE) {
 | 
			
		||||
        janet_vm.stackn++;
 | 
			
		||||
        vm_do_trace(fun, argc, argv);
 | 
			
		||||
        janet_vm.stackn--;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Push frame */
 | 
			
		||||
@@ -1322,6 +1336,10 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
 | 
			
		||||
    /* Teardown */
 | 
			
		||||
    janet_vm.stackn = oldn;
 | 
			
		||||
    janet_gcunlock(handle);
 | 
			
		||||
    if (dirty_stack) {
 | 
			
		||||
        janet_fiber_popframe(janet_vm.fiber);
 | 
			
		||||
        janet_vm.fiber->stacktop += dirty_stack;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (signal != JANET_SIGNAL_OK) {
 | 
			
		||||
        janet_panicv(*janet_vm.return_reg);
 | 
			
		||||
@@ -1330,7 +1348,7 @@ Janet janet_call(JanetFunction *fun, int32_t argc, const Janet *argv) {
 | 
			
		||||
    return *janet_vm.return_reg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out) {
 | 
			
		||||
static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out, int is_cancel) {
 | 
			
		||||
    /* Check conditions */
 | 
			
		||||
    JanetFiberStatus old_status = janet_fiber_status(fiber);
 | 
			
		||||
    if (janet_vm.stackn >= JANET_RECURSION_GUARD) {
 | 
			
		||||
@@ -1338,6 +1356,20 @@ static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out) {
 | 
			
		||||
        *out = janet_cstringv("C stack recursed too deeply");
 | 
			
		||||
        return JANET_SIGNAL_ERROR;
 | 
			
		||||
    }
 | 
			
		||||
    /* If a "task" fiber is trying to be used as a normal fiber, detect that. See bug #920.
 | 
			
		||||
     * Fibers must be marked as root fibers manually, or by the ev scheduler. */
 | 
			
		||||
    if (janet_vm.fiber != NULL && (fiber->gc.flags & JANET_FIBER_FLAG_ROOT)) {
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
        *out = janet_cstringv(is_cancel
 | 
			
		||||
                              ? "cannot cancel root fiber, use ev/cancel"
 | 
			
		||||
                              : "cannot resume root fiber, use ev/go");
 | 
			
		||||
#else
 | 
			
		||||
        *out = janet_cstringv(is_cancel
 | 
			
		||||
                              ? "cannot cancel root fiber"
 | 
			
		||||
                              : "cannot resume root fiber");
 | 
			
		||||
#endif
 | 
			
		||||
        return JANET_SIGNAL_ERROR;
 | 
			
		||||
    }
 | 
			
		||||
    if (old_status == JANET_STATUS_ALIVE ||
 | 
			
		||||
            old_status == JANET_STATUS_DEAD ||
 | 
			
		||||
            (old_status >= JANET_STATUS_USER0 && old_status <= JANET_STATUS_USER4) ||
 | 
			
		||||
@@ -1452,14 +1484,14 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
 | 
			
		||||
/* Enter the main vm loop */
 | 
			
		||||
JanetSignal janet_continue(JanetFiber *fiber, Janet in, Janet *out) {
 | 
			
		||||
    /* Check conditions */
 | 
			
		||||
    JanetSignal tmp_signal = janet_check_can_resume(fiber, out);
 | 
			
		||||
    JanetSignal tmp_signal = janet_check_can_resume(fiber, out, 0);
 | 
			
		||||
    if (tmp_signal) return tmp_signal;
 | 
			
		||||
    return janet_continue_no_check(fiber, in, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Enter the main vm loop but immediately raise a signal */
 | 
			
		||||
JanetSignal janet_continue_signal(JanetFiber *fiber, Janet in, Janet *out, JanetSignal sig) {
 | 
			
		||||
    JanetSignal tmp_signal = janet_check_can_resume(fiber, out);
 | 
			
		||||
    JanetSignal tmp_signal = janet_check_can_resume(fiber, out, sig != JANET_SIGNAL_OK);
 | 
			
		||||
    if (tmp_signal) return tmp_signal;
 | 
			
		||||
    if (sig != JANET_SIGNAL_OK) {
 | 
			
		||||
        JanetFiber *child = fiber;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -57,8 +57,8 @@ extern "C" {
 | 
			
		||||
#define JANET_BSD 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Check for Mac */
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
/* Check for macOS or OS X */
 | 
			
		||||
#if defined(__APPLE__) && defined(__MACH__)
 | 
			
		||||
#define JANET_APPLE 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
@@ -163,6 +163,14 @@ extern "C" {
 | 
			
		||||
#define JANET_DYNAMIC_MODULES
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable the FFI library. Currently, FFI only enabled on
 | 
			
		||||
 * x86-64 operating systems. */
 | 
			
		||||
#ifndef JANET_NO_FFI
 | 
			
		||||
#if !defined(__EMSCRIPTEN__) && (defined(__x86_64__) || defined(_M_X64))
 | 
			
		||||
#define JANET_FFI
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Enable or disable the assembler. Enabled by default. */
 | 
			
		||||
#ifndef JANET_NO_ASSEMBLER
 | 
			
		||||
#define JANET_ASSEMBLER
 | 
			
		||||
@@ -228,7 +236,7 @@ extern "C" {
 | 
			
		||||
/* Maximum depth to follow table prototypes before giving up and returning nil. */
 | 
			
		||||
#define JANET_MAX_PROTO_DEPTH 200
 | 
			
		||||
 | 
			
		||||
/* Maximum depth to follow table prototypes before giving up and returning nil. */
 | 
			
		||||
/* Prevent macros to expand too deeply and error out. */
 | 
			
		||||
#define JANET_MAX_MACRO_EXPAND 200
 | 
			
		||||
 | 
			
		||||
/* Define default max stack size for stacks before raising a stack overflow error.
 | 
			
		||||
@@ -299,10 +307,10 @@ typedef struct {
 | 
			
		||||
    JANET_CURRENT_CONFIG_BITS })
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* What to do when out of memory */
 | 
			
		||||
#ifndef JANET_OUT_OF_MEMORY
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
 | 
			
		||||
/* Some extra includes if EV is enabled */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
typedef struct JanetOSMutex JanetOSMutex;
 | 
			
		||||
typedef struct JanetOSRWLock JanetOSRWLock;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/***** END SECTION CONFIG *****/
 | 
			
		||||
@@ -322,23 +330,10 @@ typedef struct {
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
/* Some extra includes if EV is enabled */
 | 
			
		||||
#ifdef JANET_EV
 | 
			
		||||
#ifdef JANET_WINDOWS
 | 
			
		||||
typedef struct JanetDudCriticalSection {
 | 
			
		||||
    /* Avoid including windows.h here - instead, create a structure of the same size */
 | 
			
		||||
    /* Needs to be same size as crtical section see WinNT.h for CRITCIAL_SECTION definition */
 | 
			
		||||
    void *debug_info;
 | 
			
		||||
    long lock_count;
 | 
			
		||||
    long recursion_count;
 | 
			
		||||
    void *owning_thread;
 | 
			
		||||
    void *lock_semaphore;
 | 
			
		||||
    unsigned long spin_count;
 | 
			
		||||
} JanetOSMutex;
 | 
			
		||||
#else
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
typedef pthread_mutex_t JanetOSMutex;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* What to do when out of memory */
 | 
			
		||||
#ifndef JANET_OUT_OF_MEMORY
 | 
			
		||||
#define JANET_OUT_OF_MEMORY do { fprintf(stderr, "janet out of memory\n"); exit(1); } while (0)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_BSD
 | 
			
		||||
@@ -849,6 +844,7 @@ JANET_API Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer);
 | 
			
		||||
 | 
			
		||||
JANET_API int janet_checkint(Janet x);
 | 
			
		||||
JANET_API int janet_checkint64(Janet x);
 | 
			
		||||
JANET_API int janet_checkuint64(Janet x);
 | 
			
		||||
JANET_API int janet_checksize(Janet x);
 | 
			
		||||
JANET_API JanetAbstract janet_checkabstract(Janet x, const JanetAbstractType *at);
 | 
			
		||||
#define janet_checkintrange(x) ((x) >= INT32_MIN && (x) <= INT32_MAX && (x) == (int32_t)(x))
 | 
			
		||||
@@ -1180,17 +1176,6 @@ typedef struct {
 | 
			
		||||
    Janet payload;
 | 
			
		||||
} JanetTryState;
 | 
			
		||||
 | 
			
		||||
/* Thread types */
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
typedef struct JanetThread JanetThread;
 | 
			
		||||
typedef struct JanetMailbox JanetMailbox;
 | 
			
		||||
struct JanetThread {
 | 
			
		||||
    JanetMailbox *mailbox;
 | 
			
		||||
    JanetTable *encode;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/***** END SECTION TYPES *****/
 | 
			
		||||
 | 
			
		||||
/***** START SECTION OPCODES *****/
 | 
			
		||||
@@ -1379,11 +1364,19 @@ JANET_API void *janet_abstract_threaded(const JanetAbstractType *atype, size_t s
 | 
			
		||||
JANET_API int32_t janet_abstract_incref(void *abst);
 | 
			
		||||
JANET_API int32_t janet_abstract_decref(void *abst);
 | 
			
		||||
 | 
			
		||||
/* Expose some OS sync primitives to make portable abstract types easier to implement */
 | 
			
		||||
/* Expose some OS sync primitives */
 | 
			
		||||
JANET_API size_t janet_os_mutex_size(void);
 | 
			
		||||
JANET_API size_t janet_os_rwlock_size(void);
 | 
			
		||||
JANET_API void janet_os_mutex_init(JanetOSMutex *mutex);
 | 
			
		||||
JANET_API void janet_os_mutex_deinit(JanetOSMutex *mutex);
 | 
			
		||||
JANET_API void janet_os_mutex_lock(JanetOSMutex *mutex);
 | 
			
		||||
JANET_API void janet_os_mutex_unlock(JanetOSMutex *mutex);
 | 
			
		||||
JANET_API void janet_os_rwlock_init(JanetOSRWLock *rwlock);
 | 
			
		||||
JANET_API void janet_os_rwlock_deinit(JanetOSRWLock *rwlock);
 | 
			
		||||
JANET_API void janet_os_rwlock_rlock(JanetOSRWLock *rwlock);
 | 
			
		||||
JANET_API void janet_os_rwlock_wlock(JanetOSRWLock *rwlock);
 | 
			
		||||
JANET_API void janet_os_rwlock_runlock(JanetOSRWLock *rwlock);
 | 
			
		||||
JANET_API void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock);
 | 
			
		||||
 | 
			
		||||
/* Get last error from an IO operation */
 | 
			
		||||
JANET_API Janet janet_ev_lasterr(void);
 | 
			
		||||
@@ -1678,6 +1671,7 @@ JANET_API JanetModule janet_native(const char *name, JanetString *error);
 | 
			
		||||
 | 
			
		||||
/* Marshaling */
 | 
			
		||||
#define JANET_MARSHAL_UNSAFE 0x20000
 | 
			
		||||
#define JANET_MARSHAL_NO_CYCLES 0x40000
 | 
			
		||||
 | 
			
		||||
JANET_API void janet_marshal(
 | 
			
		||||
    JanetBuffer *buf,
 | 
			
		||||
@@ -1779,7 +1773,9 @@ typedef enum {
 | 
			
		||||
    JANET_BINDING_NONE,
 | 
			
		||||
    JANET_BINDING_DEF,
 | 
			
		||||
    JANET_BINDING_VAR,
 | 
			
		||||
    JANET_BINDING_MACRO
 | 
			
		||||
    JANET_BINDING_MACRO,
 | 
			
		||||
    JANET_BINDING_DYNAMIC_DEF,
 | 
			
		||||
    JANET_BINDING_DYNAMIC_MACRO
 | 
			
		||||
} JanetBindingType;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
@@ -1923,6 +1919,7 @@ JANET_API void *janet_getpointer(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API int32_t janet_getnat(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API int32_t janet_getinteger(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API int64_t janet_getinteger64(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API uint64_t janet_getuinteger64(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API size_t janet_getsize(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetView janet_getindexed(const Janet *argv, int32_t n);
 | 
			
		||||
JANET_API JanetByteView janet_getbytes(const Janet *argv, int32_t n);
 | 
			
		||||
@@ -1970,7 +1967,6 @@ extern JANET_API const JanetAbstractType janet_file_type;
 | 
			
		||||
#define JANET_FILE_CLOSED 32
 | 
			
		||||
#define JANET_FILE_BINARY 64
 | 
			
		||||
#define JANET_FILE_SERIALIZABLE 128
 | 
			
		||||
#define JANET_FILE_PIPED 256
 | 
			
		||||
#define JANET_FILE_NONIL 512
 | 
			
		||||
 | 
			
		||||
JANET_API Janet janet_makefile(FILE *f, int32_t flags);
 | 
			
		||||
@@ -2077,16 +2073,6 @@ JANET_API int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef JANET_THREADS
 | 
			
		||||
 | 
			
		||||
extern JANET_API const JanetAbstractType janet_thread_type;
 | 
			
		||||
 | 
			
		||||
JANET_API int janet_thread_receive(Janet *msg_out, double timeout);
 | 
			
		||||
JANET_API int janet_thread_send(JanetThread *thread, Janet msg, double timeout);
 | 
			
		||||
JANET_API JanetThread *janet_thread_current(void);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Custom allocator support */
 | 
			
		||||
JANET_API void *(janet_malloc)(size_t);
 | 
			
		||||
JANET_API void *(janet_realloc)(void *, size_t);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
* Copyright (c) 2021 Calvin Rose
 | 
			
		||||
* Copyright (c) 2022 Calvin Rose
 | 
			
		||||
*
 | 
			
		||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
* of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <janet.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
#include <windows.h>
 | 
			
		||||
@@ -75,6 +76,9 @@ static void simpleline(JanetBuffer *buffer) {
 | 
			
		||||
    int c;
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        c = fgetc(in);
 | 
			
		||||
        if (c < 0 && !feof(in) && errno == EINTR) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (feof(in) || c < 0) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -83,8 +87,30 @@ static void simpleline(JanetBuffer *buffer) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Windows */
 | 
			
		||||
#if defined(JANET_WINDOWS) || defined(JANET_SIMPLE_GETLINE)
 | 
			
		||||
/* State */
 | 
			
		||||
 | 
			
		||||
#ifndef JANET_SIMPLE_GETLINE
 | 
			
		||||
/* static state */
 | 
			
		||||
#define JANET_LINE_MAX 1024
 | 
			
		||||
#define JANET_MATCH_MAX 256
 | 
			
		||||
#define JANET_HISTORY_MAX 100
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_plen = 2;
 | 
			
		||||
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_len = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_pos = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_cols = 80;
 | 
			
		||||
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_history_count = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_historyi = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_match_count = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* Fallback */
 | 
			
		||||
#if defined(JANET_SIMPLE_GETLINE)
 | 
			
		||||
 | 
			
		||||
void janet_line_init() {
 | 
			
		||||
    ;
 | 
			
		||||
@@ -101,6 +127,80 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
 | 
			
		||||
    simpleline(buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Rich implementation */
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
/* Windows */
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <io.h>
 | 
			
		||||
 | 
			
		||||
static void setup_console_output(void) {
 | 
			
		||||
    /* Enable color console on windows 10 console and utf8 output and other processing */
 | 
			
		||||
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
 | 
			
		||||
    DWORD dwMode = 0;
 | 
			
		||||
    GetConsoleMode(hOut, &dwMode);
 | 
			
		||||
    dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
 | 
			
		||||
    SetConsoleMode(hOut, dwMode);
 | 
			
		||||
    SetConsoleOutputCP(65001);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ansi terminal raw mode */
 | 
			
		||||
static int rawmode(void) {
 | 
			
		||||
    if (gbl_israwmode) return 0;
 | 
			
		||||
    HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
 | 
			
		||||
    DWORD dwMode = 0;
 | 
			
		||||
    GetConsoleMode(hOut, &dwMode);
 | 
			
		||||
    dwMode &= ~ENABLE_LINE_INPUT;
 | 
			
		||||
    dwMode &= ~ENABLE_INSERT_MODE;
 | 
			
		||||
    dwMode &= ~ENABLE_ECHO_INPUT;
 | 
			
		||||
    dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
 | 
			
		||||
    dwMode &= ~ENABLE_PROCESSED_INPUT;
 | 
			
		||||
    if (!SetConsoleMode(hOut, dwMode)) return 1;
 | 
			
		||||
    gbl_israwmode = 1;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Disable raw mode */
 | 
			
		||||
static void norawmode(void) {
 | 
			
		||||
    if (!gbl_israwmode) return;
 | 
			
		||||
    HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
 | 
			
		||||
    DWORD dwMode = 0;
 | 
			
		||||
    GetConsoleMode(hOut, &dwMode);
 | 
			
		||||
    dwMode |= ENABLE_LINE_INPUT;
 | 
			
		||||
    dwMode |= ENABLE_INSERT_MODE;
 | 
			
		||||
    dwMode |= ENABLE_ECHO_INPUT;
 | 
			
		||||
    dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
 | 
			
		||||
    dwMode |= ENABLE_PROCESSED_INPUT;
 | 
			
		||||
    SetConsoleMode(hOut, dwMode);
 | 
			
		||||
    gbl_israwmode = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static long write_console(const char *bytes, size_t n) {
 | 
			
		||||
    DWORD nwritten = 0;
 | 
			
		||||
    BOOL result = WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), bytes, (DWORD) n, &nwritten, NULL);
 | 
			
		||||
    if (!result) return -1; /* error */
 | 
			
		||||
    return (long)nwritten;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static long read_console(char *into, size_t n) {
 | 
			
		||||
    DWORD numread;
 | 
			
		||||
    BOOL result = ReadConsole(GetStdHandle(STD_INPUT_HANDLE), into, (DWORD) n, &numread, NULL);
 | 
			
		||||
    if (!result) return -1; /* error */
 | 
			
		||||
    return (long)numread;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int check_simpleline(JanetBuffer *buffer) {
 | 
			
		||||
    if (!_isatty(_fileno(stdin)) || rawmode()) {
 | 
			
		||||
        simpleline(buffer);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Posix */
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
@@ -112,7 +212,6 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
@@ -122,24 +221,7 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
 | 
			
		||||
/* static state */
 | 
			
		||||
#define JANET_LINE_MAX 1024
 | 
			
		||||
#define JANET_MATCH_MAX 256
 | 
			
		||||
#define JANET_HISTORY_MAX 100
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_plen = 2;
 | 
			
		||||
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_len = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_pos = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_cols = 80;
 | 
			
		||||
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_history_count = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_historyi = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL struct termios gbl_termios_start;
 | 
			
		||||
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_match_count = 0;
 | 
			
		||||
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
 | 
			
		||||
 | 
			
		||||
/* Unsupported terminal list from linenoise */
 | 
			
		||||
static const char *badterms[] = {
 | 
			
		||||
@@ -149,15 +231,6 @@ static const char *badterms[] = {
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static char *sdup(const char *s) {
 | 
			
		||||
    size_t len = strlen(s) + 1;
 | 
			
		||||
    char *mem = janet_malloc(len);
 | 
			
		||||
    if (!mem) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return memcpy(mem, s, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Ansi terminal raw mode */
 | 
			
		||||
static int rawmode(void) {
 | 
			
		||||
    struct termios t;
 | 
			
		||||
@@ -183,13 +256,53 @@ static void norawmode(void) {
 | 
			
		||||
        gbl_israwmode = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int checktermsupport() {
 | 
			
		||||
    const char *t = getenv("TERM");
 | 
			
		||||
    int i;
 | 
			
		||||
    if (!t) return 1;
 | 
			
		||||
    for (i = 0; badterms[i]; i++)
 | 
			
		||||
        if (!strcmp(t, badterms[i])) return 0;
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static long write_console(char *bytes, size_t n) {
 | 
			
		||||
    return write(STDOUT_FILENO, bytes, n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static long read_console(char *into, size_t n) {
 | 
			
		||||
    return read(STDIN_FILENO, into, n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int check_simpleline(JanetBuffer *buffer) {
 | 
			
		||||
    if (!isatty(STDIN_FILENO) || !checktermsupport()) {
 | 
			
		||||
        simpleline(buffer);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    if (rawmode()) {
 | 
			
		||||
        simpleline(buffer);
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static char *sdup(const char *s) {
 | 
			
		||||
    size_t len = strlen(s) + 1;
 | 
			
		||||
    char *mem = janet_malloc(len);
 | 
			
		||||
    if (!mem) {
 | 
			
		||||
        return NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return memcpy(mem, s, len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int curpos(void) {
 | 
			
		||||
    char buf[32];
 | 
			
		||||
    int cols, rows;
 | 
			
		||||
    unsigned int i = 0;
 | 
			
		||||
    if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1;
 | 
			
		||||
    if (write_console("\x1b[6n", 4) != 4) return -1;
 | 
			
		||||
    while (i < sizeof(buf) - 1) {
 | 
			
		||||
        if (read(STDIN_FILENO, buf + i, 1) != 1) break;
 | 
			
		||||
        if (read_console(buf + i, 1) != 1) break;
 | 
			
		||||
        if (buf[i] == 'R') break;
 | 
			
		||||
        i++;
 | 
			
		||||
    }
 | 
			
		||||
@@ -200,18 +313,23 @@ static int curpos(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int getcols(void) {
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    CONSOLE_SCREEN_BUFFER_INFO csbi;
 | 
			
		||||
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
 | 
			
		||||
    return (int)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
 | 
			
		||||
#else
 | 
			
		||||
    struct winsize ws;
 | 
			
		||||
    if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
 | 
			
		||||
        int start, cols;
 | 
			
		||||
        start = curpos();
 | 
			
		||||
        if (start == -1) goto failed;
 | 
			
		||||
        if (write(STDOUT_FILENO, "\x1b[999C", 6) != 6) goto failed;
 | 
			
		||||
        if (write_console("\x1b[999C", 6) != 6) goto failed;
 | 
			
		||||
        cols = curpos();
 | 
			
		||||
        if (cols == -1) goto failed;
 | 
			
		||||
        if (cols > start) {
 | 
			
		||||
            char seq[32];
 | 
			
		||||
            snprintf(seq, 32, "\x1b[%dD", cols - start);
 | 
			
		||||
            if (write(STDOUT_FILENO, seq, strlen(seq)) == -1) {
 | 
			
		||||
            if (write_console(seq, strlen(seq)) == -1) {
 | 
			
		||||
                exit(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -221,10 +339,11 @@ static int getcols(void) {
 | 
			
		||||
    }
 | 
			
		||||
failed:
 | 
			
		||||
    return 80;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void clear(void) {
 | 
			
		||||
    if (write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7) <= 0) {
 | 
			
		||||
    if (write_console("\x1b[H\x1b[2J", 7) <= 0) {
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -256,7 +375,7 @@ static void refresh(void) {
 | 
			
		||||
    /* Move cursor to original position. */
 | 
			
		||||
    snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen));
 | 
			
		||||
    janet_buffer_push_cstring(&b, seq);
 | 
			
		||||
    if (write(STDOUT_FILENO, b.data, b.count) == -1) {
 | 
			
		||||
    if (write_console((char *) b.data, b.count) == -1) {
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    janet_buffer_deinit(&b);
 | 
			
		||||
@@ -282,7 +401,7 @@ static int insert(char c, int draw) {
 | 
			
		||||
                if (gbl_plen + gbl_len < gbl_cols) {
 | 
			
		||||
                    /* Avoid a full update of the line in the
 | 
			
		||||
                     * trivial case. */
 | 
			
		||||
                    if (write(STDOUT_FILENO, &c, 1) == -1) return -1;
 | 
			
		||||
                    if (write_console(&c, 1) == -1) return -1;
 | 
			
		||||
                } else {
 | 
			
		||||
                    refresh();
 | 
			
		||||
                }
 | 
			
		||||
@@ -309,7 +428,7 @@ static void historymove(int delta) {
 | 
			
		||||
            gbl_historyi = gbl_history_count - 1;
 | 
			
		||||
        }
 | 
			
		||||
        strncpy(gbl_buf, gbl_history[gbl_historyi], JANET_LINE_MAX - 1);
 | 
			
		||||
        gbl_pos = gbl_len = strlen(gbl_buf);
 | 
			
		||||
        gbl_pos = gbl_len = (int) strlen(gbl_buf);
 | 
			
		||||
        gbl_buf[gbl_len] = '\0';
 | 
			
		||||
 | 
			
		||||
        refresh();
 | 
			
		||||
@@ -524,6 +643,7 @@ static void check_specials(JanetByteView src) {
 | 
			
		||||
    check_cmatch(src, "unquote");
 | 
			
		||||
    check_cmatch(src, "var");
 | 
			
		||||
    check_cmatch(src, "while");
 | 
			
		||||
    check_cmatch(src, "upscope");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void resolve_format(JanetTable *entry) {
 | 
			
		||||
@@ -737,12 +857,16 @@ static int line() {
 | 
			
		||||
 | 
			
		||||
    addhistory();
 | 
			
		||||
 | 
			
		||||
    if (write(STDOUT_FILENO, gbl_prompt, gbl_plen) == -1) return -1;
 | 
			
		||||
    if (write_console((char *) gbl_prompt, gbl_plen) == -1) return -1;
 | 
			
		||||
    for (;;) {
 | 
			
		||||
        char c;
 | 
			
		||||
        char seq[3];
 | 
			
		||||
 | 
			
		||||
        if (read(STDIN_FILENO, &c, 1) <= 0) return -1;
 | 
			
		||||
        int rc;
 | 
			
		||||
        do {
 | 
			
		||||
            rc = read_console(&c, 1);
 | 
			
		||||
        } while (rc < 0 && errno == EINTR);
 | 
			
		||||
        if (rc <= 0) return -1;
 | 
			
		||||
 | 
			
		||||
        switch (c) {
 | 
			
		||||
            default:
 | 
			
		||||
@@ -757,8 +881,13 @@ static int line() {
 | 
			
		||||
                kleft();
 | 
			
		||||
                break;
 | 
			
		||||
            case 3:     /* ctrl-c */
 | 
			
		||||
                clearlines();
 | 
			
		||||
                norawmode();
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
                ExitProcess(1);
 | 
			
		||||
#else
 | 
			
		||||
                kill(getpid(), SIGINT);
 | 
			
		||||
#endif
 | 
			
		||||
            /* fallthrough */
 | 
			
		||||
            case 17:    /* ctrl-q */
 | 
			
		||||
                gbl_cancel_current_repl_form = 1;
 | 
			
		||||
@@ -819,23 +948,25 @@ static int line() {
 | 
			
		||||
            case 23: /* ctrl-w */
 | 
			
		||||
                kbackspacew();
 | 
			
		||||
                break;
 | 
			
		||||
#ifndef _WIN32
 | 
			
		||||
            case 26: /* ctrl-z */
 | 
			
		||||
                norawmode();
 | 
			
		||||
                kill(getpid(), SIGSTOP);
 | 
			
		||||
                rawmode();
 | 
			
		||||
                refresh();
 | 
			
		||||
                break;
 | 
			
		||||
#endif
 | 
			
		||||
            case 27:    /* escape sequence */
 | 
			
		||||
                /* Read the next two bytes representing the escape sequence.
 | 
			
		||||
                 * Use two calls to handle slow terminals returning the two
 | 
			
		||||
                 * chars at different times. */
 | 
			
		||||
                if (read(STDIN_FILENO, seq, 1) == -1) break;
 | 
			
		||||
                if (read_console(seq, 1) == -1) break;
 | 
			
		||||
                /* Esc[ = Control Sequence Introducer (CSI) */
 | 
			
		||||
                if (seq[0] == '[') {
 | 
			
		||||
                    if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
 | 
			
		||||
                    if (read_console(seq + 1, 1) == -1) break;
 | 
			
		||||
                    if (seq[1] >= '0' && seq[1] <= '9') {
 | 
			
		||||
                        /* Extended escape, read additional byte. */
 | 
			
		||||
                        if (read(STDIN_FILENO, seq + 2, 1) == -1) break;
 | 
			
		||||
                        if (read_console(seq + 2, 1) == -1) break;
 | 
			
		||||
                        if (seq[2] == '~') {
 | 
			
		||||
                            switch (seq[1]) {
 | 
			
		||||
                                case '1': /* Home */
 | 
			
		||||
@@ -854,7 +985,7 @@ static int line() {
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if (seq[0] == 'O') {
 | 
			
		||||
                        if (read(STDIN_FILENO, seq + 1, 1) == -1) break;
 | 
			
		||||
                        if (read_console(seq + 1, 1) == -1) break;
 | 
			
		||||
                        switch (seq[1]) {
 | 
			
		||||
                            default:
 | 
			
		||||
                                break;
 | 
			
		||||
@@ -937,28 +1068,12 @@ void janet_line_deinit() {
 | 
			
		||||
    gbl_historyi = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int checktermsupport() {
 | 
			
		||||
    const char *t = getenv("TERM");
 | 
			
		||||
    int i;
 | 
			
		||||
    if (!t) return 1;
 | 
			
		||||
    for (i = 0; badterms[i]; i++)
 | 
			
		||||
        if (!strcmp(t, badterms[i])) return 0;
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void janet_line_get(const char *p, JanetBuffer *buffer) {
 | 
			
		||||
    gbl_prompt = p;
 | 
			
		||||
    buffer->count = 0;
 | 
			
		||||
    gbl_historyi = 0;
 | 
			
		||||
    if (check_simpleline(buffer)) return;
 | 
			
		||||
    FILE *out = janet_dynfile("err", stderr);
 | 
			
		||||
    if (!isatty(STDIN_FILENO) || !checktermsupport()) {
 | 
			
		||||
        simpleline(buffer);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (rawmode()) {
 | 
			
		||||
        simpleline(buffer);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (line()) {
 | 
			
		||||
        norawmode();
 | 
			
		||||
        fputc('\n', out);
 | 
			
		||||
@@ -974,6 +1089,13 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
 | 
			
		||||
    replacehistory();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void clear_at_exit(void) {
 | 
			
		||||
    if (!gbl_israwmode) {
 | 
			
		||||
        clearlines();
 | 
			
		||||
        norawmode();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -986,18 +1108,11 @@ int main(int argc, char **argv) {
 | 
			
		||||
    JanetTable *env;
 | 
			
		||||
 | 
			
		||||
#ifdef _WIN32
 | 
			
		||||
    /* Enable color console on windows 10 console and utf8 output. */
 | 
			
		||||
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
 | 
			
		||||
    DWORD dwMode = 0;
 | 
			
		||||
    GetConsoleMode(hOut, &dwMode);
 | 
			
		||||
    dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
 | 
			
		||||
    SetConsoleMode(hOut, dwMode);
 | 
			
		||||
    SetConsoleOutputCP(65001);
 | 
			
		||||
    setup_console_output();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(JANET_WINDOWS) && !defined(JANET_SIMPLE_GETLINE)
 | 
			
		||||
    /* Try and not leave the terminal in a bad state */
 | 
			
		||||
    atexit(norawmode);
 | 
			
		||||
#if !defined(JANET_SIMPLE_GETLINE)
 | 
			
		||||
    atexit(clear_at_exit);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(JANET_PRF)
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
  (def str (string e))
 | 
			
		||||
  (if x
 | 
			
		||||
    (when is-verbose (eprintf "\e[32m✔\e[0m %s: %v" (describe e) x))
 | 
			
		||||
    (eprintf "\n\e[31m✘\e[0m %s: %v" (describe e) x))
 | 
			
		||||
    (eprintf "\e[31m✘\e[0m %s: %v" (describe e) x))
 | 
			
		||||
  x)
 | 
			
		||||
 | 
			
		||||
(defmacro assert-error
 | 
			
		||||
 
 | 
			
		||||
@@ -295,6 +295,21 @@
 | 
			
		||||
    (++ i))
 | 
			
		||||
  (assert (= i 6) "when macro"))
 | 
			
		||||
 | 
			
		||||
# Dynamic defs
 | 
			
		||||
 | 
			
		||||
(def staticdef1 0)
 | 
			
		||||
(defn staticdef1-inc [] (+ 1 staticdef1))
 | 
			
		||||
(assert (= 1 (staticdef1-inc)) "before redefinition without :redef")
 | 
			
		||||
(def staticdef1 1)
 | 
			
		||||
(assert (= 1 (staticdef1-inc)) "after redefinition without :redef")
 | 
			
		||||
(setdyn :redef true)
 | 
			
		||||
(def dynamicdef2 0)
 | 
			
		||||
(defn dynamicdef2-inc [] (+ 1 dynamicdef2))
 | 
			
		||||
(assert (= 1 (dynamicdef2-inc)) "before redefinition with dyn :redef")
 | 
			
		||||
(def dynamicdef2 1)
 | 
			
		||||
(assert (= 2 (dynamicdef2-inc)) "after redefinition with dyn :redef")
 | 
			
		||||
(setdyn :redef nil)
 | 
			
		||||
 | 
			
		||||
# Denormal tables and structs
 | 
			
		||||
 | 
			
		||||
(assert (= (length {1 2 nil 3}) 1) "nil key struct literal")
 | 
			
		||||
@@ -396,7 +411,7 @@
 | 
			
		||||
      compare-poly-tests
 | 
			
		||||
      [[(int/s64 3) (int/u64 3) 0]
 | 
			
		||||
       [(int/s64 -3) (int/u64 3) -1]
 | 
			
		||||
       [(int/s64 3) (int/u64 2) 1] 
 | 
			
		||||
       [(int/s64 3) (int/u64 2) 1]
 | 
			
		||||
       [(int/s64 3) 3 0] [(int/s64 3) 4 -1] [(int/s64 3) -9 1]
 | 
			
		||||
       [(int/u64 3) 3 0] [(int/u64 3) 4 -1] [(int/u64 3) -9 1]
 | 
			
		||||
       [3 (int/s64 3) 0] [3 (int/s64 4) -1] [3 (int/s64 -5) 1]
 | 
			
		||||
@@ -406,7 +421,7 @@
 | 
			
		||||
       [(int/u64 MAX_INT_IN_DBL_STRING) (scan-number MAX_INT_IN_DBL_STRING) 0]
 | 
			
		||||
       [(+ 1 (int/u64 MAX_INT_IN_DBL_STRING)) (scan-number MAX_INT_IN_DBL_STRING) 1]
 | 
			
		||||
       [(int/s64 0) INF -1] [(int/u64 0) INF -1]
 | 
			
		||||
       [MINUS_INF (int/u64 0) -1] [MINUS_INF (int/s64 0) -1] 
 | 
			
		||||
       [MINUS_INF (int/u64 0) -1] [MINUS_INF (int/s64 0) -1]
 | 
			
		||||
       [(int/s64 1) NAN 0] [NAN (int/u64 1) 0]]]
 | 
			
		||||
  (each [x y c] compare-poly-tests
 | 
			
		||||
    (assert (= c (compare x y)) (string/format "compare polymorphic %q %q %d" x y c))))
 | 
			
		||||
 
 | 
			
		||||
@@ -137,6 +137,39 @@
 | 
			
		||||
  (assert (= a 1) "dictionary destructuring 3")
 | 
			
		||||
  (assert (= b 2) "dictionary destructuring 4")
 | 
			
		||||
  (assert (= c 4) "dictionary destructuring 5 - expression as key"))
 | 
			
		||||
(let [test-tuple [:a :b 1 2]]
 | 
			
		||||
  (def [a b one two] test-tuple)
 | 
			
		||||
  (assert (= a :a) "tuple destructuring 1")
 | 
			
		||||
  (assert (= b :b) "tuple destructuring 2")
 | 
			
		||||
  (assert (= two 2) "tuple destructuring 3"))
 | 
			
		||||
(let [test-tuple [:a :b 1 2]]
 | 
			
		||||
  (def [a & rest] test-tuple)
 | 
			
		||||
  (assert (= a :a) "tuple destructuring 4 - rest")
 | 
			
		||||
  (assert (= rest [:b 1 2]) "tuple destructuring 5 - rest"))
 | 
			
		||||
(do
 | 
			
		||||
  (def [a b & rest] [:a :b nil :d])
 | 
			
		||||
  (assert (= a :a) "tuple destructuring 6 - rest")
 | 
			
		||||
  (assert (= b :b) "tuple destructuring 7 - rest")
 | 
			
		||||
  (assert (= rest [nil :d]) "tuple destructuring 8 - rest"))
 | 
			
		||||
(do
 | 
			
		||||
  (def [[a b] x & rest] [[1 2] :a :c :b :a])
 | 
			
		||||
  (assert (= a 1) "tuple destructuring 9 - rest")
 | 
			
		||||
  (assert (= b 2) "tuple destructuring 10 - rest")
 | 
			
		||||
  (assert (= x :a) "tuple destructuring 11 - rest")
 | 
			
		||||
  (assert (= rest [:c :b :a]) "tuple destructuring 12 - rest"))
 | 
			
		||||
(do
 | 
			
		||||
  (def [a b & rest] [:a :b])
 | 
			
		||||
  (assert (= a :a) "tuple destructuring 13 - rest")
 | 
			
		||||
  (assert (= b :b) "tuple destructuring 14 - rest")
 | 
			
		||||
  (assert (= rest []) "tuple destructuring 15 - rest"))
 | 
			
		||||
 | 
			
		||||
(do
 | 
			
		||||
  (def [[a b & r1] c & r2] [[:a :b 1 2] :c 3 4])
 | 
			
		||||
  (assert (= a :a) "tuple destructuring 16 - rest")
 | 
			
		||||
  (assert (= b :b) "tuple destructuring 17 - rest")
 | 
			
		||||
  (assert (= c :c) "tuple destructuring 18 - rest")
 | 
			
		||||
  (assert (= r1 [1 2]) "tuple destructuring 19 - rest")
 | 
			
		||||
  (assert (= r2 [3 4]) "tuple destructuring 20 - rest"))
 | 
			
		||||
 | 
			
		||||
# Marshal
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,25 @@
 | 
			
		||||
   (def c (u64 "32rvv_vv_vv_vv"))
 | 
			
		||||
   (def d (u64 "123456789"))))
 | 
			
		||||
 | 
			
		||||
# Conversion back to an int32
 | 
			
		||||
(assert (= (int/to-number (u64 0xFaFa)) 0xFaFa))
 | 
			
		||||
(assert (= (int/to-number (i64 0xFaFa)) 0xFaFa))
 | 
			
		||||
(assert (= (int/to-number (u64 9007199254740991)) 9007199254740991))
 | 
			
		||||
(assert (= (int/to-number (i64 9007199254740991)) 9007199254740991))
 | 
			
		||||
(assert (= (int/to-number (i64 -9007199254740991)) -9007199254740991))
 | 
			
		||||
 | 
			
		||||
(assert-error
 | 
			
		||||
  "u64 out of bounds for safe integer"
 | 
			
		||||
  (int/to-number (u64 "9007199254740993"))
 | 
			
		||||
 | 
			
		||||
(assert-error
 | 
			
		||||
  "s64 out of bounds for safe integer"
 | 
			
		||||
  (int/to-number (i64 "-9007199254740993"))))
 | 
			
		||||
 | 
			
		||||
(assert-error
 | 
			
		||||
  "int/to-number fails on non-abstract types"
 | 
			
		||||
  (int/to-number 1))
 | 
			
		||||
 | 
			
		||||
(assert-no-error
 | 
			
		||||
 "create some int64 bigints"
 | 
			
		||||
 (do
 | 
			
		||||
@@ -72,6 +91,39 @@
 | 
			
		||||
 "trap INT64_MIN / -1"
 | 
			
		||||
 (:/ (int/s64 "-0x8000_0000_0000_0000") -1))
 | 
			
		||||
 | 
			
		||||
# int/s64 and int/u64 serialization
 | 
			
		||||
(assert (deep= (int/to-bytes (u64 0)) @"\x00\x00\x00\x00\x00\x00\x00\x00"))
 | 
			
		||||
 | 
			
		||||
(assert (deep= (int/to-bytes (i64 1) :le) @"\x01\x00\x00\x00\x00\x00\x00\x00"))
 | 
			
		||||
(assert (deep= (int/to-bytes (i64 1) :be) @"\x00\x00\x00\x00\x00\x00\x00\x01"))
 | 
			
		||||
(assert (deep= (int/to-bytes (i64 -1)) @"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"))
 | 
			
		||||
(assert (deep= (int/to-bytes (i64 -5) :be) @"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFB"))
 | 
			
		||||
 | 
			
		||||
(assert (deep= (int/to-bytes (u64 1) :le) @"\x01\x00\x00\x00\x00\x00\x00\x00"))
 | 
			
		||||
(assert (deep= (int/to-bytes (u64 1) :be) @"\x00\x00\x00\x00\x00\x00\x00\x01"))
 | 
			
		||||
(assert (deep= (int/to-bytes (u64 300) :be) @"\x00\x00\x00\x00\x00\x00\x01\x2C"))
 | 
			
		||||
 | 
			
		||||
# int/s64 int/u64 to existing buffer
 | 
			
		||||
(let [buf1 @""
 | 
			
		||||
      buf2 @"abcd"]
 | 
			
		||||
  (assert (deep= (int/to-bytes (i64 1) :le buf1) @"\x01\x00\x00\x00\x00\x00\x00\x00"))
 | 
			
		||||
  (assert (deep= buf1 @"\x01\x00\x00\x00\x00\x00\x00\x00"))
 | 
			
		||||
  (assert (deep= (int/to-bytes (u64 300) :be buf2) @"abcd\x00\x00\x00\x00\x00\x00\x01\x2C")))
 | 
			
		||||
 | 
			
		||||
# int/s64 and int/u64 paramater type checking
 | 
			
		||||
(assert-error
 | 
			
		||||
 "bad value passed to int/to-bytes"
 | 
			
		||||
 (int/to-bytes 1))
 | 
			
		||||
 | 
			
		||||
(assert-error
 | 
			
		||||
  "invalid endianness passed to int/to-bytes"
 | 
			
		||||
   (int/to-bytes (u64 0) :little))
 | 
			
		||||
 | 
			
		||||
(assert-error
 | 
			
		||||
  "invalid buffer passed to int/to-bytes"
 | 
			
		||||
   (int/to-bytes (u64 0) :little :buffer))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Dynamic bindings
 | 
			
		||||
(setdyn :a 10)
 | 
			
		||||
(assert (= 40 (with-dyns [:a 25 :b 15] (+ (dyn :a) (dyn :b)))) "dyn usage 1")
 | 
			
		||||
 
 | 
			
		||||
@@ -106,6 +106,10 @@
 | 
			
		||||
(assert (= nil (match {:a :hi} {:a a :b b} a)) "match 3")
 | 
			
		||||
(assert (= nil (match [1 2] [a b c] a)) "match 4")
 | 
			
		||||
(assert (= 2 (match [1 2] [a b] b)) "match 5")
 | 
			
		||||
(assert (= [2 :a :b] (match [1 2 :a :b] [o & rest] rest)) "match 6")
 | 
			
		||||
(assert (= [] (match @[:a] @[x & r] r :fallback)) "match 7")
 | 
			
		||||
(assert (= :fallback (match @[1] @[x y & r] r :fallback)) "match 8")
 | 
			
		||||
(assert (= [1 2 3 4] (match @[1 2 3 4] @[x y z & r] [x y z ;r] :fallback)) "match 9")
 | 
			
		||||
 | 
			
		||||
# And/or checks
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -115,8 +115,21 @@
 | 
			
		||||
   # Cast to string to enable comparison
 | 
			
		||||
   (assert (= "123\n456\n" (string (slurp "unique.txt"))) "File writing 4.2")
 | 
			
		||||
   (os/rm "unique.txt"))
 | 
			
		||||
     
 | 
			
		||||
# ev/gather
 | 
			
		||||
 | 
			
		||||
# Test that the stream created by os/open can be read from
 | 
			
		||||
(comment
 | 
			
		||||
  (assert-no-error "File reading 1.1"
 | 
			
		||||
    (def outstream (os/open "unique.txt" :wct))
 | 
			
		||||
    (defer (:close outstream)
 | 
			
		||||
      (:write outstream "123\n")
 | 
			
		||||
      (:write outstream "456\n"))
 | 
			
		||||
 | 
			
		||||
    (def outstream (os/open "unique.txt" :r))
 | 
			
		||||
    (defer (:close outstream)
 | 
			
		||||
      (assert (= "123\n456\n" (string (:read outstream :all))) "File reading 1.2"))
 | 
			
		||||
    (os/rm "unique.txt")))
 | 
			
		||||
 | 
			
		||||
  # ev/gather
 | 
			
		||||
 | 
			
		||||
(assert (deep= @[1 2 3] (ev/gather 1 2 3)) "ev/gather 1")
 | 
			
		||||
(assert (deep= @[] (ev/gather)) "ev/gather 2")
 | 
			
		||||
@@ -151,36 +164,26 @@
 | 
			
		||||
 | 
			
		||||
  (:close s))
 | 
			
		||||
 | 
			
		||||
(defn check-matching-names [stream]
 | 
			
		||||
  (def ln (net/localname stream))
 | 
			
		||||
  (def pn (net/peername stream))
 | 
			
		||||
  (def [my-ip my-port] ln)
 | 
			
		||||
  (def [remote-ip remote-port] pn)
 | 
			
		||||
  (def msg (string my-ip " " my-port " " remote-ip " " remote-port))
 | 
			
		||||
  (def buf @"")
 | 
			
		||||
  (ev/gather
 | 
			
		||||
    (net/write stream msg)
 | 
			
		||||
    (net/read stream 1024 buf))
 | 
			
		||||
  (def comparison (string/split " " buf))
 | 
			
		||||
  (assert (and (= my-ip (get comparison 2))
 | 
			
		||||
               (= (string my-port) (get comparison 3))
 | 
			
		||||
               (= remote-ip (get comparison 0))
 | 
			
		||||
               (= (string remote-port) (get comparison 1)))
 | 
			
		||||
          (string/format "localname should match peername: msg=%j, buf=%j" msg buf)))
 | 
			
		||||
 | 
			
		||||
# Test on both server and client
 | 
			
		||||
(defn names-handler
 | 
			
		||||
  [stream]
 | 
			
		||||
  (defer (:close stream)
 | 
			
		||||
    (check-matching-names stream)))
 | 
			
		||||
    # prevent immediate close
 | 
			
		||||
    (ev/read stream 1)
 | 
			
		||||
    (def [host port] (net/localname stream))
 | 
			
		||||
    (assert (= host "127.0.0.1") "localname host server")
 | 
			
		||||
    (assert (= port 8000) "localname port server")))
 | 
			
		||||
 | 
			
		||||
# Test localname and peername
 | 
			
		||||
(repeat 20
 | 
			
		||||
(repeat 10
 | 
			
		||||
  (with [s (net/server "127.0.0.1" "8000" names-handler)]
 | 
			
		||||
    (defn test-names []
 | 
			
		||||
    (repeat 10
 | 
			
		||||
      (with [conn (net/connect "127.0.0.1" "8000")]
 | 
			
		||||
        (check-matching-names conn)))
 | 
			
		||||
    (repeat 20 (test-names)))
 | 
			
		||||
        (def [host port] (net/peername conn))
 | 
			
		||||
        (assert (= host "127.0.0.1") "peername host client ")
 | 
			
		||||
        (assert (= port 8000) "peername port client")
 | 
			
		||||
        # let server close
 | 
			
		||||
        (ev/write conn " "))))
 | 
			
		||||
  (gccollect))
 | 
			
		||||
 | 
			
		||||
# Create pipe
 | 
			
		||||
@@ -253,4 +256,13 @@
 | 
			
		||||
(ev/rselect c2)
 | 
			
		||||
(assert (= (slice arr) (slice (range 100))) "ev/chan-close 3")
 | 
			
		||||
 | 
			
		||||
# threaded channels
 | 
			
		||||
 | 
			
		||||
(def ch (ev/thread-chan 2))
 | 
			
		||||
(def att (ev/thread-chan 109))
 | 
			
		||||
(assert att "`att` was nil after creation")
 | 
			
		||||
(ev/give ch att)
 | 
			
		||||
(ev/do-thread
 | 
			
		||||
  (assert (ev/take ch) "channel packing bug for threaded abstracts on threaded channels."))
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
# Copyright (c) 2021 Calvin Rose & contributors
 | 
			
		||||
# Copyright (c) 2022 Calvin Rose & contributors
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to
 | 
			
		||||
@@ -21,18 +21,86 @@
 | 
			
		||||
(import ./helper :prefix "" :exit true)
 | 
			
		||||
(start-suite 11)
 | 
			
		||||
 | 
			
		||||
(assert (< 11899423.08 (math/gamma 11.5) 11899423.085)
 | 
			
		||||
        "math/gamma")
 | 
			
		||||
# math gamma
 | 
			
		||||
 | 
			
		||||
(assert (< 2605.1158 (math/log-gamma 500) 2605.1159)
 | 
			
		||||
        "math/log-gamma")
 | 
			
		||||
(assert (< 11899423.08 (math/gamma 11.5) 11899423.085) "math/gamma")
 | 
			
		||||
(assert (< 2605.1158 (math/log-gamma 500) 2605.1159) "math/log-gamma")
 | 
			
		||||
 | 
			
		||||
(def ch (ev/thread-chan 2))
 | 
			
		||||
(def att (ev/thread-chan 109))
 | 
			
		||||
(assert att "`att` was nil after creation")
 | 
			
		||||
(ev/give ch att)
 | 
			
		||||
(ev/do-thread
 | 
			
		||||
  (assert (ev/take ch) "channel packing bug for threaded abstracts on threaded channels."))
 | 
			
		||||
# missing symbols
 | 
			
		||||
 | 
			
		||||
(defn lookup-symbol [sym] (defglobal sym 10) (dyn sym))
 | 
			
		||||
 | 
			
		||||
(setdyn :missing-symbol lookup-symbol)
 | 
			
		||||
 | 
			
		||||
(assert (= (eval-string "(+ a 5)") 15) "lookup missing symbol")
 | 
			
		||||
 | 
			
		||||
(setdyn :missing-symbol nil)
 | 
			
		||||
(setdyn 'a nil)
 | 
			
		||||
 | 
			
		||||
(assert-error "compile error" (eval-string "(+ a 5)"))
 | 
			
		||||
 | 
			
		||||
# 919
 | 
			
		||||
(defn test
 | 
			
		||||
  []
 | 
			
		||||
  (var x 1)
 | 
			
		||||
  (set x ~(,x ()))
 | 
			
		||||
  x)
 | 
			
		||||
 | 
			
		||||
(assert (= (test) '(1 ())) "issue #919")
 | 
			
		||||
 | 
			
		||||
(assert (= (hash 0) (hash (* -1 0))) "hash -0 same as hash 0")
 | 
			
		||||
 | 
			
		||||
# os/execute regressions
 | 
			
		||||
(for i 0 10
 | 
			
		||||
  (assert (= i (os/execute [(dyn :executable) "-e" (string/format "(os/exit %d)" i)] :p)) (string "os/execute " i)))
 | 
			
		||||
 | 
			
		||||
# to/thru bug
 | 
			
		||||
(def pattern
 | 
			
		||||
  (peg/compile
 | 
			
		||||
    '{:dd (sequence :d :d)
 | 
			
		||||
      :sep (set "/-")
 | 
			
		||||
      :date (sequence :dd :sep :dd)
 | 
			
		||||
      :wsep (some (set " \t"))
 | 
			
		||||
      :entry (group (sequence (capture :date) :wsep (capture :date)))
 | 
			
		||||
      :main (some (thru :entry))}))
 | 
			
		||||
 | 
			
		||||
(def alt-pattern
 | 
			
		||||
  (peg/compile
 | 
			
		||||
    '{:dd (sequence :d :d)
 | 
			
		||||
      :sep (set "/-")
 | 
			
		||||
      :date (sequence :dd :sep :dd)
 | 
			
		||||
      :wsep (some (set " \t"))
 | 
			
		||||
      :entry (group (sequence (capture :date) :wsep (capture :date)))
 | 
			
		||||
      :main (some (choice :entry 1))}))
 | 
			
		||||
 | 
			
		||||
(def text "1800-10-818-9-818 16/12\n17/12 19/12\n20/12 11/01")
 | 
			
		||||
(assert (deep= (peg/match pattern text) (peg/match alt-pattern text)) "to/thru bug #971")
 | 
			
		||||
 | 
			
		||||
(assert-error
 | 
			
		||||
  "table rawget regression"
 | 
			
		||||
  (table/new -1))
 | 
			
		||||
 | 
			
		||||
# Named arguments
 | 
			
		||||
(defn named-arguments
 | 
			
		||||
  [&named bob sally joe]
 | 
			
		||||
  (+ bob sally joe))
 | 
			
		||||
 | 
			
		||||
(assert (= 15 (named-arguments :bob 3 :sally 5 :joe 7)) "named arguments 1")
 | 
			
		||||
 | 
			
		||||
(defn named-opt-arguments
 | 
			
		||||
  [&opt x &named a b c]
 | 
			
		||||
  (+ x a b c))
 | 
			
		||||
 | 
			
		||||
(assert (= 10 (named-opt-arguments 1 :a 2 :b 3 :c 4)) "named arguments 2")
 | 
			
		||||
 | 
			
		||||
(let [b @""]
 | 
			
		||||
  (defn dummy [a b c]
 | 
			
		||||
    (+ a b c))
 | 
			
		||||
  (trace dummy)
 | 
			
		||||
  (defn errout [arg]
 | 
			
		||||
    (buffer/push b arg))
 | 
			
		||||
  (assert (= 6 (with-dyns [*err* errout] (dummy 1 2 3))) "trace to custom err function")
 | 
			
		||||
  (assert (deep= @"trace (dummy 1 2 3)\n" b) "trace buffer correct"))
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								test/suite0012.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								test/suite0012.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
# Copyright (c) 2022 Calvin Rose & contributors
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
(import ./helper :prefix "" :exit true)
 | 
			
		||||
(start-suite 12)
 | 
			
		||||
 | 
			
		||||
(var counter 0)
 | 
			
		||||
(def thunk (delay (++ counter)))
 | 
			
		||||
(assert (= (thunk) 1) "delay 1")
 | 
			
		||||
(assert (= counter 1) "delay 2")
 | 
			
		||||
(assert (= (thunk) 1) "delay 3")
 | 
			
		||||
(assert (= counter 1) "delay 4")
 | 
			
		||||
 | 
			
		||||
(def has-ffi (dyn 'ffi/native))
 | 
			
		||||
 | 
			
		||||
# FFI check
 | 
			
		||||
(compwhen has-ffi
 | 
			
		||||
  (ffi/context))
 | 
			
		||||
 | 
			
		||||
(compwhen has-ffi
 | 
			
		||||
  (ffi/defbind memcpy :ptr [dest :ptr src :ptr n :size]))
 | 
			
		||||
(compwhen has-ffi
 | 
			
		||||
  (def buffer1 @"aaaa")
 | 
			
		||||
  (def buffer2 @"bbbb")
 | 
			
		||||
  (memcpy buffer1 buffer2 4)
 | 
			
		||||
  (assert (= (string buffer1) "bbbb") "ffi 1 - memcpy"))
 | 
			
		||||
 | 
			
		||||
(compwhen has-ffi
 | 
			
		||||
  (assert (= 8 (ffi/size [:int :char])) "size unpacked struct 1")
 | 
			
		||||
  (assert (= 5 (ffi/size [:pack :int :char])) "size packed struct 1")
 | 
			
		||||
  (assert (= 5 (ffi/size [:int :pack-all :char])) "size packed struct 2")
 | 
			
		||||
  (assert (= 4 (ffi/align [:int :char])) "align 1")
 | 
			
		||||
  (assert (= 1 (ffi/align [:pack :int :char])) "align 2")
 | 
			
		||||
  (assert (= 1 (ffi/align [:int :char :pack-all])) "align 3")
 | 
			
		||||
  (assert (= 26 (ffi/size [:char :pack :int @[:char 21]])) "array struct size"))
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										43
									
								
								test/suite0013.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								test/suite0013.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
# Copyright (c) 2022 Calvin Rose & contributors
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to
 | 
			
		||||
# 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.
 | 
			
		||||
 | 
			
		||||
(import ./helper :prefix "" :exit true)
 | 
			
		||||
(start-suite 13)
 | 
			
		||||
 | 
			
		||||
(assert (deep= (tabseq [i :in (range 3)] i (* 3 i))
 | 
			
		||||
               @{0 0 1 3 2 6}))
 | 
			
		||||
 | 
			
		||||
(assert (deep= (tabseq [i :in (range 3)] i)
 | 
			
		||||
               @{}))
 | 
			
		||||
 | 
			
		||||
(def- sym-prefix-peg
 | 
			
		||||
  (peg/compile
 | 
			
		||||
    ~{:symchar (+ (range "\x80\xff" "AZ" "az" "09") (set "!$%&*+-./:<?=>@^_"))
 | 
			
		||||
      :anchor (drop (cmt ($) ,|(= $ 0)))
 | 
			
		||||
      :cap (* (+ (> -1 (not :symchar)) :anchor) (* ($) '(some :symchar)))
 | 
			
		||||
      :recur (+ :cap (> -1 :recur))
 | 
			
		||||
      :main (> -1 :recur)}))
 | 
			
		||||
 | 
			
		||||
(assert (deep= (peg/match sym-prefix-peg @"123" 3) @[0 "123"]) "peg lookback")
 | 
			
		||||
(assert (deep= (peg/match sym-prefix-peg @"1234" 4) @[0 "1234"]) "peg lookback 2")
 | 
			
		||||
 | 
			
		||||
(assert (deep= (peg/replace-all '(* (<- 1) 1 (backmatch)) "xxx" "aba cdc efa") @"xxx xxx efa") "peg replace-all 1")
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
							
								
								
									
										20
									
								
								test/suite0014.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								test/suite0014.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
(import ./helper :prefix "" :exit true)
 | 
			
		||||
(start-suite 14)
 | 
			
		||||
 | 
			
		||||
(assert (deep=
 | 
			
		||||
  (peg/match '(not (* (constant 7) "a")) "hello")
 | 
			
		||||
  @[]) "peg not")
 | 
			
		||||
 | 
			
		||||
(assert (deep=
 | 
			
		||||
  (peg/match '(if-not (* (constant 7) "a") "hello") "hello")
 | 
			
		||||
  @[]) "peg if-not")
 | 
			
		||||
 | 
			
		||||
(assert (deep=
 | 
			
		||||
  (peg/match '(if-not (drop (* (constant 7) "a")) "hello") "hello")
 | 
			
		||||
  @[]) "peg if-not drop")
 | 
			
		||||
 | 
			
		||||
(assert (deep=
 | 
			
		||||
  (peg/match '(if (not (* (constant 7) "a")) "hello") "hello")
 | 
			
		||||
  @[]) "peg if not")
 | 
			
		||||
 | 
			
		||||
(end-suite)
 | 
			
		||||
							
								
								
									
										24
									
								
								tools/hashbench/ints1.janet
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tools/hashbench/ints1.janet
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
(def f @{})
 | 
			
		||||
(var collisions 0)
 | 
			
		||||
(loop [x :range [0 300] y :range [0 300]]  
 | 
			
		||||
  (def key (hash (+ (* x 1000) y))) 
 | 
			
		||||
  (if (in f key) 
 | 
			
		||||
    (++ collisions)) 
 | 
			
		||||
  (put f key true))
 | 
			
		||||
(print "ints 1 collisions: " collisions)
 | 
			
		||||
 | 
			
		||||
(def f @{})
 | 
			
		||||
(var collisions 0)
 | 
			
		||||
(loop [x :range [100000 101000] y :range [100000 101000]]
 | 
			
		||||
  (def key (hash [x y]))
 | 
			
		||||
  (if (in f key) (++ collisions))
 | 
			
		||||
  (put f key true))
 | 
			
		||||
(print "int pair 1 collisions: " collisions)
 | 
			
		||||
 | 
			
		||||
(def f @{})
 | 
			
		||||
(var collisions 0)
 | 
			
		||||
(loop [x :range [10000 11000] y :range [10000 11000]]
 | 
			
		||||
  (def key (hash [x y]))
 | 
			
		||||
  (if (in f key) (++ collisions))
 | 
			
		||||
  (put f key true))
 | 
			
		||||
(print "int pair 2 collisions: " collisions)
 | 
			
		||||
@@ -159,6 +159,7 @@
 | 
			
		||||
            <Condition>ALLUSERS=1</Condition>
 | 
			
		||||
            <Environment Id="PATH_PERMACHINE" Name="PATH" Value="[BinDir]" Action="set" Permanent="no" System="yes" Part="last"/>
 | 
			
		||||
            <Environment Id="JANET_BINPATH_PERMACHINE" Name="JANET_BINPATH" Value="[BinDir]" Action="set" Permanent="no" System="yes"/>
 | 
			
		||||
            <Environment Id="JANET_MANPATH_PERMACHINE" Name="JANET_MANPATH" Value="[DocsDir]" Action="set" Permanent="no" System="yes"/>
 | 
			
		||||
            <Environment Id="JANET_PATH_PERMACHINE" Name="JANET_PATH" Value="[LibraryDir]" Action="set" Permanent="no" System="yes" />
 | 
			
		||||
            <Environment Id="JANET_HEADERPATH_PERMACHINE" Name="JANET_HEADERPATH" Value="[CDir]" Action="set" Permanent="no" System="yes"/>
 | 
			
		||||
            <Environment Id="JANET_LIBPATH_PERMACHINE" Name="JANET_LIBPATH" Value="[CDir]" Action="set" Permanent="no" System="yes"/>
 | 
			
		||||
@@ -167,6 +168,7 @@
 | 
			
		||||
            <Condition>NOT ALLUSERS=1</Condition>
 | 
			
		||||
            <Environment Id="PATH_PERUSER" Name="PATH" Value="[BinDir]" Action="set" Permanent="no" System="no" Part="last"/>
 | 
			
		||||
            <Environment Id="JANET_BINPATH_PERUSER" Name="JANET_BINPATH" Value="[BinDir]" Action="set" Permanent="no" System="no"/>
 | 
			
		||||
            <Environment Id="JANET_MANPATH_PERUSER" Name="JANET_MANPATH" Value="[DocsDir]" Action="set" Permanent="no" System="no"/>
 | 
			
		||||
            <Environment Id="JANET_PATH_PERUSER" Name="JANET_PATH" Value="[LibraryDir]" Action="set" Permanent="no" System="no" />
 | 
			
		||||
            <Environment Id="JANET_HEADERPATH_PERUSER" Name="JANET_HEADERPATH" Value="[CDir]" Action="set" Permanent="no" System="no"/>
 | 
			
		||||
            <Environment Id="JANET_LIBPATH_PERUSER" Name="JANET_LIBPATH" Value="[CDir]" Action="set" Permanent="no" System="no"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@
 | 
			
		||||
      "quote"
 | 
			
		||||
      "quasiquote"
 | 
			
		||||
      "unquote"
 | 
			
		||||
      "upscope"
 | 
			
		||||
      "splice"]
 | 
			
		||||
    (all-bindings)))
 | 
			
		||||
(def allsyms (dyn :allsyms))
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user