mirror of
https://github.com/janet-lang/janet
synced 2025-11-21 09:44:49 +00:00
Compare commits
832 Commits
op_subtrac
...
compile-op
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ead0ed5d41 | ||
|
|
73b1cf547e | ||
|
|
ed2ae562c6 | ||
|
|
dd59d84b51 | ||
|
|
06873fbf0b | ||
|
|
1ff26d702a | ||
|
|
4da568254a | ||
|
|
357f1f94ca | ||
|
|
015e49c806 | ||
|
|
6b06ab5f9c | ||
|
|
fe6c6e15a6 | ||
|
|
b4eb52ca45 | ||
|
|
aca5428846 | ||
|
|
3dab9737e2 | ||
|
|
e601e8faab | ||
|
|
07cf63622f | ||
|
|
8e7b1e9ce0 | ||
|
|
355c514f0e | ||
|
|
976329abc1 | ||
|
|
ab3e843433 | ||
|
|
148e108864 | ||
|
|
c90c737345 | ||
|
|
13b9976382 | ||
|
|
095a81286a | ||
|
|
82416e4e4e | ||
|
|
ae51434a05 | ||
|
|
bb6ac423a7 | ||
|
|
c5ba3c0513 | ||
|
|
e9c6678614 | ||
|
|
800457c1bf | ||
|
|
2a85781616 | ||
|
|
7c15e7f7dc | ||
|
|
896c28b0c8 | ||
|
|
e7bb0dd58e | ||
|
|
4e02f27eb9 | ||
|
|
fd234461d7 | ||
|
|
eabb215391 | ||
|
|
deede6bae0 | ||
|
|
697fdcff6d | ||
|
|
ad8a5cb6c7 | ||
|
|
99abada2c2 | ||
|
|
0624936711 | ||
|
|
f764788b36 | ||
|
|
4701bc6543 | ||
|
|
156fb0c999 | ||
|
|
bf34340737 | ||
|
|
20535e8626 | ||
|
|
1ead670e33 | ||
|
|
3ad86108f2 | ||
|
|
0aee7765cf | ||
|
|
4894a4673a | ||
|
|
f00d3199c3 | ||
|
|
e34a8545e6 | ||
|
|
f974c0667b | ||
|
|
ddc122958b | ||
|
|
2e363bf29c | ||
|
|
312f9faae8 | ||
|
|
8c9cd63cb1 | ||
|
|
2af3f21d69 | ||
|
|
c4e3fa03fa | ||
|
|
91b7bcad3d | ||
|
|
8d2a9c1148 | ||
|
|
f1d47bd05a | ||
|
|
58b1491592 | ||
|
|
21a6ed3bd3 | ||
|
|
e815c91e85 | ||
|
|
42bc504188 | ||
|
|
d96e584869 | ||
|
|
f4ecb5a90f | ||
|
|
f181948aa9 | ||
|
|
bbe6b90331 | ||
|
|
27f01e2664 | ||
|
|
877967966a | ||
|
|
56c5a0ca09 | ||
|
|
f3ad13c2d4 | ||
|
|
8ac4eec370 | ||
|
|
92e91259c3 | ||
|
|
e355cb07e0 | ||
|
|
5bbfcdacd5 | ||
|
|
790a4f2636 | ||
|
|
84bb84b0b7 | ||
|
|
29f2b5c345 | ||
|
|
4643c8fa35 | ||
|
|
a8e2c8e5b8 | ||
|
|
3d3e880f52 | ||
|
|
bef8ba5e06 | ||
|
|
523639362c | ||
|
|
c3317905a1 | ||
|
|
0066a5a304 | ||
|
|
4b6d5e5671 | ||
|
|
a695454dae | ||
|
|
f2eaa5dee8 | ||
|
|
3dd6e744de | ||
|
|
862b4e9688 | ||
|
|
c9305a0a42 | ||
|
|
3cbdf26aa2 | ||
|
|
b27c830d90 | ||
|
|
92a852f2df | ||
|
|
647e218bed | ||
|
|
5ebe945ffd | ||
|
|
6254fffad0 | ||
|
|
5705b2f6c7 | ||
|
|
90a33bc88a | ||
|
|
1ba077c87d | ||
|
|
34629ae314 | ||
|
|
3edc4f35b2 | ||
|
|
06d01c099f | ||
|
|
d493eaf485 | ||
|
|
332f123abe | ||
|
|
38e841fc5c | ||
|
|
e8187fdee5 | ||
|
|
2fedb67cb3 | ||
|
|
bdab93c999 | ||
|
|
a9ff8b388f | ||
|
|
b12dfd784e | ||
|
|
e2cc8f2965 | ||
|
|
17524d2ed3 | ||
|
|
d2ee4aa074 | ||
|
|
363e32d455 | ||
|
|
31920e574d | ||
|
|
cf714ed591 | ||
|
|
11e6a5a315 | ||
|
|
871f8ebf4e | ||
|
|
c677c72a73 | ||
|
|
af73e214b2 | ||
|
|
a6e0a8228c | ||
|
|
b458404b41 | ||
|
|
707463a645 | ||
|
|
eac37ab869 | ||
|
|
a24e5b1eaa | ||
|
|
09ac85b1b9 | ||
|
|
87c1eab7d4 | ||
|
|
5a29a28c11 | ||
|
|
2ed186664f | ||
|
|
73334f3485 | ||
|
|
a5b8da8d67 | ||
|
|
e8cccfced5 | ||
|
|
88984f7ffb | ||
|
|
182170b3be | ||
|
|
f92412841b | ||
|
|
18c00e89da | ||
|
|
7c38a55a9a | ||
|
|
a15916ec9c | ||
|
|
3583d4c92f | ||
|
|
a456c67a7b | ||
|
|
6e226e4073 | ||
|
|
d23e6614b1 | ||
|
|
ab6afa72fd | ||
|
|
9538b8a77c | ||
|
|
36d3804dd2 | ||
|
|
a34b8ea68f | ||
|
|
55c10f98bb | ||
|
|
4b5a2a14c0 | ||
|
|
665705d06d | ||
|
|
cc8cd4bace | ||
|
|
8f8b6ed001 | ||
|
|
aa9efee868 | ||
|
|
e0a0e2ed42 | ||
|
|
39f5c539d7 | ||
|
|
1b6437a4f8 | ||
|
|
c62c1a58f0 | ||
|
|
9a1cd6fdd9 | ||
|
|
768c9b23e1 | ||
|
|
059253fdee | ||
|
|
3441bcbd69 | ||
|
|
2e6001316a | ||
|
|
53bcc15207 | ||
|
|
dd609bb1bb | ||
|
|
5c56c7fa91 | ||
|
|
9a892363a3 | ||
|
|
5f550ea5d4 | ||
|
|
1b6cc023a5 | ||
|
|
410f8d69bc | ||
|
|
6da44bdb6a | ||
|
|
d30fd27575 | ||
|
|
1b278fc657 | ||
|
|
eecffe01a5 | ||
|
|
f63a33884f | ||
|
|
fa75a395cb | ||
|
|
1f34ec9902 | ||
|
|
f75c08a78e | ||
|
|
5e93f0e34b | ||
|
|
49f151e265 | ||
|
|
2b73a15ad8 | ||
|
|
06d581dde3 | ||
|
|
2b49903c82 | ||
|
|
a17ae977a5 | ||
|
|
8a6b44cb4e | ||
|
|
60d9f97750 | ||
|
|
f252933f62 | ||
|
|
6dbd7b476c | ||
|
|
a47eb847fb | ||
|
|
ba5990ef21 | ||
|
|
753911fe2d | ||
|
|
746ced5501 | ||
|
|
1b49934e4f | ||
|
|
682f0f584f | ||
|
|
611b2a6c3a | ||
|
|
8043caf581 | ||
|
|
b2d2690eb9 | ||
|
|
7f745a34c3 | ||
|
|
b16cf17246 | ||
|
|
67e8518ba6 | ||
|
|
e94e8dc484 | ||
|
|
1a24d4fc86 | ||
|
|
6ee05785d1 | ||
|
|
268ff666d2 | ||
|
|
91bb34c3bf | ||
|
|
17d5fb3210 | ||
|
|
687b987f7e | ||
|
|
4daecc9a41 | ||
|
|
a85eacadda | ||
|
|
ed63987fd1 | ||
|
|
ff173047f4 | ||
|
|
83e8aab289 | ||
|
|
85cb35e68f | ||
|
|
952906279c | ||
|
|
5b79b48ae0 | ||
|
|
7c44127bcb | ||
|
|
9338312103 | ||
|
|
a0eeb630e7 | ||
|
|
4396f01297 | ||
|
|
046d299d77 | ||
|
|
9fa9286fca | ||
|
|
c13ef02ea2 | ||
|
|
6535d72bd4 | ||
|
|
52cedbc4b4 | ||
|
|
a7d424bc81 | ||
|
|
d345e551f1 | ||
|
|
0fb1773c19 | ||
|
|
2bceba4a7a | ||
|
|
a6ea38a23b | ||
|
|
bc79489068 | ||
|
|
b096babcbf | ||
|
|
e3159bb0f5 | ||
|
|
5d1bd8a932 | ||
|
|
bafa6bfff0 | ||
|
|
e2eb7ab4b2 | ||
|
|
9f4497a5ae | ||
|
|
70de8bf092 | ||
|
|
e52575e23a | ||
|
|
10994cbc6a | ||
|
|
abad9d7db9 | ||
|
|
5e443cd29d | ||
|
|
7bf3a9d24c | ||
|
|
d80a7094ae | ||
|
|
ad77bc391c | ||
|
|
2b84fb14b4 | ||
|
|
07155ce657 | ||
|
|
046d28662d | ||
|
|
84dd3db620 | ||
|
|
bed80bf1d3 | ||
|
|
282f2671ea | ||
|
|
3fc2be3e6e | ||
|
|
d10c1fe759 | ||
|
|
d18472b07d | ||
|
|
43a68dcd2a | ||
|
|
3d93028088 | ||
|
|
99f0af92bd | ||
|
|
71d81b14a2 | ||
|
|
80ed6538d0 | ||
|
|
6577a18cef | ||
|
|
731592a80e | ||
|
|
3894f4021a | ||
|
|
ea332ff81e | ||
|
|
f36d544deb | ||
|
|
e96dd512f3 | ||
|
|
72c659d1ee | ||
|
|
8f879b4adc | ||
|
|
18f2847dc1 | ||
|
|
a588f1f242 | ||
|
|
ae15eadfaf | ||
|
|
89b7ff9daf | ||
|
|
26c263d6be | ||
|
|
2570e0f7a0 | ||
|
|
8084e4c728 | ||
|
|
ee90f9df62 | ||
|
|
906a982ace | ||
|
|
88e60c309c | ||
|
|
9694aee819 | ||
|
|
2697b0e425 | ||
|
|
3618b72f4d | ||
|
|
c0d7a49b19 | ||
|
|
f9a6f52d9c | ||
|
|
c02c2e3f02 | ||
|
|
1fcd47dd7b | ||
|
|
384ee4f6a9 | ||
|
|
e9deec8231 | ||
|
|
2fc77a1b63 | ||
|
|
442fe8209d | ||
|
|
968a0dc4ac | ||
|
|
40c93d0786 | ||
|
|
83b0bc688c | ||
|
|
6185b253be | ||
|
|
17da53d0d9 | ||
|
|
9ffec43d2b | ||
|
|
e4f4a42751 | ||
|
|
4f65c2707e | ||
|
|
75bdea5155 | ||
|
|
f553c5da47 | ||
|
|
5f70a85f7e | ||
|
|
c82fd106a7 | ||
|
|
0e9b866b98 | ||
|
|
67a8c6df09 | ||
|
|
86cf8127b6 | ||
|
|
828e0a07cd | ||
|
|
90018b35c0 | ||
|
|
5a199716cb | ||
|
|
43ecd4f2d8 | ||
|
|
c5a9602be9 | ||
|
|
e88aab6d68 | ||
|
|
ce528251d5 | ||
|
|
9e334da2d6 | ||
|
|
c0e508e334 | ||
|
|
b63b3bef74 | ||
|
|
05d0b5ac05 | ||
|
|
c56d6e8fc1 | ||
|
|
33d2f9a522 | ||
|
|
e53d22fad2 | ||
|
|
33f55dc32f | ||
|
|
7e6aad2221 | ||
|
|
3c0c22259c | ||
|
|
42f6af4bf1 | ||
|
|
f274b02653 | ||
|
|
70c29b4e5d | ||
|
|
84d43d1039 | ||
|
|
5c67c1165d | ||
|
|
85028967d8 | ||
|
|
6ceff6ecc9 | ||
|
|
06eec06ff0 | ||
|
|
2dcc0adc0e | ||
|
|
8ca1e44af1 | ||
|
|
2aedc6beff | ||
|
|
af2eb06298 | ||
|
|
7ff545bd2e | ||
|
|
a59b5765b6 | ||
|
|
6bd58dd4c0 | ||
|
|
e3406cd922 | ||
|
|
ab70524d85 | ||
|
|
ce36c4c0d6 | ||
|
|
2b01b780da | ||
|
|
f3048a3d6b | ||
|
|
accac6c662 | ||
|
|
631622aa48 | ||
|
|
aaeaa3a944 | ||
|
|
d1104b5a65 | ||
|
|
1f074671ce | ||
|
|
872b39cc32 | ||
|
|
9eab57d194 | ||
|
|
8edd873c3e | ||
|
|
771956b5b6 | ||
|
|
ecc4da5113 | ||
|
|
f5555d21b9 | ||
|
|
342a29c7be | ||
|
|
368b891499 | ||
|
|
f62539ad55 | ||
|
|
4835ecb950 | ||
|
|
31f0ff0d84 | ||
|
|
b7b594205c | ||
|
|
190056b863 | ||
|
|
ae6b359109 | ||
|
|
3078686f8f | ||
|
|
0f4ecd93ab | ||
|
|
4af187d0ca | ||
|
|
a5d6b22838 | ||
|
|
3510e235ee | ||
|
|
b6fb7ae69c | ||
|
|
e5765b26d4 | ||
|
|
cdb3baaca3 | ||
|
|
fda0a081f5 | ||
|
|
94b7a69741 | ||
|
|
6518257129 | ||
|
|
dc325188d0 | ||
|
|
c413bc2b4e | ||
|
|
dfdf734fc7 | ||
|
|
0b51ab157d | ||
|
|
f95de25b15 | ||
|
|
f424f2936b | ||
|
|
2d6c2ee7c0 | ||
|
|
7cd106a10c | ||
|
|
0d9e999113 | ||
|
|
75710ccabd | ||
|
|
0f60115f27 | ||
|
|
16a3c85baa | ||
|
|
92ff1d3be4 | ||
|
|
314e684097 | ||
|
|
232a8faa35 | ||
|
|
58441dc49f | ||
|
|
dbc5d688e2 | ||
|
|
c31d8b52ff | ||
|
|
f0395763b7 | ||
|
|
5b3c5a5969 | ||
|
|
af10c1d4b5 | ||
|
|
3995fa86e2 | ||
|
|
9d7a279999 | ||
|
|
e2a8951f68 | ||
|
|
3e273ce03a | ||
|
|
25b7c74089 | ||
|
|
f0f03ad519 | ||
|
|
9e47cd94bd | ||
|
|
7ea118f248 | ||
|
|
480c5b5e9d | ||
|
|
8a394f2506 | ||
|
|
e37575e763 | ||
|
|
f4fd481415 | ||
|
|
8fca6b7af4 | ||
|
|
600e822933 | ||
|
|
2028ac8a20 | ||
|
|
7bae7d9efd | ||
|
|
cb54fb02c1 | ||
|
|
7529abb542 | ||
|
|
16ac681ed9 | ||
|
|
74560ff805 | ||
|
|
fe348187cc | ||
|
|
fd5315793c | ||
|
|
87db463f4e | ||
|
|
1225cd31c8 | ||
|
|
6998865d7b | ||
|
|
b8aec50763 | ||
|
|
7efb39d608 | ||
|
|
f7c90bc1ff | ||
|
|
aee077c1bd | ||
|
|
6968275ddf | ||
|
|
074ae4fc0d | ||
|
|
6cd35ed9c8 | ||
|
|
7911e74222 | ||
|
|
2fafe2b5d1 | ||
|
|
de977819ce | ||
|
|
1844beecc3 | ||
|
|
cb529bbd63 | ||
|
|
25990867e2 | ||
|
|
4fbc71c70d | ||
|
|
eb21d4fff4 | ||
|
|
6d5fc1d743 | ||
|
|
e88042b2fa | ||
|
|
750b448f75 | ||
|
|
14d1dc8749 | ||
|
|
8e0340252b | ||
|
|
641a16c133 | ||
|
|
533d78bffe | ||
|
|
ae2c5820a1 | ||
|
|
8334504f4e | ||
|
|
2260a593bd | ||
|
|
7d8af2f99a | ||
|
|
46bdcece4d | ||
|
|
7387a1d91e | ||
|
|
ae4b8078df | ||
|
|
60e0c8ea92 | ||
|
|
7d3acc0ed6 | ||
|
|
2637b33957 | ||
|
|
58ccb66659 | ||
|
|
634429cf61 | ||
|
|
6ac65e603d | ||
|
|
03166a745a | ||
|
|
4d61ba20ce | ||
|
|
751ff677fe | ||
|
|
ace60e1898 | ||
|
|
876b7f106f | ||
|
|
809b6589a1 | ||
|
|
02f53ca014 | ||
|
|
0b03ddb21b | ||
|
|
ea5d4fd3af | ||
|
|
e6b73f8cd1 | ||
|
|
af232ef729 | ||
|
|
2e2f8abfc0 | ||
|
|
91a583db27 | ||
|
|
dc5cc630ff | ||
|
|
c1647a74c5 | ||
|
|
721f280966 | ||
|
|
258ebb9145 | ||
|
|
e914eaf055 | ||
|
|
fe54013679 | ||
|
|
fdaf2e1594 | ||
|
|
f0092ef69b | ||
|
|
a88ae7e1d9 | ||
|
|
9946f3bdf4 | ||
|
|
c747e8d16c | ||
|
|
3e402d397e | ||
|
|
2c208f5d01 | ||
|
|
0350834cd3 | ||
|
|
980981c9ee | ||
|
|
08e6051af8 | ||
|
|
3c8346f24e | ||
|
|
42bd27c24b | ||
|
|
4a0f67f3bd | ||
|
|
09b6fc4670 | ||
|
|
4d9bcd6bcc | ||
|
|
cd34b89977 | ||
|
|
3151fa3988 | ||
|
|
5e58110e19 | ||
|
|
e1cdd0f8cc | ||
|
|
1f39a0f180 | ||
|
|
19212e6f5c | ||
|
|
8875adf69e | ||
|
|
367c4b14f5 | ||
|
|
9c437796d3 | ||
|
|
60e22d9703 | ||
|
|
ee7362e847 | ||
|
|
745567a2e0 | ||
|
|
ef2dfcd7c3 | ||
|
|
f582fe1f69 | ||
|
|
3cc3312b7b | ||
|
|
f2d25a0da2 | ||
|
|
369f96b80e | ||
|
|
7c5ed04ab1 | ||
|
|
4779a445e0 | ||
|
|
f0f1b7ce9e | ||
|
|
7c9157a0ed | ||
|
|
522a6cb435 | ||
|
|
d0d551d739 | ||
|
|
71a123fef7 | ||
|
|
3f40c8d7fb | ||
|
|
983c2e5499 | ||
|
|
eebb4c3ade | ||
|
|
50425eac72 | ||
|
|
382ff77bbe | ||
|
|
bf680fb5d3 | ||
|
|
4ed7db4f91 | ||
|
|
bf19920d65 | ||
|
|
174b5f6686 | ||
|
|
4173645b81 | ||
|
|
af511f1f55 | ||
|
|
83c6080380 | ||
|
|
2f0c789ea1 | ||
|
|
a9b8f8e8a9 | ||
|
|
f92f3eb6fa | ||
|
|
89e74dca3e | ||
|
|
f2e86d2f8d | ||
|
|
623da131e5 | ||
|
|
e89ec31ae5 | ||
|
|
68a6ed208e | ||
|
|
c01b32c4f3 | ||
|
|
ee11ff9da9 | ||
|
|
ed56d5d6ff | ||
|
|
b317ab755c | ||
|
|
9819994999 | ||
|
|
e9dbaa81d2 | ||
|
|
9f9146ffae | ||
|
|
9d9732af97 | ||
|
|
ebb8fa9787 | ||
|
|
9e6abbf4d4 | ||
|
|
6032a6d658 | ||
|
|
c29ab22e6d | ||
|
|
592ac4904c | ||
|
|
03ae2ec153 | ||
|
|
3bc42d0d37 | ||
|
|
12630d3e54 | ||
|
|
c9897f99c3 | ||
|
|
e66dc14b3a | ||
|
|
7a2868c147 | ||
|
|
9e0daaee09 | ||
|
|
c293c7de93 | ||
|
|
49eb5f8563 | ||
|
|
674b375b2c | ||
|
|
7e94c091eb | ||
|
|
5885ccba61 | ||
|
|
431ecd3d1a | ||
|
|
f6df8ff935 | ||
|
|
3fd70f0951 | ||
|
|
bebb635d4f | ||
|
|
354896bc4b | ||
|
|
5ddefff27e | ||
|
|
91827eef4f | ||
|
|
9c14c09962 | ||
|
|
e85a84171f | ||
|
|
3a4f86c3d7 | ||
|
|
5e75963312 | ||
|
|
184d9289b5 | ||
|
|
b7ff9577c0 | ||
|
|
942a1aaac6 | ||
|
|
69f0fe004d | ||
|
|
2a04347a42 | ||
|
|
1394f1a5c0 | ||
|
|
cf4d19a8ea | ||
|
|
23b0fe9f8e | ||
|
|
1ba718b15e | ||
|
|
df5f79ff35 | ||
|
|
6d7e8528ea | ||
|
|
197bb73a62 | ||
|
|
f91e599451 | ||
|
|
5b9aa9237c | ||
|
|
61f38fab37 | ||
|
|
9142f38cbc | ||
|
|
e8ed961572 | ||
|
|
be11a2a1ad | ||
|
|
ea75086300 | ||
|
|
9eeefbd79a | ||
|
|
c573a98363 | ||
|
|
11d7af3f95 | ||
|
|
a10b4f61d8 | ||
|
|
a0cb7514f1 | ||
|
|
b066edc116 | ||
|
|
938f5a689e | ||
|
|
772f4c26e8 | ||
|
|
6b5d151beb | ||
|
|
a9176a77e6 | ||
|
|
16f409c6a9 | ||
|
|
9593c930de | ||
|
|
56f33f514b | ||
|
|
1ccd544b94 | ||
|
|
93c83a2ee2 | ||
|
|
f459e32ada | ||
|
|
9b640c8e9c | ||
|
|
a3228f4997 | ||
|
|
715eb69d92 | ||
|
|
dfd05ddf7e | ||
|
|
df2d5cb3d3 | ||
|
|
31be7bad8e | ||
|
|
3b189eab64 | ||
|
|
3a782d27b1 | ||
|
|
f08874e65e | ||
|
|
609b629c22 | ||
|
|
e74365fe38 | ||
|
|
46b34833c2 | ||
|
|
045c80869d | ||
|
|
2ea2e72ddd | ||
|
|
1b17e12fd6 | ||
|
|
cc5beda0d2 | ||
|
|
a363fd926d | ||
|
|
21ebede529 | ||
|
|
15d67e9191 | ||
|
|
b5996f5f02 | ||
|
|
83204dc293 | ||
|
|
e3f4142d2a | ||
|
|
f18ad36b1b | ||
|
|
6a78b6d1c6 | ||
|
|
cb25a2ecd6 | ||
|
|
741a5036e8 | ||
|
|
549ee95f3d | ||
|
|
6ae81058aa | ||
|
|
267c603824 | ||
|
|
a8f583a372 | ||
|
|
2b5d90f73a | ||
|
|
4139e426fe | ||
|
|
a775a89e01 | ||
|
|
990f6352e0 | ||
|
|
b344702304 | ||
|
|
d497612bce | ||
|
|
2a3b101bd8 | ||
|
|
63e93af421 | ||
|
|
ab055b3ebe | ||
|
|
a9a013473f | ||
|
|
87de1e5766 | ||
|
|
894aaef267 | ||
|
|
e209e54ffe | ||
|
|
7511eadaa7 | ||
|
|
6c4906605a | ||
|
|
8a9be9d837 | ||
|
|
b72098cc71 | ||
|
|
defe60e08b | ||
|
|
7f852b8af4 | ||
|
|
d71c100ca7 | ||
|
|
5442c8e86d | ||
|
|
cf4901e713 | ||
|
|
4b8c1ac2d2 | ||
|
|
555e0c0b85 | ||
|
|
dc301305de | ||
|
|
f1111c135b | ||
|
|
3905e92965 | ||
|
|
1418ada38f | ||
|
|
9256a66b76 | ||
|
|
e8c013a778 | ||
|
|
fea8242ea7 | ||
|
|
7bfb17c209 | ||
|
|
e7e4341e70 | ||
|
|
6186be4443 | ||
|
|
d07f01d7cb | ||
|
|
73291a30a0 | ||
|
|
a3b129845b | ||
|
|
0ff8f58be8 | ||
|
|
66292beec9 | ||
|
|
bf2af1051f | ||
|
|
b6e3020d4c | ||
|
|
8f516a1e28 | ||
|
|
5f2e287efd | ||
|
|
8c0d65cf9f | ||
|
|
fa609a5079 | ||
|
|
c708ff9708 | ||
|
|
2ea90334a3 | ||
|
|
eea8aa555f | ||
|
|
51a75e1872 | ||
|
|
af7ed4322e | ||
|
|
7cdd7cf6eb | ||
|
|
26aa622afc | ||
|
|
84ad161f1e | ||
|
|
6efb965dab | ||
|
|
8c90a12e0f | ||
|
|
2d54e88e74 | ||
|
|
16ea5323e0 | ||
|
|
7a23ce2367 | ||
|
|
e05bc7eb54 | ||
|
|
b3a6e25ce0 | ||
|
|
b63d41102e | ||
|
|
964295b59d | ||
|
|
d19db30f3d | ||
|
|
d12464fc0e | ||
|
|
a96971c8a7 | ||
|
|
f6f769503a | ||
|
|
82917ac6e3 | ||
|
|
a6ffafb1a2 | ||
|
|
fb8c529f2e | ||
|
|
1ee98e1e66 | ||
|
|
81f35f5dd1 | ||
|
|
1b402347cd | ||
|
|
7599656784 | ||
|
|
dccb60ba35 | ||
|
|
ae642ceca0 | ||
|
|
471b6f9966 | ||
|
|
5dd18bac2c | ||
|
|
018f4e0891 | ||
|
|
e85809a98a | ||
|
|
e6e9bd8147 | ||
|
|
221645d2ce | ||
|
|
2f4a6214a2 | ||
|
|
e00a461c26 | ||
|
|
c31314be38 | ||
|
|
ee142c4be0 | ||
|
|
aeacc0b31b | ||
|
|
7b4c3bdbcc | ||
|
|
910b9cf1fd | ||
|
|
b10aaceab0 | ||
|
|
169bd812c9 | ||
|
|
34767f1e13 | ||
|
|
4f642c0843 | ||
|
|
4e5889ed59 | ||
|
|
a1b848ad76 | ||
|
|
dbcc1fad3e | ||
|
|
db366558e7 | ||
|
|
a23c03fbd0 | ||
|
|
ff18b92eb0 | ||
|
|
7f148522ab | ||
|
|
159c612924 | ||
|
|
b95dfd4bdf | ||
|
|
e69954af2f | ||
|
|
a5ff26f602 | ||
|
|
a7536268e1 | ||
|
|
541469371a | ||
|
|
97963d1396 | ||
|
|
a13aeaf955 | ||
|
|
9cf674cdcb | ||
|
|
efbc46c69e | ||
|
|
9b9f67c371 | ||
|
|
61791e4a4c | ||
|
|
51c0cf97bc | ||
|
|
4cb1f616c5 | ||
|
|
645109048b | ||
|
|
f969fb69e1 | ||
|
|
bfb60fdb84 | ||
|
|
2f43cb843e | ||
|
|
874fd2aba7 | ||
|
|
33d1371186 | ||
|
|
d2dd241e6b | ||
|
|
4ecadfabf4 | ||
|
|
ffd79c6097 | ||
|
|
35a8d2a519 | ||
|
|
21eab7e9cc | ||
|
|
d9605c2856 | ||
|
|
70a467d469 | ||
|
|
6e8979336d | ||
|
|
c3a4fb6735 | ||
|
|
ee01045db5 | ||
|
|
b7f8224588 | ||
|
|
e5893d0692 | ||
|
|
5f5e5cf693 | ||
|
|
ca4c1e4259 | ||
|
|
91712add3d | ||
|
|
7198dcb416 | ||
|
|
08e20e912d | ||
|
|
f45571033c | ||
|
|
2ac36a0572 | ||
|
|
3df1d54847 | ||
|
|
f3969b6066 | ||
|
|
6222f35bc8 | ||
|
|
2f178963c0 | ||
|
|
15760b0950 | ||
|
|
43a6a70e1e | ||
|
|
cd36f1ef5f | ||
|
|
46bda4e6fa | ||
|
|
cdd7083c86 | ||
|
|
fdbf4f2666 | ||
|
|
8df7364319 | ||
|
|
b939671b79 | ||
|
|
63023722d1 | ||
|
|
4b8e7a416f | ||
|
|
79c12e5116 | ||
|
|
1e1e7a5cfd | ||
|
|
91e459e4a5 | ||
|
|
b6adc257f4 | ||
|
|
a2bd98390e | ||
|
|
53e16944a1 | ||
|
|
d9912f38f8 | ||
|
|
7475362c85 | ||
|
|
9238b82cde | ||
|
|
7049f658ec | ||
|
|
701913fb19 | ||
|
|
8007806c8e | ||
|
|
831f41a62b | ||
|
|
de2440d458 | ||
|
|
43ab06467f | ||
|
|
3fe4cfd14c | ||
|
|
75be5fd4c6 | ||
|
|
7c7136fd70 | ||
|
|
cfa32d58a7 | ||
|
|
0ea1da80e7 | ||
|
|
06eea74b98 | ||
|
|
c8c0e112bc | ||
|
|
7417e82c51 | ||
|
|
ecc4d80a5a | ||
|
|
3df24c52f4 | ||
|
|
8a70fb95b5 | ||
|
|
d8b45ecd61 | ||
|
|
61712bae9c | ||
|
|
4ff81a5a25 | ||
|
|
08f0e55d8f | ||
|
|
080b37cb31 | ||
|
|
bbdcd035ba | ||
|
|
f9233ef90b | ||
|
|
7cc176f0c0 | ||
|
|
cd3573a4d2 | ||
|
|
738fe24e6d | ||
|
|
c2e55b5486 | ||
|
|
989f0726e3 | ||
|
|
bdefd3ba1e | ||
|
|
4efcff33bd | ||
|
|
8183cc5a8d | ||
|
|
f3bda1536d | ||
|
|
4d7baef89e | ||
|
|
29af4a932d | ||
|
|
ef94a0f0b4 | ||
|
|
517dc208ca | ||
|
|
fd7579dd07 | ||
|
|
6b74400f2a |
@@ -1,4 +1,4 @@
|
||||
image: freebsd/12.x
|
||||
image: freebsd/14.x
|
||||
sources:
|
||||
- https://git.sr.ht/~bakpakin/janet
|
||||
packages:
|
||||
@@ -9,3 +9,4 @@ tasks:
|
||||
gmake
|
||||
gmake test
|
||||
sudo gmake install
|
||||
sudo gmake uninstall
|
||||
|
||||
@@ -19,3 +19,8 @@ tasks:
|
||||
ninja
|
||||
ninja test
|
||||
sudo ninja install
|
||||
- meson_min: |
|
||||
cd janet
|
||||
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
|
||||
cd build_meson_min
|
||||
ninja
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
image: openbsd/latest
|
||||
image: openbsd/7.6
|
||||
sources:
|
||||
- https://git.sr.ht/~bakpakin/janet
|
||||
packages:
|
||||
@@ -11,6 +11,7 @@ tasks:
|
||||
gmake test
|
||||
doas gmake install
|
||||
gmake test-install
|
||||
doas gmake uninstall
|
||||
- meson_min: |
|
||||
cd janet
|
||||
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
|
||||
@@ -29,4 +30,3 @@ tasks:
|
||||
ninja
|
||||
ninja test
|
||||
doas ninja install
|
||||
|
||||
|
||||
38
.github/cosmo/build
vendored
Normal file
38
.github/cosmo/build
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
set -eux
|
||||
|
||||
COSMO_DIR="/sc/cosmocc"
|
||||
|
||||
# build x86_64
|
||||
X86_64_CC="/sc/cosmocc/bin/x86_64-unknown-cosmo-cc"
|
||||
X86_64_AR="/sc/cosmocc/bin/x86_64-unknown-cosmo-ar"
|
||||
mkdir -p /sc/cosmocc/x86_64
|
||||
make -j CC="$X86_64_CC" AR="$X86_64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
|
||||
cp build/janet /sc/cosmocc/x86_64/janet
|
||||
make clean
|
||||
|
||||
# build aarch64
|
||||
AARCH64_CC="/sc/cosmocc/bin/aarch64-unknown-cosmo-cc"
|
||||
AARCH64_AR="/sc/cosmocc/bin/aarch64-unknown-cosmo-ar"
|
||||
mkdir -p /sc/cosmocc/aarch64
|
||||
make -j CC="$AARCH64_CC" AR="$AARCH64_AR" HAS_SHARED=0 JANET_NO_AMALG=1
|
||||
cp build/janet /sc/cosmocc/aarch64/janet
|
||||
make clean
|
||||
|
||||
# fat binary
|
||||
apefat () {
|
||||
OUTPUT="$1"
|
||||
OLDNAME_X86_64="$(basename -- "$2")"
|
||||
OLDNAME_AARCH64="$(basename -- "$3")"
|
||||
TARG_FOLD="$(dirname "$OUTPUT")"
|
||||
"$COSMO_DIR/bin/apelink" -l "$COSMO_DIR/bin/ape-x86_64.elf" \
|
||||
-l "$COSMO_DIR/bin/ape-aarch64.elf" \
|
||||
-M "$COSMO_DIR/bin/ape-m1.c" \
|
||||
-o "$OUTPUT" \
|
||||
"$2" \
|
||||
"$3"
|
||||
cp "$2" "$TARG_FOLD/$OLDNAME_X86_64.x86_64"
|
||||
cp "$3" "$TARG_FOLD/$OLDNAME_AARCH64.aarch64"
|
||||
}
|
||||
|
||||
apefat /sc/cosmocc/janet.com /sc/cosmocc/x86_64/janet /sc/cosmocc/aarch64/janet
|
||||
21
.github/cosmo/setup
vendored
Normal file
21
.github/cosmo/setup
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
sudo apt update
|
||||
sudo apt-get install -y ca-certificates libssl-dev\
|
||||
qemu qemu-utils qemu-user-static\
|
||||
texinfo groff\
|
||||
cmake ninja-build bison zip\
|
||||
pkg-config build-essential autoconf re2c
|
||||
|
||||
# download cosmocc
|
||||
cd /sc
|
||||
wget https://github.com/jart/cosmopolitan/releases/download/3.3.3/cosmocc-3.3.3.zip
|
||||
mkdir -p cosmocc
|
||||
cd cosmocc
|
||||
unzip ../cosmocc-3.3.3.zip
|
||||
|
||||
# register
|
||||
cd /sc/cosmocc
|
||||
sudo cp ./bin/ape-x86_64.elf /usr/bin/ape
|
||||
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
|
||||
7
.github/workflows/codeql.yml
vendored
7
.github/workflows/codeql.yml
vendored
@@ -27,15 +27,16 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
tools: linked
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
|
||||
58
.github/workflows/release.yml
vendored
58
.github/workflows/release.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, macos-latest ]
|
||||
os: [ ubuntu-latest, macos-13 ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
@@ -39,6 +39,35 @@ jobs:
|
||||
build/c/janet.c
|
||||
build/c/shell.c
|
||||
|
||||
release-arm:
|
||||
permissions:
|
||||
contents: write # for softprops/action-gh-release to create GitHub release
|
||||
name: Build release binaries
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ macos-latest ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Set the version
|
||||
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
|
||||
- name: Set the platform
|
||||
run: echo "platform=$(tr '[A-Z]' '[a-z]' <<< $RUNNER_OS)" >> $GITHUB_ENV
|
||||
- name: Compile the project
|
||||
run: make clean && make
|
||||
- name: Build the artifact
|
||||
run: JANET_DIST_DIR=janet-${{ env.version }}-${{ env.platform }} make build/janet-${{ env.version }}-${{ env.platform }}-aarch64.tar.gz
|
||||
- name: Draft the release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
build/*.gz
|
||||
build/janet.h
|
||||
build/c/janet.c
|
||||
build/c/shell.c
|
||||
|
||||
release-windows:
|
||||
permissions:
|
||||
contents: write # for softprops/action-gh-release to create GitHub release
|
||||
@@ -60,3 +89,30 @@ jobs:
|
||||
./dist/*.zip
|
||||
./*.zip
|
||||
./*.msi
|
||||
|
||||
release-cosmo:
|
||||
permissions:
|
||||
contents: write # for softprops/action-gh-release to create GitHub release
|
||||
name: Build release binaries for Cosmo
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: create build folder
|
||||
run: |
|
||||
sudo mkdir -p /sc
|
||||
sudo chmod -R 0777 /sc
|
||||
- name: setup Cosmopolitan Libc
|
||||
run: bash ./.github/cosmo/setup
|
||||
- name: Set the version
|
||||
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
|
||||
- name: Set the platform
|
||||
run: echo "platform=cosmo" >> $GITHUB_ENV
|
||||
- name: build Janet APE binary
|
||||
run: bash ./.github/cosmo/build
|
||||
- name: push binary to github
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
/sc/cosmocc/janet.com
|
||||
|
||||
65
.github/workflows/test.yml
vendored
65
.github/workflows/test.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, macos-latest ]
|
||||
os: [ ubuntu-latest, macos-latest, macos-13 ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
@@ -23,7 +23,10 @@ jobs:
|
||||
|
||||
test-windows:
|
||||
name: Build and test on Windows
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-latest, windows-2022 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
@@ -35,28 +38,61 @@ jobs:
|
||||
- name: Test the project
|
||||
shell: cmd
|
||||
run: build_win test
|
||||
- name: Test installer build
|
||||
shell: cmd
|
||||
run: build_win dist
|
||||
|
||||
test-windows-min:
|
||||
name: Build and test on Windows Minimal build
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ windows-2022 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Setup MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install Python Dependencies
|
||||
run: pip install meson ninja
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
meson setup build_meson_min --buildtype=release -Dsingle_threaded=true -Dnanbox=false -Ddynamic_modules=false -Ddocstrings=false -Dnet=false -Dsourcemaps=false -Dpeg=false -Dassembler=false -Dint_types=false -Dreduced_os=true -Dffi=false
|
||||
cd build_meson_min
|
||||
ninja
|
||||
|
||||
test-mingw:
|
||||
name: Build on Windows with Mingw (no test yet)
|
||||
name: Build on Windows with Mingw
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
strategy:
|
||||
matrix:
|
||||
msystem: [ UCRT64, CLANG64 ]
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Setup Mingw
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: UCRT64
|
||||
msystem: ${{ matrix.msystem }}
|
||||
update: true
|
||||
install: >-
|
||||
base-devel
|
||||
git
|
||||
gcc
|
||||
- name: Build the project
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: make -j CC=gcc
|
||||
run: make -j4 CC=gcc
|
||||
- name: Test
|
||||
shell: cmd
|
||||
run: make -j4 CC=gcc test
|
||||
|
||||
test-mingw-linux:
|
||||
name: Build and test with Mingw on Linux + Wine
|
||||
@@ -73,7 +109,7 @@ jobs:
|
||||
- name: Compile the project
|
||||
run: make clean && make CC=x86_64-w64-mingw32-gcc LD=x86_64-w64-mingw32-gcc UNAME=MINGW RUN=wine
|
||||
- name: Test the project
|
||||
run: make test UNAME=MINGW RUN=wine
|
||||
run: make test UNAME=MINGW RUN=wine VERBOSE=1
|
||||
|
||||
test-arm-linux:
|
||||
name: Build and test ARM32 cross compilation
|
||||
@@ -86,6 +122,17 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-arm-linux-gnueabi qemu-user
|
||||
- name: Compile the project
|
||||
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc
|
||||
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" CC=arm-linux-gnueabi-gcc LD=arm-linux-gnueabi-gcc
|
||||
- name: Test the project
|
||||
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test
|
||||
run: make RUN="qemu-arm -L /usr/arm-linux-gnueabi/" SUBRUN="qemu-arm -L /usr/arm-linux-gnueabi/" test VERBOSE=1
|
||||
|
||||
test-s390x-linux:
|
||||
name: Build and test s390x in qemu
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@master
|
||||
- name: Enable qemu
|
||||
run: docker run --privileged --rm tonistiigi/binfmt --install s390x
|
||||
- name: Build and run on emulated architecture
|
||||
run: docker run --rm -v .:/janet --platform linux/s390x alpine sh -c "apk update && apk add --no-interactive git build-base && cd /janet && make -j3 && make test"
|
||||
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -34,8 +34,12 @@ local
|
||||
|
||||
# Common test files I use.
|
||||
temp.janet
|
||||
temp*.janet
|
||||
temp.c
|
||||
temp*janet
|
||||
temp*.c
|
||||
temp.*
|
||||
scratch.janet
|
||||
scratch.c
|
||||
|
||||
# Emscripten
|
||||
*.bc
|
||||
@@ -45,6 +49,8 @@ janet.wasm
|
||||
# Generated files
|
||||
*.gen.h
|
||||
*.gen.c
|
||||
*.tmp
|
||||
temp.*
|
||||
|
||||
# Generate test files
|
||||
*.out
|
||||
@@ -57,6 +63,7 @@ xxd.exe
|
||||
# VSCode
|
||||
.vs
|
||||
.clangd
|
||||
.cache
|
||||
|
||||
# Swap files
|
||||
*.swp
|
||||
@@ -122,6 +129,9 @@ vgcore.*
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# GGov
|
||||
*.gcov
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
@@ -130,6 +140,9 @@ Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
# Coverage files
|
||||
*.cov
|
||||
|
||||
# End of https://www.gitignore.io/api/c
|
||||
|
||||
# Created by https://www.gitignore.io/api/cmake
|
||||
|
||||
157
CHANGELOG.md
157
CHANGELOG.md
@@ -2,6 +2,153 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## Unreleased - ???
|
||||
- Add `os/posix-chroot`
|
||||
- Fix `ev/deadline` with interrupt race condition bug on Windows.
|
||||
- Improve `flycheck` by allowing functions and macros to define their own flycheck behavior via the metadata `:flycheck`.
|
||||
- Add `*flychecking*` dynamic binding to check if inside flycheck evalutation
|
||||
- Add `gcperthread` callback for abstract types. This lets threaded abstracts have a finalizer that is called per thread, as well as a global finalizer.
|
||||
- Add `JANET_DO_ERROR_*` flags to describe the return value of `janet_dobytes` and `janet_dostring`.
|
||||
|
||||
## 1.39.1 - 2025-08-30
|
||||
- Add support for chdir in os/spawn on older macOS versions
|
||||
- Expose channels properly in C API
|
||||
|
||||
## 1.39.0 - 2025-08-24
|
||||
- Various bug fixes
|
||||
- Add `net/socket`
|
||||
- Add support for illumos OS
|
||||
- Raise helpful errors for incorrect arguments to `import`.
|
||||
- Allow configuring `JANET_THREAD_LOCAL` during builds to allow multi-threading on unknown compilers.
|
||||
- Make `ffi/write` append to a buffer instead of insert at 0 by default.
|
||||
- Add `os/getpid` to get the current process id.
|
||||
- Add `:out` option to `os/spawn` to be able to redirect stderr to stdout with pipes.
|
||||
Add `interrupt?` argument to `ev/deadline` to use VM interruptions.
|
||||
|
||||
## 1.38.0 - 2025-03-18
|
||||
- Add `bundle/replace`
|
||||
- Add CLI flags for the `bundle/` module to install and manage bundles.
|
||||
- Improve `?` peg special termination behavior
|
||||
- Add IEEE hex floats to grammar.
|
||||
- Add buffer peg literal support
|
||||
- Improve `split` peg special edge case behavior
|
||||
- Add Arm64 .msi support
|
||||
- Add `no-reuse` argument to `net/listen` to disable reusing server sockets
|
||||
- Add `struct/rawget`
|
||||
- Fix `deep=` and `deep-not=` to better handle degenerate cases with mutable table keys
|
||||
- Long strings will now dedent on `\r\n` instead of just `\n`.
|
||||
- Add `ev/to-file` for synchronous resource operations
|
||||
- Improve `file/open` error message by including path
|
||||
|
||||
## 1.37.1 - 2024-12-05
|
||||
- Fix meson cross compilation
|
||||
- Update timeout documentation for networking APIs: timeouts raise errors and do not return nil.
|
||||
- Add `janet_addtimeout_nil(double sec);` to the C API.
|
||||
- Change string hashing.
|
||||
- Fix string equality bug.
|
||||
- Add `assertf`
|
||||
- Change how JANET_PROFILE is loaded to allow more easily customizing the environment.
|
||||
- Add `*repl-prompt*` dynamic binding to allow customizing the built in repl.
|
||||
- Add multiple path support in the `JANET_PATH` environment variables. This lets
|
||||
user more easily import modules from many directories.
|
||||
- Add `nth` and `only-tags` PEG specials to select from sub-captures while
|
||||
dropping the rest.
|
||||
|
||||
## 1.36.0 - 2024-09-07
|
||||
- Improve error messages in `bundle/add*` functions.
|
||||
- Add CI testing and verify tests pass on the s390x architecture.
|
||||
- Save `:source-form` in environment entries when `*debug*` is set.
|
||||
- Add experimental `filewatch/` module for listening to file system changes on Linux and Windows.
|
||||
- Add `bundle/who-is` to query which bundle a file on disk was installed by.
|
||||
- Add `geomean` function
|
||||
- Add `:R` and `:W` flags to `os/pipe` to create blocking pipes on Posix and Windows systems.
|
||||
These streams cannot be directly read to and written from, but can be passed to subprocesses.
|
||||
- Add `array/join`
|
||||
- Add `tuple/join`
|
||||
- Add `bundle/add-bin` to make installing scripts easier. This also establishes a packaging convention for it.
|
||||
- Fix marshalling weak tables and weak arrays.
|
||||
- Fix bug in `ev/` module that could accidentally close sockets on accident.
|
||||
- Expose C functions for constructing weak tables in janet.h
|
||||
- Let range take non-integer values.
|
||||
|
||||
## 1.35.2 - 2024-06-16
|
||||
- Fix some documentation typos.
|
||||
- Allow using `:only` in import without quoting.
|
||||
|
||||
## 1.35.0 - 2024-06-15
|
||||
- Add `:only` argument to `import` to allow for easier control over imported bindings.
|
||||
- Add extra optional `env` argument to `eval` and `eval-string`.
|
||||
- Allow naming function literals with a keyword. This allows better stacktraces for macros without
|
||||
accidentally adding new bindings.
|
||||
- Add `bundle/` module for managing packages within Janet. This should replace the jpm packaging
|
||||
format eventually and is much simpler and amenable to more complicated builds.
|
||||
- Add macros `ev/with-lock`, `ev/with-rlock`, and `ev/with-wlock` for using mutexes and rwlocks.
|
||||
- Add `with-env`
|
||||
- Add *module-make-env* dynamic binding
|
||||
- Add buffer/format-at
|
||||
- Add long form command line options for readable CLI usage
|
||||
- Fix bug with `net/accept-loop` that would sometimes miss connections.
|
||||
|
||||
## 1.34.0 - 2024-03-22
|
||||
- Add a new (split) PEG special by @ianthehenry
|
||||
- Add buffer/push-* sized int and float by @pnelson
|
||||
- Documentation improvements: @amano-kenji, @llmII, @MaxGyver83, @pepe, @sogaiu.
|
||||
- Expose _exit to skip certain cleanup with os/exit.
|
||||
- Swap set / body order for each by @sogaiu.
|
||||
- Abort on assert failure instead of exit.
|
||||
- Fix: os/proc-wait by @llmII.
|
||||
- Fix macex1 to keep syntax location for all tuples.
|
||||
- Restore if-let tail calls.
|
||||
- Don't try and resume fibers that can't be resumed.
|
||||
- Register stream on unmarshal.
|
||||
- Fix asm roundtrip issue.
|
||||
|
||||
## 1.33.0 - 2024-01-07
|
||||
- Add more + and * keywords to default-peg-grammar by @sogaiu.
|
||||
- Use libc strlen in janet_buffer_push_cstring by @williewillus.
|
||||
- Be a bit safer with reference counting.
|
||||
- Add support for atomic loads in Janet's atomic abstraction.
|
||||
- Fix poll event loop CPU usage issue.
|
||||
- Add ipv6, shared, and cryptorand options to meson.
|
||||
- Add more ipv6 feature detection.
|
||||
- Fix loop for forever loop.
|
||||
- Cleaned up unused NetStateConnect, fixed janet_async_end() ev refcount by @zevv.
|
||||
- Fix warnings w/ MSVC and format.
|
||||
- Fix marshal_one_env w/ JANET_MARSHAL_UNSAFE.
|
||||
- Fix `(default)`.
|
||||
- Fix cannot marshal fiber with c stackframe, in a dynamic way that is fairly conservative.
|
||||
- Fix typo for SIGALARM in os/proc-kill.
|
||||
- Prevent bytecode optimization from remove mk* instructions.
|
||||
- Fix arity typo in peg.c by @pepe.
|
||||
- Update Makefile for MinGW.
|
||||
- Fix canceling waiting fiber.
|
||||
- Add a new (sub) PEG special by @ianthehenry.
|
||||
- Fix if net/server's handler has incorrect arity.
|
||||
- Fix macex raising on ().
|
||||
|
||||
## 1.32.1 - 2023-10-15
|
||||
- Fix return value from C function `janet_dobytes` when called on Janet functions that yield to event loop.
|
||||
- Change C API for event loop interaction - get rid of JanetListener and instead use `janet_async_start` and `janet_async_end`.
|
||||
- Rework event loop to make fewer system calls on kqueue and epoll.
|
||||
- Expose atomic refcount abstraction in janet.h
|
||||
- Add `array/weak` for weak references in arrays
|
||||
- Add support for weak tables via `table/weak`, `table/weak-keys`, and `table/weak-values`.
|
||||
- Fix compiler bug with using the result of `(break x)` expression in some contexts.
|
||||
- Rework internal event loop code to be better behaved on Windows
|
||||
- Update meson build to work better on windows
|
||||
|
||||
## 1.31.0 - 2023-09-17
|
||||
- Report line and column when using `janet_dobytes`
|
||||
- Add `:unless` loop modifier
|
||||
- Allow calling `reverse` on generators.
|
||||
- Improve performance of a number of core functions including `partition`, `mean`, `keys`, `values`, `pairs`, `interleave`.
|
||||
- Add `lengthable?`
|
||||
- Add `os/sigaction`
|
||||
- Change `every?` and `any?` to behave like the functional versions of the `and` and `or` macros.
|
||||
- Fix bug with garbage collecting threaded abstract types.
|
||||
- Add `:signal` to the `sandbox` function to allow intercepting signals.
|
||||
|
||||
## 1.30.0 - 2023-08-05
|
||||
- Change indexing of `array/remove` to start from -1 at the end instead of -2.
|
||||
- Add new string escape sequences `\\a`, `\\b`, `\\?`, and `\\'`.
|
||||
- Fix bug with marshalling channels
|
||||
- Add `div` for floored division
|
||||
@@ -40,7 +187,7 @@ All notable changes to this project will be documented in this file.
|
||||
See http://no-color.org/
|
||||
- Disallow using `(splice x)` in contexts where it doesn't make sense rather than silently coercing to `x`.
|
||||
Instead, raise a compiler error.
|
||||
- Change the names of `:user8` and `:user9` sigals to `:interrupt` and `:await`
|
||||
- Change the names of `:user8` and `:user9` signals to `:interrupt` and `:await`
|
||||
- Change the names of `:user8` and `:user9` fiber statuses to `:interrupted` and `:suspended`.
|
||||
- Add `ev/all-tasks` to see all currently suspended fibers.
|
||||
- Add `keep-syntax` and `keep-syntax!` functions to make writing macros easier.
|
||||
@@ -211,7 +358,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Add the ability to close channels with `ev/chan-close` (or `:close`).
|
||||
- Add threaded channels with `ev/thread-chan`.
|
||||
- Add `JANET_FN` and `JANET_REG` macros to more easily define C functions that export their source mapping information.
|
||||
- Add `janet_interpreter_interupt` and `janet_loop1_interrupt` to interrupt the interpreter while running.
|
||||
- Add `janet_interpreter_interrupt` and `janet_loop1_interrupt` to interrupt the interpreter while running.
|
||||
- Add `table/clear`
|
||||
- Add build option to disable the threading library without disabling all threads.
|
||||
- Remove JPM from the main Janet distribution. Instead, JPM must be installed
|
||||
@@ -265,7 +412,7 @@ saving and restoring the entire VM state.
|
||||
- Sort keys in pretty printing output.
|
||||
|
||||
## 1.15.3 - 2021-02-28
|
||||
- Fix a fiber bug that occured in deeply nested fibers
|
||||
- Fix a fiber bug that occurred in deeply nested fibers
|
||||
- Add `unref` combinator to pegs.
|
||||
- Small docstring changes.
|
||||
|
||||
@@ -415,13 +562,13 @@ saving and restoring the entire VM state.
|
||||
- Add `symbol/slice`
|
||||
- Add `keyword/slice`
|
||||
- Allow cross compilation with Makefile.
|
||||
- Change `compare-primitve` to `cmp` and make it more efficient.
|
||||
- Change `compare-primitive` to `cmp` and make it more efficient.
|
||||
- Add `reverse!` for reversing an array or buffer in place.
|
||||
- `janet_dobytes` and `janet_dostring` return parse errors in \*out
|
||||
- Add `repeat` macro for iterating something n times.
|
||||
- Add `eachy` (each yield) macro for iterating a fiber.
|
||||
- Fix `:generate` verb in loop macro to accept non symbols as bindings.
|
||||
- Add `:h`, `:h+`, and `:h*` in `default-peg-grammar` for hexidecimal digits.
|
||||
- Add `:h`, `:h+`, and `:h*` in `default-peg-grammar` for hexadecimal digits.
|
||||
- Fix `%j` formatter to print numbers precisely (using the `%.17g` format string to printf).
|
||||
|
||||
## 1.10.1 - 2020-06-18
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2023 Calvin Rose and contributors
|
||||
Copyright (c) 2025 Calvin Rose and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
68
Makefile
68
Makefile
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2023 Calvin Rose
|
||||
# Copyright (c) 2025 Calvin Rose
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
@@ -33,6 +33,7 @@ CLIBS=-lm -lpthread
|
||||
JANET_TARGET=build/janet
|
||||
JANET_BOOT=build/janet_boot
|
||||
JANET_IMPORT_LIB=build/janet.lib
|
||||
JANET_LIBRARY_IMPORT_LIB=build/libjanet.lib
|
||||
JANET_LIBRARY=build/libjanet.so
|
||||
JANET_STATIC_LIBRARY=build/libjanet.a
|
||||
JANET_PATH?=$(LIBDIR)/janet
|
||||
@@ -42,16 +43,22 @@ JANET_DIST_DIR?=janet-dist
|
||||
JANET_BOOT_FLAGS:=. JANET_PATH '$(JANET_PATH)'
|
||||
JANET_TARGET_OBJECTS=build/janet.o build/shell.o
|
||||
JPM_TAG?=master
|
||||
SPORK_TAG?=master
|
||||
HAS_SHARED?=1
|
||||
DEBUGGER=gdb
|
||||
SONAME_SETTER=-Wl,-soname,
|
||||
STRIPFLAGS=-x -S
|
||||
|
||||
# For cross compilation
|
||||
HOSTCC?=$(CC)
|
||||
HOSTAR?=$(AR)
|
||||
CFLAGS?=-O2 -g
|
||||
# Symbols are (optionally) removed later, keep -g as default!
|
||||
CFLAGS?=-O0 -g
|
||||
LDFLAGS?=-rdynamic
|
||||
LIBJANET_LDFLAGS?=$(LDFLAGS)
|
||||
RUN:=$(RUN)
|
||||
|
||||
|
||||
COMMON_CFLAGS:=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fvisibility=hidden -fPIC
|
||||
BOOT_CFLAGS:=-DJANET_BOOTSTRAP -DJANET_BUILD=$(JANET_BUILD) -O0 $(COMMON_CFLAGS) -g
|
||||
BUILD_CFLAGS:=$(CFLAGS) $(COMMON_CFLAGS)
|
||||
@@ -74,6 +81,12 @@ ifeq ($(UNAME), Darwin)
|
||||
LDCONFIG:=true
|
||||
else ifeq ($(UNAME), Linux)
|
||||
CLIBS:=$(CLIBS) -lrt -ldl
|
||||
else ifeq ($(UNAME), SunOS)
|
||||
BUILD_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
|
||||
BOOT_CFLAGS+=-D__EXTENSIONS__ -DJANET_NO_NANBOX
|
||||
CLIBS:=-lsocket -lm
|
||||
STRIPFLAGS=-x
|
||||
LDCONFIG:=false
|
||||
endif
|
||||
|
||||
# For other unix likes, add flags here!
|
||||
@@ -89,15 +102,26 @@ endif
|
||||
endif
|
||||
|
||||
# Mingw
|
||||
MINGW_COMPILER=
|
||||
ifeq ($(findstring MINGW,$(UNAME)), MINGW)
|
||||
MINGW_COMPILER=gcc
|
||||
CLIBS:=-lws2_32 -lpsapi -lwsock32
|
||||
LDFLAGS:=-Wl,--out-implib,$(JANET_IMPORT_LIB)
|
||||
LIBJANET_LDFLAGS:=-Wl,--out-implib,$(JANET_LIBRARY_IMPORT_LIB)
|
||||
JANET_TARGET:=$(JANET_TARGET).exe
|
||||
JANET_BOOT:=$(JANET_BOOT).exe
|
||||
COMPILER_VERSION:=$(shell $(CC) --version)
|
||||
ifeq ($(findstring clang,$(COMPILER_VERSION)), clang)
|
||||
MINGW_COMPILER=clang
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
$(shell mkdir -p build/core build/c build/boot build/mainclient)
|
||||
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.h
|
||||
all: $(JANET_TARGET) $(JANET_STATIC_LIBRARY) build/janet.h
|
||||
ifeq ($(HAS_SHARED), 1)
|
||||
all: $(JANET_LIBRARY)
|
||||
endif
|
||||
|
||||
######################
|
||||
##### Name Files #####
|
||||
@@ -114,7 +138,8 @@ JANET_LOCAL_HEADERS=src/core/features.h \
|
||||
src/core/regalloc.h \
|
||||
src/core/compile.h \
|
||||
src/core/emit.h \
|
||||
src/core/symcache.h
|
||||
src/core/symcache.h \
|
||||
src/core/sysir.h
|
||||
|
||||
JANET_CORE_SOURCES=src/core/abstract.c \
|
||||
src/core/array.c \
|
||||
@@ -130,6 +155,7 @@ JANET_CORE_SOURCES=src/core/abstract.c \
|
||||
src/core/ev.c \
|
||||
src/core/ffi.c \
|
||||
src/core/fiber.c \
|
||||
src/core/filewatch.c \
|
||||
src/core/gc.c \
|
||||
src/core/inttypes.c \
|
||||
src/core/io.c \
|
||||
@@ -148,6 +174,9 @@ JANET_CORE_SOURCES=src/core/abstract.c \
|
||||
src/core/strtod.c \
|
||||
src/core/struct.c \
|
||||
src/core/symcache.c \
|
||||
src/core/sysir.c \
|
||||
src/core/sysir_c.c \
|
||||
src/core/sysir_x86.c \
|
||||
src/core/table.c \
|
||||
src/core/tuple.c \
|
||||
src/core/util.c \
|
||||
@@ -195,9 +224,14 @@ build/%.bin.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
|
||||
########################
|
||||
|
||||
ifeq ($(UNAME), Darwin)
|
||||
SONAME=libjanet.1.29.dylib
|
||||
SONAME=libjanet.1.40.dylib
|
||||
else
|
||||
SONAME=libjanet.so.1.29
|
||||
SONAME=libjanet.so.1.40
|
||||
endif
|
||||
|
||||
ifeq ($(MINGW_COMPILER), clang)
|
||||
SONAME=
|
||||
SONAME_SETTER=
|
||||
endif
|
||||
|
||||
build/c/shell.c: src/mainclient/shell.c
|
||||
@@ -219,7 +253,7 @@ $(JANET_TARGET): $(JANET_TARGET_OBJECTS)
|
||||
$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) -o $@ $^ $(CLIBS)
|
||||
|
||||
$(JANET_LIBRARY): $(JANET_TARGET_OBJECTS)
|
||||
$(HOSTCC) $(LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
|
||||
$(HOSTCC) $(LIBJANET_LDFLAGS) $(BUILD_CFLAGS) $(SONAME_SETTER)$(SONAME) -shared -o $@ $^ $(CLIBS)
|
||||
|
||||
$(JANET_STATIC_LIBRARY): $(JANET_TARGET_OBJECTS)
|
||||
$(HOSTAR) rcs $@ $^
|
||||
@@ -262,20 +296,25 @@ dist: build/janet-dist.tar.gz
|
||||
|
||||
build/janet-%.tar.gz: $(JANET_TARGET) \
|
||||
build/janet.h \
|
||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
|
||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_STATIC_LIBRARY) \
|
||||
README.md build/c/janet.c build/c/shell.c
|
||||
mkdir -p build/$(JANET_DIST_DIR)/bin
|
||||
cp $(JANET_TARGET) build/$(JANET_DIST_DIR)/bin/
|
||||
strip $(STRIPFLAGS) 'build/$(JANET_DIST_DIR)/bin/janet'
|
||||
mkdir -p build/$(JANET_DIST_DIR)/include
|
||||
cp build/janet.h build/$(JANET_DIST_DIR)/include/
|
||||
mkdir -p build/$(JANET_DIST_DIR)/lib/
|
||||
cp $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
|
||||
cp $(JANET_STATIC_LIBRARY) build/$(JANET_DIST_DIR)/lib/
|
||||
cp $(JANET_LIBRARY) build/$(JANET_DIST_DIR)/lib/ || true
|
||||
mkdir -p build/$(JANET_DIST_DIR)/man/man1/
|
||||
cp janet.1 build/$(JANET_DIST_DIR)/man/man1/janet.1
|
||||
mkdir -p build/$(JANET_DIST_DIR)/src/
|
||||
cp build/c/janet.c build/c/shell.c build/$(JANET_DIST_DIR)/src/
|
||||
cp CONTRIBUTING.md LICENSE README.md build/$(JANET_DIST_DIR)/
|
||||
cd build && tar -czvf ../$@ ./$(JANET_DIST_DIR)
|
||||
ifeq ($(HAS_SHARED), 1)
|
||||
build/janet-%.tar.gz: $(JANET_LIBRARY)
|
||||
endif
|
||||
|
||||
#########################
|
||||
##### Documentation #####
|
||||
@@ -308,7 +347,7 @@ build/janet.pc: $(JANET_TARGET)
|
||||
install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc build/janet.h
|
||||
mkdir -p '$(DESTDIR)$(BINDIR)'
|
||||
cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet'
|
||||
strip -x -S '$(DESTDIR)$(BINDIR)/janet'
|
||||
strip $(STRIPFLAGS) '$(DESTDIR)$(BINDIR)/janet'
|
||||
mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet'
|
||||
cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet'
|
||||
ln -sf ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h'
|
||||
@@ -329,6 +368,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
|
||||
mkdir -p '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)'
|
||||
cp build/janet.pc '$(DESTDIR)$(JANET_PKG_CONFIG_PATH)/janet.pc'
|
||||
cp '$(JANET_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
|
||||
cp '$(JANET_LIBRARY_IMPORT_LIB)' '$(DESTDIR)$(LIBDIR)' || echo 'no import lib to install (mingw only)'
|
||||
[ -z '$(DESTDIR)' ] && $(LDCONFIG) || echo "You can ignore this error for non-Linux systems or local installs"
|
||||
|
||||
install-jpm-git: $(JANET_TARGET)
|
||||
@@ -343,6 +383,12 @@ install-jpm-git: $(JANET_TARGET)
|
||||
JANET_LIBPATH='$(LIBDIR)' \
|
||||
$(RUN) ../../$(JANET_TARGET) ./bootstrap.janet
|
||||
|
||||
install-spork-git: $(JANET_TARGET)
|
||||
mkdir -p build
|
||||
rm -rf build/spork
|
||||
git clone --depth=1 --branch='$(SPORK_TAG)' https://github.com/janet-lang/spork.git build/spork
|
||||
$(JANET_TARGET) -e '(bundle/install "build/spork")'
|
||||
|
||||
uninstall:
|
||||
-rm '$(DESTDIR)$(BINDIR)/janet'
|
||||
-rm -rf '$(DESTDIR)$(INCLUDEDIR)/janet'
|
||||
@@ -364,7 +410,7 @@ build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
|
||||
$(RUN) $(JANET_TARGET) $< > $@
|
||||
|
||||
compile-commands:
|
||||
# Requires pip install copmiledb
|
||||
# Requires pip install compiledb
|
||||
compiledb make
|
||||
|
||||
clean:
|
||||
|
||||
37
README.md
37
README.md
@@ -1,4 +1,4 @@
|
||||
[](https://gitter.im/janet-language/community)
|
||||
[](https://janet.zulipchat.com)
|
||||
|
||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/freebsd.yml?)
|
||||
[](https://builds.sr.ht/~bakpakin/janet/commits/master/openbsd.yml?)
|
||||
@@ -18,9 +18,6 @@ to run script files. This client program is separate from the core runtime, so
|
||||
Janet can be embedded in other programs. Try Janet in your browser at
|
||||
<https://janet-lang.org>.
|
||||
|
||||
If you'd like to financially support the ongoing development of Janet, consider
|
||||
[sponsoring its primary author](https://github.com/sponsors/bakpakin) through GitHub.
|
||||
|
||||
<br>
|
||||
|
||||
## Examples
|
||||
@@ -168,6 +165,21 @@ make install-jpm-git
|
||||
|
||||
Find out more about the available make targets by running `make help`.
|
||||
|
||||
### Alpine Linux
|
||||
|
||||
To build a statically-linked build of Janet, Alpine Linux + MUSL is a good combination. Janet can also
|
||||
be built inside a docker container or similar in this manner.
|
||||
|
||||
```sh
|
||||
docker run -it --rm alpine /bin/ash
|
||||
$ apk add make gcc musl-dev git
|
||||
$ git clone https://github.com/janet-lang/janet.git
|
||||
$ cd janet
|
||||
$ make -j10
|
||||
$ make test
|
||||
$ make install
|
||||
```
|
||||
|
||||
### 32-bit Haiku
|
||||
|
||||
32-bit Haiku build instructions are the same as the UNIX-like build instructions,
|
||||
@@ -201,6 +213,10 @@ gmake install-jpm-git
|
||||
NetBSD build instructions are the same as the FreeBSD build instructions.
|
||||
Alternatively, install the package directly with `pkgin install janet`.
|
||||
|
||||
### illumos
|
||||
|
||||
Building on illumos is exactly the same as building on FreeBSD.
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#).
|
||||
@@ -210,7 +226,7 @@ Alternatively, install the package directly with `pkgin install janet`.
|
||||
|
||||
To build an `.msi` installer executable, in addition to the above steps, you will have to:
|
||||
|
||||
5. Install, or otherwise add to your PATH the [WiX 3.11 Toolset](https://github.com/wixtoolset/wix3/releases).
|
||||
5. Install, or otherwise add to your PATH the [WiX 3.14 Toolset](https://github.com/wixtoolset/wix3/releases).
|
||||
6. Run `build_win dist`.
|
||||
|
||||
Now you should have an `.msi`. You can run `build_win install` to install the `.msi`, or execute the file itself.
|
||||
@@ -253,8 +269,10 @@ Emacs, and Atom each have syntax packages for the Janet language, though.
|
||||
|
||||
## Installation
|
||||
|
||||
See the [Introduction](https://janet-lang.org/docs/index.html) for more details. If you just want
|
||||
to try out the language, you don't need to install anything. You can also move the `janet` executable wherever you want on your system and run it.
|
||||
If you just want to try out the language, you don't need to install anything.
|
||||
In this case you can also move the `janet` executable wherever you want on
|
||||
your system and run it. However, for a fuller setup, please see the
|
||||
[Introduction](https://janet-lang.org/docs/index.html) for more details.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -315,8 +333,7 @@ See the [Embedding Section](https://janet-lang.org/capi/embedding.html) on the w
|
||||
|
||||
## Discussion
|
||||
|
||||
Feel free to ask questions and join the discussion on the [Janet Gitter channel](https://gitter.im/janet-language/community).
|
||||
Gitter provides Matrix and IRC bridges as well.
|
||||
Feel free to ask questions and join the discussion on the [Janet Zulip Instance](https://janet.zulipchat.com/)
|
||||
|
||||
## FAQ
|
||||
|
||||
@@ -383,7 +400,7 @@ Usually, one of a few reasons:
|
||||
### Can I bind to Rust/Zig/Go/Java/Nim/C++/D/Pascal/Fortran/Odin/Jai/(Some new "Systems" Programming Language)?
|
||||
|
||||
Probably, if that language has a good interface with C. But the programmer may need to do
|
||||
some extra work to map Janet's internal memory model may need some to that of the bound language. Janet
|
||||
some extra work to map Janet's internal memory model to that of the bound language. Janet
|
||||
also uses `setjmp`/`longjmp` for non-local returns internally. This
|
||||
approach is out of favor with many programmers now and doesn't always play well with other languages
|
||||
that have exceptions or stack-unwinding.
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
@setlocal
|
||||
|
||||
@rem Example use asan
|
||||
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
|
||||
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
|
||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi /DEBUG
|
||||
@set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib /DEBUG
|
||||
|
||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
|
||||
@set JANET_LINK=link /nologo
|
||||
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
|
||||
@rem set JANET_LINK=link /nologo
|
||||
|
||||
@set JANET_LINK_STATIC=lib /nologo
|
||||
|
||||
@@ -49,7 +49,9 @@ for %%f in (src\boot\*.c) do (
|
||||
)
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@rem note that there is no default sysroot being baked in
|
||||
build\janet_boot . > build\c\janet.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the sources
|
||||
%JANET_COMPILE% /Fobuild\janet.obj build\c\janet.c
|
||||
@@ -59,12 +61,13 @@ build\janet_boot . > build\c\janet.c
|
||||
|
||||
@rem Build the resources
|
||||
rc /nologo /fobuild\janet_win.res janet_win.rc
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Link everything to main client
|
||||
%JANET_LINK% /out:janet.exe build\janet.obj build\shell.obj build\janet_win.res
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build static library (libjanet.a)
|
||||
@rem Build static library (libjanet.lib)
|
||||
%JANET_LINK_STATIC% /out:build\libjanet.lib build\janet.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@@ -89,9 +92,11 @@ exit /b 0
|
||||
|
||||
@rem Clean build artifacts
|
||||
:CLEAN
|
||||
del *.exe *.lib *.exp
|
||||
del *.exe *.lib *.exp *.msi *.wixpdb
|
||||
rd /s /q build
|
||||
rd /s /q dist
|
||||
if exist dist (
|
||||
rd /s /q dist
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
@rem Run tests
|
||||
@@ -134,11 +139,18 @@ if defined APPVEYOR_REPO_TAG_NAME (
|
||||
set RELEASE_VERSION=%JANET_VERSION%
|
||||
)
|
||||
if defined CI (
|
||||
set WIXBIN="c:\Program Files (x86)\WiX Toolset v3.11\bin\"
|
||||
set WIXBIN="%WIX%bin\"
|
||||
echo WIXBIN = %WIXBIN%
|
||||
) else (
|
||||
set WIXBIN=
|
||||
)
|
||||
%WIXBIN%candle.exe tools\msi\janet.wxs -arch %BUILDARCH% -out build\
|
||||
|
||||
set WIXARCH=%BUILDARCH%
|
||||
if "%WIXARCH%"=="aarch64" (
|
||||
set WIXARCH=arm64
|
||||
)
|
||||
|
||||
%WIXBIN%candle.exe tools\msi\janet.wxs -arch %WIXARCH% -out build\
|
||||
%WIXBIN%light.exe "-sice:ICE38" -b tools\msi -ext WixUIExtension build\janet.wixobj -out janet-%RELEASE_VERSION%-windows-%BUILDARCH%-installer.msi
|
||||
exit /b 0
|
||||
|
||||
|
||||
6
examples/abstract-unix-socket.janet
Normal file
6
examples/abstract-unix-socket.janet
Normal file
@@ -0,0 +1,6 @@
|
||||
# Linux only - uses abstract unix domain sockets
|
||||
(ev/spawn (net/server :unix "@abc123" (fn [conn] (print (:read conn 1024)) (:close conn))))
|
||||
(ev/sleep 1)
|
||||
(def s (net/connect :unix "@abc123" :stream))
|
||||
(:write s "hello")
|
||||
(:close s)
|
||||
35
examples/chatserver.janet
Normal file
35
examples/chatserver.janet
Normal file
@@ -0,0 +1,35 @@
|
||||
(def conmap @{})
|
||||
|
||||
(defn broadcast [em msg]
|
||||
(eachk par conmap
|
||||
(if (not= par em)
|
||||
(if-let [tar (get conmap par)]
|
||||
(net/write tar (string/format "[%s]:%s" em msg))))))
|
||||
|
||||
(defn handler
|
||||
[connection]
|
||||
(print "connection: " connection)
|
||||
(net/write connection "Whats your name?\n")
|
||||
(def name (string/trim (string (ev/read connection 100))))
|
||||
(print name " connected")
|
||||
(if (get conmap name)
|
||||
(do
|
||||
(net/write connection "Name already taken!")
|
||||
(:close connection))
|
||||
(do
|
||||
(put conmap name connection)
|
||||
(net/write connection (string/format "Welcome %s\n" name))
|
||||
(defer (do
|
||||
(put conmap name nil)
|
||||
(:close connection))
|
||||
(while (def msg (ev/read connection 100))
|
||||
(broadcast name (string msg)))
|
||||
(print name " disconnected")))))
|
||||
|
||||
(defn main [& args]
|
||||
(printf "STARTING SERVER...")
|
||||
(flush)
|
||||
(def my-server (net/listen "127.0.0.1" "8000"))
|
||||
(forever
|
||||
(def connection (net/accept my-server))
|
||||
(ev/call handler connection)))
|
||||
@@ -35,6 +35,11 @@ typedef struct {
|
||||
int c;
|
||||
} intintint;
|
||||
|
||||
typedef struct {
|
||||
uint64_t a;
|
||||
uint64_t b;
|
||||
} uint64pair;
|
||||
|
||||
typedef struct {
|
||||
int64_t a;
|
||||
int64_t b;
|
||||
@@ -203,3 +208,20 @@ EXPORTER
|
||||
int sixints_fn_3(SixInts s, int x) {
|
||||
return x + s.u + s.v + s.w + s.x + s.y + s.z;
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
intint stack_spill_fn(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
|
||||
uint8_t e, uint8_t f, uint8_t g, uint8_t h,
|
||||
float i, float j, float k, float l,
|
||||
float m, float n, float o, float p,
|
||||
float s1, int8_t s2, uint8_t s3, double s4, uint8_t s5, intint s6) {
|
||||
return (intint) {
|
||||
(a | b | c | d | e | f | g | h) + (i + j + k + l + m + n + o + p),
|
||||
s1 *s6.a + s2 *s6.b + s3 *s4 *s5
|
||||
};
|
||||
}
|
||||
|
||||
EXPORTER
|
||||
double stack_spill_fn_2(uint64pair a, uint64pair b, uint64pair c, int8_t d, uint64pair e, int8_t f) {
|
||||
return (double)(a.a * c.a + a.b * c.b + b.a * e.a) * f - (double)(b.b * e.b) + d;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
|
||||
(if is-windows
|
||||
(os/execute ["cl.exe" "/nologo" "/LD" ffi/source-loc "/link" "/DLL" (string "/OUT:" ffi/loc)] :px)
|
||||
(os/execute ["cc" ffi/source-loc "-shared" "-o" ffi/loc] :px))
|
||||
(os/execute ["cc" ffi/source-loc "-g" "-shared" "-o" ffi/loc] :px))
|
||||
|
||||
(ffi/context ffi/loc)
|
||||
|
||||
(def intint (ffi/struct :int :int))
|
||||
(def intintint (ffi/struct :int :int :int))
|
||||
(def uint64pair (ffi/struct :u64 :u64))
|
||||
(def big (ffi/struct :s64 :s64 :s64))
|
||||
(def split (ffi/struct :int :int :float :float))
|
||||
(def split-flip (ffi/struct :float :float :int :int))
|
||||
@@ -55,6 +57,14 @@
|
||||
(ffi/defbind sixints-fn six-ints [])
|
||||
(ffi/defbind sixints-fn-2 :int [x :int s six-ints])
|
||||
(ffi/defbind sixints-fn-3 :int [s six-ints x :int])
|
||||
(ffi/defbind stack-spill-fn intint
|
||||
[a :u8 b :u8 c :u8 d :u8
|
||||
e :u8 f :u8 g :u8 h :u8
|
||||
i :float j :float k :float l :float
|
||||
m :float n :float o :float p :float
|
||||
s1 :float s2 :s8 s3 :u8 s4 :double s5 :u8 s6 intint])
|
||||
(ffi/defbind stack-spill-fn-2 :double [a uint64pair b uint64pair c uint64pair d :s8 e uint64pair f :s8])
|
||||
(ffi/defbind-alias int-fn int-fn-aliased :int [a :int b :int])
|
||||
|
||||
#
|
||||
# Struct reading and writing
|
||||
@@ -119,6 +129,7 @@
|
||||
(tracev (return-struct 42))
|
||||
(tracev (double-lots 1 2 3 4 5 6 700 800 9 10))
|
||||
(tracev (struct-big 11 99.5))
|
||||
(tracev (int-fn-aliased 10 20))
|
||||
|
||||
(assert (= [10 10 12 12] (split-ret-fn 10 12)))
|
||||
(assert (= [12 12 10 10] (split-flip-ret-fn 10 12)))
|
||||
@@ -130,5 +141,10 @@
|
||||
(assert (= 21 (math/round (double-many 1 2 3 4 5 6.01))))
|
||||
(assert (= 19 (double-lots 1 2 3 4 5 6 7 8 9 10)))
|
||||
(assert (= 204 (float-fn 8 4 17)))
|
||||
(assert (= [0 38534415] (stack-spill-fn
|
||||
0 0 0 0 0 0 0 0
|
||||
0 0 0 0 0 0 0 0
|
||||
1.5 -32 196 65536.5 3 [-15 32])))
|
||||
(assert (= -2806 (stack-spill-fn-2 [2 3] [5 7] [9 11] -19 [13 17] -23)))
|
||||
|
||||
(print "Done.")
|
||||
|
||||
5
examples/posix-exec.janet
Normal file
5
examples/posix-exec.janet
Normal file
@@ -0,0 +1,5 @@
|
||||
# Switch to python
|
||||
|
||||
(print "running in Janet")
|
||||
(os/posix-exec ["python"] :p)
|
||||
(print "will not print")
|
||||
1
examples/sample-bad-bundle/badmod.janet
Normal file
1
examples/sample-bad-bundle/badmod.janet
Normal file
@@ -0,0 +1 @@
|
||||
(def abc 123)
|
||||
7
examples/sample-bad-bundle/bundle.janet
Normal file
7
examples/sample-bad-bundle/bundle.janet
Normal file
@@ -0,0 +1,7 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "badmod.janet"))
|
||||
|
||||
(defn check
|
||||
[&]
|
||||
(error "Check failed!"))
|
||||
1
examples/sample-bundle-aliases/aliases-mod.janet
Normal file
1
examples/sample-bundle-aliases/aliases-mod.janet
Normal file
@@ -0,0 +1 @@
|
||||
(defn fun [x] (range x))
|
||||
3
examples/sample-bundle-aliases/bundle.janet
Normal file
3
examples/sample-bundle-aliases/bundle.janet
Normal file
@@ -0,0 +1,3 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "aliases-mod.janet"))
|
||||
4
examples/sample-bundle-aliases/info.jdn
Normal file
4
examples/sample-bundle-aliases/info.jdn
Normal file
@@ -0,0 +1,4 @@
|
||||
@{
|
||||
:name "sample-bundle-aliases"
|
||||
:dependencies ["sample-dep1" "sample-dep2"]
|
||||
}
|
||||
4
examples/sample-bundle/bundle/info.jdn
Normal file
4
examples/sample-bundle/bundle/info.jdn
Normal file
@@ -0,0 +1,4 @@
|
||||
@{
|
||||
:name "sample-bundle"
|
||||
:dependencies ["sample-dep1" "sample-dep2"]
|
||||
}
|
||||
3
examples/sample-bundle/bundle/init.janet
Normal file
3
examples/sample-bundle/bundle/init.janet
Normal file
@@ -0,0 +1,3 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "mymod.janet"))
|
||||
7
examples/sample-bundle/mymod.janet
Normal file
7
examples/sample-bundle/mymod.janet
Normal file
@@ -0,0 +1,7 @@
|
||||
(import dep1)
|
||||
(import dep2)
|
||||
|
||||
(defn myfn
|
||||
[x]
|
||||
(def y (dep2/function x))
|
||||
(dep1/function y))
|
||||
4
examples/sample-dep1/bundle/info.jdn
Normal file
4
examples/sample-dep1/bundle/info.jdn
Normal file
@@ -0,0 +1,4 @@
|
||||
@{
|
||||
:name "sample-dep1"
|
||||
:dependencies [{:name "sample-dep2"}]
|
||||
}
|
||||
3
examples/sample-dep1/bundle/init.janet
Normal file
3
examples/sample-dep1/bundle/init.janet
Normal file
@@ -0,0 +1,3 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "dep1.janet"))
|
||||
3
examples/sample-dep1/dep1.janet
Normal file
3
examples/sample-dep1/dep1.janet
Normal file
@@ -0,0 +1,3 @@
|
||||
(defn function
|
||||
[x]
|
||||
(+ x x))
|
||||
3
examples/sample-dep2/bundle/info.jdn
Normal file
3
examples/sample-dep2/bundle/info.jdn
Normal file
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
:name "sample-dep2"
|
||||
}
|
||||
3
examples/sample-dep2/bundle/init.janet
Normal file
3
examples/sample-dep2/bundle/init.janet
Normal file
@@ -0,0 +1,3 @@
|
||||
(defn install
|
||||
[manifest &]
|
||||
(bundle/add-file manifest "dep2.janet"))
|
||||
3
examples/sample-dep2/dep2.janet
Normal file
3
examples/sample-dep2/dep2.janet
Normal file
@@ -0,0 +1,3 @@
|
||||
(defn function
|
||||
[x]
|
||||
(* x x))
|
||||
41
examples/sigaction.janet
Normal file
41
examples/sigaction.janet
Normal file
@@ -0,0 +1,41 @@
|
||||
###
|
||||
### Usage: janet examples/sigaction.janet 1|2|3|4 &
|
||||
###
|
||||
### Then at shell: kill -s SIGTERM $!
|
||||
###
|
||||
|
||||
(defn action
|
||||
[]
|
||||
(print "Handled SIGTERM!")
|
||||
(flush)
|
||||
(os/exit 1))
|
||||
|
||||
(defn main1
|
||||
[]
|
||||
(os/sigaction :term action true)
|
||||
(forever))
|
||||
|
||||
(defn main2
|
||||
[]
|
||||
(os/sigaction :term action)
|
||||
(forever))
|
||||
|
||||
(defn main3
|
||||
[]
|
||||
(os/sigaction :term action true)
|
||||
(forever (ev/sleep math/inf)))
|
||||
|
||||
(defn main4
|
||||
[]
|
||||
(os/sigaction :term action)
|
||||
(forever (ev/sleep math/inf)))
|
||||
|
||||
(defn main
|
||||
[& args]
|
||||
(def which (scan-number (get args 1 "1")))
|
||||
(case which
|
||||
1 (main1) # should work
|
||||
2 (main2) # will not work
|
||||
3 (main3) # should work
|
||||
4 (main4) # should work
|
||||
(error "bad main")))
|
||||
71
examples/sysir/drawing.janet
Normal file
71
examples/sysir/drawing.janet
Normal file
@@ -0,0 +1,71 @@
|
||||
###
|
||||
### Create a .bmp file on linux.
|
||||
###
|
||||
|
||||
# Quick run and view on Linux:
|
||||
# build/janet examples/sysir/drawing.janet > temp.c && cc temp.c && ./a.out > temp.bmp && feh temp.bmp
|
||||
|
||||
(use ./frontend)
|
||||
|
||||
(defn-external write:void [fd:int mem:pointer size:uint])
|
||||
(defn-external exit:void [x:int])
|
||||
|
||||
# assume 128x128 32 bit color image
|
||||
# Size : 128 * 128 * 4 + align(14 + 40, 4) = 65592
|
||||
# dib offset : align(14 + 40, 4) = 56
|
||||
|
||||
(defsys write_32:void [x:uint]
|
||||
(write 1 (address x) 4)
|
||||
(return))
|
||||
|
||||
(defsys write_16:void [x:uint]
|
||||
(write 1 (address x) 2)
|
||||
(return))
|
||||
|
||||
(defsys write_header:void [w:uint h:uint]
|
||||
(write 1 "BM" 2)
|
||||
(def size:uint (+ 56 (* w h 4)))
|
||||
(write_32 size)
|
||||
(write_32 0)
|
||||
(write_32 56) # pixel array offset
|
||||
# Begin DIB
|
||||
(write_32 40) # dib size
|
||||
(write_32 w)
|
||||
(write_32 h)
|
||||
(write_16 1) # color panes - must be 1
|
||||
(write_16 32) # bits per pixel
|
||||
(write_32 0) # compression method - no compression
|
||||
(write_32 0) # image size - not needed when no compression, 0 should be fine
|
||||
(write_32 4096) # pixels per meter - horizontal resolution
|
||||
(write_32 4096) # pixels per meter - vertical resolution
|
||||
(write_32 0) # number of colors in palette - no palette so 0
|
||||
(write_32 0) # number of "important colors" ignored in practice
|
||||
(write_16 0) # add "gap 1" to align pixel array to multiple of 4 bytes
|
||||
(return))
|
||||
|
||||
(defsys draw:void [w:uint h:uint]
|
||||
(def red:uint 0xFFFF0000)
|
||||
(def blue:uint 0xFF0000FF)
|
||||
(def size:uint (* w h 4))
|
||||
(var y:uint 0)
|
||||
(while (< y h)
|
||||
(var x:uint 0)
|
||||
(while (< x w)
|
||||
(write_32 (if (> y 64) blue red))
|
||||
(set x (+ 1 x)))
|
||||
(set y (+ y 1)))
|
||||
(return))
|
||||
|
||||
(defsys main:int []
|
||||
(def w:uint 512)
|
||||
(def h:uint 512)
|
||||
(write_header w h)
|
||||
(draw w h)
|
||||
(return 0))
|
||||
|
||||
####
|
||||
|
||||
#(dump)
|
||||
(print "#include <unistd.h>")
|
||||
(dumpc)
|
||||
#(dumpx64)
|
||||
86
examples/sysir/drawing2.janet
Normal file
86
examples/sysir/drawing2.janet
Normal file
@@ -0,0 +1,86 @@
|
||||
###
|
||||
### Create a .bmp file on linux.
|
||||
###
|
||||
|
||||
# Quick run and view on Linux:
|
||||
# build/janet examples/sysir/drawing2.janet > temp.c && cc temp.c && ./a.out > temp.bmp && feh temp.bmp
|
||||
|
||||
(use ./frontend)
|
||||
|
||||
(setdyn :verbose true)
|
||||
|
||||
# Pointer types
|
||||
(defpointer p32 uint)
|
||||
(defpointer p16 u16)
|
||||
(defpointer cursor p32)
|
||||
|
||||
# External
|
||||
(defn-external write:void [fd:int mem:pointer size:uint])
|
||||
(defn-external exit:void [x:int])
|
||||
(defn-external malloc:p32 [size:uint])
|
||||
|
||||
(defsys w32:void [c:cursor x:uint]
|
||||
(def p:p32 (load c))
|
||||
(store p x)
|
||||
(store c (the p32 (pointer-add p 1)))
|
||||
(return))
|
||||
|
||||
(defsys w16:void [c:cursor x:uint]
|
||||
# Casting needs revisiting
|
||||
(def p:p16 (cast (the p32 (load c))))
|
||||
(store p (the u16 (cast x)))
|
||||
(store c (the p32 (cast (the p16 (pointer-add p 1)))))
|
||||
(return))
|
||||
|
||||
(defsys makebmp:p32 [w:uint h:uint]
|
||||
(def size:uint (+ 56 (* w h 4)))
|
||||
(def mem:p32 (malloc size))
|
||||
(def c:cursor (cast (malloc 4)))
|
||||
#(def cursor_data:p32 mem)
|
||||
#(def c:cursor (address cursor_data))
|
||||
(store c mem)
|
||||
(w16 c 0x4D42) # ascii "BM"
|
||||
(w32 c size)
|
||||
(w32 c 0)
|
||||
(w32 c 56)
|
||||
(w32 c 40)
|
||||
(w32 c w)
|
||||
(w32 c h)
|
||||
(w16 c 1)
|
||||
(w16 c 32)
|
||||
(w32 c 0)
|
||||
(w32 c 0)
|
||||
(w32 c 4096)
|
||||
(w32 c 4096)
|
||||
(w32 c 0)
|
||||
(w32 c 0)
|
||||
(w16 c 0) # padding
|
||||
# Draw
|
||||
(def red:uint 0xFFFF0000)
|
||||
(def blue:uint 0xFF0000FF)
|
||||
(def green:uint 0xFF00FF00)
|
||||
(var y:uint 0)
|
||||
(while (< y h)
|
||||
(var x:uint 0)
|
||||
(while (< x w)
|
||||
(def d2:uint (+ (* x x) (* y y)))
|
||||
(if (> d2 100000)
|
||||
(if (> d2 200000) (w32 c green) (w32 c blue))
|
||||
(w32 c red))
|
||||
(set x (+ 1 x)))
|
||||
(set y (+ y 1)))
|
||||
(write 1 mem size)
|
||||
(return mem))
|
||||
|
||||
(defsys main:int []
|
||||
(def w:uint 512)
|
||||
(def h:uint 512)
|
||||
(makebmp w h)
|
||||
(return 0))
|
||||
|
||||
####
|
||||
|
||||
(dumpx64)
|
||||
|
||||
#(print "#include <unistd.h>")
|
||||
#(dumpc)
|
||||
567
examples/sysir/frontend.janet
Normal file
567
examples/sysir/frontend.janet
Normal file
@@ -0,0 +1,567 @@
|
||||
# Make a language frontend for the sysir.
|
||||
# Dialect:
|
||||
# TODO -
|
||||
# * arrays (declaration, loads, stores)
|
||||
|
||||
(defdyn *ret-type* "Current function return type")
|
||||
|
||||
(def slot-to-name @[])
|
||||
(def name-to-slot @{})
|
||||
(def type-to-name @[])
|
||||
(def name-to-type @{})
|
||||
(def slot-types @{})
|
||||
(def functions @{})
|
||||
(def type-fields @{})
|
||||
(def syscalls @{})
|
||||
|
||||
(defn get-slot
|
||||
[&opt new-name]
|
||||
(def next-slot (length slot-to-name))
|
||||
(array/push slot-to-name new-name)
|
||||
(if new-name (put name-to-slot new-name next-slot))
|
||||
next-slot)
|
||||
|
||||
(defn named-slot
|
||||
[name]
|
||||
(assert (get name-to-slot name)))
|
||||
|
||||
(defn make-type
|
||||
[&opt new-name]
|
||||
(def next-type (length type-to-name))
|
||||
(array/push type-to-name new-name)
|
||||
(if new-name (put name-to-type new-name next-type))
|
||||
next-type)
|
||||
|
||||
(defn named-type
|
||||
[name]
|
||||
(def t (get name-to-type name))
|
||||
(assert t)
|
||||
t)
|
||||
|
||||
(defn binding-type
|
||||
[name]
|
||||
(def slot (assert (get name-to-slot name)))
|
||||
(assert (get slot-types slot)))
|
||||
|
||||
(defn slot-type
|
||||
[slot]
|
||||
(assert (get slot-types slot)))
|
||||
|
||||
(defn assign-type
|
||||
[name typ]
|
||||
(def slot (get name-to-slot name))
|
||||
(put slot-types slot typ))
|
||||
|
||||
(defn assign-slot-type
|
||||
[slot typ]
|
||||
(put slot-types slot typ))
|
||||
|
||||
(defn setup-default-types
|
||||
[ctx]
|
||||
(def into @[])
|
||||
(defn add-prim-type
|
||||
[name native-name]
|
||||
(array/push into ~(type-prim ,name ,native-name))
|
||||
(make-type name))
|
||||
(add-prim-type 'float 'f32)
|
||||
(add-prim-type 'double 'f64)
|
||||
(add-prim-type 'int 's32)
|
||||
(add-prim-type 'uint 'u32)
|
||||
(add-prim-type 'long 's64)
|
||||
(add-prim-type 'ulong 'u64)
|
||||
(add-prim-type 'boolean 'boolean)
|
||||
(add-prim-type 's16 's16)
|
||||
(add-prim-type 'u16 'u16)
|
||||
(add-prim-type 'byte 'u8)
|
||||
(add-prim-type 'void 'void)
|
||||
(array/push into ~(type-pointer pointer void))
|
||||
(make-type 'pointer)
|
||||
(sysir/asm ctx into)
|
||||
ctx)
|
||||
|
||||
(defn type-extract
|
||||
"Given a symbol:type combination, extract the proper name and the type separately"
|
||||
[combined-name &opt default-type]
|
||||
(def parts (string/split ":" combined-name 0 2))
|
||||
(def [name tp] parts)
|
||||
[(symbol name) (symbol (or tp default-type))])
|
||||
|
||||
(var do-binop nil)
|
||||
(var do-comp nil)
|
||||
|
||||
###
|
||||
### Inside functions
|
||||
###
|
||||
|
||||
(defn visit1
|
||||
"Take in a form and compile code and put it into `into`. Return result slot."
|
||||
[code into &opt no-return type-hint]
|
||||
(def subresult
|
||||
(cond
|
||||
|
||||
# Compile a constant
|
||||
(string? code) ~(pointer ,code)
|
||||
(boolean? code) ~(boolean ,code)
|
||||
(number? code) ~(,(or type-hint 'double) ,code) # TODO - should default to double
|
||||
|
||||
# Needed?
|
||||
(= :core/u64 (type code)) ~(ulong ,code)
|
||||
(= :core/s64 (type code)) ~(long ,code)
|
||||
|
||||
# Binding
|
||||
(symbol? code)
|
||||
(named-slot code)
|
||||
|
||||
# Array literals
|
||||
(and (tuple? code) (= :brackets (tuple/type code)))
|
||||
(do
|
||||
(assert type-hint (string/format "unknown type for array literal %v" code))
|
||||
~(,type-hint ,code))
|
||||
|
||||
# Compile forms
|
||||
(and (tuple? code) (= :parens (tuple/type code)))
|
||||
(do
|
||||
(assert (> (length code) 0))
|
||||
(def [op & args] code)
|
||||
(case op
|
||||
|
||||
# Arithmetic
|
||||
'+ (do-binop 'add args into type-hint)
|
||||
'- (do-binop 'subtract args into type-hint)
|
||||
'* (do-binop 'multiply args into type-hint)
|
||||
'/ (do-binop 'divide args into type-hint)
|
||||
'<< (do-binop 'shl args into type-hint)
|
||||
'>> (do-binop 'shr args into type-hint)
|
||||
|
||||
# Comparison
|
||||
'= (do-comp 'eq args into)
|
||||
'not= (do-comp 'neq args into)
|
||||
'< (do-comp 'lt args into)
|
||||
'<= (do-comp 'lte args into)
|
||||
'> (do-comp 'gt args into)
|
||||
'>= (do-comp 'gte args into)
|
||||
|
||||
# Pointers
|
||||
'pointer-add
|
||||
(do
|
||||
(assert (= 2 (length args)))
|
||||
(def [base offset] args)
|
||||
(def base-slot (visit1 base into false type-hint))
|
||||
(def offset-slot (visit1 offset into false 'int))
|
||||
(def slot (get-slot))
|
||||
(when type-hint (array/push into ~(bind ,slot ,type-hint)))
|
||||
(array/push into ~(pointer-add ,slot ,base-slot ,offset-slot))
|
||||
slot)
|
||||
|
||||
'pointer-sub
|
||||
(do
|
||||
(assert (= 2 (length args)))
|
||||
(def [base offset] args)
|
||||
(def base-slot (visit1 base into false type-hint))
|
||||
(def offset-slot (visit1 offset into false 'int))
|
||||
(def slot (get-slot))
|
||||
(when type-hint (array/push into ~(bind ,slot ,type-hint)))
|
||||
(array/push into ~(pointer-subtract ,slot ,base-slot ,offset-slot))
|
||||
slot)
|
||||
|
||||
# Type hinting
|
||||
'the
|
||||
(do
|
||||
(assert (= 2 (length args)))
|
||||
(def [xtype x] args)
|
||||
(def result (visit1 x into false xtype))
|
||||
(if (tuple? result) # constant
|
||||
(let [[t y] result]
|
||||
(assertf (= t xtype) "type mismatch, %p doesn't match %p" t xtype)
|
||||
[xtype y])
|
||||
(do
|
||||
(array/push into ~(bind ,result ,xtype))
|
||||
result)))
|
||||
|
||||
# Casting
|
||||
'cast
|
||||
(do
|
||||
(assert (= 1 (length args)))
|
||||
(assert type-hint) # should we add an explicit cast type?
|
||||
(def [x] args)
|
||||
(def slot (get-slot))
|
||||
(def result (visit1 x into false))
|
||||
(array/push into ~(bind ,slot ,type-hint))
|
||||
(array/push into ~(cast ,slot ,result))
|
||||
slot)
|
||||
|
||||
# Named bindings
|
||||
'def
|
||||
(do
|
||||
(assert (= 2 (length args)))
|
||||
(def [full-name value] args)
|
||||
(assert (symbol? full-name))
|
||||
(def [name tp] (type-extract full-name 'int))
|
||||
(def result (visit1 value into false tp))
|
||||
(def slot (get-slot name))
|
||||
(assign-type name tp)
|
||||
(array/push into ~(bind ,slot ,tp))
|
||||
(array/push into ~(move ,slot ,result))
|
||||
slot)
|
||||
|
||||
# Named variables
|
||||
'var
|
||||
(do
|
||||
(assert (= 2 (length args)))
|
||||
(def [full-name value] args)
|
||||
(assert (symbol? full-name))
|
||||
(def [name tp] (type-extract full-name 'int))
|
||||
(def result (visit1 value into false tp))
|
||||
(def slot (get-slot name))
|
||||
(assign-type name tp)
|
||||
(array/push into ~(bind ,slot ,tp))
|
||||
(array/push into ~(move ,slot ,result))
|
||||
slot)
|
||||
|
||||
# Address of (& operator in C)
|
||||
'address
|
||||
(do
|
||||
(assert (= 1 (length args)))
|
||||
(def [thing] args)
|
||||
(def [name tp] (type-extract thing 'int))
|
||||
(def result (visit1 thing into false tp))
|
||||
(def slot (get-slot))
|
||||
#
|
||||
(array/push into ~(bind ,slot ,type-hint))
|
||||
(array/push into ~(address ,slot ,result))
|
||||
slot)
|
||||
|
||||
'load
|
||||
(do
|
||||
(assert (= 1 (length args)))
|
||||
(assert type-hint)
|
||||
(def [thing] args)
|
||||
# (def [name tp] (type-extract thing 'pointer))
|
||||
(def result (visit1 thing into false))
|
||||
(def slot (get-slot))
|
||||
(def ptype type-hint)
|
||||
(array/push into ~(bind ,slot ,ptype))
|
||||
(array/push into ~(load ,slot ,result))
|
||||
slot)
|
||||
|
||||
'store
|
||||
(do
|
||||
(assert (= 2 (length args)))
|
||||
(def [dest value] args)
|
||||
# (def [name tp] (type-extract dest 'pointer))
|
||||
(def dest-r (visit1 dest into false))
|
||||
(def value-r (visit1 value into false))
|
||||
(array/push into ~(store ,dest-r ,value-r))
|
||||
value-r)
|
||||
|
||||
# Assignment
|
||||
'set
|
||||
(do
|
||||
(assert (= 2 (length args)))
|
||||
(def [to x] args)
|
||||
(def type-hint (binding-type to))
|
||||
(def result (visit1 x into false type-hint))
|
||||
(def toslot (named-slot to))
|
||||
(array/push into ~(move ,toslot ,result))
|
||||
toslot)
|
||||
|
||||
# Return
|
||||
'return
|
||||
(do
|
||||
(assert (>= 1 (length args)))
|
||||
(if (empty? args)
|
||||
(array/push into '(return))
|
||||
(do
|
||||
(def [x] args)
|
||||
(array/push into ~(return ,(visit1 x into false (dyn *ret-type*))))))
|
||||
nil)
|
||||
|
||||
# Sequence of operations
|
||||
'do
|
||||
(do
|
||||
(each form (slice args 0 -2) (visit1 form into true))
|
||||
(visit1 (last args) into false type-hint))
|
||||
|
||||
# While loop
|
||||
'while
|
||||
(do
|
||||
(def lab-test (keyword (gensym)))
|
||||
(def lab-exit (keyword (gensym)))
|
||||
(assert (< 1 (length args)))
|
||||
(def [cnd & body] args)
|
||||
(array/push into lab-test)
|
||||
(def condition-slot (visit1 cnd into false 'boolean))
|
||||
(array/push into ~(branch-not ,condition-slot ,lab-exit))
|
||||
(each code body
|
||||
(visit1 code into true))
|
||||
(array/push into ~(jump ,lab-test))
|
||||
(array/push into lab-exit)
|
||||
nil)
|
||||
|
||||
# Branch
|
||||
'if
|
||||
(do
|
||||
(def lab (keyword (gensym)))
|
||||
(def lab-end (keyword (gensym)))
|
||||
(assert (< 2 (length args) 4))
|
||||
(def [cnd tru fal] args)
|
||||
(def condition-slot (visit1 cnd into false 'boolean))
|
||||
(def ret (if type-hint (get-slot)))
|
||||
(when type-hint (array/push into ~(bind ,ret ,type-hint)))
|
||||
(array/push into ~(branch ,condition-slot ,lab))
|
||||
# false path
|
||||
(if type-hint
|
||||
(array/push into ~(move ,ret ,(visit1 fal into false type-hint)))
|
||||
(visit1 fal into true))
|
||||
(array/push into ~(jump ,lab-end))
|
||||
(array/push into lab)
|
||||
# true path
|
||||
(if type-hint
|
||||
(array/push into ~(move ,ret ,(visit1 tru into false type-hint)))
|
||||
(visit1 tru into true))
|
||||
(array/push into lab-end)
|
||||
ret)
|
||||
|
||||
# Insert IR
|
||||
'ir
|
||||
(do
|
||||
(assert no-return)
|
||||
(array/push into ;args)
|
||||
nil)
|
||||
|
||||
# Assume function call or syscall
|
||||
(do
|
||||
(def slots @[])
|
||||
(def signature (get functions op))
|
||||
(def is-syscall (get syscalls op))
|
||||
(assert signature (string "unknown function " op))
|
||||
(def ret (if no-return nil (get-slot)))
|
||||
(when ret
|
||||
(array/push into ~(bind ,ret ,(first signature)))
|
||||
(assign-type ret (first signature)))
|
||||
(each [arg-type arg] (map tuple (drop 1 signature) args)
|
||||
(array/push slots (visit1 arg into false arg-type)))
|
||||
(if is-syscall
|
||||
(array/push into ~(syscall :default ,ret (int ,is-syscall) ,;slots))
|
||||
(array/push into ~(call :default ,ret [pointer ,op] ,;slots)))
|
||||
ret)))
|
||||
|
||||
(errorf "cannot compile %q" code)))
|
||||
|
||||
# Check type-hint matches return type
|
||||
(if type-hint
|
||||
(when-let [t (first subresult)] # TODO - Disallow empty types
|
||||
(assert (= type-hint t) (string/format "%j, expected type %v, got %v" code type-hint t))))
|
||||
|
||||
subresult)
|
||||
|
||||
(varfn do-binop
|
||||
"Emit an operation such as (+ x y).
|
||||
Extended to support any number of arguments such as (+ x y z ...)"
|
||||
[opcode args into type-hint]
|
||||
(var typ type-hint)
|
||||
(var final nil)
|
||||
(def slots @[])
|
||||
(each arg args
|
||||
(def right (visit1 arg into false typ))
|
||||
(when (number? right) (array/push slots right))
|
||||
|
||||
# If we don't have a type hint, infer types from bottom up
|
||||
(when (nil? typ)
|
||||
(when-let [new-typ (get slot-types right)]
|
||||
(set typ new-typ)))
|
||||
|
||||
(set final
|
||||
(if final
|
||||
(let [result (get-slot)]
|
||||
(array/push slots result)
|
||||
(array/push into ~(,opcode ,result ,final ,right))
|
||||
result)
|
||||
right)))
|
||||
(assert typ (string "unable to infer type for %j" [opcode ;args]))
|
||||
(each slot (distinct slots)
|
||||
(array/push into ~(bind ,slot ,typ)))
|
||||
(assert final))
|
||||
|
||||
(varfn do-comp
|
||||
"Emit a comparison form such as (= x y z ...)"
|
||||
[opcode args into]
|
||||
(def result (get-slot))
|
||||
(def needs-temp (> 2 (length args)))
|
||||
(def temp-result (if needs-temp (get-slot) nil))
|
||||
(array/push into ~(bind ,result boolean))
|
||||
(when needs-temp
|
||||
(array/push into ~(bind ,temp-result boolean)))
|
||||
(var left nil)
|
||||
(var first-compare true)
|
||||
(var typ nil)
|
||||
(each arg args
|
||||
(def right (visit1 arg into false typ))
|
||||
# If we don't have a type hint, infer types from bottom up
|
||||
(when (nil? typ)
|
||||
(when-let [new-typ (get slot-types right)]
|
||||
(set typ new-typ)))
|
||||
(when left
|
||||
(if first-compare
|
||||
(array/push into ~(,opcode ,result ,left ,right))
|
||||
(do
|
||||
(array/push into ~(,opcode ,temp-result ,left ,right))
|
||||
(array/push into ~(and ,result ,temp-result ,result))))
|
||||
(set first-compare false))
|
||||
(set left right))
|
||||
result)
|
||||
|
||||
###
|
||||
### Top level
|
||||
###
|
||||
|
||||
(defn top
|
||||
"Visit and emit code for a top level form."
|
||||
[ctx form]
|
||||
(assert (tuple? form))
|
||||
(def [head & rest] form)
|
||||
(case head
|
||||
|
||||
# Declare a struct
|
||||
'defstruct
|
||||
(do
|
||||
(def into @[])
|
||||
(def [name & fields] rest)
|
||||
(assert (even? (length fields)) "expected an even number of fields for struct definition")
|
||||
(def field-types @[])
|
||||
(each [field-name typ] (partition 2 fields)
|
||||
# TODO - don't ignore field names
|
||||
(array/push field-types typ))
|
||||
(array/push into ~(type-struct ,name ,;field-types))
|
||||
# (eprintf "%.99M" into)
|
||||
(sysir/asm ctx into))
|
||||
|
||||
# Declare a union
|
||||
'defunion
|
||||
(do
|
||||
(def into @[])
|
||||
(def [name & fields] rest)
|
||||
(assert (even? (length fields)) "expected an even number of fields for struct definition")
|
||||
(def field-types @[])
|
||||
(each [field-name typ] (partition 2 fields)
|
||||
# TODO - don't ignore field names
|
||||
(array/push field-types typ))
|
||||
(array/push into ~(type-union ,name ,;field-types))
|
||||
# (eprintf "%.99M" into)
|
||||
(sysir/asm ctx into))
|
||||
|
||||
# Declare a pointer type
|
||||
'defpointer
|
||||
(do
|
||||
(def into @[])
|
||||
(def [name element] rest)
|
||||
(def field-types @[])
|
||||
(array/push into ~(type-pointer ,name ,element))
|
||||
# (eprintf "%.99M" into)
|
||||
(sysir/asm ctx into))
|
||||
|
||||
# Declare an array type
|
||||
'defarray
|
||||
(do
|
||||
(def into @[])
|
||||
(def [name element cnt] rest)
|
||||
(assert (and (pos? cnt) (int? cnt)) "expected positive integer for array count")
|
||||
(array/push into ~(type-array ,name ,element ,cnt))
|
||||
# (eprintf "%.99M" into)
|
||||
(sysir/asm ctx into))
|
||||
|
||||
# External function
|
||||
'defn-external
|
||||
(do
|
||||
(def [name args] rest)
|
||||
(assert (tuple? args))
|
||||
(def [fn-name fn-tp] (type-extract name 'void))
|
||||
(def pcount (length args)) #TODO - more complicated signatures
|
||||
(def signature @[fn-tp])
|
||||
(each arg args
|
||||
(def [name tp] (type-extract arg 'int))
|
||||
(array/push signature tp))
|
||||
(put functions fn-name (freeze signature)))
|
||||
|
||||
# External syscall
|
||||
'defn-syscall
|
||||
(do
|
||||
(def [name sysnum args] rest)
|
||||
(assert (tuple? args))
|
||||
(def [fn-name fn-tp] (type-extract name 'void))
|
||||
(def pcount (length args)) #TODO - more complicated signatures
|
||||
(def signature @[fn-tp])
|
||||
(each arg args
|
||||
(def [name tp] (type-extract arg 'int))
|
||||
(array/push signature tp))
|
||||
(put syscalls fn-name sysnum)
|
||||
(put functions fn-name (freeze signature)))
|
||||
|
||||
# Top level function definition
|
||||
'defn
|
||||
(do
|
||||
# TODO doc strings
|
||||
(table/clear name-to-slot)
|
||||
(table/clear slot-types)
|
||||
(array/clear slot-to-name)
|
||||
(def [name args & body] rest)
|
||||
(assert (tuple? args))
|
||||
(def [fn-name fn-tp] (type-extract name 'void))
|
||||
(def pcount (length args)) #TODO - more complicated signatures
|
||||
(def ir-asm
|
||||
@[~(link-name ,(string fn-name))
|
||||
~(parameter-count ,pcount)])
|
||||
(def signature @[fn-tp])
|
||||
(each arg args
|
||||
(def [name tp] (type-extract arg 'int))
|
||||
(def slot (get-slot name))
|
||||
(assign-type name tp)
|
||||
(array/push signature tp)
|
||||
(array/push ir-asm ~(bind ,slot ,tp)))
|
||||
(with-dyns [*ret-type* fn-tp]
|
||||
(each part body
|
||||
(visit1 part ir-asm true)))
|
||||
(put functions fn-name (freeze signature))
|
||||
(when (dyn :verbose) (eprintf "%.99M" ir-asm))
|
||||
(sysir/asm ctx ir-asm))
|
||||
|
||||
(errorf "unknown form %p" form)))
|
||||
|
||||
###
|
||||
### Setup
|
||||
###
|
||||
|
||||
(def ctx (sysir/context))
|
||||
(setup-default-types ctx)
|
||||
|
||||
(defn compile1
|
||||
[x]
|
||||
(top ctx x))
|
||||
|
||||
(defn dump
|
||||
[]
|
||||
(eprintf "%.99M\n" (sysir/to-ir ctx)))
|
||||
|
||||
(defn dumpx64
|
||||
[]
|
||||
(print (sysir/to-x64 ctx)))
|
||||
|
||||
(defn dumpx64-windows
|
||||
[]
|
||||
(print (sysir/to-x64 ctx @"" :windows)))
|
||||
|
||||
(defn dumpc
|
||||
[]
|
||||
(print (sysir/to-c ctx)))
|
||||
|
||||
###
|
||||
### Top Level aliases
|
||||
###
|
||||
|
||||
(defmacro defstruct [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defstruct ,;args))])
|
||||
(defmacro defunion [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defunion ,;args))])
|
||||
(defmacro defarray [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defarray ,;args))])
|
||||
(defmacro defpointer [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defpointer ,;args))])
|
||||
(defmacro defn-external [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defn-external ,;args))])
|
||||
(defmacro defn-syscall [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defn-syscall ,;args))])
|
||||
(defmacro defsys [& args] [compile1 ~',(keep-syntax! (dyn *macro-form*) ~(defn ,;args))])
|
||||
16
examples/sysir/hello.janet
Normal file
16
examples/sysir/hello.janet
Normal file
@@ -0,0 +1,16 @@
|
||||
(use ./frontend)
|
||||
|
||||
(defn-external printf:int [fmt:pointer])
|
||||
(defn-external exit:void [x:int])
|
||||
|
||||
(defsys _start:void []
|
||||
(printf "hello, world!\n")
|
||||
(exit (the int 0))
|
||||
(return))
|
||||
|
||||
(defn main [& args]
|
||||
(def [_ what] args)
|
||||
(eprint "MODE: " what)
|
||||
(case what
|
||||
"c" (dumpc)
|
||||
"x64" (dumpx64)))
|
||||
5
examples/sysir/run_drawing.sh
Executable file
5
examples/sysir/run_drawing.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
valgrind build/janet examples/sysir/drawing.janet > temp.c
|
||||
cc temp.c
|
||||
./a.out > temp.bmp
|
||||
feh temp.bmp
|
||||
5
examples/sysir/run_drawing2.sh
Executable file
5
examples/sysir/run_drawing2.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
valgrind build/janet examples/sysir/drawing2.janet > temp.nasm
|
||||
nasm -felf64 temp.nasm -l temp.lst -o temp.o
|
||||
ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o
|
||||
valgrind ./temp.bin
|
||||
4
examples/sysir/run_samples.bat
Normal file
4
examples/sysir/run_samples.bat
Normal file
@@ -0,0 +1,4 @@
|
||||
janet.exe examples/sysir/windows_samples.janet > temp.nasm
|
||||
nasm -fwin64 temp.nasm -l temp.lst -o temp.o
|
||||
link /entry:Start /subsystem:windows kernel32.lib user32.lib temp.o /out:temp.exe
|
||||
temp.exe
|
||||
5
examples/sysir/run_samples.sh
Executable file
5
examples/sysir/run_samples.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
valgrind build/janet examples/sysir/samples.janet > temp.nasm
|
||||
nasm -felf64 temp.nasm -l temp.lst -o temp.o
|
||||
ld -o temp.bin -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc temp.o
|
||||
valgrind ./temp.bin
|
||||
72
examples/sysir/samples.janet
Normal file
72
examples/sysir/samples.janet
Normal file
@@ -0,0 +1,72 @@
|
||||
(use ./frontend)
|
||||
|
||||
(defstruct vec3
|
||||
a float
|
||||
b float
|
||||
c float)
|
||||
|
||||
(defunion myunion
|
||||
a float
|
||||
b double
|
||||
c long)
|
||||
|
||||
(defarray myvec float 4)
|
||||
(defarray mymat myvec 4)
|
||||
|
||||
(defn-external printf:int [fmt:pointer x:int]) # TODO varargs
|
||||
|
||||
(defn-external exit:void [x:int])
|
||||
|
||||
(defsys square:int
|
||||
[num:int]
|
||||
(return (* 1 num num)))
|
||||
|
||||
(defsys simple:int [x:int]
|
||||
(def xyz:int (+ 1 2 3))
|
||||
(return (* x 2 x)))
|
||||
|
||||
(defsys myprog:int []
|
||||
(def xyz:int (+ 1 2 3))
|
||||
(def abc:int (* 4 5 6))
|
||||
(def x:boolean (= xyz 5))
|
||||
(var i:int 0)
|
||||
(while (< i 10)
|
||||
(set i (+ 1 i))
|
||||
(printf "i = %d\n" i))
|
||||
(printf "hello, world!\n%d\n" (if x abc xyz))
|
||||
(return (simple (* abc xyz))))
|
||||
|
||||
(defsys doloop [x:int y:int]
|
||||
(var i:int x)
|
||||
(while (< i y)
|
||||
(set i (+ 1 i))
|
||||
(printf "i = %d\n" i))
|
||||
(myprog)
|
||||
(return x))
|
||||
|
||||
(defsys _start:void []
|
||||
#(syscall 1 1 "Hello, world!\n" 14)
|
||||
(doloop 10 20)
|
||||
(exit (the int 0))
|
||||
(return))
|
||||
|
||||
(defsys test_inttypes:ulong []
|
||||
(def x:ulong 123:u)
|
||||
(return (+ x x)))
|
||||
|
||||
(defsys test_arrays:myvec [a:myvec b:myvec]
|
||||
(return (+ a b)))
|
||||
|
||||
'(defsys make_array:myvec []
|
||||
(def vec:myvec [0 0 0 0])
|
||||
(return vec))
|
||||
|
||||
'(defsys make_mat:mymat []
|
||||
(def mat:mymat [[1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1]])
|
||||
(return mat))
|
||||
|
||||
####
|
||||
|
||||
#(dump)
|
||||
#(dumpc)
|
||||
(dumpx64)
|
||||
10
examples/sysir/typeerr1.janet
Normal file
10
examples/sysir/typeerr1.janet
Normal file
@@ -0,0 +1,10 @@
|
||||
(def ir-asm
|
||||
'((link-name "redefine_type_fail")
|
||||
(type-prim Real f32)
|
||||
(type-prim 1 s32)
|
||||
(bind bob Real)
|
||||
(return bob)))
|
||||
|
||||
(def ctx (sysir/context))
|
||||
(sysir/asm ctx ir-asm)
|
||||
(print (sysir/to-c ctx))
|
||||
14
examples/sysir/windows_samples.janet
Normal file
14
examples/sysir/windows_samples.janet
Normal file
@@ -0,0 +1,14 @@
|
||||
(use ./frontend)
|
||||
|
||||
(def winmain
|
||||
'(defn Start:void []
|
||||
(MessageBoxExA (the pointer 0) "Hello, world!" "Test" 0 (the s16 0))
|
||||
(ExitProcess (the int 0))
|
||||
(return)))
|
||||
|
||||
####
|
||||
|
||||
(compile1 winmain)
|
||||
#(dump)
|
||||
#(dumpc)
|
||||
(dumpx64-windows)
|
||||
24
examples/sysir/x64.janet
Normal file
24
examples/sysir/x64.janet
Normal file
@@ -0,0 +1,24 @@
|
||||
(use ./frontend)
|
||||
|
||||
(defn-external printf:int [fmt:pointer x:int])
|
||||
(defn-external exit:void [x:int])
|
||||
|
||||
(defsys doloop [x:int y:int]
|
||||
(var i:int x)
|
||||
(printf "initial i = %d\n" i)
|
||||
(while (< i y)
|
||||
(set i (+ 1 i))
|
||||
(printf "i = %d\n" i))
|
||||
(return x))
|
||||
|
||||
(defsys _start:void []
|
||||
(doloop 10 20)
|
||||
(exit (the int 0))
|
||||
(return))
|
||||
|
||||
(defn main [& args]
|
||||
(def [_ what] args)
|
||||
(eprint "MODE: " what)
|
||||
(case what
|
||||
"c" (dumpc)
|
||||
"x64" (dumpx64)))
|
||||
20
examples/weak-tables.janet
Normal file
20
examples/weak-tables.janet
Normal file
@@ -0,0 +1,20 @@
|
||||
(def weak-k (table/weak-keys 10))
|
||||
(def weak-v (table/weak-values 10))
|
||||
(def weak-kv (table/weak 10))
|
||||
|
||||
(put weak-kv (gensym) 10)
|
||||
(put weak-kv :hello :world)
|
||||
(put weak-k :abc123zz77asda :stuff)
|
||||
(put weak-k true :abc123zz77asda)
|
||||
(put weak-k :zyzzyz false)
|
||||
(put weak-v (gensym) 10)
|
||||
(put weak-v 20 (gensym))
|
||||
(print "before gc")
|
||||
(tracev weak-k)
|
||||
(tracev weak-v)
|
||||
(tracev weak-kv)
|
||||
(gccollect)
|
||||
(print "after gc")
|
||||
(tracev weak-k)
|
||||
(tracev weak-v)
|
||||
(tracev weak-kv)
|
||||
4
janet.1
4
janet.1
@@ -214,7 +214,7 @@ Don't execute a script, only compile it to check for errors. Useful for linting
|
||||
.BR \-m\ syspath
|
||||
Set the dynamic binding :syspath to the string syspath so that Janet will load system modules
|
||||
from a directory different than the default. The default is set when Janet is built, and defaults to
|
||||
/usr/local/lib/janet on Linux/Posix, and C:/Janet/Library on Windows. This option supersedes JANET_PATH.
|
||||
/usr/local/lib/janet on Linux/Posix. On Windows, there is no default value. This option supersedes JANET_PATH.
|
||||
|
||||
.TP
|
||||
.BR \-c\ source\ output
|
||||
@@ -255,7 +255,7 @@ and then arguments to the script.
|
||||
.RS
|
||||
The location to look for Janet libraries. This is the only environment variable Janet needs to
|
||||
find native and source code modules. If no JANET_PATH is set, Janet will look in
|
||||
the default location set at compile time.
|
||||
the default location set at compile time. This should be a colon-separated list of directory names on Linux/Posix, and a semicolon-separated list on Windows. Note that a typical setup (i.e. not NixOS / Guix) will only use a single directory.
|
||||
.RE
|
||||
|
||||
.B JANET_PROFILE
|
||||
|
||||
124
meson.build
124
meson.build
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2023 Calvin Rose and contributors
|
||||
# Copyright (c) 2025 Calvin Rose and contributors
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,16 +20,34 @@
|
||||
|
||||
project('janet', 'c',
|
||||
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
|
||||
version : '1.29.1')
|
||||
version : '1.40.0')
|
||||
|
||||
# Global settings
|
||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
|
||||
header_path = join_paths(get_option('prefix'), get_option('includedir'), 'janet')
|
||||
|
||||
# Link math library on all systems
|
||||
# Compilers
|
||||
cc = meson.get_compiler('c')
|
||||
native_cc = meson.get_compiler('c', native : true)
|
||||
|
||||
# Native deps
|
||||
native_m_dep = native_cc.find_library('m', required : false)
|
||||
native_dl_dep = native_cc.find_library('dl', required : false)
|
||||
native_android_spawn_dep = native_cc.find_library('android-spawn', required : false)
|
||||
native_thread_dep = dependency('threads', native : true)
|
||||
|
||||
# Deps
|
||||
m_dep = cc.find_library('m', required : false)
|
||||
dl_dep = cc.find_library('dl', required : false)
|
||||
|
||||
# for MINGW/MSYS2
|
||||
native_ws2_dep = native_cc.find_library('ws2_32', required: false)
|
||||
native_psapi_dep = native_cc.find_library('psapi', required: false)
|
||||
native_wsock_dep = native_cc.find_library('wsock32', required: false)
|
||||
ws2_dep = cc.find_library('ws2_32', required: false)
|
||||
psapi_dep = cc.find_library('psapi', required: false)
|
||||
wsock_dep = cc.find_library('wsock32', required: false)
|
||||
|
||||
android_spawn_dep = cc.find_library('android-spawn', required : false)
|
||||
thread_dep = dependency('threads')
|
||||
|
||||
@@ -61,6 +79,7 @@ conf.set('JANET_NO_SOURCEMAPS', not get_option('sourcemaps'))
|
||||
conf.set('JANET_NO_ASSEMBLER', not get_option('assembler'))
|
||||
conf.set('JANET_NO_PEG', not get_option('peg'))
|
||||
conf.set('JANET_NO_NET', not get_option('net'))
|
||||
conf.set('JANET_NO_IPV6', not get_option('ipv6'))
|
||||
conf.set('JANET_NO_EV', not get_option('ev') or get_option('single_threaded'))
|
||||
conf.set('JANET_REDUCED_OS', get_option('reduced_os'))
|
||||
conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
|
||||
@@ -78,12 +97,17 @@ conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
|
||||
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
|
||||
conf.set('JANET_NO_FFI', not get_option('ffi'))
|
||||
conf.set('JANET_NO_FFI_JIT', not get_option('ffi_jit'))
|
||||
conf.set('JANET_NO_FILEWATCH', not get_option('filewatch'))
|
||||
conf.set('JANET_NO_CRYPTORAND', not get_option('cryptorand'))
|
||||
if get_option('os_name') != ''
|
||||
conf.set('JANET_OS_NAME', get_option('os_name'))
|
||||
endif
|
||||
if get_option('arch_name') != ''
|
||||
conf.set('JANET_ARCH_NAME', get_option('arch_name'))
|
||||
endif
|
||||
if get_option('thread_local_prefix') != ''
|
||||
conf.set('JANET_THREAD_LOCAL', get_option('thread_local_prefix'))
|
||||
endif
|
||||
jconf = configure_file(output : 'janetconf.h',
|
||||
configuration : conf)
|
||||
|
||||
@@ -102,7 +126,8 @@ core_headers = [
|
||||
'src/core/regalloc.h',
|
||||
'src/core/compile.h',
|
||||
'src/core/emit.h',
|
||||
'src/core/symcache.h'
|
||||
'src/core/symcache.h',
|
||||
'src/core/sysir.h',
|
||||
]
|
||||
|
||||
core_src = [
|
||||
@@ -120,6 +145,7 @@ core_src = [
|
||||
'src/core/ev.c',
|
||||
'src/core/ffi.c',
|
||||
'src/core/fiber.c',
|
||||
'src/core/filewatch.c',
|
||||
'src/core/gc.c',
|
||||
'src/core/inttypes.c',
|
||||
'src/core/io.c',
|
||||
@@ -138,6 +164,9 @@ core_src = [
|
||||
'src/core/strtod.c',
|
||||
'src/core/struct.c',
|
||||
'src/core/symcache.c',
|
||||
'src/core/sysir.c',
|
||||
'src/core/sysir_c.c',
|
||||
'src/core/sysir_x86.c',
|
||||
'src/core/table.c',
|
||||
'src/core/tuple.c',
|
||||
'src/core/util.c',
|
||||
@@ -160,16 +189,23 @@ mainclient_src = [
|
||||
'src/mainclient/shell.c'
|
||||
]
|
||||
|
||||
janet_dependencies = [m_dep, dl_dep, android_spawn_dep, ws2_dep, psapi_dep, wsock_dep]
|
||||
janet_native_dependencies = [native_m_dep, native_dl_dep, native_android_spawn_dep, native_ws2_dep, native_psapi_dep, native_wsock_dep]
|
||||
if not get_option('single_threaded')
|
||||
janet_dependencies += thread_dep
|
||||
janet_native_dependencies += native_thread_dep
|
||||
endif
|
||||
|
||||
# Build boot binary
|
||||
janet_boot = executable('janet-boot', core_src, boot_src,
|
||||
include_directories : incdir,
|
||||
c_args : '-DJANET_BOOTSTRAP',
|
||||
dependencies : [m_dep, dl_dep, thread_dep, android_spawn_dep],
|
||||
dependencies : janet_native_dependencies,
|
||||
native : true)
|
||||
|
||||
# Build janet.c
|
||||
janetc = custom_target('janetc',
|
||||
input : [janet_boot],
|
||||
input : [janet_boot, 'src/boot/boot.janet'],
|
||||
output : 'janet.c',
|
||||
capture : true,
|
||||
command : [
|
||||
@@ -177,30 +213,41 @@ janetc = custom_target('janetc',
|
||||
'JANET_PATH', janet_path
|
||||
])
|
||||
|
||||
janet_dependencies = [m_dep, dl_dep, android_spawn_dep]
|
||||
if not get_option('single_threaded')
|
||||
janet_dependencies += thread_dep
|
||||
# Allow building with no shared library
|
||||
if cc.has_argument('-fvisibility=hidden')
|
||||
lib_cflags = ['-fvisibility=hidden']
|
||||
else
|
||||
lib_cflags = []
|
||||
endif
|
||||
|
||||
libjanet = library('janet', janetc,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
version: meson.project_version(),
|
||||
soversion: version_parts[0] + '.' + version_parts[1],
|
||||
install : true)
|
||||
|
||||
if get_option('shared')
|
||||
libjanet = library('janet', janetc,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
version: meson.project_version(),
|
||||
soversion: version_parts[0] + '.' + version_parts[1],
|
||||
c_args : lib_cflags,
|
||||
install : true)
|
||||
# Extra c flags - adding -fvisibility=hidden matches the Makefile and
|
||||
# shaves off about 10k on linux x64, likely similar on other platforms.
|
||||
if cc.has_argument('-fvisibility=hidden')
|
||||
extra_cflags = ['-fvisibility=hidden']
|
||||
if cc.has_argument('-fvisibility=hidden')
|
||||
extra_cflags = ['-fvisibility=hidden', '-DJANET_DLL_IMPORT']
|
||||
else
|
||||
extra_cflags = ['-DJANET_DLL_IMPORT']
|
||||
endif
|
||||
janet_mainclient = executable('janet', mainclient_src,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
link_with: [libjanet],
|
||||
c_args : extra_cflags,
|
||||
install : true)
|
||||
else
|
||||
extra_cflags = []
|
||||
# No shared library
|
||||
janet_mainclient = executable('janet', mainclient_src, janetc,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
c_args : lib_cflags,
|
||||
install : true)
|
||||
endif
|
||||
janet_mainclient = executable('janet', janetc, mainclient_src,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
c_args : extra_cflags,
|
||||
install : true)
|
||||
|
||||
if meson.is_cross_build()
|
||||
native_cc = meson.get_compiler('c', native: true)
|
||||
@@ -211,7 +258,7 @@ if meson.is_cross_build()
|
||||
endif
|
||||
janet_nativeclient = executable('janet-native', janetc, mainclient_src,
|
||||
include_directories : incdir,
|
||||
dependencies : janet_dependencies,
|
||||
dependencies : janet_native_dependencies,
|
||||
c_args : extra_native_cflags,
|
||||
native : true)
|
||||
else
|
||||
@@ -231,13 +278,16 @@ test_files = [
|
||||
'test/suite-asm.janet',
|
||||
'test/suite-boot.janet',
|
||||
'test/suite-buffer.janet',
|
||||
'test/suite-bundle.janet',
|
||||
'test/suite-capi.janet',
|
||||
'test/suite-cfuns.janet',
|
||||
'test/suite-compile.janet',
|
||||
'test/suite-corelib.janet',
|
||||
'test/suite-debug.janet',
|
||||
'test/suite-ev.janet',
|
||||
'test/suite-ev2.janet',
|
||||
'test/suite-ffi.janet',
|
||||
'test/suite-filewatch.janet',
|
||||
'test/suite-inttypes.janet',
|
||||
'test/suite-io.janet',
|
||||
'test/suite-marsh.janet',
|
||||
@@ -251,7 +301,9 @@ test_files = [
|
||||
'test/suite-strtod.janet',
|
||||
'test/suite-struct.janet',
|
||||
'test/suite-symcache.janet',
|
||||
'test/suite-sysir.janet',
|
||||
'test/suite-table.janet',
|
||||
'test/suite-tuple.janet',
|
||||
'test/suite-unknown.janet',
|
||||
'test/suite-value.janet',
|
||||
'test/suite-vm.janet'
|
||||
@@ -264,14 +316,15 @@ endforeach
|
||||
run_target('repl', command : [janet_nativeclient])
|
||||
|
||||
# For use as meson subproject (wrap)
|
||||
janet_dep = declare_dependency(include_directories : incdir,
|
||||
link_with : libjanet)
|
||||
|
||||
if get_option('shared')
|
||||
janet_dep = declare_dependency(include_directories : incdir,
|
||||
link_with : libjanet)
|
||||
# pkgconfig
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(libjanet,
|
||||
subdirs: 'janet',
|
||||
description: 'Library for the Janet programming language.')
|
||||
pkg = import('pkgconfig')
|
||||
pkg.generate(libjanet,
|
||||
subdirs: 'janet',
|
||||
description: 'Library for the Janet programming language.')
|
||||
endif
|
||||
|
||||
# Installation
|
||||
install_man('janet.1')
|
||||
@@ -281,11 +334,12 @@ patched_janet = custom_target('patched-janeth',
|
||||
install : true,
|
||||
install_dir : join_paths(get_option('includedir'), 'janet'),
|
||||
build_by_default : true,
|
||||
output : ['janet.h'],
|
||||
output : ['janet_' + meson.project_version() + '.h'],
|
||||
command : [janet_nativeclient, '@INPUT@', '@OUTPUT@'])
|
||||
|
||||
# Create a version of the janet.h header that matches what jpm often expects
|
||||
if meson.version().version_compare('>=0.61')
|
||||
install_symlink('janet.h', pointing_to: 'janet/janet.h', install_dir: get_option('includedir'))
|
||||
install_symlink('janet.h', pointing_to: 'janet/janet_' + meson.project_version() + '.h', install_dir: get_option('includedir'))
|
||||
install_symlink('janet.h', pointing_to: 'janet_' + meson.project_version() + '.h', install_dir: join_paths(get_option('includedir'), 'janet'))
|
||||
endif
|
||||
|
||||
|
||||
@@ -11,16 +11,18 @@ option('peg', type : 'boolean', value : true)
|
||||
option('int_types', type : 'boolean', value : true)
|
||||
option('prf', type : 'boolean', value : false)
|
||||
option('net', type : 'boolean', value : true)
|
||||
option('ipv6', type : 'boolean', value : true)
|
||||
option('ev', type : 'boolean', value : true)
|
||||
option('processes', type : 'boolean', value : true)
|
||||
option('umask', type : 'boolean', value : true)
|
||||
option('realpath', type : 'boolean', value : true)
|
||||
option('simple_getline', type : 'boolean', value : false)
|
||||
option('epoll', type : 'boolean', value : false)
|
||||
option('kqueue', type : 'boolean', value : false)
|
||||
option('interpreter_interrupt', type : 'boolean', value : false)
|
||||
option('epoll', type : 'boolean', value : true)
|
||||
option('kqueue', type : 'boolean', value : true)
|
||||
option('interpreter_interrupt', type : 'boolean', value : true)
|
||||
option('ffi', type : 'boolean', value : true)
|
||||
option('ffi_jit', type : 'boolean', value : true)
|
||||
option('filewatch', type : 'boolean', value : true)
|
||||
|
||||
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
|
||||
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)
|
||||
@@ -28,4 +30,7 @@ option('max_macro_expand', type : 'integer', min : 1, max : 8000, value : 200)
|
||||
option('stack_max', type : 'integer', min : 8096, max : 0x7fffffff, value : 0x7fffffff)
|
||||
|
||||
option('arch_name', type : 'string', value: '')
|
||||
option('thread_local_prefix', type : 'string', value: '')
|
||||
option('os_name', type : 'string', value: '')
|
||||
option('shared', type : 'boolean', value: true)
|
||||
option('cryptorand', type : 'boolean', value: true)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
1741
src/boot/boot.janet
1741
src/boot/boot.janet
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <janet.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "tests.h"
|
||||
@@ -35,6 +35,11 @@ int system_test() {
|
||||
assert(sizeof(void *) == 8);
|
||||
#endif
|
||||
|
||||
/* Check the version defines are self consistent */
|
||||
char version_combined[256];
|
||||
sprintf(version_combined, "%d.%d.%d%s", JANET_VERSION_MAJOR, JANET_VERSION_MINOR, JANET_VERSION_PATCH, JANET_VERSION_EXTRA);
|
||||
assert(!strcmp(JANET_VERSION, version_combined));
|
||||
|
||||
/* Reflexive testing and nanbox testing */
|
||||
assert(janet_equals(janet_wrap_nil(), janet_wrap_nil()));
|
||||
assert(janet_equals(janet_wrap_false(), janet_wrap_false()));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
#define JANETCONF_H
|
||||
|
||||
#define JANET_VERSION_MAJOR 1
|
||||
#define JANET_VERSION_MINOR 29
|
||||
#define JANET_VERSION_PATCH 1
|
||||
#define JANET_VERSION_MINOR 40
|
||||
#define JANET_VERSION_PATCH 0
|
||||
#define JANET_VERSION_EXTRA ""
|
||||
#define JANET_VERSION "1.29.1"
|
||||
#define JANET_VERSION "1.40.0"
|
||||
|
||||
/* #define JANET_BUILD "local" */
|
||||
|
||||
/* These settings all affect linking, so use cautiously. */
|
||||
/* #define JANET_SINGLE_THREADED */
|
||||
/* #define JANET_THREAD_LOCAL _Thread_local */
|
||||
/* #define JANET_NO_DYNAMIC_MODULES */
|
||||
/* #define JANET_NO_NANBOX */
|
||||
/* #define JANET_API __attribute__((visibility ("default"))) */
|
||||
@@ -29,6 +30,7 @@
|
||||
/* #define JANET_NO_NET */
|
||||
/* #define JANET_NO_INT_TYPES */
|
||||
/* #define JANET_NO_EV */
|
||||
/* #define JANET_NO_FILEWATCH */
|
||||
/* #define JANET_NO_REALPATH */
|
||||
/* #define JANET_NO_SYMLINKS */
|
||||
/* #define JANET_NO_UMASK */
|
||||
@@ -52,6 +54,9 @@
|
||||
/* #define JANET_EV_NO_EPOLL */
|
||||
/* #define JANET_EV_NO_KQUEUE */
|
||||
/* #define JANET_NO_INTERPRETER_INTERRUPT */
|
||||
/* #define JANET_NO_IPV6 */
|
||||
/* #define JANET_NO_CRYPTORAND */
|
||||
/* #define JANET_USE_STDATOMIC */
|
||||
|
||||
/* Custom vm allocator support */
|
||||
/* #include <mimalloc.h> */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -31,8 +31,6 @@
|
||||
#ifdef JANET_EV
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <stdatomic.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -97,14 +95,6 @@ size_t janet_os_rwlock_size(void) {
|
||||
return sizeof(void *);
|
||||
}
|
||||
|
||||
static int32_t janet_incref(JanetAbstractHead *ab) {
|
||||
return InterlockedIncrement((LONG volatile *) &ab->gc.data.refcount);
|
||||
}
|
||||
|
||||
static int32_t janet_decref(JanetAbstractHead *ab) {
|
||||
return InterlockedDecrement((LONG volatile *) &ab->gc.data.refcount);
|
||||
}
|
||||
|
||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
|
||||
InitializeCriticalSection((CRITICAL_SECTION *) mutex);
|
||||
}
|
||||
@@ -157,14 +147,6 @@ size_t janet_os_rwlock_size(void) {
|
||||
return sizeof(pthread_rwlock_t);
|
||||
}
|
||||
|
||||
static int32_t janet_incref(JanetAbstractHead *ab) {
|
||||
return __atomic_add_fetch(&ab->gc.data.refcount, 1, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
static int32_t janet_decref(JanetAbstractHead *ab) {
|
||||
return __atomic_add_fetch(&ab->gc.data.refcount, -1, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
@@ -182,7 +164,7 @@ void janet_os_mutex_lock(JanetOSMutex *mutex) {
|
||||
|
||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
|
||||
int ret = pthread_mutex_unlock((pthread_mutex_t *) mutex);
|
||||
if (ret) janet_panic("cannot release lock");
|
||||
if (ret) janet_panicf("cannot release lock: %s", strerror(ret));
|
||||
}
|
||||
|
||||
void janet_os_rwlock_init(JanetOSRWLock *rwlock) {
|
||||
@@ -212,11 +194,11 @@ void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) {
|
||||
#endif
|
||||
|
||||
int32_t janet_abstract_incref(void *abst) {
|
||||
return janet_incref(janet_abstract_head(abst));
|
||||
return janet_atomic_inc(&janet_abstract_head(abst)->gc.data.refcount);
|
||||
}
|
||||
|
||||
int32_t janet_abstract_decref(void *abst) {
|
||||
return janet_decref(janet_abstract_head(abst));
|
||||
return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -30,9 +30,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Creates a new array */
|
||||
JanetArray *janet_array(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
|
||||
static void janet_array_impl(JanetArray *array, int32_t capacity) {
|
||||
Janet *data = NULL;
|
||||
if (capacity > 0) {
|
||||
janet_vm.next_collection += capacity * sizeof(Janet);
|
||||
@@ -44,6 +42,19 @@ JanetArray *janet_array(int32_t capacity) {
|
||||
array->count = 0;
|
||||
array->capacity = capacity;
|
||||
array->data = data;
|
||||
}
|
||||
|
||||
/* Creates a new array */
|
||||
JanetArray *janet_array(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
|
||||
janet_array_impl(array, capacity);
|
||||
return array;
|
||||
}
|
||||
|
||||
/* Creates a new array with weak references */
|
||||
JanetArray *janet_array_weak(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY_WEAK, sizeof(JanetArray));
|
||||
janet_array_impl(array, capacity);
|
||||
return array;
|
||||
}
|
||||
|
||||
@@ -132,6 +143,15 @@ JANET_CORE_FN(cfun_array_new,
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_array_weak,
|
||||
"(array/weak capacity)",
|
||||
"Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
JanetArray *array = janet_array_weak(cap);
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_array_new_filled,
|
||||
"(array/new-filled count &opt value)",
|
||||
"Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") {
|
||||
@@ -177,8 +197,8 @@ JANET_CORE_FN(cfun_array_peek,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_array_push,
|
||||
"(array/push arr x)",
|
||||
"Insert an element in the end of an array. Modifies the input array and returns it.") {
|
||||
"(array/push arr & xs)",
|
||||
"Push all the elements of xs to the end of an array. Modifies the input array and returns it.") {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
if (INT32_MAX - argc + 1 <= array->count) {
|
||||
@@ -211,7 +231,7 @@ JANET_CORE_FN(cfun_array_slice,
|
||||
"Takes a slice of array or tuple from `start` to `end`. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the "
|
||||
"end of the array. By default, `start` is 0 and `end` is the length of the array. "
|
||||
"Note that index -1 is synonymous with index `(length arrtup)` to allow a full "
|
||||
"Note that if the range is negative, it is taken as (start, end] to allow a full "
|
||||
"negative slice range. Returns a new array.") {
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
@@ -255,12 +275,37 @@ JANET_CORE_FN(cfun_array_concat,
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_array_join,
|
||||
"(array/join arr & parts)",
|
||||
"Join a variable number of arrays and tuples into the first argument, "
|
||||
"which must be an array. "
|
||||
"Return the modified array `arr`.") {
|
||||
int32_t i;
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
for (i = 1; i < argc; i++) {
|
||||
int32_t j, len = 0;
|
||||
const Janet *vals = NULL;
|
||||
if (!janet_indexed_view(argv[i], &vals, &len)) {
|
||||
janet_panicf("expected indexed type for argument %d, got %v", i, argv[i]);
|
||||
}
|
||||
if (array->data == vals) {
|
||||
int32_t newcount = array->count + len;
|
||||
janet_array_ensure(array, newcount, 2);
|
||||
janet_indexed_view(argv[i], &vals, &len);
|
||||
}
|
||||
for (j = 0; j < len; j++)
|
||||
janet_array_push(array, vals[j]);
|
||||
}
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_array_insert,
|
||||
"(array/insert arr at & xs)",
|
||||
"Insert all `xs` into array `arr` at index `at`. `at` should be an integer between "
|
||||
"0 and the length of the array. A negative value for `at` will index backwards from "
|
||||
"the end of the array, such that inserting at -1 appends to the array. "
|
||||
"Returns the array.") {
|
||||
"the end of the array, inserting after the index such that inserting at -1 appends to "
|
||||
"the array. Returns the array.") {
|
||||
size_t chunksize, restsize;
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
@@ -297,7 +342,7 @@ JANET_CORE_FN(cfun_array_remove,
|
||||
int32_t at = janet_getinteger(argv, 1);
|
||||
int32_t n = 1;
|
||||
if (at < 0) {
|
||||
at = array->count + at + 1;
|
||||
at = array->count + at;
|
||||
}
|
||||
if (at < 0 || at > array->count)
|
||||
janet_panicf("removal index %d out of range [0,%d]", at, array->count);
|
||||
@@ -352,6 +397,7 @@ JANET_CORE_FN(cfun_array_clear,
|
||||
void janet_lib_array(JanetTable *env) {
|
||||
JanetRegExt array_cfuns[] = {
|
||||
JANET_CORE_REG("array/new", cfun_array_new),
|
||||
JANET_CORE_REG("array/weak", cfun_array_weak),
|
||||
JANET_CORE_REG("array/new-filled", cfun_array_new_filled),
|
||||
JANET_CORE_REG("array/fill", cfun_array_fill),
|
||||
JANET_CORE_REG("array/pop", cfun_array_pop),
|
||||
@@ -364,6 +410,7 @@ void janet_lib_array(JanetTable *env) {
|
||||
JANET_CORE_REG("array/remove", cfun_array_remove),
|
||||
JANET_CORE_REG("array/trim", cfun_array_trim),
|
||||
JANET_CORE_REG("array/clear", cfun_array_clear),
|
||||
JANET_CORE_REG("array/join", cfun_array_join),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, array_cfuns);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -560,6 +560,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
x = janet_get1(s, janet_ckeywordv("vararg"));
|
||||
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
|
||||
|
||||
/* Initialize slotcount */
|
||||
def->slotcount = !!(def->flags & JANET_FUNCDEF_FLAG_VARARG) + def->arity;
|
||||
|
||||
/* Check structarg */
|
||||
x = janet_get1(s, janet_ckeywordv("structarg"));
|
||||
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
@@ -784,8 +787,9 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
}
|
||||
|
||||
/* Verify the func def */
|
||||
if (janet_verify(def)) {
|
||||
janet_asm_error(&a, "invalid assembly");
|
||||
int verify_status = janet_verify(def);
|
||||
if (verify_status) {
|
||||
janet_asm_errorv(&a, janet_formatc("invalid assembly (%d)", verify_status));
|
||||
}
|
||||
|
||||
/* Add final flags */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -135,8 +135,7 @@ void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
|
||||
|
||||
/* Push a cstring to buffer */
|
||||
void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) {
|
||||
int32_t len = 0;
|
||||
while (cstring[len]) ++len;
|
||||
int32_t len = (int32_t) strlen(cstring);
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len);
|
||||
}
|
||||
|
||||
@@ -221,6 +220,20 @@ JANET_CORE_FN(cfun_buffer_new_filled,
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_frombytes,
|
||||
"(buffer/from-bytes & byte-vals)",
|
||||
"Creates a buffer from integer parameters with byte values. All integers "
|
||||
"will be coerced to the range of 1 byte 0-255.") {
|
||||
int32_t i;
|
||||
JanetBuffer *buffer = janet_buffer(argc);
|
||||
for (i = 0; i < argc; i++) {
|
||||
int32_t c = janet_getinteger(argv, i);
|
||||
buffer->data[i] = c & 0xFF;
|
||||
}
|
||||
buffer->count = argc;
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_fill,
|
||||
"(buffer/fill buffer &opt byte)",
|
||||
"Fill up a buffer with bytes, defaulting to 0s. Does not change the buffer's length. "
|
||||
@@ -307,6 +320,133 @@ JANET_CORE_FN(cfun_buffer_chars,
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static int should_reverse_bytes(const Janet *argv, int32_t argc) {
|
||||
JanetKeyword order_kw = janet_getkeyword(argv, argc);
|
||||
if (!janet_cstrcmp(order_kw, "le")) {
|
||||
#if JANET_BIG_ENDIAN
|
||||
return 1;
|
||||
#endif
|
||||
} else if (!janet_cstrcmp(order_kw, "be")) {
|
||||
#if JANET_LITTLE_ENDIAN
|
||||
return 1;
|
||||
#endif
|
||||
} else if (!janet_cstrcmp(order_kw, "native")) {
|
||||
return 0;
|
||||
} else {
|
||||
janet_panicf("expected endianness :le, :be or :native, got %v", argv[1]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reverse_u32(uint8_t bytes[4]) {
|
||||
uint8_t temp;
|
||||
temp = bytes[3];
|
||||
bytes[3] = bytes[0];
|
||||
bytes[0] = temp;
|
||||
temp = bytes[2];
|
||||
bytes[2] = bytes[1];
|
||||
bytes[1] = temp;
|
||||
}
|
||||
|
||||
static void reverse_u64(uint8_t bytes[8]) {
|
||||
uint8_t temp;
|
||||
temp = bytes[7];
|
||||
bytes[7] = bytes[0];
|
||||
bytes[0] = temp;
|
||||
temp = bytes[6];
|
||||
bytes[6] = bytes[1];
|
||||
bytes[1] = temp;
|
||||
temp = bytes[5];
|
||||
bytes[5] = bytes[2];
|
||||
bytes[2] = temp;
|
||||
temp = bytes[4];
|
||||
bytes[4] = bytes[3];
|
||||
bytes[3] = temp;
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_uint16,
|
||||
"(buffer/push-uint16 buffer order data)",
|
||||
"Push a 16 bit unsigned integer data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
uint16_t data = janet_getuinteger16(argv, 2);
|
||||
uint8_t bytes[sizeof(data)];
|
||||
memcpy(bytes, &data, sizeof(bytes));
|
||||
if (reverse) {
|
||||
uint8_t temp = bytes[1];
|
||||
bytes[1] = bytes[0];
|
||||
bytes[0] = temp;
|
||||
}
|
||||
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_uint32,
|
||||
"(buffer/push-uint32 buffer order data)",
|
||||
"Push a 32 bit unsigned integer data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
uint32_t data = janet_getuinteger(argv, 2);
|
||||
uint8_t bytes[sizeof(data)];
|
||||
memcpy(bytes, &data, sizeof(bytes));
|
||||
if (reverse)
|
||||
reverse_u32(bytes);
|
||||
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_uint64,
|
||||
"(buffer/push-uint64 buffer order data)",
|
||||
"Push a 64 bit unsigned integer data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
uint64_t data = janet_getuinteger64(argv, 2);
|
||||
uint8_t bytes[sizeof(data)];
|
||||
memcpy(bytes, &data, sizeof(bytes));
|
||||
if (reverse)
|
||||
reverse_u64(bytes);
|
||||
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_float32,
|
||||
"(buffer/push-float32 buffer order data)",
|
||||
"Push the underlying bytes of a 32 bit float data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
float data = (float) janet_getnumber(argv, 2);
|
||||
uint8_t bytes[sizeof(data)];
|
||||
memcpy(bytes, &data, sizeof(bytes));
|
||||
if (reverse)
|
||||
reverse_u32(bytes);
|
||||
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_push_float64,
|
||||
"(buffer/push-float64 buffer order data)",
|
||||
"Push the underlying bytes of a 64 bit float data onto the end of the buffer. "
|
||||
"Returns the modified buffer.") {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int reverse = should_reverse_bytes(argv, 1);
|
||||
double data = janet_getnumber(argv, 2);
|
||||
uint8_t bytes[sizeof(data)];
|
||||
memcpy(bytes, &data, sizeof(bytes));
|
||||
if (reverse)
|
||||
reverse_u64(bytes);
|
||||
janet_buffer_push_bytes(buffer, bytes, sizeof(bytes));
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static void buffer_push_impl(JanetBuffer *buffer, Janet *argv, int32_t argc_offset, int32_t argc) {
|
||||
for (int32_t i = argc_offset; i < argc; i++) {
|
||||
if (janet_checktype(argv[i], JANET_NUMBER)) {
|
||||
@@ -462,13 +602,15 @@ JANET_CORE_FN(cfun_buffer_blit,
|
||||
int same_buf = src.bytes == dest->data;
|
||||
int32_t offset_dest = 0;
|
||||
int32_t offset_src = 0;
|
||||
if (argc > 2)
|
||||
if (argc > 2 && !janet_checktype(argv[2], JANET_NIL))
|
||||
offset_dest = janet_gethalfrange(argv, 2, dest->count, "dest-start");
|
||||
if (argc > 3)
|
||||
if (argc > 3 && !janet_checktype(argv[3], JANET_NIL))
|
||||
offset_src = janet_gethalfrange(argv, 3, src.len, "src-start");
|
||||
int32_t length_src;
|
||||
if (argc > 4) {
|
||||
int32_t src_end = janet_gethalfrange(argv, 4, src.len, "src-end");
|
||||
int32_t src_end = src.len;
|
||||
if (!janet_checktype(argv[4], JANET_NIL))
|
||||
src_end = janet_gethalfrange(argv, 4, src.len, "src-end");
|
||||
length_src = src_end - offset_src;
|
||||
if (length_src < 0) length_src = 0;
|
||||
} else {
|
||||
@@ -503,15 +645,42 @@ JANET_CORE_FN(cfun_buffer_format,
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_buffer_format_at,
|
||||
"(buffer/format-at buffer at format & args)",
|
||||
"Snprintf like functionality for printing values into a buffer. Returns "
|
||||
"the modified buffer.") {
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int32_t at = janet_getinteger(argv, 1);
|
||||
if (at < 0) {
|
||||
at += buffer->count + 1;
|
||||
}
|
||||
if (at > buffer->count || at < 0) janet_panicf("expected index at to be in range [0, %d), got %d", buffer->count, at);
|
||||
int32_t oldcount = buffer->count;
|
||||
buffer->count = at;
|
||||
const char *strfrmt = (const char *) janet_getstring(argv, 2);
|
||||
janet_buffer_format(buffer, strfrmt, 2, argc, argv);
|
||||
if (buffer->count < oldcount) {
|
||||
buffer->count = oldcount;
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
void janet_lib_buffer(JanetTable *env) {
|
||||
JanetRegExt buffer_cfuns[] = {
|
||||
JANET_CORE_REG("buffer/new", cfun_buffer_new),
|
||||
JANET_CORE_REG("buffer/new-filled", cfun_buffer_new_filled),
|
||||
JANET_CORE_REG("buffer/from-bytes", cfun_buffer_frombytes),
|
||||
JANET_CORE_REG("buffer/fill", cfun_buffer_fill),
|
||||
JANET_CORE_REG("buffer/trim", cfun_buffer_trim),
|
||||
JANET_CORE_REG("buffer/push-byte", cfun_buffer_u8),
|
||||
JANET_CORE_REG("buffer/push-word", cfun_buffer_word),
|
||||
JANET_CORE_REG("buffer/push-string", cfun_buffer_chars),
|
||||
JANET_CORE_REG("buffer/push-uint16", cfun_buffer_push_uint16),
|
||||
JANET_CORE_REG("buffer/push-uint32", cfun_buffer_push_uint32),
|
||||
JANET_CORE_REG("buffer/push-uint64", cfun_buffer_push_uint64),
|
||||
JANET_CORE_REG("buffer/push-float32", cfun_buffer_push_float32),
|
||||
JANET_CORE_REG("buffer/push-float64", cfun_buffer_push_float64),
|
||||
JANET_CORE_REG("buffer/push", cfun_buffer_push),
|
||||
JANET_CORE_REG("buffer/push-at", cfun_buffer_push_at),
|
||||
JANET_CORE_REG("buffer/popn", cfun_buffer_popn),
|
||||
@@ -523,6 +692,7 @@ void janet_lib_buffer(JanetTable *env) {
|
||||
JANET_CORE_REG("buffer/bit-toggle", cfun_buffer_bittoggle),
|
||||
JANET_CORE_REG("buffer/blit", cfun_buffer_blit),
|
||||
JANET_CORE_REG("buffer/format", cfun_buffer_format),
|
||||
JANET_CORE_REG("buffer/format-at", cfun_buffer_format_at),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, buffer_cfuns);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -140,7 +140,7 @@ void janet_bytecode_remove_noops(JanetFuncDef *def) {
|
||||
/* relative pc is in DS field of instruction */
|
||||
old_jump_target = i + (((int32_t)instr) >> 8);
|
||||
new_jump_target = pc_map[old_jump_target];
|
||||
instr += (new_jump_target - old_jump_target + (i - j)) << 8;
|
||||
instr += (uint32_t)(new_jump_target - old_jump_target + (i - j)) << 8;
|
||||
break;
|
||||
case JOP_JUMP_IF:
|
||||
case JOP_JUMP_IF_NIL:
|
||||
@@ -149,7 +149,7 @@ void janet_bytecode_remove_noops(JanetFuncDef *def) {
|
||||
/* relative pc is in ES field of instruction */
|
||||
old_jump_target = i + (((int32_t)instr) >> 16);
|
||||
new_jump_target = pc_map[old_jump_target];
|
||||
instr += (new_jump_target - old_jump_target + (i - j)) << 16;
|
||||
instr += (uint32_t)(new_jump_target - old_jump_target + (i - j)) << 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -226,6 +226,7 @@ void janet_bytecode_movopt(JanetFuncDef *def) {
|
||||
case JOP_LOAD_TRUE:
|
||||
case JOP_LOAD_FALSE:
|
||||
case JOP_LOAD_SELF:
|
||||
break;
|
||||
case JOP_MAKE_ARRAY:
|
||||
case JOP_MAKE_BUFFER:
|
||||
case JOP_MAKE_STRING:
|
||||
@@ -233,6 +234,8 @@ void janet_bytecode_movopt(JanetFuncDef *def) {
|
||||
case JOP_MAKE_TABLE:
|
||||
case JOP_MAKE_TUPLE:
|
||||
case JOP_MAKE_BRACKET_TUPLE:
|
||||
/* Reads from the stack, don't remove */
|
||||
janetc_regalloc_touch(&ra, DD);
|
||||
break;
|
||||
|
||||
/* Read A */
|
||||
|
||||
162
src/core/capi.c
162
src/core/capi.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -25,14 +25,24 @@
|
||||
#include <janet.h>
|
||||
#include "state.h"
|
||||
#include "fiber.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#ifndef JANET_SINGLE_THREADED
|
||||
#ifndef JANET_WINDOWS
|
||||
#include <pthread.h>
|
||||
#else
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef JANET_USE_STDATOMIC
|
||||
#include <stdatomic.h>
|
||||
/* We don't need stdatomic on most compilers since we use compiler builtins for atomic operations.
|
||||
* Some (TCC), explicitly require using stdatomic.h and don't have any exposed builtins (that I know of).
|
||||
* For TCC and similar compilers, one would need -std=c11 or similar then to get access. */
|
||||
#endif
|
||||
|
||||
JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
|
||||
@@ -52,6 +62,18 @@ JANET_NO_RETURN static void janet_top_level_signal(const char *msg) {
|
||||
|
||||
void janet_signalv(JanetSignal sig, Janet message) {
|
||||
if (janet_vm.return_reg != NULL) {
|
||||
/* Should match logic in janet_call for coercing everything not ok to an error (no awaits, yields, etc.) */
|
||||
if (janet_vm.coerce_error && sig != JANET_SIGNAL_OK) {
|
||||
#ifdef JANET_EV
|
||||
if (NULL != janet_vm.root_fiber && sig == JANET_SIGNAL_EVENT) {
|
||||
janet_vm.root_fiber->sched_id++;
|
||||
}
|
||||
#endif
|
||||
if (sig != JANET_SIGNAL_ERROR) {
|
||||
message = janet_wrap_string(janet_formatc("%v coerced from %s to error", message, janet_signal_names[sig]));
|
||||
}
|
||||
sig = JANET_SIGNAL_ERROR;
|
||||
}
|
||||
*janet_vm.return_reg = message;
|
||||
if (NULL != janet_vm.fiber) {
|
||||
janet_vm.fiber->flags |= JANET_FIBER_DID_LONGJUMP;
|
||||
@@ -296,11 +318,28 @@ int32_t janet_getinteger(const Janet *argv, int32_t n) {
|
||||
uint32_t janet_getuinteger(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
if (!janet_checkuint(x)) {
|
||||
janet_panicf("bad slot #%d, expected 32 bit signed integer, got %v", n, x);
|
||||
janet_panicf("bad slot #%d, expected 32 bit unsigned integer, got %v", n, x);
|
||||
}
|
||||
return janet_unwrap_integer(x);
|
||||
return (uint32_t) janet_unwrap_number(x);
|
||||
}
|
||||
|
||||
int16_t janet_getinteger16(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
if (!janet_checkint16(x)) {
|
||||
janet_panicf("bad slot #%d, expected 16 bit signed integer, got %v", n, x);
|
||||
}
|
||||
return (int16_t) janet_unwrap_number(x);
|
||||
}
|
||||
|
||||
uint16_t janet_getuinteger16(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
if (!janet_checkuint16(x)) {
|
||||
janet_panicf("bad slot #%d, expected 16 bit unsigned integer, got %v", n, x);
|
||||
}
|
||||
return (uint16_t) janet_unwrap_number(x);
|
||||
}
|
||||
|
||||
|
||||
int64_t janet_getinteger64(const Janet *argv, int32_t n) {
|
||||
#ifdef JANET_INT_TYPES
|
||||
return janet_unwrap_s64(argv[n]);
|
||||
@@ -338,16 +377,30 @@ int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const c
|
||||
int32_t not_raw = raw;
|
||||
if (not_raw < 0) not_raw += length + 1;
|
||||
if (not_raw < 0 || not_raw > length)
|
||||
janet_panicf("%s index %d out of range [%d,%d]", which, raw, -length - 1, length);
|
||||
janet_panicf("%s index %d out of range [%d,%d]", which, (int64_t) raw, -(int64_t)length - 1, (int64_t) length);
|
||||
return not_raw;
|
||||
}
|
||||
|
||||
int32_t janet_getstartrange(const Janet *argv, int32_t argc, int32_t n, int32_t length) {
|
||||
if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
|
||||
return 0;
|
||||
}
|
||||
return janet_gethalfrange(argv, n, length, "start");
|
||||
}
|
||||
|
||||
int32_t janet_getendrange(const Janet *argv, int32_t argc, int32_t n, int32_t length) {
|
||||
if (n >= argc || janet_checktype(argv[n], JANET_NIL)) {
|
||||
return length;
|
||||
}
|
||||
return janet_gethalfrange(argv, n, length, "end");
|
||||
}
|
||||
|
||||
int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which) {
|
||||
int32_t raw = janet_getinteger(argv, n);
|
||||
int32_t not_raw = raw;
|
||||
if (not_raw < 0) not_raw += length;
|
||||
if (not_raw < 0 || not_raw > length)
|
||||
janet_panicf("%s index %d out of range [%d,%d)", which, raw, -length, length);
|
||||
janet_panicf("%s index %d out of range [%d,%d)", which, (int64_t)raw, -(int64_t)length, (int64_t)length);
|
||||
return not_raw;
|
||||
}
|
||||
|
||||
@@ -394,24 +447,10 @@ JanetRange janet_getslice(int32_t argc, const Janet *argv) {
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetRange range;
|
||||
int32_t length = janet_length(argv[0]);
|
||||
if (argc == 1) {
|
||||
range.start = 0;
|
||||
range.end = length;
|
||||
} else if (argc == 2) {
|
||||
range.start = janet_checktype(argv[1], JANET_NIL)
|
||||
? 0
|
||||
: janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = length;
|
||||
} else {
|
||||
range.start = janet_checktype(argv[1], JANET_NIL)
|
||||
? 0
|
||||
: janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = janet_checktype(argv[2], JANET_NIL)
|
||||
? length
|
||||
: janet_gethalfrange(argv, 2, length, "end");
|
||||
if (range.end < range.start)
|
||||
range.end = range.start;
|
||||
}
|
||||
range.start = janet_getstartrange(argv, argc, 1, length);
|
||||
range.end = janet_getendrange(argv, argc, 2, length);
|
||||
if (range.end < range.start)
|
||||
range.end = range.start;
|
||||
return range;
|
||||
}
|
||||
|
||||
@@ -439,6 +478,33 @@ void janet_setdyn(const char *name, Janet value) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a function that when called, returns X. Trivial in Janet, a pain in C. */
|
||||
JanetFunction *janet_thunk_delay(Janet x) {
|
||||
static const uint32_t bytecode[] = {
|
||||
JOP_LOAD_CONSTANT,
|
||||
JOP_RETURN
|
||||
};
|
||||
JanetFuncDef *def = janet_funcdef_alloc();
|
||||
def->arity = 0;
|
||||
def->min_arity = 0;
|
||||
def->max_arity = INT32_MAX;
|
||||
def->flags = JANET_FUNCDEF_FLAG_VARARG;
|
||||
def->slotcount = 1;
|
||||
def->bytecode = janet_malloc(sizeof(bytecode));
|
||||
def->bytecode_length = (int32_t)(sizeof(bytecode) / sizeof(uint32_t));
|
||||
def->constants = janet_malloc(sizeof(Janet));
|
||||
def->constants_length = 1;
|
||||
def->name = NULL;
|
||||
if (!def->bytecode || !def->constants) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
def->constants[0] = x;
|
||||
memcpy(def->bytecode, bytecode, sizeof(bytecode));
|
||||
janet_def_addflags(def);
|
||||
/* janet_verify(def); */
|
||||
return janet_thunk(def);
|
||||
}
|
||||
|
||||
uint64_t janet_getflags(const Janet *argv, int32_t n, const char *flags) {
|
||||
uint64_t ret = 0;
|
||||
const uint8_t *keyw = janet_getkeyword(argv, n);
|
||||
@@ -491,9 +557,51 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA
|
||||
return janet_getabstract(argv, n, at);
|
||||
}
|
||||
|
||||
/* Atomic refcounts */
|
||||
|
||||
JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) {
|
||||
#ifdef _MSC_VER
|
||||
return _InterlockedIncrement(x);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_fetch_add_explicit(x, 1, memory_order_relaxed) + 1;
|
||||
#else
|
||||
return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED);
|
||||
#endif
|
||||
}
|
||||
|
||||
JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) {
|
||||
#ifdef _MSC_VER
|
||||
return _InterlockedDecrement(x);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_fetch_add_explicit(x, -1, memory_order_acq_rel) - 1;
|
||||
#else
|
||||
return __atomic_add_fetch(x, -1, __ATOMIC_ACQ_REL);
|
||||
#endif
|
||||
}
|
||||
|
||||
JanetAtomicInt janet_atomic_load(JanetAtomicInt volatile *x) {
|
||||
#ifdef _MSC_VER
|
||||
return _InterlockedOr(x, 0);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_load_explicit(x, memory_order_acquire);
|
||||
#else
|
||||
return __atomic_load_n(x, __ATOMIC_ACQUIRE);
|
||||
#endif
|
||||
}
|
||||
|
||||
JanetAtomicInt janet_atomic_load_relaxed(JanetAtomicInt volatile *x) {
|
||||
#ifdef _MSC_VER
|
||||
return _InterlockedOr(x, 0);
|
||||
#elif defined(JANET_USE_STDATOMIC)
|
||||
return atomic_load_explicit(x, memory_order_relaxed);
|
||||
#else
|
||||
return __atomic_load_n(x, __ATOMIC_RELAXED);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Some definitions for function-like macros */
|
||||
|
||||
JANET_API JanetStructHead *(janet_struct_head)(const JanetKV *st) {
|
||||
JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) {
|
||||
return janet_struct_head(st);
|
||||
}
|
||||
|
||||
@@ -501,10 +609,10 @@ JANET_API JanetAbstractHead *(janet_abstract_head)(const void *abstract) {
|
||||
return janet_abstract_head(abstract);
|
||||
}
|
||||
|
||||
JANET_API JanetStringHead *(janet_string_head)(const uint8_t *s) {
|
||||
JANET_API JanetStringHead *(janet_string_head)(JanetString s) {
|
||||
return janet_string_head(s);
|
||||
}
|
||||
|
||||
JANET_API JanetTupleHead *(janet_tuple_head)(const Janet *tuple) {
|
||||
JANET_API JanetTupleHead *(janet_tuple_head)(JanetTuple tuple) {
|
||||
return janet_tuple_head(tuple);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -99,7 +99,7 @@ static JanetSlot opfunction(
|
||||
static int can_be_imm(Janet x, int8_t *out) {
|
||||
if (!janet_checkint(x)) return 0;
|
||||
int32_t integer = janet_unwrap_integer(x);
|
||||
if (integer > 127 || integer < -127) return 0;
|
||||
if (integer > INT8_MAX || integer < INT8_MIN) return 0;
|
||||
*out = (int8_t) integer;
|
||||
return 1;
|
||||
}
|
||||
@@ -121,8 +121,6 @@ static JanetSlot opreduce(
|
||||
JanetCompiler *c = opts.compiler;
|
||||
int32_t i, len;
|
||||
int8_t imm = 0;
|
||||
int neg = opim < 0;
|
||||
if (opim < 0) opim = -opim;
|
||||
len = janet_v_count(args);
|
||||
JanetSlot t;
|
||||
if (len == 0) {
|
||||
@@ -139,13 +137,13 @@ static JanetSlot opreduce(
|
||||
}
|
||||
t = janetc_gettarget(opts);
|
||||
if (opim && can_slot_be_imm(args[1], &imm)) {
|
||||
janetc_emit_ssi(c, opim, t, args[0], neg ? -imm : imm, 1);
|
||||
janetc_emit_ssi(c, opim, t, args[0], imm, 1);
|
||||
} else {
|
||||
janetc_emit_sss(c, op, t, args[0], args[1], 1);
|
||||
}
|
||||
for (i = 2; i < len; i++) {
|
||||
if (opim && can_slot_be_imm(args[i], &imm)) {
|
||||
janetc_emit_ssi(c, opim, t, t, neg ? -imm : imm, 1);
|
||||
janetc_emit_ssi(c, opim, t, t, imm, 1);
|
||||
} else {
|
||||
janetc_emit_sss(c, op, t, t, args[i], 1);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -934,7 +934,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
|
||||
int32_t slotchunks = (def->slotcount + 31) >> 5;
|
||||
/* numchunks is min of slotchunks and scope->ua.count */
|
||||
int32_t numchunks = slotchunks > scope->ua.count ? scope->ua.count : slotchunks;
|
||||
uint32_t *chunks = janet_calloc(sizeof(uint32_t), slotchunks);
|
||||
uint32_t *chunks = janet_calloc(slotchunks, sizeof(uint32_t));
|
||||
if (NULL == chunks) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
@@ -1056,7 +1056,7 @@ JanetCompileResult janet_compile_lint(Janet source,
|
||||
|
||||
if (c.result.status == JANET_COMPILE_OK) {
|
||||
JanetFuncDef *def = janetc_pop_funcdef(&c);
|
||||
def->name = janet_cstring("_thunk");
|
||||
def->name = janet_cstring("thunk");
|
||||
janet_def_addflags(def);
|
||||
c.result.funcdef = def;
|
||||
} else {
|
||||
@@ -1127,4 +1127,5 @@ void janet_lib_compile(JanetTable *env) {
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, cfuns);
|
||||
janet_lib_sysir(env);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -262,12 +262,15 @@ void janetc_popscope(JanetCompiler *c);
|
||||
void janetc_popscope_keepslot(JanetCompiler *c, JanetSlot retslot);
|
||||
JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c);
|
||||
|
||||
/* Create a destory slots */
|
||||
/* Create a destroy slot */
|
||||
JanetSlot janetc_cslot(Janet x);
|
||||
|
||||
/* Search for a symbol */
|
||||
JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym);
|
||||
|
||||
/* Load the system dialect IR */
|
||||
void janet_lib_sysir(JanetTable *env);
|
||||
|
||||
/* Bytecode optimization */
|
||||
void janet_bytecode_movopt(JanetFuncDef *def);
|
||||
void janet_bytecode_remove_noops(JanetFuncDef *def);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -66,18 +66,18 @@ JanetModule janet_native(const char *name, const uint8_t **error) {
|
||||
JanetBuildConfig modconf = getter();
|
||||
JanetBuildConfig host = janet_config_current();
|
||||
if (host.major != modconf.major ||
|
||||
host.minor < modconf.minor ||
|
||||
host.minor != modconf.minor ||
|
||||
host.bits != modconf.bits) {
|
||||
char errbuf[128];
|
||||
sprintf(errbuf, "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
|
||||
host.major,
|
||||
host.minor,
|
||||
host.patch,
|
||||
host.bits,
|
||||
modconf.major,
|
||||
modconf.minor,
|
||||
modconf.patch,
|
||||
modconf.bits);
|
||||
snprintf(errbuf, sizeof(errbuf), "config mismatch - host %d.%.d.%d(%.4x) vs. module %d.%d.%d(%.4x)",
|
||||
host.major,
|
||||
host.minor,
|
||||
host.patch,
|
||||
host.bits,
|
||||
modconf.major,
|
||||
modconf.minor,
|
||||
modconf.patch,
|
||||
modconf.bits);
|
||||
*error = janet_cstring(errbuf);
|
||||
return NULL;
|
||||
}
|
||||
@@ -110,14 +110,14 @@ JANET_CORE_FN(janet_core_expand_path,
|
||||
"(module/expand-path path template)",
|
||||
"Expands a path template as found in `module/paths` for `module/find`. "
|
||||
"This takes in a path (the argument to require) and a template string, "
|
||||
"to expand the path to a path that can be "
|
||||
"used for importing files. The replacements are as follows:\n\n"
|
||||
"to expand the path to a path that can be used for importing files. "
|
||||
"The replacements are as follows:\n\n"
|
||||
"* :all: -- the value of path verbatim.\n\n"
|
||||
"* :@all: -- Same as :all:, but if `path` starts with the @ character,\n"
|
||||
" the first path segment is replaced with a dynamic binding\n"
|
||||
" `(dyn <first path segment as keyword>)`.\n\n"
|
||||
"* :cur: -- the current file, or (dyn :current-file)\n\n"
|
||||
"* :dir: -- the directory containing the current file\n\n"
|
||||
"* :@all: -- Same as :all:, but if `path` starts with the @ character, "
|
||||
"the first path segment is replaced with a dynamic binding "
|
||||
"`(dyn <first path segment as keyword>)`.\n\n"
|
||||
"* :cur: -- the directory portion, if any, of (dyn :current-file)\n\n"
|
||||
"* :dir: -- the directory portion, if any, of the path argument\n\n"
|
||||
"* :name: -- the name component of path, with extension if given\n\n"
|
||||
"* :native: -- the extension used to load natives, .so or .dll\n\n"
|
||||
"* :sys: -- the system path, or (dyn :syspath)") {
|
||||
@@ -426,6 +426,48 @@ JANET_CORE_FN(janet_core_slice,
|
||||
}
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_range,
|
||||
"(range & args)",
|
||||
"Create an array of values [start, end) with a given step. "
|
||||
"With one argument, returns a range [0, end). With two arguments, returns "
|
||||
"a range [start, end). With three, returns a range with optional step size.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
double start = 0, stop = 0, step = 1, count = 0;
|
||||
if (argc == 3) {
|
||||
start = janet_getnumber(argv, 0);
|
||||
stop = janet_getnumber(argv, 1);
|
||||
step = janet_getnumber(argv, 2);
|
||||
count = (step > 0) ? (stop - start) / step :
|
||||
((step < 0) ? (stop - start) / step : 0);
|
||||
} else if (argc == 2) {
|
||||
start = janet_getnumber(argv, 0);
|
||||
stop = janet_getnumber(argv, 1);
|
||||
count = stop - start;
|
||||
} else {
|
||||
stop = janet_getnumber(argv, 0);
|
||||
count = stop;
|
||||
}
|
||||
count = (count > 0) ? count : 0;
|
||||
int32_t int_count;
|
||||
janet_assert(count >= 0, "bad range code");
|
||||
if (count > (double) INT32_MAX) {
|
||||
janet_panicf("range is too large, %f elements", count);
|
||||
} else {
|
||||
int_count = (int32_t) ceil(count);
|
||||
}
|
||||
if (step > 0.0) {
|
||||
janet_assert(start + int_count * step >= stop, "bad range code");
|
||||
} else {
|
||||
janet_assert(start + int_count * step <= stop, "bad range code");
|
||||
}
|
||||
JanetArray *array = janet_array(int_count);
|
||||
for (int32_t i = 0; i < int_count; i++) {
|
||||
array->data[i] = janet_wrap_number((double) start + (double) i * step);
|
||||
}
|
||||
array->count = int_count;
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_table,
|
||||
"(table & kvs)",
|
||||
"Creates a new table from a variadic number of keys and values. "
|
||||
@@ -611,27 +653,56 @@ JANET_CORE_FN(janet_core_check_int,
|
||||
"(int? x)",
|
||||
"Check if x can be exactly represented as a 32 bit signed two's complement integer.") {
|
||||
janet_fixarity(argc, 1);
|
||||
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
|
||||
double num = janet_unwrap_number(argv[0]);
|
||||
return janet_wrap_boolean(num == (double)((int32_t)num));
|
||||
ret_false:
|
||||
return janet_wrap_false();
|
||||
return janet_wrap_boolean(janet_checkint(argv[0]));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_check_nat,
|
||||
"(nat? x)",
|
||||
"Check if x can be exactly represented as a non-negative 32 bit signed two's complement integer.") {
|
||||
janet_fixarity(argc, 1);
|
||||
if (!janet_checktype(argv[0], JANET_NUMBER)) goto ret_false;
|
||||
double num = janet_unwrap_number(argv[0]);
|
||||
return janet_wrap_boolean(num >= 0 && (num == (double)((int32_t)num)));
|
||||
ret_false:
|
||||
return janet_wrap_false();
|
||||
if (!janet_checkint(argv[0])) return janet_wrap_false();
|
||||
return janet_wrap_boolean(janet_unwrap_integer(argv[0]) >= 0);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_bytes,
|
||||
"(bytes? x)",
|
||||
"Check if x is a string, symbol, keyword, or buffer.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_BYTES));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_indexed,
|
||||
"(indexed? x)",
|
||||
"Check if x is an array or tuple.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_INDEXED));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_dictionary,
|
||||
"(dictionary? x)",
|
||||
"Check if x is a table or struct.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_DICTIONARY));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_is_lengthable,
|
||||
"(lengthable? x)",
|
||||
"Check if x is a bytes, indexed, or dictionary.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(janet_checktypes(argv[0], JANET_TFLAG_LENGTHABLE));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_core_signal,
|
||||
"(signal what x)",
|
||||
"Raise a signal with payload x. ") {
|
||||
"Raise a signal with payload x. `what` can be an integer\n"
|
||||
"from 0 through 7 indicating user(0-7), or one of:\n\n"
|
||||
"* :ok\n"
|
||||
"* :error\n"
|
||||
"* :debug\n"
|
||||
"* :yield\n"
|
||||
"* :user(0-7)\n"
|
||||
"* :interrupt\n"
|
||||
"* :await") {
|
||||
janet_arity(argc, 1, 2);
|
||||
Janet payload = argc == 2 ? argv[1] : janet_wrap_nil();
|
||||
if (janet_checkint(argv[0])) {
|
||||
@@ -675,6 +746,7 @@ typedef struct SandboxOption {
|
||||
|
||||
static const SandboxOption sandbox_options[] = {
|
||||
{"all", JANET_SANDBOX_ALL},
|
||||
{"chroot", JANET_SANDBOX_CHROOT},
|
||||
{"env", JANET_SANDBOX_ENV},
|
||||
{"ffi", JANET_SANDBOX_FFI},
|
||||
{"ffi-define", JANET_SANDBOX_FFI_DEFINE},
|
||||
@@ -690,6 +762,7 @@ static const SandboxOption sandbox_options[] = {
|
||||
{"net-connect", JANET_SANDBOX_NET_CONNECT},
|
||||
{"net-listen", JANET_SANDBOX_NET_LISTEN},
|
||||
{"sandbox", JANET_SANDBOX_SANDBOX},
|
||||
{"signal", JANET_SANDBOX_SIGNAL},
|
||||
{"subprocess", JANET_SANDBOX_SUBPROCESS},
|
||||
{NULL, 0}
|
||||
};
|
||||
@@ -699,6 +772,7 @@ JANET_CORE_FN(janet_core_sandbox,
|
||||
"Disable feature sets to prevent the interpreter from using certain system resources. "
|
||||
"Once a feature is disabled, there is no way to re-enable it. Capabilities can be:\n\n"
|
||||
"* :all - disallow all (except IO to stdout, stderr, and stdin)\n"
|
||||
"* :chroot - disallow calling `os/posix-chroot`\n"
|
||||
"* :env - disallow reading and write env variables\n"
|
||||
"* :ffi - disallow FFI (recommended if disabling anything else)\n"
|
||||
"* :ffi-define - disallow loading new FFI modules and binding new functions\n"
|
||||
@@ -714,6 +788,7 @@ JANET_CORE_FN(janet_core_sandbox,
|
||||
"* :net-connect - disallow making outbound network connections\n"
|
||||
"* :net-listen - disallow accepting inbound network connections\n"
|
||||
"* :sandbox - disallow calling this function\n"
|
||||
"* :signal - disallow adding or removing signal handlers\n"
|
||||
"* :subprocess - disallow running subprocesses") {
|
||||
uint32_t flags = 0;
|
||||
for (int32_t i = 0; i < argc; i++) {
|
||||
@@ -916,18 +991,17 @@ static void make_apply(JanetTable *env) {
|
||||
/* Push the array */
|
||||
S(JOP_PUSH_ARRAY, 5),
|
||||
|
||||
/* Call the funciton */
|
||||
/* Call the function */
|
||||
S(JOP_TAILCALL, 0)
|
||||
};
|
||||
janet_quick_asm(env, JANET_FUN_APPLY | JANET_FUNCDEF_FLAG_VARARG,
|
||||
"apply", 1, 1, INT32_MAX, 6, apply_asm, sizeof(apply_asm),
|
||||
JDOC("(apply f & args)\n\n"
|
||||
"Applies a function to a variable number of arguments. Each element in args "
|
||||
"is used as an argument to f, except the last element in args, which is expected to "
|
||||
"be an array-like. Each element in this last argument is then also pushed as an argument to "
|
||||
"f. For example:\n\n"
|
||||
"\t(apply + 1000 (range 10))\n\n"
|
||||
"sums the first 10 integers and 1000."));
|
||||
"Applies a function f to a variable number of arguments. Each "
|
||||
"element in args is used as an argument to f, except the last "
|
||||
"element in args, which is expected to be an array or a tuple. "
|
||||
"Each element in this last argument is then also pushed as an "
|
||||
"argument to f."));
|
||||
}
|
||||
|
||||
static const uint32_t error_asm[] = {
|
||||
@@ -1023,7 +1097,12 @@ static void janet_load_libs(JanetTable *env) {
|
||||
JANET_CORE_REG("module/expand-path", janet_core_expand_path),
|
||||
JANET_CORE_REG("int?", janet_core_check_int),
|
||||
JANET_CORE_REG("nat?", janet_core_check_nat),
|
||||
JANET_CORE_REG("bytes?", janet_core_is_bytes),
|
||||
JANET_CORE_REG("indexed?", janet_core_is_indexed),
|
||||
JANET_CORE_REG("dictionary?", janet_core_is_dictionary),
|
||||
JANET_CORE_REG("lengthable?", janet_core_is_lengthable),
|
||||
JANET_CORE_REG("slice", janet_core_slice),
|
||||
JANET_CORE_REG("range", janet_core_range),
|
||||
JANET_CORE_REG("signal", janet_core_signal),
|
||||
JANET_CORE_REG("memcmp", janet_core_memcmp),
|
||||
JANET_CORE_REG("getproto", janet_core_getproto),
|
||||
@@ -1056,6 +1135,9 @@ static void janet_load_libs(JanetTable *env) {
|
||||
#endif
|
||||
#ifdef JANET_EV
|
||||
janet_lib_ev(env);
|
||||
#ifdef JANET_FILEWATCH
|
||||
janet_lib_filewatch(env);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef JANET_NET
|
||||
janet_lib_net(env);
|
||||
@@ -1072,79 +1154,82 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
janet_quick_asm(env, JANET_FUN_CMP,
|
||||
"cmp", 2, 2, 2, 2, cmp_asm, sizeof(cmp_asm),
|
||||
JDOC("(cmp x y)\n\n"
|
||||
"Returns -1 if x is strictly less than y, 1 if y is strictly greater "
|
||||
"than x, and 0 otherwise. To return 0, x and y must be the exact same type."));
|
||||
"Returns -1 if x is strictly less than y, 1 if y is strictly greater "
|
||||
"than x, and 0 otherwise. To return 0, x and y must be the exact same type."));
|
||||
janet_quick_asm(env, JANET_FUN_NEXT,
|
||||
"next", 2, 1, 2, 2, next_asm, sizeof(next_asm),
|
||||
JDOC("(next ds &opt key)\n\n"
|
||||
"Gets the next key in a data structure. Can be used to iterate through "
|
||||
"the keys of a data structure in an unspecified order. Keys are guaranteed "
|
||||
"to be seen only once per iteration if they data structure is not mutated "
|
||||
"during iteration. If key is nil, next returns the first key. If next "
|
||||
"returns nil, there are no more keys to iterate through."));
|
||||
"Gets the next key in a data structure. Can be used to iterate through "
|
||||
"the keys of a data structure in an unspecified order. Keys are guaranteed "
|
||||
"to be seen only once per iteration if the data structure is not mutated "
|
||||
"during iteration. If key is nil, next returns the first key. If next "
|
||||
"returns nil, there are no more keys to iterate through."));
|
||||
janet_quick_asm(env, JANET_FUN_PROP,
|
||||
"propagate", 2, 2, 2, 2, propagate_asm, sizeof(propagate_asm),
|
||||
JDOC("(propagate x fiber)\n\n"
|
||||
"Propagate a signal from a fiber to the current fiber. The resulting "
|
||||
"stack trace from the current fiber will include frames from fiber. If "
|
||||
"fiber is in a state that can be resumed, resuming the current fiber will "
|
||||
"first resume fiber. This function can be used to re-raise an error without "
|
||||
"losing the original stack trace."));
|
||||
"Propagate a signal from a fiber to the current fiber and "
|
||||
"set the last value of the current fiber to `x`. The signal "
|
||||
"value is then available as the status of the current fiber. "
|
||||
"The resulting stack trace from the current fiber will include "
|
||||
"frames from fiber. If fiber is in a state that can be resumed, "
|
||||
"resuming the current fiber will first resume `fiber`. "
|
||||
"This function can be used to re-raise an error without losing "
|
||||
"the original stack trace."));
|
||||
janet_quick_asm(env, JANET_FUN_DEBUG,
|
||||
"debug", 1, 0, 1, 1, debug_asm, sizeof(debug_asm),
|
||||
JDOC("(debug &opt x)\n\n"
|
||||
"Throws a debug signal that can be caught by a parent fiber and used to inspect "
|
||||
"the running state of the current fiber. Returns the value passed in by resume."));
|
||||
"Throws a debug signal that can be caught by a parent fiber and used to inspect "
|
||||
"the running state of the current fiber. Returns the value passed in by resume."));
|
||||
janet_quick_asm(env, JANET_FUN_ERROR,
|
||||
"error", 1, 1, 1, 1, error_asm, sizeof(error_asm),
|
||||
JDOC("(error e)\n\n"
|
||||
"Throws an error e that can be caught and handled by a parent fiber."));
|
||||
"Throws an error e that can be caught and handled by a parent fiber."));
|
||||
janet_quick_asm(env, JANET_FUN_YIELD,
|
||||
"yield", 1, 0, 1, 2, yield_asm, sizeof(yield_asm),
|
||||
JDOC("(yield &opt x)\n\n"
|
||||
"Yield a value to a parent fiber. When a fiber yields, its execution is paused until "
|
||||
"another thread resumes it. The fiber will then resume, and the last yield call will "
|
||||
"return the value that was passed to resume."));
|
||||
"Yield a value to a parent fiber. When a fiber yields, its execution is paused until "
|
||||
"another thread resumes it. The fiber will then resume, and the last yield call will "
|
||||
"return the value that was passed to resume."));
|
||||
janet_quick_asm(env, JANET_FUN_CANCEL,
|
||||
"cancel", 2, 2, 2, 2, cancel_asm, sizeof(cancel_asm),
|
||||
JDOC("(cancel fiber err)\n\n"
|
||||
"Resume a fiber but have it immediately raise an error. This lets a programmer unwind a pending fiber. "
|
||||
"Returns the same result as resume."));
|
||||
"Resume a fiber but have it immediately raise an error. This lets a programmer unwind a pending fiber. "
|
||||
"Returns the same result as resume."));
|
||||
janet_quick_asm(env, JANET_FUN_RESUME,
|
||||
"resume", 2, 1, 2, 2, resume_asm, sizeof(resume_asm),
|
||||
JDOC("(resume fiber &opt x)\n\n"
|
||||
"Resume a new or suspended fiber and optionally pass in a value to the fiber that "
|
||||
"will be returned to the last yield in the case of a pending fiber, or the argument to "
|
||||
"the dispatch function in the case of a new fiber. Returns either the return result of "
|
||||
"the fiber's dispatch function, or the value from the next yield call in fiber."));
|
||||
"Resume a new or suspended fiber and optionally pass in a value to the fiber that "
|
||||
"will be returned to the last yield in the case of a pending fiber, or the argument to "
|
||||
"the dispatch function in the case of a new fiber. Returns either the return result of "
|
||||
"the fiber's dispatch function, or the value from the next yield call in fiber."));
|
||||
janet_quick_asm(env, JANET_FUN_IN,
|
||||
"in", 3, 2, 3, 4, in_asm, sizeof(in_asm),
|
||||
JDOC("(in ds key &opt dflt)\n\n"
|
||||
"Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
|
||||
"strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
|
||||
"and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
|
||||
"take any value as a key except nil and will return nil or dflt if not found."));
|
||||
"Get value in ds at key, works on associative data structures. Arrays, tuples, tables, structs, "
|
||||
"strings, symbols, and buffers are all associative and can be used. Arrays, tuples, strings, buffers, "
|
||||
"and symbols must use integer keys that are in bounds or an error is raised. Structs and tables can "
|
||||
"take any value as a key except nil and will return nil or dflt if not found."));
|
||||
janet_quick_asm(env, JANET_FUN_GET,
|
||||
"get", 3, 2, 3, 4, get_asm, sizeof(in_asm),
|
||||
JDOC("(get ds key &opt dflt)\n\n"
|
||||
"Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
|
||||
"Similar to in, but will not throw an error if the key is invalid for the data structure "
|
||||
"unless the data structure is an abstract type. In that case, the abstract type getter may throw "
|
||||
"an error."));
|
||||
"Get the value mapped to key in data structure ds, and return dflt or nil if not found. "
|
||||
"Similar to in, but will not throw an error if the key is invalid for the data structure "
|
||||
"unless the data structure is an abstract type. In that case, the abstract type getter may throw "
|
||||
"an error."));
|
||||
janet_quick_asm(env, JANET_FUN_PUT,
|
||||
"put", 3, 3, 3, 3, put_asm, sizeof(put_asm),
|
||||
JDOC("(put ds key value)\n\n"
|
||||
"Associate a key with a value in any mutable associative data structure. Indexed data structures "
|
||||
"(arrays and buffers) only accept non-negative integer keys, and will expand if an out of bounds "
|
||||
"value is provided. In an array, extra space will be filled with nils, and in a buffer, extra "
|
||||
"space will be filled with 0 bytes. In a table, putting a key that is contained in the table prototype "
|
||||
"will hide the association defined by the prototype, but will not mutate the prototype table. Putting "
|
||||
"a value nil into a table will remove the key from the table. Returns the data structure ds."));
|
||||
"Associate a key with a value in any mutable associative data structure. Indexed data structures "
|
||||
"(arrays and buffers) only accept non-negative integer keys, and will expand if an out of bounds "
|
||||
"value is provided. In an array, extra space will be filled with nils, and in a buffer, extra "
|
||||
"space will be filled with 0 bytes. In a table, putting a key that is contained in the table prototype "
|
||||
"will hide the association defined by the prototype, but will not mutate the prototype table. Putting "
|
||||
"a value nil into a table will remove the key from the table. Returns the data structure ds."));
|
||||
janet_quick_asm(env, JANET_FUN_LENGTH,
|
||||
"length", 1, 1, 1, 1, length_asm, sizeof(length_asm),
|
||||
JDOC("(length ds)\n\n"
|
||||
"Returns the length or count of a data structure in constant time as an integer. For "
|
||||
"structs and tables, returns the number of key-value pairs in the data structure."));
|
||||
"Returns the length or count of a data structure in constant time as an integer. For "
|
||||
"structs and tables, returns the number of key-value pairs in the data structure."));
|
||||
janet_quick_asm(env, JANET_FUN_BNOT,
|
||||
"bnot", 1, 1, 1, 1, bnot_asm, sizeof(bnot_asm),
|
||||
JDOC("(bnot x)\n\nReturns the bit-wise inverse of integer x."));
|
||||
@@ -1153,74 +1238,74 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
/* Variadic ops */
|
||||
templatize_varop(env, JANET_FUN_ADD, "+", 0, 0, JOP_ADD,
|
||||
JDOC("(+ & xs)\n\n"
|
||||
"Returns the sum of all xs. xs must be integers or real numbers only. If xs is empty, return 0."));
|
||||
"Returns the sum of all xs. xs must be integers or real numbers only. If xs is empty, return 0."));
|
||||
templatize_varop(env, JANET_FUN_SUBTRACT, "-", 0, 0, JOP_SUBTRACT,
|
||||
JDOC("(- & xs)\n\n"
|
||||
"Returns the difference of xs. If xs is empty, returns 0. If xs has one element, returns the "
|
||||
"negative value of that element. Otherwise, returns the first element in xs minus the sum of "
|
||||
"the rest of the elements."));
|
||||
"Returns the difference of xs. If xs is empty, returns 0. If xs has one element, returns the "
|
||||
"negative value of that element. Otherwise, returns the first element in xs minus the sum of "
|
||||
"the rest of the elements."));
|
||||
templatize_varop(env, JANET_FUN_MULTIPLY, "*", 1, 1, JOP_MULTIPLY,
|
||||
JDOC("(* & xs)\n\n"
|
||||
"Returns the product of all elements in xs. If xs is empty, returns 1."));
|
||||
"Returns the product of all elements in xs. If xs is empty, returns 1."));
|
||||
templatize_varop(env, JANET_FUN_DIVIDE, "/", 1, 1, JOP_DIVIDE,
|
||||
JDOC("(/ & xs)\n\n"
|
||||
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
"Returns the quotient of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
templatize_varop(env, JANET_FUN_DIVIDE_FLOOR, "div", 1, 1, JOP_DIVIDE_FLOOR,
|
||||
JDOC("(div & xs)\n\n"
|
||||
"Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
"Returns the floored division of xs. If xs is empty, returns 1. If xs has one value x, returns "
|
||||
"the reciprocal of x. Otherwise return the first value of xs repeatedly divided by the remaining "
|
||||
"values."));
|
||||
templatize_varop(env, JANET_FUN_MODULO, "mod", 0, 1, JOP_MODULO,
|
||||
JDOC("(mod & xs)\n\n"
|
||||
"Returns the result of applying the modulo operator on the first value of xs with each remaining value. "
|
||||
"`(mod x 0)` is defined to be `x`."));
|
||||
"Returns the result of applying the modulo operator on the first value of xs with each remaining value. "
|
||||
"`(mod x 0)` is defined to be `x`."));
|
||||
templatize_varop(env, JANET_FUN_REMAINDER, "%", 0, 1, JOP_REMAINDER,
|
||||
JDOC("(% & xs)\n\n"
|
||||
"Returns the remainder of dividing the first value of xs by each remaining value."));
|
||||
"Returns the remainder of dividing the first value of xs by each remaining value."));
|
||||
templatize_varop(env, JANET_FUN_BAND, "band", -1, -1, JOP_BAND,
|
||||
JDOC("(band & xs)\n\n"
|
||||
"Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
|
||||
"Returns the bit-wise and of all values in xs. Each x in xs must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_BOR, "bor", 0, 0, JOP_BOR,
|
||||
JDOC("(bor & xs)\n\n"
|
||||
"Returns the bit-wise or of all values in xs. Each x in xs must be an integer."));
|
||||
"Returns the bit-wise or of all values in xs. Each x in xs must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_BXOR, "bxor", 0, 0, JOP_BXOR,
|
||||
JDOC("(bxor & xs)\n\n"
|
||||
"Returns the bit-wise xor of all values in xs. Each in xs must be an integer."));
|
||||
"Returns the bit-wise xor of all values in xs. Each in xs must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_LSHIFT, "blshift", 1, 1, JOP_SHIFT_LEFT,
|
||||
JDOC("(blshift x & shifts)\n\n"
|
||||
"Returns the value of x bit shifted left by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
"Returns the value of x bit shifted left by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_RSHIFT, "brshift", 1, 1, JOP_SHIFT_RIGHT,
|
||||
JDOC("(brshift x & shifts)\n\n"
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer."));
|
||||
templatize_varop(env, JANET_FUN_RSHIFTU, "brushift", 1, 1, JOP_SHIFT_RIGHT_UNSIGNED,
|
||||
JDOC("(brushift x & shifts)\n\n"
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer. The sign of x is not preserved, so "
|
||||
"for positive shifts the return value will always be positive."));
|
||||
"Returns the value of x bit shifted right by the sum of all values in shifts. x "
|
||||
"and each element in shift must be an integer. The sign of x is not preserved, so "
|
||||
"for positive shifts the return value will always be positive."));
|
||||
|
||||
/* Variadic comparators */
|
||||
templatize_comparator(env, JANET_FUN_GT, ">", 0, JOP_GREATER_THAN,
|
||||
JDOC("(> & xs)\n\n"
|
||||
"Check if xs is in descending order. Returns a boolean."));
|
||||
"Check if xs is in descending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_LT, "<", 0, JOP_LESS_THAN,
|
||||
JDOC("(< & xs)\n\n"
|
||||
"Check if xs is in ascending order. Returns a boolean."));
|
||||
"Check if xs is in ascending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_GTE, ">=", 0, JOP_GREATER_THAN_EQUAL,
|
||||
JDOC("(>= & xs)\n\n"
|
||||
"Check if xs is in non-ascending order. Returns a boolean."));
|
||||
"Check if xs is in non-ascending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_LTE, "<=", 0, JOP_LESS_THAN_EQUAL,
|
||||
JDOC("(<= & xs)\n\n"
|
||||
"Check if xs is in non-descending order. Returns a boolean."));
|
||||
"Check if xs is in non-descending order. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_EQ, "=", 0, JOP_EQUALS,
|
||||
JDOC("(= & xs)\n\n"
|
||||
"Check if all values in xs are equal. Returns a boolean."));
|
||||
"Check if all values in xs are equal. Returns a boolean."));
|
||||
templatize_comparator(env, JANET_FUN_NEQ, "not=", 1, JOP_EQUALS,
|
||||
JDOC("(not= & xs)\n\n"
|
||||
"Check if any values in xs are not equal. Returns a boolean."));
|
||||
"Check if any values in xs are not equal. Returns a boolean."));
|
||||
|
||||
/* Platform detection */
|
||||
janet_def(env, "janet/version", janet_cstringv(JANET_VERSION),
|
||||
@@ -1229,7 +1314,7 @@ JanetTable *janet_core_env(JanetTable *replacements) {
|
||||
JDOC("The build identifier of the running janet program."));
|
||||
janet_def(env, "janet/config-bits", janet_wrap_integer(JANET_CURRENT_CONFIG_BITS),
|
||||
JDOC("The flag set of config options from janetconf.h which is used to check "
|
||||
"if native modules are compatible with the host program."));
|
||||
"if native modules are compatible with the host program."));
|
||||
|
||||
/* Allow references to the environment */
|
||||
janet_def(env, "root-env", janet_wrap_table(env),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -102,7 +102,7 @@ void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
||||
}
|
||||
|
||||
/* Error reporting. This can be emulated from within Janet, but for
|
||||
* consitency with the top level code it is defined once. */
|
||||
* consistency with the top level code it is defined once. */
|
||||
void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
|
||||
|
||||
int32_t fi;
|
||||
@@ -164,7 +164,7 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
|
||||
}
|
||||
}
|
||||
if (frame->flags & JANET_STACKFRAME_TAILCALL)
|
||||
janet_eprintf(" (tailcall)");
|
||||
janet_eprintf(" (tail call)");
|
||||
if (frame->func && frame->pc) {
|
||||
int32_t off = (int32_t)(frame->pc - def->bytecode);
|
||||
if (def->sourcemap) {
|
||||
@@ -180,6 +180,11 @@ void janet_stacktrace_ext(JanetFiber *fiber, Janet err, const char *prefix) {
|
||||
}
|
||||
}
|
||||
janet_eprintf("\n");
|
||||
/* Print fiber points optionally. Clutters traces but provides info
|
||||
if (i <= 0 && fi > 0) {
|
||||
janet_eprintf(" in parent fiber\n");
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,8 +393,8 @@ JANET_CORE_FN(cfun_debug_stack,
|
||||
JANET_CORE_FN(cfun_debug_stacktrace,
|
||||
"(debug/stacktrace fiber &opt err prefix)",
|
||||
"Prints a nice looking stacktrace for a fiber. Can optionally provide "
|
||||
"an error value to print the stack trace with. If `err` is nil or not "
|
||||
"provided, and no prefix is given, will skip the error line. Returns the fiber.") {
|
||||
"an error value to print the stack trace with. If `prefix` is nil or not "
|
||||
"provided, will skip the error line. Returns the fiber.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
Janet x = argc == 1 ? janet_wrap_nil() : argv[1];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
#include "regalloc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Get a register */
|
||||
@@ -128,7 +129,8 @@ static void janetc_movenear(JanetCompiler *c,
|
||||
((uint32_t)(src.envindex) << 16) |
|
||||
((uint32_t)(dest) << 8) |
|
||||
JOP_LOAD_UPVALUE);
|
||||
} else if (src.index > 0xFF || src.index != dest) {
|
||||
} else if (src.index != dest) {
|
||||
janet_assert(src.index >= 0, "bad slot");
|
||||
janetc_emit(c,
|
||||
((uint32_t)(src.index) << 16) |
|
||||
((uint32_t)(dest) << 8) |
|
||||
@@ -155,6 +157,7 @@ static void janetc_moveback(JanetCompiler *c,
|
||||
((uint32_t)(src) << 8) |
|
||||
JOP_SET_UPVALUE);
|
||||
} else if (dest.index != src) {
|
||||
janet_assert(dest.index >= 0, "bad slot");
|
||||
janetc_emit(c,
|
||||
((uint32_t)(dest.index) << 16) |
|
||||
((uint32_t)(src) << 8) |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
1537
src/core/ev.c
1537
src/core/ev.c
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -76,4 +76,6 @@
|
||||
#define __BSD_VISIBLE 1
|
||||
#endif
|
||||
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#endif
|
||||
|
||||
322
src/core/ffi.c
322
src/core/ffi.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -56,6 +56,9 @@
|
||||
#if (defined(__x86_64__) || defined(_M_X64)) && !defined(JANET_WINDOWS)
|
||||
#define JANET_FFI_SYSV64_ENABLED
|
||||
#endif
|
||||
#if (defined(__aarch64__) || defined(_M_ARM64)) && !defined(JANET_WINDOWS)
|
||||
#define JANET_FFI_AAPCS64_ENABLED
|
||||
#endif
|
||||
|
||||
typedef struct JanetFFIType JanetFFIType;
|
||||
typedef struct JanetFFIStruct JanetFFIStruct;
|
||||
@@ -140,7 +143,13 @@ typedef enum {
|
||||
JANET_WIN64_REGISTER,
|
||||
JANET_WIN64_STACK,
|
||||
JANET_WIN64_REGISTER_REF,
|
||||
JANET_WIN64_STACK_REF
|
||||
JANET_WIN64_STACK_REF,
|
||||
JANET_AAPCS64_GENERAL,
|
||||
JANET_AAPCS64_SSE,
|
||||
JANET_AAPCS64_GENERAL_REF,
|
||||
JANET_AAPCS64_STACK,
|
||||
JANET_AAPCS64_STACK_REF,
|
||||
JANET_AAPCS64_NONE
|
||||
} JanetFFIWordSpec;
|
||||
|
||||
/* Describe how each Janet argument is interpreted in terms of machine words
|
||||
@@ -155,13 +164,16 @@ typedef struct {
|
||||
typedef enum {
|
||||
JANET_FFI_CC_NONE,
|
||||
JANET_FFI_CC_SYSV_64,
|
||||
JANET_FFI_CC_WIN_64
|
||||
JANET_FFI_CC_WIN_64,
|
||||
JANET_FFI_CC_AAPCS64
|
||||
} JanetFFICallingConvention;
|
||||
|
||||
#ifdef JANET_FFI_WIN64_ENABLED
|
||||
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_WIN_64
|
||||
#elif defined(JANET_FFI_SYSV64_ENABLED)
|
||||
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_SYSV_64
|
||||
#elif defined(JANET_FFI_AAPCS64_ENABLED)
|
||||
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_AAPCS64
|
||||
#else
|
||||
#define JANET_FFI_CC_DEFAULT JANET_FFI_CC_NONE
|
||||
#endif
|
||||
@@ -301,6 +313,9 @@ static JanetFFICallingConvention decode_ffi_cc(const uint8_t *name) {
|
||||
#endif
|
||||
#ifdef JANET_FFI_SYSV64_ENABLED
|
||||
if (!janet_cstrcmp(name, "sysv64")) return JANET_FFI_CC_SYSV_64;
|
||||
#endif
|
||||
#ifdef JANET_FFI_AAPCS64_ENABLED
|
||||
if (!janet_cstrcmp(name, "aapcs64")) return JANET_FFI_CC_AAPCS64;
|
||||
#endif
|
||||
if (!janet_cstrcmp(name, "default")) return JANET_FFI_CC_DEFAULT;
|
||||
janet_panicf("unknown calling convention %s", name);
|
||||
@@ -385,7 +400,7 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
|
||||
|
||||
JanetFFIStruct *st = janet_abstract(&janet_struct_type,
|
||||
sizeof(JanetFFIStruct) + argc * sizeof(JanetFFIStructMember));
|
||||
st->field_count = member_count;
|
||||
st->field_count = 0;
|
||||
st->size = 0;
|
||||
st->align = 1;
|
||||
if (argc == 0) {
|
||||
@@ -403,6 +418,7 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
|
||||
st->fields[i].type = decode_ffi_type(argv[j]);
|
||||
size_t el_size = type_size(st->fields[i].type);
|
||||
size_t el_align = type_align(st->fields[i].type);
|
||||
if (el_align <= 0) janet_panicf("bad field type %V", argv[j]);
|
||||
if (all_packed || pack_one) {
|
||||
if (st->size % el_align != 0) is_aligned = 0;
|
||||
st->fields[i].offset = st->size;
|
||||
@@ -418,6 +434,7 @@ static JanetFFIStruct *build_struct_type(int32_t argc, const Janet *argv) {
|
||||
st->size += (st->align - 1);
|
||||
st->size /= st->align;
|
||||
st->size *= st->align;
|
||||
st->field_count = member_count;
|
||||
return st;
|
||||
}
|
||||
|
||||
@@ -475,7 +492,7 @@ JANET_CORE_FN(cfun_ffi_align,
|
||||
static void *janet_ffi_getpointer(const Janet *argv, int32_t n) {
|
||||
switch (janet_type(argv[n])) {
|
||||
default:
|
||||
janet_panicf("bad slot #%d, expected ffi pointer convertable type, got %v", n, argv[n]);
|
||||
janet_panicf("bad slot #%d, expected ffi pointer convertible type, got %v", n, argv[n]);
|
||||
case JANET_POINTER:
|
||||
case JANET_STRING:
|
||||
case JANET_KEYWORD:
|
||||
@@ -763,6 +780,101 @@ static JanetFFIWordSpec sysv64_classify(JanetFFIType type) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JANET_FFI_AAPCS64_ENABLED
|
||||
/* Procedure Call Standard for the Arm® 64-bit Architecture (AArch64) 2023Q3 – October 6, 2023
|
||||
* See section 6.8.2 Parameter passing rules.
|
||||
* https://github.com/ARM-software/abi-aa/releases/download/2023Q3/aapcs64.pdf
|
||||
*
|
||||
* Additional documentation needed for Apple platforms.
|
||||
* https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms */
|
||||
|
||||
#define JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, alignment) (ptr = ((ptr) + ((alignment) - 1)) & ~((alignment) - 1))
|
||||
#if !defined(JANET_APPLE)
|
||||
#define JANET_FFI_AAPCS64_STACK_ALIGN(ptr, alignment) ((void) alignment, JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, 8))
|
||||
#else
|
||||
#define JANET_FFI_AAPCS64_STACK_ALIGN(ptr, alignment) JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ptr, alignment)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint64_t a;
|
||||
uint64_t b;
|
||||
} Aapcs64Variant1ReturnGeneral;
|
||||
|
||||
typedef struct {
|
||||
double a;
|
||||
double b;
|
||||
double c;
|
||||
double d;
|
||||
} Aapcs64Variant2ReturnSse;
|
||||
|
||||
/* Workaround for passing a return value pointer through x8.
|
||||
* Limits struct returns to 128 bytes. */
|
||||
typedef struct {
|
||||
uint64_t a;
|
||||
uint64_t b;
|
||||
uint64_t c;
|
||||
uint64_t d;
|
||||
uint64_t e;
|
||||
uint64_t f;
|
||||
uint64_t g;
|
||||
uint64_t h;
|
||||
uint64_t i;
|
||||
uint64_t j;
|
||||
uint64_t k;
|
||||
uint64_t l;
|
||||
uint64_t m;
|
||||
uint64_t n;
|
||||
uint64_t o;
|
||||
uint64_t p;
|
||||
} Aapcs64Variant3ReturnPointer;
|
||||
|
||||
static JanetFFIWordSpec aapcs64_classify(JanetFFIType type) {
|
||||
switch (type.prim) {
|
||||
case JANET_FFI_TYPE_PTR:
|
||||
case JANET_FFI_TYPE_STRING:
|
||||
case JANET_FFI_TYPE_BOOL:
|
||||
case JANET_FFI_TYPE_INT8:
|
||||
case JANET_FFI_TYPE_INT16:
|
||||
case JANET_FFI_TYPE_INT32:
|
||||
case JANET_FFI_TYPE_INT64:
|
||||
case JANET_FFI_TYPE_UINT8:
|
||||
case JANET_FFI_TYPE_UINT16:
|
||||
case JANET_FFI_TYPE_UINT32:
|
||||
case JANET_FFI_TYPE_UINT64:
|
||||
return JANET_AAPCS64_GENERAL;
|
||||
case JANET_FFI_TYPE_DOUBLE:
|
||||
case JANET_FFI_TYPE_FLOAT:
|
||||
return JANET_AAPCS64_SSE;
|
||||
case JANET_FFI_TYPE_STRUCT: {
|
||||
JanetFFIStruct *st = type.st;
|
||||
if (st->field_count <= 4 && aapcs64_classify(st->fields[0].type) == JANET_AAPCS64_SSE) {
|
||||
bool is_hfa = true;
|
||||
for (uint32_t i = 1; i < st->field_count; i++) {
|
||||
if (st->fields[0].type.prim != st->fields[i].type.prim) {
|
||||
is_hfa = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_hfa) {
|
||||
return JANET_AAPCS64_SSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (type_size(type) > 16) {
|
||||
return JANET_AAPCS64_GENERAL_REF;
|
||||
}
|
||||
|
||||
return JANET_AAPCS64_GENERAL;
|
||||
}
|
||||
case JANET_FFI_TYPE_VOID:
|
||||
return JANET_AAPCS64_NONE;
|
||||
default:
|
||||
janet_panic("nyi");
|
||||
return JANET_AAPCS64_NONE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
JANET_CORE_FN(cfun_ffi_signature,
|
||||
"(ffi/signature calling-convention ret-type & arg-types)",
|
||||
"Create a function signature object that can be used to make calls "
|
||||
@@ -960,6 +1072,96 @@ JANET_CORE_FN(cfun_ffi_signature,
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef JANET_FFI_AAPCS64_ENABLED
|
||||
case JANET_FFI_CC_AAPCS64: {
|
||||
uint32_t next_general_reg = 0;
|
||||
uint32_t next_fp_reg = 0;
|
||||
uint32_t stack_offset = 0;
|
||||
uint32_t ref_stack_offset = 0;
|
||||
|
||||
JanetFFIWordSpec ret_spec = aapcs64_classify(ret_type);
|
||||
ret.spec = ret_spec;
|
||||
if (ret_spec == JANET_AAPCS64_SSE) {
|
||||
variant = 1;
|
||||
} else if (ret_spec == JANET_AAPCS64_GENERAL_REF) {
|
||||
if (type_size(ret_type) > sizeof(Aapcs64Variant3ReturnPointer)) {
|
||||
janet_panic("return value bigger than supported");
|
||||
}
|
||||
variant = 2;
|
||||
} else {
|
||||
variant = 0;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < arg_count; i++) {
|
||||
mappings[i].type = decode_ffi_type(argv[i + 2]);
|
||||
mappings[i].spec = aapcs64_classify(mappings[i].type);
|
||||
size_t arg_size = type_size(mappings[i].type);
|
||||
|
||||
switch (mappings[i].spec) {
|
||||
case JANET_AAPCS64_GENERAL: {
|
||||
bool arg_is_struct = mappings[i].type.prim == JANET_FFI_TYPE_STRUCT;
|
||||
uint32_t needed_registers = (arg_size + 7) / 8;
|
||||
if (next_general_reg + needed_registers <= 8) {
|
||||
mappings[i].offset = next_general_reg;
|
||||
next_general_reg += needed_registers;
|
||||
} else {
|
||||
size_t arg_align = arg_is_struct ? 8 : type_align(mappings[i].type);
|
||||
mappings[i].spec = JANET_AAPCS64_STACK;
|
||||
mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, arg_align);
|
||||
#if !defined(JANET_APPLE)
|
||||
stack_offset += arg_size > 8 ? arg_size : 8;
|
||||
#else
|
||||
stack_offset += arg_size;
|
||||
#endif
|
||||
next_general_reg = 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_AAPCS64_GENERAL_REF:
|
||||
if (next_general_reg < 8) {
|
||||
mappings[i].offset = next_general_reg++;
|
||||
} else {
|
||||
mappings[i].spec = JANET_AAPCS64_STACK_REF;
|
||||
mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, 8);
|
||||
stack_offset += 8;
|
||||
}
|
||||
mappings[i].offset2 = JANET_FFI_AAPCS64_FORCE_STACK_ALIGN(ref_stack_offset, 8);
|
||||
ref_stack_offset += arg_size;
|
||||
break;
|
||||
case JANET_AAPCS64_SSE: {
|
||||
uint32_t needed_registers = (arg_size + 7) / 8;
|
||||
if (next_fp_reg + needed_registers <= 8) {
|
||||
mappings[i].offset = next_fp_reg;
|
||||
next_fp_reg += needed_registers;
|
||||
} else {
|
||||
mappings[i].spec = JANET_AAPCS64_STACK;
|
||||
mappings[i].offset = JANET_FFI_AAPCS64_STACK_ALIGN(stack_offset, 8);
|
||||
#if !defined(JANET_APPLE)
|
||||
stack_offset += 8;
|
||||
#else
|
||||
stack_offset += arg_size;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
janet_panic("nyi");
|
||||
}
|
||||
}
|
||||
|
||||
stack_offset = (stack_offset + 15) & ~0xFUL;
|
||||
ref_stack_offset = (ref_stack_offset + 15) & ~0xFUL;
|
||||
stack_count = stack_offset + ref_stack_offset;
|
||||
|
||||
for (uint32_t i = 0; i < arg_count; i++) {
|
||||
if (mappings[i].spec == JANET_AAPCS64_GENERAL_REF || mappings[i].spec == JANET_AAPCS64_STACK_REF) {
|
||||
mappings[i].offset2 = stack_offset + mappings[i].offset2;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Create signature abstract value */
|
||||
@@ -1294,6 +1496,99 @@ static Janet janet_ffi_win64(JanetFFISignature *signature, void *function_pointe
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef JANET_FFI_AAPCS64_ENABLED
|
||||
|
||||
static void janet_ffi_aapcs64_standard_callback(void *ctx, void *userdata) {
|
||||
janet_ffi_trampoline(ctx, userdata);
|
||||
}
|
||||
|
||||
typedef Aapcs64Variant1ReturnGeneral janet_aapcs64_variant_1(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
|
||||
double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
|
||||
typedef Aapcs64Variant2ReturnSse janet_aapcs64_variant_2(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
|
||||
double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
|
||||
typedef Aapcs64Variant3ReturnPointer janet_aapcs64_variant_3(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7,
|
||||
double v0, double v1, double v2, double v3, double v4, double v5, double v6, double v7);
|
||||
|
||||
|
||||
static Janet janet_ffi_aapcs64(JanetFFISignature *signature, void *function_pointer, const Janet *argv) {
|
||||
union {
|
||||
Aapcs64Variant1ReturnGeneral general_return;
|
||||
Aapcs64Variant2ReturnSse sse_return;
|
||||
Aapcs64Variant3ReturnPointer pointer_return;
|
||||
} retu;
|
||||
uint64_t regs[8];
|
||||
double fp_regs[8];
|
||||
void *ret_mem = &retu.general_return;
|
||||
|
||||
/* Apple's stack values do not need to be 8-byte aligned,
|
||||
* thus all stack offsets refer to actual byte positions. */
|
||||
uint8_t *stack = alloca(signature->stack_count);
|
||||
#if defined(JANET_APPLE)
|
||||
/* Values must be zero-extended by the caller instead of the callee. */
|
||||
memset(stack, 0, signature->stack_count);
|
||||
#endif
|
||||
for (uint32_t i = 0; i < signature->arg_count; i++) {
|
||||
int32_t n = i + 2;
|
||||
JanetFFIMapping arg = signature->args[i];
|
||||
void *to = NULL;
|
||||
|
||||
switch (arg.spec) {
|
||||
case JANET_AAPCS64_GENERAL:
|
||||
to = regs + arg.offset;
|
||||
break;
|
||||
case JANET_AAPCS64_GENERAL_REF:
|
||||
to = stack + arg.offset2;
|
||||
regs[arg.offset] = (uint64_t) to;
|
||||
break;
|
||||
case JANET_AAPCS64_SSE:
|
||||
to = fp_regs + arg.offset;
|
||||
break;
|
||||
case JANET_AAPCS64_STACK:
|
||||
to = stack + arg.offset;
|
||||
break;
|
||||
case JANET_AAPCS64_STACK_REF:
|
||||
to = stack + arg.offset2;
|
||||
uint64_t *ptr = (uint64_t *) stack + arg.offset;
|
||||
*ptr = (uint64_t) to;
|
||||
break;
|
||||
default:
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
if (to) {
|
||||
janet_ffi_write_one(to, argv, n, arg.type, JANET_FFI_MAX_RECUR);
|
||||
}
|
||||
}
|
||||
|
||||
switch (signature->variant) {
|
||||
case 0:
|
||||
retu.general_return = ((janet_aapcs64_variant_1 *)(function_pointer))(
|
||||
regs[0], regs[1], regs[2], regs[3],
|
||||
regs[4], regs[5], regs[6], regs[7],
|
||||
fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
|
||||
fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
|
||||
break;
|
||||
case 1:
|
||||
retu.sse_return = ((janet_aapcs64_variant_2 *)(function_pointer))(
|
||||
regs[0], regs[1], regs[2], regs[3],
|
||||
regs[4], regs[5], regs[6], regs[7],
|
||||
fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
|
||||
fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
|
||||
break;
|
||||
case 2: {
|
||||
retu.pointer_return = ((janet_aapcs64_variant_3 *)(function_pointer))(
|
||||
regs[0], regs[1], regs[2], regs[3],
|
||||
regs[4], regs[5], regs[6], regs[7],
|
||||
fp_regs[0], fp_regs[1], fp_regs[2], fp_regs[3],
|
||||
fp_regs[4], fp_regs[5], fp_regs[6], fp_regs[7]);
|
||||
}
|
||||
}
|
||||
|
||||
return janet_ffi_read_one(ret_mem, signature->ret.type, JANET_FFI_MAX_RECUR);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Allocate executable memory chunks in sizes of a page. Ideally we would keep
|
||||
* an allocator around so that multiple JIT allocations would point to the same
|
||||
* region but it isn't really worth it. */
|
||||
@@ -1373,6 +1668,10 @@ JANET_CORE_FN(cfun_ffi_call,
|
||||
#ifdef JANET_FFI_SYSV64_ENABLED
|
||||
case JANET_FFI_CC_SYSV_64:
|
||||
return janet_ffi_sysv64(signature, function_pointer, argv);
|
||||
#endif
|
||||
#ifdef JANET_FFI_AAPCS64_ENABLED
|
||||
case JANET_FFI_CC_AAPCS64:
|
||||
return janet_ffi_aapcs64(signature, function_pointer, argv);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1381,13 +1680,13 @@ JANET_CORE_FN(cfun_ffi_buffer_write,
|
||||
"(ffi/write ffi-type data &opt buffer index)",
|
||||
"Append a native type to a buffer such as it would appear in memory. This can be used "
|
||||
"to pass pointers to structs in the ffi, or send C/C++/native structs over the network "
|
||||
"or to files. Returns a modifed buffer or a new buffer if one is not supplied.") {
|
||||
"or to files. Returns a modified buffer or a new buffer if one is not supplied.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_FFI_USE);
|
||||
janet_arity(argc, 2, 4);
|
||||
JanetFFIType type = decode_ffi_type(argv[0]);
|
||||
uint32_t el_size = (uint32_t) type_size(type);
|
||||
JanetBuffer *buffer = janet_optbuffer(argv, argc, 2, el_size);
|
||||
int32_t index = janet_optnat(argv, argc, 3, 0);
|
||||
int32_t index = janet_optnat(argv, argc, 3, buffer->count);
|
||||
int32_t old_count = buffer->count;
|
||||
if (index > old_count) janet_panic("index out of bounds");
|
||||
buffer->count = index;
|
||||
@@ -1442,6 +1741,10 @@ JANET_CORE_FN(cfun_ffi_get_callback_trampoline,
|
||||
#ifdef JANET_FFI_SYSV64_ENABLED
|
||||
case JANET_FFI_CC_SYSV_64:
|
||||
return janet_wrap_pointer(janet_ffi_sysv64_standard_callback);
|
||||
#endif
|
||||
#ifdef JANET_FFI_AAPCS64_ENABLED
|
||||
case JANET_FFI_CC_AAPCS64:
|
||||
return janet_wrap_pointer(janet_ffi_aapcs64_standard_callback);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1548,7 +1851,7 @@ JANET_CORE_FN(cfun_ffi_pointer_cfunction,
|
||||
|
||||
JANET_CORE_FN(cfun_ffi_supported_calling_conventions,
|
||||
"(ffi/calling-conventions)",
|
||||
"Get an array of all supported calling conventions on the current arhcitecture. Some architectures may have some FFI "
|
||||
"Get an array of all supported calling conventions on the current architecture. Some architectures may have some FFI "
|
||||
"functionality (ffi/malloc, ffi/free, ffi/read, ffi/write, etc.) but not support "
|
||||
"any calling conventions. This function can be used to get all supported calling conventions "
|
||||
"that can be used on this architecture. All architectures support the :none calling "
|
||||
@@ -1561,6 +1864,9 @@ JANET_CORE_FN(cfun_ffi_supported_calling_conventions,
|
||||
#endif
|
||||
#ifdef JANET_FFI_SYSV64_ENABLED
|
||||
janet_array_push(array, janet_ckeywordv("sysv64"));
|
||||
#endif
|
||||
#ifdef JANET_FFI_AAPCS64_ENABLED
|
||||
janet_array_push(array, janet_ckeywordv("aapcs64"));
|
||||
#endif
|
||||
janet_array_push(array, janet_ckeywordv("none"));
|
||||
return janet_wrap_array(array);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -39,8 +39,10 @@ static void fiber_reset(JanetFiber *fiber) {
|
||||
fiber->env = NULL;
|
||||
fiber->last_value = janet_wrap_nil();
|
||||
#ifdef JANET_EV
|
||||
fiber->waiting = NULL;
|
||||
fiber->sched_id = 0;
|
||||
fiber->ev_callback = NULL;
|
||||
fiber->ev_state = NULL;
|
||||
fiber->ev_stream = NULL;
|
||||
fiber->supervisor_channel = NULL;
|
||||
#endif
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_NEW);
|
||||
@@ -85,7 +87,6 @@ JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t
|
||||
if (janet_fiber_funcframe(fiber, callee)) return NULL;
|
||||
janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
|
||||
#ifdef JANET_EV
|
||||
fiber->waiting = NULL;
|
||||
fiber->supervisor_channel = NULL;
|
||||
#endif
|
||||
return fiber;
|
||||
@@ -238,8 +239,8 @@ int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) {
|
||||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead)
|
||||
: janet_wrap_tuple(janet_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead));
|
||||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,8 +370,8 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead)
|
||||
: janet_wrap_tuple(janet_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead));
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead));
|
||||
}
|
||||
stacksize = tuplehead - fiber->stackstart + 1;
|
||||
} else {
|
||||
@@ -661,7 +662,7 @@ JANET_CORE_FN(cfun_fiber_can_resume,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_fiber_last_value,
|
||||
"(fiber/last-value)",
|
||||
"(fiber/last-value fiber)",
|
||||
"Get the last value returned or signaled from the fiber.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -59,6 +59,9 @@
|
||||
#define JANET_FIBER_EV_FLAG_CANCELED 0x10000
|
||||
#define JANET_FIBER_EV_FLAG_SUSPENDED 0x20000
|
||||
#define JANET_FIBER_FLAG_ROOT 0x40000
|
||||
#define JANET_FIBER_EV_FLAG_IN_FLIGHT 0x1
|
||||
|
||||
/* used only on windows, should otherwise be unset */
|
||||
|
||||
#define janet_fiber_set_status(f, s) do {\
|
||||
(f)->flags &= ~JANET_FIBER_STATUS_MASK;\
|
||||
|
||||
688
src/core/filewatch.c
Normal file
688
src/core/filewatch.c
Normal file
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#ifdef JANET_EV
|
||||
#ifdef JANET_FILEWATCH
|
||||
|
||||
#ifdef JANET_LINUX
|
||||
#include <sys/inotify.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
uint32_t flag;
|
||||
} JanetWatchFlagName;
|
||||
|
||||
typedef struct {
|
||||
#ifndef JANET_WINDOWS
|
||||
JanetStream *stream;
|
||||
#endif
|
||||
JanetTable *watch_descriptors;
|
||||
JanetChannel *channel;
|
||||
uint32_t default_flags;
|
||||
int is_watching;
|
||||
} JanetWatcher;
|
||||
|
||||
#ifdef JANET_LINUX
|
||||
|
||||
#include <sys/inotify.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const JanetWatchFlagName watcher_flags_linux[] = {
|
||||
{"access", IN_ACCESS},
|
||||
{"all", IN_ALL_EVENTS},
|
||||
{"attrib", IN_ATTRIB},
|
||||
{"close-nowrite", IN_CLOSE_NOWRITE},
|
||||
{"close-write", IN_CLOSE_WRITE},
|
||||
{"create", IN_CREATE},
|
||||
{"delete", IN_DELETE},
|
||||
{"delete-self", IN_DELETE_SELF},
|
||||
{"ignored", IN_IGNORED},
|
||||
{"modify", IN_MODIFY},
|
||||
{"move-self", IN_MOVE_SELF},
|
||||
{"moved-from", IN_MOVED_FROM},
|
||||
{"moved-to", IN_MOVED_TO},
|
||||
{"open", IN_OPEN},
|
||||
{"q-overflow", IN_Q_OVERFLOW},
|
||||
{"unmount", IN_UNMOUNT},
|
||||
};
|
||||
|
||||
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
|
||||
uint32_t flags = 0;
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
if (!(janet_checktype(options[i], JANET_KEYWORD))) {
|
||||
janet_panicf("expected keyword, got %v", options[i]);
|
||||
}
|
||||
JanetKeyword keyw = janet_unwrap_keyword(options[i]);
|
||||
const JanetWatchFlagName *result = janet_strbinsearch(watcher_flags_linux,
|
||||
sizeof(watcher_flags_linux) / sizeof(JanetWatchFlagName),
|
||||
sizeof(JanetWatchFlagName),
|
||||
keyw);
|
||||
if (!result) {
|
||||
janet_panicf("unknown inotify flag %v", options[i]);
|
||||
}
|
||||
flags |= result->flag;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
|
||||
int fd;
|
||||
do {
|
||||
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
} while (fd == -1 && errno == EINTR);
|
||||
if (fd == -1) {
|
||||
janet_panicv(janet_ev_lasterr());
|
||||
}
|
||||
watcher->watch_descriptors = janet_table(0);
|
||||
watcher->channel = channel;
|
||||
watcher->default_flags = default_flags;
|
||||
watcher->is_watching = 0;
|
||||
watcher->stream = janet_stream(fd, JANET_STREAM_READABLE, NULL);
|
||||
}
|
||||
|
||||
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
|
||||
if (watcher->stream == NULL) janet_panic("watcher closed");
|
||||
int result;
|
||||
do {
|
||||
result = inotify_add_watch(watcher->stream->handle, path, flags);
|
||||
} while (result == -1 && errno == EINTR);
|
||||
if (result == -1) {
|
||||
janet_panicv(janet_ev_lasterr());
|
||||
}
|
||||
Janet name = janet_cstringv(path);
|
||||
Janet wd = janet_wrap_integer(result);
|
||||
janet_table_put(watcher->watch_descriptors, name, wd);
|
||||
janet_table_put(watcher->watch_descriptors, wd, name);
|
||||
}
|
||||
|
||||
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
|
||||
if (watcher->stream == NULL) janet_panic("watcher closed");
|
||||
Janet check = janet_table_get(watcher->watch_descriptors, janet_cstringv(path));
|
||||
janet_assert(janet_checktype(check, JANET_NUMBER), "bad watch descriptor");
|
||||
int watch_handle = janet_unwrap_integer(check);
|
||||
int result;
|
||||
do {
|
||||
result = inotify_rm_watch(watcher->stream->handle, watch_handle);
|
||||
} while (result != -1 && errno == EINTR);
|
||||
if (result == -1) {
|
||||
janet_panicv(janet_ev_lasterr());
|
||||
}
|
||||
}
|
||||
|
||||
static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
JanetStream *stream = fiber->ev_stream;
|
||||
JanetWatcher *watcher = *((JanetWatcher **) fiber->ev_state);
|
||||
char buf[1024];
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_MARK:
|
||||
janet_mark(janet_wrap_abstract(watcher));
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_CLOSE:
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
janet_async_end(fiber);
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_ERR: {
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
janet_async_end(fiber);
|
||||
break;
|
||||
}
|
||||
read_more:
|
||||
case JANET_ASYNC_EVENT_HUP:
|
||||
case JANET_ASYNC_EVENT_INIT:
|
||||
case JANET_ASYNC_EVENT_READ: {
|
||||
Janet name = janet_wrap_nil();
|
||||
|
||||
/* Assumption - read will never return partial events *
|
||||
* From documentation:
|
||||
*
|
||||
* The behavior when the buffer given to read(2) is too small to
|
||||
* return information about the next event depends on the kernel
|
||||
* version: before Linux 2.6.21, read(2) returns 0; since Linux
|
||||
* 2.6.21, read(2) fails with the error EINVAL. Specifying a buffer
|
||||
* of size
|
||||
*
|
||||
* sizeof(struct inotify_event) + NAME_MAX + 1
|
||||
*
|
||||
* will be sufficient to read at least one event. */
|
||||
ssize_t nread;
|
||||
do {
|
||||
nread = read(stream->handle, buf, sizeof(buf));
|
||||
} while (nread == -1 && errno == EINTR);
|
||||
|
||||
/* Check for errors - special case errors that can just be waited on to fix */
|
||||
if (nread == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
break;
|
||||
}
|
||||
janet_cancel(fiber, janet_ev_lasterr());
|
||||
fiber->ev_state = NULL;
|
||||
janet_async_end(fiber);
|
||||
break;
|
||||
}
|
||||
if (nread < (ssize_t) sizeof(struct inotify_event)) break;
|
||||
|
||||
/* Iterate through all events read from the buffer */
|
||||
char *cursor = buf;
|
||||
while (cursor < buf + nread) {
|
||||
struct inotify_event inevent;
|
||||
memcpy(&inevent, cursor, sizeof(inevent));
|
||||
cursor += sizeof(inevent);
|
||||
/* Read path of inevent */
|
||||
if (inevent.len) {
|
||||
name = janet_cstringv(cursor);
|
||||
cursor += inevent.len;
|
||||
}
|
||||
|
||||
/* Got an event */
|
||||
Janet path = janet_table_get(watcher->watch_descriptors, janet_wrap_integer(inevent.wd));
|
||||
JanetKV *event = janet_struct_begin(6);
|
||||
janet_struct_put(event, janet_ckeywordv("wd"), janet_wrap_integer(inevent.wd));
|
||||
janet_struct_put(event, janet_ckeywordv("wd-path"), path);
|
||||
if (janet_checktype(name, JANET_NIL)) {
|
||||
/* We were watching a file directly, so path is the full path. Split into dirname / basename */
|
||||
JanetString spath = janet_unwrap_string(path);
|
||||
const uint8_t *cursor = spath + janet_string_length(spath);
|
||||
const uint8_t *cursor_end = cursor;
|
||||
while (cursor > spath && cursor[0] != '/') {
|
||||
cursor--;
|
||||
}
|
||||
if (cursor == spath) {
|
||||
janet_struct_put(event, janet_ckeywordv("dir-name"), path);
|
||||
janet_struct_put(event, janet_ckeywordv("file-name"), name);
|
||||
} else {
|
||||
janet_struct_put(event, janet_ckeywordv("dir-name"), janet_wrap_string(janet_string(spath, (cursor - spath))));
|
||||
janet_struct_put(event, janet_ckeywordv("file-name"), janet_wrap_string(janet_string(cursor + 1, (cursor_end - cursor - 1))));
|
||||
}
|
||||
} else {
|
||||
janet_struct_put(event, janet_ckeywordv("dir-name"), path);
|
||||
janet_struct_put(event, janet_ckeywordv("file-name"), name);
|
||||
}
|
||||
janet_struct_put(event, janet_ckeywordv("cookie"), janet_wrap_integer(inevent.cookie));
|
||||
Janet etype = janet_ckeywordv("type");
|
||||
const JanetWatchFlagName *wfn_end = watcher_flags_linux + sizeof(watcher_flags_linux) / sizeof(watcher_flags_linux[0]);
|
||||
for (const JanetWatchFlagName *wfn = watcher_flags_linux; wfn < wfn_end; wfn++) {
|
||||
if ((inevent.mask & wfn->flag) == wfn->flag) janet_struct_put(event, etype, janet_ckeywordv(wfn->name));
|
||||
}
|
||||
Janet eventv = janet_wrap_struct(janet_struct_end(event));
|
||||
|
||||
janet_channel_give(watcher->channel, eventv);
|
||||
}
|
||||
|
||||
/* Read some more if possible */
|
||||
goto read_more;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void janet_watcher_listen(JanetWatcher *watcher) {
|
||||
if (watcher->is_watching) janet_panic("already watching");
|
||||
watcher->is_watching = 1;
|
||||
JanetFunction *thunk = janet_thunk_delay(janet_wrap_nil());
|
||||
JanetFiber *fiber = janet_fiber(thunk, 64, 0, NULL);
|
||||
JanetWatcher **state = janet_malloc(sizeof(JanetWatcher *)); /* Gross */
|
||||
*state = watcher;
|
||||
janet_async_start_fiber(fiber, watcher->stream, JANET_ASYNC_LISTEN_READ, watcher_callback_read, state);
|
||||
janet_gcroot(janet_wrap_abstract(watcher));
|
||||
}
|
||||
|
||||
static void janet_watcher_unlisten(JanetWatcher *watcher) {
|
||||
if (!watcher->is_watching) return;
|
||||
watcher->is_watching = 0;
|
||||
janet_stream_close(watcher->stream);
|
||||
janet_gcunroot(janet_wrap_abstract(watcher));
|
||||
}
|
||||
|
||||
#elif JANET_WINDOWS
|
||||
|
||||
#define WATCHFLAG_RECURSIVE 0x100000u
|
||||
|
||||
static const JanetWatchFlagName watcher_flags_windows[] = {
|
||||
{
|
||||
"all",
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
FILE_NOTIFY_CHANGE_CREATION |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_LAST_ACCESS |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
FILE_NOTIFY_CHANGE_SECURITY |
|
||||
FILE_NOTIFY_CHANGE_SIZE |
|
||||
WATCHFLAG_RECURSIVE
|
||||
},
|
||||
{"attributes", FILE_NOTIFY_CHANGE_ATTRIBUTES},
|
||||
{"creation", FILE_NOTIFY_CHANGE_CREATION},
|
||||
{"dir-name", FILE_NOTIFY_CHANGE_DIR_NAME},
|
||||
{"file-name", FILE_NOTIFY_CHANGE_FILE_NAME},
|
||||
{"last-access", FILE_NOTIFY_CHANGE_LAST_ACCESS},
|
||||
{"last-write", FILE_NOTIFY_CHANGE_LAST_WRITE},
|
||||
{"recursive", WATCHFLAG_RECURSIVE},
|
||||
{"security", FILE_NOTIFY_CHANGE_SECURITY},
|
||||
{"size", FILE_NOTIFY_CHANGE_SIZE},
|
||||
};
|
||||
|
||||
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
|
||||
uint32_t flags = 0;
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
if (!(janet_checktype(options[i], JANET_KEYWORD))) {
|
||||
janet_panicf("expected keyword, got %v", options[i]);
|
||||
}
|
||||
JanetKeyword keyw = janet_unwrap_keyword(options[i]);
|
||||
const JanetWatchFlagName *result = janet_strbinsearch(watcher_flags_windows,
|
||||
sizeof(watcher_flags_windows) / sizeof(JanetWatchFlagName),
|
||||
sizeof(JanetWatchFlagName),
|
||||
keyw);
|
||||
if (!result) {
|
||||
janet_panicf("unknown windows filewatch flag %v", options[i]);
|
||||
}
|
||||
flags |= result->flag;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
|
||||
watcher->watch_descriptors = janet_table(0);
|
||||
watcher->channel = channel;
|
||||
watcher->default_flags = default_flags;
|
||||
watcher->is_watching = 0;
|
||||
}
|
||||
|
||||
/* Since the file info padding includes embedded file names, we want to include more space for data.
|
||||
* We also need to handle manually calculating changes if path names are too long, but ideally just avoid
|
||||
* that scenario as much as possible */
|
||||
#define FILE_INFO_PADDING (4096 * 4)
|
||||
|
||||
typedef struct {
|
||||
OVERLAPPED overlapped;
|
||||
JanetStream *stream;
|
||||
JanetWatcher *watcher;
|
||||
JanetFiber *fiber;
|
||||
JanetString dir_path;
|
||||
uint32_t flags;
|
||||
uint64_t buf[FILE_INFO_PADDING / sizeof(uint64_t)]; /* Ensure alignment */
|
||||
} OverlappedWatch;
|
||||
|
||||
#define NotifyChange FILE_NOTIFY_INFORMATION
|
||||
|
||||
static void read_dir_changes(OverlappedWatch *ow) {
|
||||
BOOL result = ReadDirectoryChangesW(ow->stream->handle,
|
||||
(NotifyChange *) ow->buf,
|
||||
FILE_INFO_PADDING,
|
||||
(ow->flags & WATCHFLAG_RECURSIVE) ? TRUE : FALSE,
|
||||
ow->flags & ~WATCHFLAG_RECURSIVE,
|
||||
NULL,
|
||||
(OVERLAPPED *) ow,
|
||||
NULL);
|
||||
if (!result) {
|
||||
janet_panicv(janet_ev_lasterr());
|
||||
}
|
||||
}
|
||||
|
||||
static const char *watcher_actions_windows[] = {
|
||||
"unknown",
|
||||
"added",
|
||||
"removed",
|
||||
"modified",
|
||||
"renamed-old",
|
||||
"renamed-new",
|
||||
};
|
||||
|
||||
static void watcher_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
OverlappedWatch *ow = (OverlappedWatch *) fiber->ev_state;
|
||||
JanetWatcher *watcher = ow->watcher;
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_INIT:
|
||||
janet_async_in_flight(fiber);
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_MARK:
|
||||
janet_mark(janet_wrap_abstract(ow->stream));
|
||||
janet_mark(janet_wrap_fiber(ow->fiber));
|
||||
janet_mark(janet_wrap_abstract(watcher));
|
||||
janet_mark(janet_wrap_string(ow->dir_path));
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_CLOSE:
|
||||
janet_table_remove(ow->watcher->watch_descriptors, janet_wrap_string(ow->dir_path));
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_ERR:
|
||||
case JANET_ASYNC_EVENT_FAILED:
|
||||
janet_stream_close(ow->stream);
|
||||
break;
|
||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||
if (!watcher->is_watching) {
|
||||
janet_stream_close(ow->stream);
|
||||
break;
|
||||
}
|
||||
|
||||
NotifyChange *fni = (NotifyChange *) ow->buf;
|
||||
|
||||
while (1) {
|
||||
/* Got an event */
|
||||
|
||||
/* Extract name */
|
||||
Janet filename;
|
||||
if (fni->FileNameLength) {
|
||||
int32_t nbytes = (int32_t) WideCharToMultiByte(CP_UTF8, 0, fni->FileName, fni->FileNameLength / sizeof(wchar_t), NULL, 0, NULL, NULL);
|
||||
janet_assert(nbytes, "bad utf8 path");
|
||||
uint8_t *into = janet_string_begin(nbytes);
|
||||
WideCharToMultiByte(CP_UTF8, 0, fni->FileName, fni->FileNameLength / sizeof(wchar_t), (char *) into, nbytes, NULL, NULL);
|
||||
filename = janet_wrap_string(janet_string_end(into));
|
||||
} else {
|
||||
filename = janet_cstringv("");
|
||||
}
|
||||
|
||||
JanetKV *event = janet_struct_begin(3);
|
||||
janet_struct_put(event, janet_ckeywordv("type"), janet_ckeywordv(watcher_actions_windows[fni->Action]));
|
||||
janet_struct_put(event, janet_ckeywordv("file-name"), filename);
|
||||
janet_struct_put(event, janet_ckeywordv("dir-name"), janet_wrap_string(ow->dir_path));
|
||||
Janet eventv = janet_wrap_struct(janet_struct_end(event));
|
||||
|
||||
janet_channel_give(watcher->channel, eventv);
|
||||
|
||||
/* Next event */
|
||||
if (!fni->NextEntryOffset) break;
|
||||
fni = (NotifyChange *)((char *)fni + fni->NextEntryOffset);
|
||||
}
|
||||
|
||||
/* Make another call to read directory changes */
|
||||
read_dir_changes(ow);
|
||||
janet_async_in_flight(fiber);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void start_listening_ow(OverlappedWatch *ow) {
|
||||
read_dir_changes(ow);
|
||||
JanetStream *stream = ow->stream;
|
||||
JanetFunction *thunk = janet_thunk_delay(janet_wrap_nil());
|
||||
JanetFiber *fiber = janet_fiber(thunk, 64, 0, NULL);
|
||||
fiber->supervisor_channel = janet_root_fiber()->supervisor_channel;
|
||||
ow->fiber = fiber;
|
||||
janet_async_start_fiber(fiber, stream, JANET_ASYNC_LISTEN_READ, watcher_callback_read, ow);
|
||||
}
|
||||
|
||||
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
|
||||
HANDLE handle = CreateFileA(path,
|
||||
FILE_LIST_DIRECTORY | GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED | FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
janet_panicv(janet_ev_lasterr());
|
||||
}
|
||||
JanetStream *stream = janet_stream(handle, JANET_STREAM_READABLE, NULL);
|
||||
OverlappedWatch *ow = janet_malloc(sizeof(OverlappedWatch));
|
||||
memset(ow, 0, sizeof(OverlappedWatch));
|
||||
ow->stream = stream;
|
||||
ow->dir_path = janet_cstring(path);
|
||||
ow->fiber = NULL;
|
||||
Janet pathv = janet_wrap_string(ow->dir_path);
|
||||
ow->flags = flags | watcher->default_flags;
|
||||
ow->watcher = watcher;
|
||||
ow->overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL); /* Do we need this */
|
||||
Janet streamv = janet_wrap_pointer(ow);
|
||||
janet_table_put(watcher->watch_descriptors, pathv, streamv);
|
||||
if (watcher->is_watching) {
|
||||
start_listening_ow(ow);
|
||||
}
|
||||
}
|
||||
|
||||
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
|
||||
Janet pathv = janet_cstringv(path);
|
||||
Janet streamv = janet_table_get(watcher->watch_descriptors, pathv);
|
||||
if (janet_checktype(streamv, JANET_NIL)) {
|
||||
janet_panicf("path %v is not being watched", pathv);
|
||||
}
|
||||
janet_table_remove(watcher->watch_descriptors, pathv);
|
||||
OverlappedWatch *ow = janet_unwrap_pointer(streamv);
|
||||
janet_stream_close(ow->stream);
|
||||
}
|
||||
|
||||
static void janet_watcher_listen(JanetWatcher *watcher) {
|
||||
if (watcher->is_watching) janet_panic("already watching");
|
||||
watcher->is_watching = 1;
|
||||
for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
|
||||
const JanetKV *kv = watcher->watch_descriptors->data + i;
|
||||
if (!janet_checktype(kv->value, JANET_POINTER)) continue;
|
||||
OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
|
||||
start_listening_ow(ow);
|
||||
}
|
||||
janet_gcroot(janet_wrap_abstract(watcher));
|
||||
}
|
||||
|
||||
static void janet_watcher_unlisten(JanetWatcher *watcher) {
|
||||
if (!watcher->is_watching) return;
|
||||
watcher->is_watching = 0;
|
||||
for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
|
||||
const JanetKV *kv = watcher->watch_descriptors->data + i;
|
||||
if (!janet_checktype(kv->value, JANET_POINTER)) continue;
|
||||
OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
|
||||
janet_stream_close(ow->stream);
|
||||
}
|
||||
janet_table_clear(watcher->watch_descriptors);
|
||||
janet_gcunroot(janet_wrap_abstract(watcher));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Default implementation */
|
||||
|
||||
static uint32_t decode_watch_flags(const Janet *options, int32_t n) {
|
||||
(void) options;
|
||||
(void) n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uint32_t default_flags) {
|
||||
(void) watcher;
|
||||
(void) channel;
|
||||
(void) default_flags;
|
||||
janet_panic("filewatch not supported on this platform");
|
||||
}
|
||||
|
||||
static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t flags) {
|
||||
(void) watcher;
|
||||
(void) flags;
|
||||
(void) path;
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_remove(JanetWatcher *watcher, const char *path) {
|
||||
(void) watcher;
|
||||
(void) path;
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_listen(JanetWatcher *watcher) {
|
||||
(void) watcher;
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
static void janet_watcher_unlisten(JanetWatcher *watcher) {
|
||||
(void) watcher;
|
||||
janet_panic("nyi");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* C Functions */
|
||||
|
||||
static int janet_filewatch_mark(void *p, size_t s) {
|
||||
JanetWatcher *watcher = (JanetWatcher *) p;
|
||||
(void) s;
|
||||
if (watcher->channel == NULL) return 0; /* Incomplete initialization */
|
||||
#ifdef JANET_WINDOWS
|
||||
for (int32_t i = 0; i < watcher->watch_descriptors->capacity; i++) {
|
||||
const JanetKV *kv = watcher->watch_descriptors->data + i;
|
||||
if (!janet_checktype(kv->value, JANET_POINTER)) continue;
|
||||
OverlappedWatch *ow = janet_unwrap_pointer(kv->value);
|
||||
janet_mark(janet_wrap_fiber(ow->fiber));
|
||||
janet_mark(janet_wrap_abstract(ow->stream));
|
||||
janet_mark(janet_wrap_string(ow->dir_path));
|
||||
}
|
||||
#else
|
||||
janet_mark(janet_wrap_abstract(watcher->stream));
|
||||
#endif
|
||||
janet_mark(janet_wrap_abstract(watcher->channel));
|
||||
janet_mark(janet_wrap_table(watcher->watch_descriptors));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const JanetAbstractType janet_filewatch_at = {
|
||||
"filewatch/watcher",
|
||||
NULL,
|
||||
janet_filewatch_mark,
|
||||
JANET_ATEND_GCMARK
|
||||
};
|
||||
|
||||
JANET_CORE_FN(cfun_filewatch_make,
|
||||
"(filewatch/new channel &opt default-flags)",
|
||||
"Create a new filewatcher that will give events to a channel channel. See `filewatch/add` for available flags.\n\n"
|
||||
"When an event is triggered by the filewatcher, a struct containing information will be given to channel as with `ev/give`. "
|
||||
"The contents of the channel depend on the OS, but will contain some common keys:\n\n"
|
||||
"* `:type` -- the type of the event that was raised.\n\n"
|
||||
"* `:file-name` -- the base file name of the file that triggered the event.\n\n"
|
||||
"* `:dir-name` -- the directory name of the file that triggered the event.\n\n"
|
||||
"Events also will contain keys specific to the host OS.\n\n"
|
||||
"Windows has no extra properties on events.\n\n"
|
||||
"Linux has the following extra properties on events:\n\n"
|
||||
"* `:wd` -- the integer key returned by `filewatch/add` for the path that triggered this.\n\n"
|
||||
"* `:wd-path` -- the string path for watched directory of file. For files, will be the same as `:file-name`, and for directories, will be the same as `:dir-name`.\n\n"
|
||||
"* `:cookie` -- a randomized integer used to associate related events, such as :moved-from and :moved-to events.\n\n"
|
||||
"") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetChannel *channel = janet_getchannel(argv, 0);
|
||||
JanetWatcher *watcher = janet_abstract(&janet_filewatch_at, sizeof(JanetWatcher));
|
||||
uint32_t default_flags = decode_watch_flags(argv + 1, argc - 1);
|
||||
janet_watcher_init(watcher, channel, default_flags);
|
||||
return janet_wrap_abstract(watcher);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_filewatch_add,
|
||||
"(filewatch/add watcher path &opt flags)",
|
||||
"Add a path to the watcher. Available flags depend on the current OS, and are as follows:\n\n"
|
||||
"Windows/MINGW (flags correspond to `FILE_NOTIFY_CHANGE_*` flags in win32 documentation):\n\n"
|
||||
"* `:all` - trigger an event for all of the below triggers.\n\n"
|
||||
"* `:attributes` - `FILE_NOTIFY_CHANGE_ATTRIBUTES`\n\n"
|
||||
"* `:creation` - `FILE_NOTIFY_CHANGE_CREATION`\n\n"
|
||||
"* `:dir-name` - `FILE_NOTIFY_CHANGE_DIR_NAME`\n\n"
|
||||
"* `:last-access` - `FILE_NOTIFY_CHANGE_LAST_ACCESS`\n\n"
|
||||
"* `:last-write` - `FILE_NOTIFY_CHANGE_LAST_WRITE`\n\n"
|
||||
"* `:security` - `FILE_NOTIFY_CHANGE_SECURITY`\n\n"
|
||||
"* `:size` - `FILE_NOTIFY_CHANGE_SIZE`\n\n"
|
||||
"* `:recursive` - watch subdirectories recursively\n\n"
|
||||
"Linux (flags correspond to `IN_*` flags from <sys/inotify.h>):\n\n"
|
||||
"* `:access` - `IN_ACCESS`\n\n"
|
||||
"* `:all` - `IN_ALL_EVENTS`\n\n"
|
||||
"* `:attrib` - `IN_ATTRIB`\n\n"
|
||||
"* `:close-nowrite` - `IN_CLOSE_NOWRITE`\n\n"
|
||||
"* `:close-write` - `IN_CLOSE_WRITE`\n\n"
|
||||
"* `:create` - `IN_CREATE`\n\n"
|
||||
"* `:delete` - `IN_DELETE`\n\n"
|
||||
"* `:delete-self` - `IN_DELETE_SELF`\n\n"
|
||||
"* `:ignored` - `IN_IGNORED`\n\n"
|
||||
"* `:modify` - `IN_MODIFY`\n\n"
|
||||
"* `:move-self` - `IN_MOVE_SELF`\n\n"
|
||||
"* `:moved-from` - `IN_MOVED_FROM`\n\n"
|
||||
"* `:moved-to` - `IN_MOVED_TO`\n\n"
|
||||
"* `:open` - `IN_OPEN`\n\n"
|
||||
"* `:q-overflow` - `IN_Q_OVERFLOW`\n\n"
|
||||
"* `:unmount` - `IN_UNMOUNT`\n\n\n"
|
||||
"On Windows, events will have the following possible types:\n\n"
|
||||
"* `:unknown`\n\n"
|
||||
"* `:added`\n\n"
|
||||
"* `:removed`\n\n"
|
||||
"* `:modified`\n\n"
|
||||
"* `:renamed-old`\n\n"
|
||||
"* `:renamed-new`\n\n"
|
||||
"On Linux, events will have a `:type` corresponding to the possible flags, excluding `:all`.\n"
|
||||
"") {
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
|
||||
const char *path = janet_getcstring(argv, 1);
|
||||
uint32_t flags = watcher->default_flags | decode_watch_flags(argv + 2, argc - 2);
|
||||
janet_watcher_add(watcher, path, flags);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_filewatch_remove,
|
||||
"(filewatch/remove watcher path)",
|
||||
"Remove a path from the watcher.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
|
||||
const char *path = janet_getcstring(argv, 1);
|
||||
janet_watcher_remove(watcher, path);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_filewatch_listen,
|
||||
"(filewatch/listen watcher)",
|
||||
"Listen for changes in the watcher.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
|
||||
janet_watcher_listen(watcher);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_filewatch_unlisten,
|
||||
"(filewatch/unlisten watcher)",
|
||||
"Stop listening for changes on a given watcher.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetWatcher *watcher = janet_getabstract(argv, 0, &janet_filewatch_at);
|
||||
janet_watcher_unlisten(watcher);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_filewatch(JanetTable *env) {
|
||||
JanetRegExt cfuns[] = {
|
||||
JANET_CORE_REG("filewatch/new", cfun_filewatch_make),
|
||||
JANET_CORE_REG("filewatch/add", cfun_filewatch_add),
|
||||
JANET_CORE_REG("filewatch/remove", cfun_filewatch_remove),
|
||||
JANET_CORE_REG("filewatch/listen", cfun_filewatch_listen),
|
||||
JANET_CORE_REG("filewatch/unlisten", cfun_filewatch_unlisten),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, cfuns);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
192
src/core/gc.c
192
src/core/gc.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -132,7 +132,25 @@ static void janet_mark_many(const Janet *values, int32_t n) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark a bunch of key values items in memory */
|
||||
/* Mark only the keys from a sequence of key-value pairs */
|
||||
static void janet_mark_keys(const JanetKV *kvs, int32_t n) {
|
||||
const JanetKV *end = kvs + n;
|
||||
while (kvs < end) {
|
||||
janet_mark(kvs->key);
|
||||
kvs++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark only the values from a sequence of key-value pairs */
|
||||
static void janet_mark_values(const JanetKV *kvs, int32_t n) {
|
||||
const JanetKV *end = kvs + n;
|
||||
while (kvs < end) {
|
||||
janet_mark(kvs->value);
|
||||
kvs++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark key-value pairs */
|
||||
static void janet_mark_kvs(const JanetKV *kvs, int32_t n) {
|
||||
const JanetKV *end = kvs + n;
|
||||
while (kvs < end) {
|
||||
@@ -146,7 +164,9 @@ static void janet_mark_array(JanetArray *array) {
|
||||
if (janet_gc_reachable(array))
|
||||
return;
|
||||
janet_gc_mark(array);
|
||||
janet_mark_many(array->data, array->count);
|
||||
if (janet_gc_type((JanetGCObject *) array) == JANET_MEMORY_ARRAY) {
|
||||
janet_mark_many(array->data, array->count);
|
||||
}
|
||||
}
|
||||
|
||||
static void janet_mark_table(JanetTable *table) {
|
||||
@@ -154,7 +174,15 @@ recur: /* Manual tail recursion */
|
||||
if (janet_gc_reachable(table))
|
||||
return;
|
||||
janet_gc_mark(table);
|
||||
janet_mark_kvs(table->data, table->capacity);
|
||||
enum JanetMemoryType memtype = janet_gc_type(table);
|
||||
if (memtype == JANET_MEMORY_TABLE_WEAKK) {
|
||||
janet_mark_values(table->data, table->capacity);
|
||||
} else if (memtype == JANET_MEMORY_TABLE_WEAKV) {
|
||||
janet_mark_keys(table->data, table->capacity);
|
||||
} else if (memtype == JANET_MEMORY_TABLE) {
|
||||
janet_mark_kvs(table->data, table->capacity);
|
||||
}
|
||||
/* do nothing for JANET_MEMORY_TABLE_WEAKKV */
|
||||
if (table->proto) {
|
||||
table = table->proto;
|
||||
goto recur;
|
||||
@@ -268,6 +296,12 @@ recur:
|
||||
if (fiber->supervisor_channel) {
|
||||
janet_mark_abstract(fiber->supervisor_channel);
|
||||
}
|
||||
if (fiber->ev_stream) {
|
||||
janet_mark_abstract(fiber->ev_stream);
|
||||
}
|
||||
if (fiber->ev_callback) {
|
||||
fiber->ev_callback(fiber, JANET_ASYNC_EVENT_MARK);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Explicit tail recursion */
|
||||
@@ -287,19 +321,34 @@ static void janet_deinit_block(JanetGCObject *mem) {
|
||||
janet_symbol_deinit(((JanetStringHead *) mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_ARRAY:
|
||||
case JANET_MEMORY_ARRAY_WEAK:
|
||||
janet_free(((JanetArray *) mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_TABLE:
|
||||
case JANET_MEMORY_TABLE_WEAKK:
|
||||
case JANET_MEMORY_TABLE_WEAKV:
|
||||
case JANET_MEMORY_TABLE_WEAKKV:
|
||||
janet_free(((JanetTable *) mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_FIBER:
|
||||
janet_free(((JanetFiber *)mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_FIBER: {
|
||||
JanetFiber *f = (JanetFiber *)mem;
|
||||
#ifdef JANET_EV
|
||||
if (f->ev_state && !(f->flags & JANET_FIBER_EV_FLAG_IN_FLIGHT)) {
|
||||
janet_ev_dec_refcount();
|
||||
janet_free(f->ev_state);
|
||||
}
|
||||
#endif
|
||||
janet_free(f->data);
|
||||
}
|
||||
break;
|
||||
case JANET_MEMORY_BUFFER:
|
||||
janet_buffer_deinit((JanetBuffer *) mem);
|
||||
break;
|
||||
case JANET_MEMORY_ABSTRACT: {
|
||||
JanetAbstractHead *head = (JanetAbstractHead *)mem;
|
||||
if (head->type->gcperthread) {
|
||||
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
|
||||
}
|
||||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
@@ -326,12 +375,98 @@ static void janet_deinit_block(JanetGCObject *mem) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Check that a value x has been visited in the mark phase */
|
||||
static int janet_check_liveref(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
return 1;
|
||||
case JANET_ARRAY:
|
||||
case JANET_TABLE:
|
||||
case JANET_FUNCTION:
|
||||
case JANET_BUFFER:
|
||||
case JANET_FIBER:
|
||||
return janet_gc_reachable(janet_unwrap_pointer(x));
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
return janet_gc_reachable(janet_string_head(janet_unwrap_string(x)));
|
||||
case JANET_ABSTRACT:
|
||||
return janet_gc_reachable(janet_abstract_head(janet_unwrap_abstract(x)));
|
||||
case JANET_TUPLE:
|
||||
return janet_gc_reachable(janet_tuple_head(janet_unwrap_tuple(x)));
|
||||
case JANET_STRUCT:
|
||||
return janet_gc_reachable(janet_struct_head(janet_unwrap_struct(x)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over all allocated memory, and free memory that is not
|
||||
* marked as reachable. Flip the gc color flag for next sweep. */
|
||||
void janet_sweep() {
|
||||
JanetGCObject *previous = NULL;
|
||||
JanetGCObject *current = janet_vm.blocks;
|
||||
JanetGCObject *current = janet_vm.weak_blocks;
|
||||
JanetGCObject *next;
|
||||
|
||||
/* Sweep weak heap to drop weak refs */
|
||||
while (NULL != current) {
|
||||
next = current->data.next;
|
||||
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
|
||||
/* Check for dead references */
|
||||
enum JanetMemoryType type = janet_gc_type(current);
|
||||
if (type == JANET_MEMORY_ARRAY_WEAK) {
|
||||
JanetArray *array = (JanetArray *) current;
|
||||
for (uint32_t i = 0; i < (uint32_t) array->count; i++) {
|
||||
if (!janet_check_liveref(array->data[i])) {
|
||||
array->data[i] = janet_wrap_nil();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
JanetTable *table = (JanetTable *) current;
|
||||
int check_values = (type == JANET_MEMORY_TABLE_WEAKV) || (type == JANET_MEMORY_TABLE_WEAKKV);
|
||||
int check_keys = (type == JANET_MEMORY_TABLE_WEAKK) || (type == JANET_MEMORY_TABLE_WEAKKV);
|
||||
JanetKV *end = table->data + table->capacity;
|
||||
JanetKV *kvs = table->data;
|
||||
while (kvs < end) {
|
||||
int drop = 0;
|
||||
if (check_keys && !janet_check_liveref(kvs->key)) drop = 1;
|
||||
if (check_values && !janet_check_liveref(kvs->value)) drop = 1;
|
||||
if (drop) {
|
||||
/* Inlined from janet_table_remove without search */
|
||||
table->count--;
|
||||
table->deleted++;
|
||||
kvs->key = janet_wrap_nil();
|
||||
kvs->value = janet_wrap_false();
|
||||
}
|
||||
kvs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
|
||||
/* Sweep weak heap to free blocks */
|
||||
previous = NULL;
|
||||
current = janet_vm.weak_blocks;
|
||||
while (NULL != current) {
|
||||
next = current->data.next;
|
||||
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
|
||||
previous = current;
|
||||
current->flags &= ~JANET_MEM_REACHABLE;
|
||||
} else {
|
||||
janet_vm.block_count--;
|
||||
janet_deinit_block(current);
|
||||
if (NULL != previous) {
|
||||
previous->data.next = next;
|
||||
} else {
|
||||
janet_vm.weak_blocks = next;
|
||||
}
|
||||
janet_free(current);
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
|
||||
/* Sweep main heap to free blocks */
|
||||
previous = NULL;
|
||||
current = janet_vm.blocks;
|
||||
while (NULL != current) {
|
||||
next = current->data.next;
|
||||
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
|
||||
@@ -349,6 +484,7 @@ void janet_sweep() {
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
|
||||
#ifdef JANET_EV
|
||||
/* Sweep threaded abstract types for references to decrement */
|
||||
JanetKV *items = janet_vm.threaded_abstracts.data;
|
||||
@@ -364,20 +500,24 @@ void janet_sweep() {
|
||||
/* If not visited... */
|
||||
if (!janet_truthy(items[i].value)) {
|
||||
void *abst = janet_unwrap_abstract(items[i].key);
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gcperthread) {
|
||||
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
|
||||
}
|
||||
if (0 == janet_abstract_decref(abst)) {
|
||||
/* Run finalizer */
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
/* Mark as tombstone in place */
|
||||
items[i].key = janet_wrap_nil();
|
||||
items[i].value = janet_wrap_false();
|
||||
janet_vm.threaded_abstracts.deleted++;
|
||||
janet_vm.threaded_abstracts.count--;
|
||||
/* Free memory */
|
||||
janet_free(janet_abstract_head(abst));
|
||||
}
|
||||
|
||||
/* Mark as tombstone in place */
|
||||
items[i].key = janet_wrap_nil();
|
||||
items[i].value = janet_wrap_false();
|
||||
janet_vm.threaded_abstracts.deleted++;
|
||||
janet_vm.threaded_abstracts.count--;
|
||||
}
|
||||
|
||||
/* Reset for next sweep */
|
||||
@@ -405,8 +545,15 @@ void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
|
||||
|
||||
/* Prepend block to heap list */
|
||||
janet_vm.next_collection += size;
|
||||
mem->data.next = janet_vm.blocks;
|
||||
janet_vm.blocks = mem;
|
||||
if (type < JANET_MEMORY_TABLE_WEAKK) {
|
||||
/* normal heap */
|
||||
mem->data.next = janet_vm.blocks;
|
||||
janet_vm.blocks = mem;
|
||||
} else {
|
||||
/* weak heap */
|
||||
mem->data.next = janet_vm.weak_blocks;
|
||||
janet_vm.weak_blocks = mem;
|
||||
}
|
||||
janet_vm.block_count++;
|
||||
|
||||
return (void *)mem;
|
||||
@@ -437,7 +584,8 @@ void janet_collect(void) {
|
||||
uint32_t i;
|
||||
if (janet_vm.gc_suspend) return;
|
||||
depth = JANET_RECURSION_GUARD;
|
||||
/* Try and prevent many major collections back to back.
|
||||
janet_vm.gc_mark_phase = 1;
|
||||
/* Try to prevent many major collections back to back.
|
||||
* A full collection will take O(janet_vm.block_count) time.
|
||||
* If we have a large heap, make sure our interval is not too
|
||||
* small so we won't make many collections over it. This is just a
|
||||
@@ -456,6 +604,7 @@ void janet_collect(void) {
|
||||
Janet x = janet_vm.roots[--janet_vm.root_count];
|
||||
janet_mark(x);
|
||||
}
|
||||
janet_vm.gc_mark_phase = 0;
|
||||
janet_sweep();
|
||||
janet_vm.next_collection = 0;
|
||||
janet_free_all_scratch();
|
||||
@@ -529,8 +678,11 @@ void janet_clear_memory(void) {
|
||||
for (int32_t i = 0; i < janet_vm.threaded_abstracts.capacity; i++) {
|
||||
if (janet_checktype(items[i].key, JANET_ABSTRACT)) {
|
||||
void *abst = janet_unwrap_abstract(items[i].key);
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gcperthread) {
|
||||
janet_assert(!head->type->gcperthread(head->data, head->size), "per-thread finalizer failed");
|
||||
}
|
||||
if (0 == janet_abstract_decref(abst)) {
|
||||
JanetAbstractHead *head = janet_abstract_head(abst);
|
||||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
@@ -559,7 +711,9 @@ void janet_gcunlock(int handle) {
|
||||
janet_vm.gc_suspend = handle;
|
||||
}
|
||||
|
||||
/* Scratch memory API */
|
||||
/* Scratch memory API
|
||||
* Scratch memory allocations do not need to be free (but optionally can be), and will be automatically cleaned
|
||||
* up in the next call to janet_collect. */
|
||||
|
||||
void *janet_smalloc(size_t size) {
|
||||
JanetScratch *s = janet_malloc(sizeof(JanetScratch) + size);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -57,10 +57,14 @@ enum JanetMemoryType {
|
||||
JANET_MEMORY_FUNCENV,
|
||||
JANET_MEMORY_FUNCDEF,
|
||||
JANET_MEMORY_THREADED_ABSTRACT,
|
||||
JANET_MEMORY_TABLE_WEAKK,
|
||||
JANET_MEMORY_TABLE_WEAKV,
|
||||
JANET_MEMORY_TABLE_WEAKKV,
|
||||
JANET_MEMORY_ARRAY_WEAK
|
||||
};
|
||||
|
||||
/* To allocate collectable memory, one must call janet_alloc, initialize the memory,
|
||||
* and then call when janet_enablegc when it is initailize and reachable by the gc (on the JANET stack) */
|
||||
* and then call when janet_enablegc when it is initialized and reachable by the gc (on the JANET stack) */
|
||||
void *janet_gcalloc(enum JanetMemoryType type, size_t size);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose & contributors
|
||||
* Copyright (c) 2025 Calvin Rose & contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -73,13 +73,13 @@ static void *int64_unmarshal(JanetMarshalContext *ctx) {
|
||||
|
||||
static void it_s64_tostring(void *p, JanetBuffer *buffer) {
|
||||
char str[32];
|
||||
sprintf(str, "%" PRId64, *((int64_t *)p));
|
||||
snprintf(str, sizeof(str), "%" PRId64, *((int64_t *)p));
|
||||
janet_buffer_push_cstring(buffer, str);
|
||||
}
|
||||
|
||||
static void it_u64_tostring(void *p, JanetBuffer *buffer) {
|
||||
char str[32];
|
||||
sprintf(str, "%" PRIu64, *((uint64_t *)p));
|
||||
snprintf(str, sizeof(str), "%" PRIu64, *((uint64_t *)p));
|
||||
janet_buffer_push_cstring(buffer, str);
|
||||
}
|
||||
|
||||
@@ -118,10 +118,9 @@ int64_t janet_unwrap_s64(Janet x) {
|
||||
default:
|
||||
break;
|
||||
case JANET_NUMBER : {
|
||||
double dbl = janet_unwrap_number(x);
|
||||
if (fabs(dbl) <= MAX_INT_IN_DBL)
|
||||
return (int64_t)dbl;
|
||||
break;
|
||||
double d = janet_unwrap_number(x);
|
||||
if (!janet_checkint64range(d)) break;
|
||||
return (int64_t) d;
|
||||
}
|
||||
case JANET_STRING: {
|
||||
int64_t value;
|
||||
@@ -147,12 +146,9 @@ uint64_t janet_unwrap_u64(Janet x) {
|
||||
default:
|
||||
break;
|
||||
case JANET_NUMBER : {
|
||||
double dbl = janet_unwrap_number(x);
|
||||
/* Allow negative values to be cast to "wrap around".
|
||||
* This let's addition and subtraction work as expected. */
|
||||
if (fabs(dbl) <= MAX_INT_IN_DBL)
|
||||
return (uint64_t)dbl;
|
||||
break;
|
||||
double d = janet_unwrap_number(x);
|
||||
if (!janet_checkuint64range(d)) break;
|
||||
return (uint64_t) d;
|
||||
}
|
||||
case JANET_STRING: {
|
||||
uint64_t value;
|
||||
@@ -195,21 +191,21 @@ Janet janet_wrap_u64(uint64_t x) {
|
||||
|
||||
JANET_CORE_FN(cfun_it_s64_new,
|
||||
"(int/s64 value)",
|
||||
"Create a boxed signed 64 bit integer from a string value.") {
|
||||
"Create a boxed signed 64 bit integer from a string value or a number.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_s64(janet_unwrap_s64(argv[0]));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_it_u64_new,
|
||||
"(int/u64 value)",
|
||||
"Create a boxed unsigned 64 bit integer from a string value.") {
|
||||
"Create a boxed unsigned 64 bit integer from a string value or a number.") {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_u64(janet_unwrap_u64(argv[0]));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_to_number,
|
||||
"(int/to-number value)",
|
||||
"Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int32.") {
|
||||
"Convert an int/u64 or int/s64 to a number. Fails if the number is out of range for an int64.") {
|
||||
janet_fixarity(argc, 1);
|
||||
if (janet_type(argv[0]) == JANET_ABSTRACT) {
|
||||
void *abst = janet_unwrap_abstract(argv[0]);
|
||||
@@ -243,7 +239,7 @@ JANET_CORE_FN(cfun_to_bytes,
|
||||
"Write the bytes of an `int/s64` or `int/u64` into a buffer.\n"
|
||||
"The `buffer` parameter specifies an existing buffer to write to, if unset a new buffer will be created.\n"
|
||||
"Returns the modified buffer.\n"
|
||||
"The `endianness` paramater indicates the byte order:\n"
|
||||
"The `endianness` parameter indicates the byte order:\n"
|
||||
"- `nil` (unset): system byte order\n"
|
||||
"- `:le`: little-endian, least significant byte first\n"
|
||||
"- `:be`: big-endian, most significant byte first\n") {
|
||||
@@ -307,8 +303,8 @@ static int compare_double_double(double x, double y) {
|
||||
|
||||
static int compare_int64_double(int64_t x, double y) {
|
||||
if (isnan(y)) {
|
||||
return 0; // clojure and python do this
|
||||
} else if ((y > (- ((double) MAX_INT_IN_DBL))) && (y < ((double) MAX_INT_IN_DBL))) {
|
||||
return 0;
|
||||
} else if ((y > JANET_INTMIN_DOUBLE) && (y < JANET_INTMAX_DOUBLE)) {
|
||||
double dx = (double) x;
|
||||
return compare_double_double(dx, y);
|
||||
} else if (y > ((double) INT64_MAX)) {
|
||||
@@ -323,10 +319,10 @@ static int compare_int64_double(int64_t x, double y) {
|
||||
|
||||
static int compare_uint64_double(uint64_t x, double y) {
|
||||
if (isnan(y)) {
|
||||
return 0; // clojure and python do this
|
||||
return 0;
|
||||
} else if (y < 0) {
|
||||
return 1;
|
||||
} else if ((y >= 0) && (y < ((double) MAX_INT_IN_DBL))) {
|
||||
} else if ((y >= 0) && (y < JANET_INTMAX_DOUBLE)) {
|
||||
double dx = (double) x;
|
||||
return compare_double_double(dx, y);
|
||||
} else if (y > ((double) UINT64_MAX)) {
|
||||
@@ -339,8 +335,9 @@ static int compare_uint64_double(uint64_t x, double y) {
|
||||
|
||||
static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
if (janet_is_int(argv[0]) != JANET_INT_S64)
|
||||
if (janet_is_int(argv[0]) != JANET_INT_S64) {
|
||||
janet_panic("compare method requires int/s64 as first argument");
|
||||
}
|
||||
int64_t x = janet_unwrap_s64(argv[0]);
|
||||
switch (janet_type(argv[1])) {
|
||||
default:
|
||||
@@ -355,7 +352,6 @@ static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
|
||||
int64_t y = *(int64_t *)abst;
|
||||
return janet_wrap_number((x < y) ? -1 : (x > y ? 1 : 0));
|
||||
} else if (janet_abstract_type(abst) == &janet_u64_type) {
|
||||
// comparing signed to unsigned -- be careful!
|
||||
uint64_t y = *(uint64_t *)abst;
|
||||
if (x < 0) {
|
||||
return janet_wrap_number(-1);
|
||||
@@ -374,8 +370,9 @@ static Janet cfun_it_s64_compare(int32_t argc, Janet *argv) {
|
||||
|
||||
static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
if (janet_is_int(argv[0]) != JANET_INT_U64) // is this needed?
|
||||
if (janet_is_int(argv[0]) != JANET_INT_U64) {
|
||||
janet_panic("compare method requires int/u64 as first argument");
|
||||
}
|
||||
uint64_t x = janet_unwrap_u64(argv[0]);
|
||||
switch (janet_type(argv[1])) {
|
||||
default:
|
||||
@@ -390,7 +387,6 @@ static Janet cfun_it_u64_compare(int32_t argc, Janet *argv) {
|
||||
uint64_t y = *(uint64_t *)abst;
|
||||
return janet_wrap_number((x < y) ? -1 : (x > y ? 1 : 0));
|
||||
} else if (janet_abstract_type(abst) == &janet_s64_type) {
|
||||
// comparing unsigned to signed -- be careful!
|
||||
int64_t y = *(int64_t *)abst;
|
||||
if (y < 0) {
|
||||
return janet_wrap_number(1);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#ifndef JANET_WINDOWS
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -41,6 +42,11 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx);
|
||||
static void *io_file_unmarshal(JanetMarshalContext *ctx);
|
||||
static Janet io_file_next(void *p, Janet key);
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#define ftell _ftelli64
|
||||
#define fseek _fseeki64
|
||||
#endif
|
||||
|
||||
const JanetAbstractType janet_file_type = {
|
||||
"core/file",
|
||||
cfun_io_gc,
|
||||
@@ -126,12 +132,12 @@ JANET_CORE_FN(cfun_io_temp,
|
||||
// XXX use mkostemp when we can to avoid CLOEXEC race.
|
||||
FILE *tmp = tmpfile();
|
||||
if (!tmp)
|
||||
janet_panicf("unable to create temporary file - %s", strerror(errno));
|
||||
janet_panicf("unable to create temporary file - %s", janet_strerror(errno));
|
||||
return janet_makefile(tmp, JANET_FILE_WRITE | JANET_FILE_READ | JANET_FILE_BINARY);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_io_fopen,
|
||||
"(file/open path &opt mode)",
|
||||
"(file/open path &opt mode buffer-size)",
|
||||
"Open a file. `path` is an absolute or relative path, and "
|
||||
"`mode` is a set of flags indicating the mode to open the file in. "
|
||||
"`mode` is a keyword where each character represents a flag. If the file "
|
||||
@@ -143,8 +149,9 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
"Following one of the initial flags, 0 or more of the following flags can be appended:\n\n"
|
||||
"* b - open the file in binary mode (rather than text mode)\n\n"
|
||||
"* + - append to the file instead of overwriting it\n\n"
|
||||
"* n - error if the file cannot be opened instead of returning nil") {
|
||||
janet_arity(argc, 1, 2);
|
||||
"* n - error if the file cannot be opened instead of returning nil\n\n"
|
||||
"See fopen (<stdio.h>, C99) for further details.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
const uint8_t *fname = janet_getstring(argv, 0);
|
||||
const uint8_t *fmode;
|
||||
int32_t flags;
|
||||
@@ -157,8 +164,25 @@ JANET_CORE_FN(cfun_io_fopen,
|
||||
flags = JANET_FILE_READ;
|
||||
}
|
||||
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
||||
if (f != NULL) {
|
||||
#ifndef JANET_WINDOWS
|
||||
struct stat st;
|
||||
fstat(fileno(f), &st);
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
fclose(f);
|
||||
janet_panicf("cannot open directory: %s", fname);
|
||||
}
|
||||
#endif
|
||||
size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
||||
if (bufsize != BUFSIZ) {
|
||||
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
|
||||
if (result) {
|
||||
janet_panic("failed to set buffer size for file");
|
||||
}
|
||||
}
|
||||
}
|
||||
return f ? janet_makefile(f, flags)
|
||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, strerror(errno)), janet_wrap_nil())
|
||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
|
||||
@@ -279,7 +303,7 @@ int janet_file_close(JanetFile *file) {
|
||||
if (!(file->flags & (JANET_FILE_NOT_CLOSEABLE | JANET_FILE_CLOSED))) {
|
||||
ret = fclose(file->file);
|
||||
file->flags |= JANET_FILE_CLOSED;
|
||||
file->file = NULL; /* NULL derefence is easier to debug then other problems */
|
||||
file->file = NULL; /* NULL dereference is easier to debug then other problems */
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
@@ -327,7 +351,7 @@ JANET_CORE_FN(cfun_io_fseek,
|
||||
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
||||
if (iof->flags & JANET_FILE_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
long int offset = 0;
|
||||
int64_t offset = 0;
|
||||
int whence = SEEK_CUR;
|
||||
if (argc >= 2) {
|
||||
const uint8_t *whence_sym = janet_getkeyword(argv, 1);
|
||||
@@ -341,7 +365,7 @@ JANET_CORE_FN(cfun_io_fseek,
|
||||
janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]);
|
||||
}
|
||||
if (argc == 3) {
|
||||
offset = (long) janet_getinteger64(argv, 2);
|
||||
offset = (int64_t) janet_getinteger64(argv, 2);
|
||||
}
|
||||
}
|
||||
if (fseek(iof->file, offset, whence)) janet_panic("error seeking file");
|
||||
@@ -355,7 +379,7 @@ JANET_CORE_FN(cfun_io_ftell,
|
||||
JanetFile *iof = janet_getabstract(argv, 0, &janet_file_type);
|
||||
if (iof->flags & JANET_FILE_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
long pos = ftell(iof->file);
|
||||
int64_t pos = ftell(iof->file);
|
||||
if (pos == -1) janet_panic("error getting position in file");
|
||||
return janet_wrap_number((double)pos);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -68,8 +68,15 @@ enum {
|
||||
LB_STRUCT_PROTO, /* 223 */
|
||||
#ifdef JANET_EV
|
||||
LB_THREADED_ABSTRACT, /* 224 */
|
||||
LB_POINTER_BUFFER, /* 224 */
|
||||
LB_POINTER_BUFFER, /* 225 */
|
||||
#endif
|
||||
LB_TABLE_WEAKK, /* 226 */
|
||||
LB_TABLE_WEAKV, /* 227 */
|
||||
LB_TABLE_WEAKKV, /* 228 */
|
||||
LB_TABLE_WEAKK_PROTO, /* 229 */
|
||||
LB_TABLE_WEAKV_PROTO, /* 230 */
|
||||
LB_TABLE_WEAKKV_PROTO, /* 231 */
|
||||
LB_ARRAY_WEAK, /* 232 */
|
||||
} LeadBytes;
|
||||
|
||||
/* Helper to look inside an entry in an environment */
|
||||
@@ -185,6 +192,19 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags);
|
||||
/* Prevent stack overflows */
|
||||
#define MARSH_STACKCHECK if ((flags & 0xFFFF) > JANET_RECURSION_GUARD) janet_panic("stack overflow")
|
||||
|
||||
/* Quick check if a fiber cannot be marshalled. This is will
|
||||
* have no false positives, but may have false negatives. */
|
||||
static int fiber_cannot_be_marshalled(JanetFiber *fiber) {
|
||||
if (janet_fiber_status(fiber) == JANET_STATUS_ALIVE) return 1;
|
||||
int32_t i = fiber->frame;
|
||||
while (i > 0) {
|
||||
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
if (!frame->func) return 1; /* has cfunction on stack */
|
||||
i = frame->prevframe;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Marshal a function env */
|
||||
static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
|
||||
MARSH_STACKCHECK;
|
||||
@@ -197,7 +217,9 @@ static void marshal_one_env(MarshalState *st, JanetFuncEnv *env, int flags) {
|
||||
}
|
||||
janet_env_valid(env);
|
||||
janet_v_push(st->seen_envs, env);
|
||||
if (env->offset > 0 && (JANET_STATUS_ALIVE == janet_fiber_status(env->as.fiber))) {
|
||||
|
||||
/* Special case for early detachment */
|
||||
if (env->offset > 0 && fiber_cannot_be_marshalled(env->as.fiber)) {
|
||||
pushint(st, 0);
|
||||
pushint(st, env->length);
|
||||
Janet *values = env->as.fiber->data + env->offset;
|
||||
@@ -328,7 +350,7 @@ static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
|
||||
while (i > 0) {
|
||||
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
if (frame->env) frame->flags |= JANET_STACKFRAME_HASENV;
|
||||
if (!frame->func) janet_panic("cannot marshal fiber with c stackframe");
|
||||
if (!frame->func) janet_panicf("cannot marshal fiber with c stackframe (%v)", janet_wrap_cfunction((JanetCFunction) frame->pc));
|
||||
pushint(st, frame->flags);
|
||||
pushint(st, frame->prevframe);
|
||||
int32_t pcdiff = (int32_t)(frame->pc - frame->func->def->bytecode);
|
||||
@@ -554,7 +576,8 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
|
||||
int32_t i;
|
||||
JanetArray *a = janet_unwrap_array(x);
|
||||
MARK_SEEN();
|
||||
pushbyte(st, LB_ARRAY);
|
||||
enum JanetMemoryType memtype = janet_gc_type(a);
|
||||
pushbyte(st, memtype == JANET_MEMORY_ARRAY_WEAK ? LB_ARRAY_WEAK : LB_ARRAY);
|
||||
pushint(st, a->count);
|
||||
for (i = 0; i < a->count; i++)
|
||||
marshal_one(st, a->data[i], flags + 1);
|
||||
@@ -577,7 +600,16 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
|
||||
case JANET_TABLE: {
|
||||
JanetTable *t = janet_unwrap_table(x);
|
||||
MARK_SEEN();
|
||||
pushbyte(st, t->proto ? LB_TABLE_PROTO : LB_TABLE);
|
||||
enum JanetMemoryType memtype = janet_gc_type(t);
|
||||
if (memtype == JANET_MEMORY_TABLE_WEAKK) {
|
||||
pushbyte(st, t->proto ? LB_TABLE_WEAKK_PROTO : LB_TABLE_WEAKK);
|
||||
} else if (memtype == JANET_MEMORY_TABLE_WEAKV) {
|
||||
pushbyte(st, t->proto ? LB_TABLE_WEAKV_PROTO : LB_TABLE_WEAKV);
|
||||
} else if (memtype == JANET_MEMORY_TABLE_WEAKKV) {
|
||||
pushbyte(st, t->proto ? LB_TABLE_WEAKKV_PROTO : LB_TABLE_WEAKKV);
|
||||
} else {
|
||||
pushbyte(st, t->proto ? LB_TABLE_PROTO : LB_TABLE);
|
||||
}
|
||||
pushint(st, t->count);
|
||||
if (t->proto)
|
||||
marshal_one(st, janet_wrap_table(t->proto), flags + 1);
|
||||
@@ -1048,9 +1080,11 @@ static const uint8_t *unmarshal_one_fiber(
|
||||
fiber->env = NULL;
|
||||
fiber->last_value = janet_wrap_nil();
|
||||
#ifdef JANET_EV
|
||||
fiber->waiting = NULL;
|
||||
fiber->sched_id = 0;
|
||||
fiber->supervisor_channel = NULL;
|
||||
fiber->ev_state = NULL;
|
||||
fiber->ev_callback = NULL;
|
||||
fiber->ev_stream = NULL;
|
||||
#endif
|
||||
|
||||
/* Push fiber to seen stack */
|
||||
@@ -1245,6 +1279,18 @@ void *janet_unmarshal_abstract(JanetMarshalContext *ctx, size_t size) {
|
||||
return p;
|
||||
}
|
||||
|
||||
void *janet_unmarshal_abstract_threaded(JanetMarshalContext *ctx, size_t size) {
|
||||
#ifdef JANET_THREADS
|
||||
void *p = janet_abstract_threaded(ctx->at, size);
|
||||
janet_unmarshal_abstract_reuse(ctx, p);
|
||||
return p;
|
||||
#else
|
||||
(void) ctx;
|
||||
(void) size;
|
||||
janet_panic("threaded abstracts not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
static const uint8_t *unmarshal_one_abstract(UnmarshalState *st, const uint8_t *data, Janet *out, int flags) {
|
||||
Janet key;
|
||||
data = unmarshal_one(st, data, &key, flags + 1);
|
||||
@@ -1388,11 +1434,18 @@ static const uint8_t *unmarshal_one(
|
||||
}
|
||||
case LB_REFERENCE:
|
||||
case LB_ARRAY:
|
||||
case LB_ARRAY_WEAK:
|
||||
case LB_TUPLE:
|
||||
case LB_STRUCT:
|
||||
case LB_STRUCT_PROTO:
|
||||
case LB_TABLE:
|
||||
case LB_TABLE_PROTO:
|
||||
case LB_TABLE_WEAKK:
|
||||
case LB_TABLE_WEAKV:
|
||||
case LB_TABLE_WEAKKV:
|
||||
case LB_TABLE_WEAKK_PROTO:
|
||||
case LB_TABLE_WEAKV_PROTO:
|
||||
case LB_TABLE_WEAKKV_PROTO:
|
||||
/* Things that open with integers */
|
||||
{
|
||||
data++;
|
||||
@@ -1401,9 +1454,9 @@ static const uint8_t *unmarshal_one(
|
||||
if (lead != LB_REFERENCE) {
|
||||
MARSH_EOS(st, data - 1 + len);
|
||||
}
|
||||
if (lead == LB_ARRAY) {
|
||||
if (lead == LB_ARRAY || lead == LB_ARRAY_WEAK) {
|
||||
/* Array */
|
||||
JanetArray *array = janet_array(len);
|
||||
JanetArray *array = (lead == LB_ARRAY_WEAK) ? janet_array_weak(len) : janet_array(len);
|
||||
array->count = len;
|
||||
*out = janet_wrap_array(array);
|
||||
janet_v_push(st->lookup, *out);
|
||||
@@ -1443,10 +1496,19 @@ static const uint8_t *unmarshal_one(
|
||||
*out = st->lookup[len];
|
||||
} else {
|
||||
/* Table */
|
||||
JanetTable *t = janet_table(len);
|
||||
JanetTable *t;
|
||||
if (lead == LB_TABLE_WEAKK_PROTO || lead == LB_TABLE_WEAKK) {
|
||||
t = janet_table_weakk(len);
|
||||
} else if (lead == LB_TABLE_WEAKV_PROTO || lead == LB_TABLE_WEAKV) {
|
||||
t = janet_table_weakv(len);
|
||||
} else if (lead == LB_TABLE_WEAKKV_PROTO || lead == LB_TABLE_WEAKKV) {
|
||||
t = janet_table_weakkv(len);
|
||||
} else {
|
||||
t = janet_table(len);
|
||||
}
|
||||
*out = janet_wrap_table(t);
|
||||
janet_v_push(st->lookup, *out);
|
||||
if (lead == LB_TABLE_PROTO) {
|
||||
if (lead == LB_TABLE_PROTO || lead == LB_TABLE_WEAKK_PROTO || lead == LB_TABLE_WEAKV_PROTO || lead == LB_TABLE_WEAKKV_PROTO) {
|
||||
Janet proto;
|
||||
data = unmarshal_one(st, data, &proto, flags + 1);
|
||||
janet_asserttype(proto, JANET_TABLE, st);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -85,10 +85,10 @@ void janet_rng_longseed(JanetRNG *rng, const uint8_t *bytes, int32_t len) {
|
||||
uint8_t state[16] = {0};
|
||||
for (int32_t i = 0; i < len; i++)
|
||||
state[i & 0xF] ^= bytes[i];
|
||||
rng->a = state[0] + (state[1] << 8) + (state[2] << 16) + (state[3] << 24);
|
||||
rng->b = state[4] + (state[5] << 8) + (state[6] << 16) + (state[7] << 24);
|
||||
rng->c = state[8] + (state[9] << 8) + (state[10] << 16) + (state[11] << 24);
|
||||
rng->d = state[12] + (state[13] << 8) + (state[14] << 16) + (state[15] << 24);
|
||||
rng->a = state[0] + ((uint32_t) state[1] << 8) + ((uint32_t) state[2] << 16) + ((uint32_t) state[3] << 24);
|
||||
rng->b = state[4] + ((uint32_t) state[5] << 8) + ((uint32_t) state[6] << 16) + ((uint32_t) state[7] << 24);
|
||||
rng->c = state[8] + ((uint32_t) state[9] << 8) + ((uint32_t) state[10] << 16) + ((uint32_t) state[11] << 24);
|
||||
rng->d = state[12] + ((uint32_t) state[13] << 8) + ((uint32_t) state[14] << 16) + ((uint32_t) state[15] << 24);
|
||||
rng->counter = 0u;
|
||||
/* a, b, c, d can't all be 0 */
|
||||
if (rng->a == 0) rng->a = 1u;
|
||||
@@ -119,7 +119,7 @@ double janet_rng_double(JanetRNG *rng) {
|
||||
|
||||
JANET_CORE_FN(cfun_rng_make,
|
||||
"(math/rng &opt seed)",
|
||||
"Creates a Psuedo-Random number generator, with an optional seed. "
|
||||
"Creates a Pseudo-Random number generator, with an optional seed. "
|
||||
"The seed should be an unsigned 32 bit integer or a buffer. "
|
||||
"Do not use this for cryptography. Returns a core/rng abstract type."
|
||||
) {
|
||||
@@ -349,6 +349,26 @@ JANET_CORE_FN(janet_cfun_lcm, "(math/lcm x y)",
|
||||
return janet_wrap_number(janet_lcm(x, y));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_frexp, "(math/frexp x)",
|
||||
"Returns a tuple of (mantissa, exponent) from number.") {
|
||||
janet_fixarity(argc, 1);
|
||||
double x = janet_getnumber(argv, 0);
|
||||
int exp;
|
||||
x = frexp(x, &exp);
|
||||
Janet *result = janet_tuple_begin(2);
|
||||
result[0] = janet_wrap_number(x);
|
||||
result[1] = janet_wrap_number((double) exp);
|
||||
return janet_wrap_tuple(janet_tuple_end(result));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(janet_cfun_ldexp, "(math/ldexp m e)",
|
||||
"Creates a new number from a mantissa and an exponent.") {
|
||||
janet_fixarity(argc, 2);
|
||||
double x = janet_getnumber(argv, 0);
|
||||
int32_t y = janet_getinteger(argv, 1);
|
||||
return janet_wrap_number(ldexp(x, y));
|
||||
}
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_math(JanetTable *env) {
|
||||
JanetRegExt math_cfuns[] = {
|
||||
@@ -395,6 +415,8 @@ void janet_lib_math(JanetTable *env) {
|
||||
JANET_CORE_REG("math/next", janet_nextafter),
|
||||
JANET_CORE_REG("math/gcd", janet_cfun_gcd),
|
||||
JANET_CORE_REG("math/lcm", janet_cfun_lcm),
|
||||
JANET_CORE_REG("math/frexp", janet_cfun_frexp),
|
||||
JANET_CORE_REG("math/ldexp", janet_cfun_ldexp),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, math_cfuns);
|
||||
@@ -411,11 +433,11 @@ void janet_lib_math(JanetTable *env) {
|
||||
JANET_CORE_DEF(env, "math/int32-min", janet_wrap_number(INT32_MIN),
|
||||
"The minimum contiguous integer representable by a 32 bit signed integer");
|
||||
JANET_CORE_DEF(env, "math/int32-max", janet_wrap_number(INT32_MAX),
|
||||
"The maximum contiguous integer represtenable by a 32 bit signed integer");
|
||||
"The maximum contiguous integer representable by a 32 bit signed integer");
|
||||
JANET_CORE_DEF(env, "math/int-min", janet_wrap_number(JANET_INTMIN_DOUBLE),
|
||||
"The minimum contiguous integer representable by a double (2^53)");
|
||||
JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
|
||||
"The maximum contiguous integer represtenable by a double (-(2^53))");
|
||||
"The maximum contiguous integer representable by a double (-(2^53))");
|
||||
#ifdef NAN
|
||||
JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
|
||||
#else
|
||||
|
||||
313
src/core/net.c
313
src/core/net.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose and contributors.
|
||||
* Copyright (c) 2025 Calvin Rose and contributors.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#include "fiber.h"
|
||||
#endif
|
||||
|
||||
#ifdef JANET_NET
|
||||
@@ -78,12 +79,20 @@ const JanetAbstractType janet_address_type = {
|
||||
|
||||
/* maximum number of bytes in a socket address host (post name resolution) */
|
||||
#ifdef JANET_WINDOWS
|
||||
#ifdef JANET_NO_IPV6
|
||||
#define SA_ADDRSTRLEN (INET_ADDRSTRLEN + 1)
|
||||
#else
|
||||
#define SA_ADDRSTRLEN (INET6_ADDRSTRLEN + 1)
|
||||
#endif
|
||||
typedef unsigned short in_port_t;
|
||||
#else
|
||||
#define JANET_SA_MAX(a, b) (((a) > (b))? (a) : (b))
|
||||
#ifdef JANET_NO_IPV6
|
||||
#define SA_ADDRSTRLEN JANET_SA_MAX(INET_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
|
||||
#else
|
||||
#define SA_ADDRSTRLEN JANET_SA_MAX(INET6_ADDRSTRLEN + 1, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static JanetStream *make_stream(JSock handle, uint32_t flags);
|
||||
|
||||
@@ -111,12 +120,57 @@ static void janet_net_socknoblock(JSock s) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/* State machine for async connect */
|
||||
|
||||
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
JanetStream *stream = fiber->ev_stream;
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
#ifndef JANET_WINDOWS
|
||||
/* Wait until we have an actual event before checking.
|
||||
* Windows doesn't support async connect with this, just try immediately.*/
|
||||
case JANET_ASYNC_EVENT_INIT:
|
||||
#endif
|
||||
case JANET_ASYNC_EVENT_DEINIT:
|
||||
return;
|
||||
case JANET_ASYNC_EVENT_CLOSE:
|
||||
janet_cancel(fiber, janet_cstringv("stream closed"));
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
#ifdef JANET_WINDOWS
|
||||
int res = 0;
|
||||
int size = sizeof(res);
|
||||
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size);
|
||||
#else
|
||||
int res = 0;
|
||||
socklen_t size = sizeof res;
|
||||
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
|
||||
#endif
|
||||
if (r == 0) {
|
||||
if (res == 0) {
|
||||
janet_schedule(fiber, janet_wrap_abstract(stream));
|
||||
} else {
|
||||
janet_cancel(fiber, janet_cstringv(janet_strerror(res)));
|
||||
stream->flags |= JANET_STREAM_TOCLOSE;
|
||||
}
|
||||
} else {
|
||||
janet_cancel(fiber, janet_ev_lasterr());
|
||||
stream->flags |= JANET_STREAM_TOCLOSE;
|
||||
}
|
||||
janet_async_end(fiber);
|
||||
}
|
||||
|
||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
|
||||
}
|
||||
|
||||
/* State machine for accepting connections. */
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
|
||||
typedef struct {
|
||||
JanetListenerState head;
|
||||
WSAOVERLAPPED overlapped;
|
||||
JanetFunction *function;
|
||||
JanetStream *lstream;
|
||||
@@ -124,10 +178,10 @@ typedef struct {
|
||||
char buf[1024];
|
||||
} NetStateAccept;
|
||||
|
||||
static int net_sched_accept_impl(NetStateAccept *state, Janet *err);
|
||||
static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err);
|
||||
|
||||
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) {
|
||||
NetStateAccept *state = (NetStateAccept *)s;
|
||||
void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
@@ -138,55 +192,60 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
|
||||
break;
|
||||
}
|
||||
case JANET_ASYNC_EVENT_CLOSE:
|
||||
janet_schedule(s->fiber, janet_wrap_nil());
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||
if (state->astream->flags & JANET_STREAM_CLOSED) {
|
||||
janet_cancel(s->fiber, janet_cstringv("failed to accept connection"));
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_cancel(fiber, janet_cstringv("failed to accept connection"));
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
SOCKET lsock = (SOCKET) state->lstream->handle;
|
||||
if (NO_ERROR != setsockopt((SOCKET) state->astream->handle, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
|
||||
(char *) &lsock, sizeof(lsock))) {
|
||||
janet_cancel(s->fiber, janet_cstringv("failed to accept connection"));
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_cancel(fiber, janet_cstringv("failed to accept connection"));
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
|
||||
Janet streamv = janet_wrap_abstract(state->astream);
|
||||
if (state->function) {
|
||||
/* Schedule worker */
|
||||
JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv);
|
||||
fiber->supervisor_channel = s->fiber->supervisor_channel;
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
|
||||
sub_fiber->supervisor_channel = fiber->supervisor_channel;
|
||||
janet_schedule(sub_fiber, janet_wrap_nil());
|
||||
/* Now listen again for next connection */
|
||||
Janet err;
|
||||
if (net_sched_accept_impl(state, &err)) {
|
||||
janet_cancel(s->fiber, err);
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
if (net_sched_accept_impl(state, fiber, &err)) {
|
||||
janet_cancel(fiber, err);
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
janet_schedule(s->fiber, streamv);
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_schedule(fiber, streamv);
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return JANET_ASYNC_STATUS_NOT_DONE;
|
||||
}
|
||||
|
||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
|
||||
Janet err;
|
||||
JanetListenerState *s = janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL);
|
||||
NetStateAccept *state = (NetStateAccept *)s;
|
||||
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
||||
memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
|
||||
memset(&state->buf, 0, 1024);
|
||||
state->function = fun;
|
||||
state->lstream = stream;
|
||||
s->tag = &state->overlapped;
|
||||
if (net_sched_accept_impl(state, &err)) janet_panicv(err);
|
||||
janet_await();
|
||||
if (net_sched_accept_impl(state, janet_root_fiber(), &err)) {
|
||||
janet_free(state);
|
||||
janet_panicv(err);
|
||||
}
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
|
||||
}
|
||||
|
||||
static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
|
||||
static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet *err) {
|
||||
SOCKET lsock = (SOCKET) state->lstream->handle;
|
||||
SOCKET asock = WSASocketW(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||||
if (asock == INVALID_SOCKET) {
|
||||
@@ -198,7 +257,11 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
|
||||
int socksize = sizeof(SOCKADDR_STORAGE) + 16;
|
||||
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) {
|
||||
int code = WSAGetLastError();
|
||||
if (code == WSA_IO_PENDING) return 0; /* indicates io is happening async */
|
||||
if (code == WSA_IO_PENDING) {
|
||||
/* indicates io is happening async */
|
||||
janet_async_in_flight(fiber);
|
||||
return 0;
|
||||
}
|
||||
*err = janet_ev_lasterr();
|
||||
return 1;
|
||||
}
|
||||
@@ -208,12 +271,12 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
|
||||
#else
|
||||
|
||||
typedef struct {
|
||||
JanetListenerState head;
|
||||
JanetFunction *function;
|
||||
} NetStateAccept;
|
||||
|
||||
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) {
|
||||
NetStateAccept *state = (NetStateAccept *)s;
|
||||
void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||
JanetStream *stream = fiber->ev_stream;
|
||||
NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
|
||||
switch (event) {
|
||||
default:
|
||||
break;
|
||||
@@ -222,43 +285,47 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
|
||||
break;
|
||||
}
|
||||
case JANET_ASYNC_EVENT_CLOSE:
|
||||
janet_schedule(s->fiber, janet_wrap_nil());
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
case JANET_ASYNC_EVENT_INIT:
|
||||
case JANET_ASYNC_EVENT_READ: {
|
||||
#if defined(JANET_LINUX)
|
||||
JSock connfd = accept4(s->stream->handle, NULL, NULL, SOCK_CLOEXEC);
|
||||
JSock connfd = accept4(stream->handle, NULL, NULL, SOCK_CLOEXEC);
|
||||
#else
|
||||
/* On BSDs, CLOEXEC should be inherited from server socket */
|
||||
JSock connfd = accept(s->stream->handle, NULL, NULL);
|
||||
JSock connfd = accept(stream->handle, NULL, NULL);
|
||||
#endif
|
||||
if (JSOCKVALID(connfd)) {
|
||||
janet_net_socknoblock(connfd);
|
||||
JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||
Janet streamv = janet_wrap_abstract(stream);
|
||||
if (state->function) {
|
||||
JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv);
|
||||
fiber->supervisor_channel = s->fiber->supervisor_channel;
|
||||
janet_schedule(fiber, janet_wrap_nil());
|
||||
JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
|
||||
sub_fiber->supervisor_channel = fiber->supervisor_channel;
|
||||
janet_schedule(sub_fiber, janet_wrap_nil());
|
||||
} else {
|
||||
janet_schedule(s->fiber, streamv);
|
||||
return JANET_ASYNC_STATUS_DONE;
|
||||
janet_schedule(fiber, streamv);
|
||||
janet_async_end(fiber);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return JANET_ASYNC_STATUS_NOT_DONE;
|
||||
}
|
||||
|
||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
|
||||
NetStateAccept *state = (NetStateAccept *) janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL);
|
||||
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
||||
memset(state, 0, sizeof(NetStateAccept));
|
||||
state->function = fun;
|
||||
janet_await();
|
||||
if (fun) janet_stream_level_triggered(stream);
|
||||
janet_async_start(stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, state);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Adress info */
|
||||
/* Address info */
|
||||
|
||||
static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
|
||||
JanetKeyword stype = janet_optkeyword(argv, argc, n, NULL);
|
||||
@@ -274,7 +341,7 @@ static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
|
||||
/* Needs argc >= offset + 2 */
|
||||
/* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1,
|
||||
* otherwise 0. Also, ignores is_bind when is a unix socket. */
|
||||
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix) {
|
||||
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix, socklen_t *sizeout) {
|
||||
/* Unix socket support - not yet supported on windows. */
|
||||
#ifndef JANET_WINDOWS
|
||||
if (janet_keyeq(argv[offset], "unix")) {
|
||||
@@ -285,15 +352,14 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
|
||||
}
|
||||
saddr->sun_family = AF_UNIX;
|
||||
size_t path_size = sizeof(saddr->sun_path);
|
||||
snprintf(saddr->sun_path, path_size, "%s", path);
|
||||
*sizeout = sizeof(struct sockaddr_un);
|
||||
#ifdef JANET_LINUX
|
||||
if (path[0] == '@') {
|
||||
saddr->sun_path[0] = '\0';
|
||||
snprintf(saddr->sun_path + 1, path_size - 1, "%s", path + 1);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
snprintf(saddr->sun_path, path_size, "%s", path);
|
||||
*sizeout = offsetof(struct sockaddr_un, sun_path) + janet_string_length(path);
|
||||
}
|
||||
#endif
|
||||
*is_unix = 1;
|
||||
return (struct addrinfo *) saddr;
|
||||
}
|
||||
@@ -318,6 +384,11 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
|
||||
janet_panicf("could not get address info: %s", gai_strerror(status));
|
||||
}
|
||||
*is_unix = 0;
|
||||
#ifdef JANET_WINDOWS
|
||||
*sizeout = 0;
|
||||
#else
|
||||
*sizeout = sizeof(struct sockaddr_un);
|
||||
#endif
|
||||
return ai;
|
||||
}
|
||||
|
||||
@@ -338,12 +409,13 @@ JANET_CORE_FN(cfun_net_sockaddr,
|
||||
int socktype = janet_get_sockettype(argv, argc, 2);
|
||||
int is_unix = 0;
|
||||
int make_arr = (argc >= 3 && janet_truthy(argv[3]));
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
|
||||
socklen_t addrsize = 0;
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, &addrsize);
|
||||
#ifndef JANET_WINDOWS
|
||||
/* no unix domain socket support on windows yet */
|
||||
if (is_unix) {
|
||||
void *abst = janet_abstract(&janet_address_type, sizeof(struct sockaddr_un));
|
||||
memcpy(abst, ai, sizeof(struct sockaddr_un));
|
||||
void *abst = janet_abstract(&janet_address_type, addrsize);
|
||||
memcpy(abst, ai, addrsize);
|
||||
Janet ret = janet_wrap_abstract(abst);
|
||||
return make_arr ? janet_wrap_array(janet_array_n(&ret, 1)) : ret;
|
||||
}
|
||||
@@ -394,7 +466,8 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
|
||||
/* Where we're connecting to */
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
|
||||
socklen_t addrlen = 0;
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix, &addrlen);
|
||||
|
||||
/* Check if we're binding address */
|
||||
struct addrinfo *binding = NULL;
|
||||
@@ -419,7 +492,6 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
/* Create socket */
|
||||
JSock sock = JSOCKDEFAULT;
|
||||
void *addr = NULL;
|
||||
socklen_t addrlen = 0;
|
||||
#ifndef JANET_WINDOWS
|
||||
if (is_unix) {
|
||||
sock = socket(AF_UNIX, socktype | JSOCKFLAGS, 0);
|
||||
@@ -429,7 +501,6 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
janet_panicf("could not create socket: %V", v);
|
||||
}
|
||||
addr = (void *) ai;
|
||||
addrlen = sizeof(struct sockaddr_un);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -476,7 +547,9 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
|
||||
/* Wrap socket in abstract type JanetStream */
|
||||
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||
uint32_t udp_flag = 0;
|
||||
if (socktype == SOCK_DGRAM) udp_flag = JANET_STREAM_UDPSERVER;
|
||||
JanetStream *stream = make_stream(sock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | udp_flag);
|
||||
|
||||
/* Set up the socket for non-blocking IO before connecting */
|
||||
janet_net_socknoblock(sock);
|
||||
@@ -487,7 +560,10 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
int err = WSAGetLastError();
|
||||
freeaddrinfo(ai);
|
||||
#else
|
||||
int status = connect(sock, addr, addrlen);
|
||||
int status;
|
||||
do {
|
||||
status = connect(sock, addr, addrlen);
|
||||
} while (status == -1 && errno == EINTR);
|
||||
int err = errno;
|
||||
if (is_unix) {
|
||||
janet_free(ai);
|
||||
@@ -496,7 +572,7 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (status != 0) {
|
||||
if (status) {
|
||||
#ifdef JANET_WINDOWS
|
||||
if (err != WSAEWOULDBLOCK) {
|
||||
#else
|
||||
@@ -508,23 +584,76 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the connect() result in the event loop*/
|
||||
janet_ev_connect(stream, MSG_NOSIGNAL);
|
||||
|
||||
janet_await();
|
||||
net_sched_connect(stream);
|
||||
}
|
||||
|
||||
static const char *serverify_socket(JSock sfd) {
|
||||
JANET_CORE_FN(cfun_net_socket,
|
||||
"(net/socket &opt type)",
|
||||
"Creates a new unbound socket. Type is an optional keyword, "
|
||||
"either a :stream (usually tcp), or :datagram (usually udp). The default is :stream.") {
|
||||
janet_arity(argc, 0, 1);
|
||||
|
||||
int socktype = janet_get_sockettype(argv, argc, 0);
|
||||
|
||||
/* Create socket */
|
||||
JSock sfd = JSOCKDEFAULT;
|
||||
struct addrinfo *ai = NULL;
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = socktype;
|
||||
hints.ai_flags = 0;
|
||||
int status = getaddrinfo(NULL, "0", &hints, &ai);
|
||||
if (status) {
|
||||
janet_panicf("could not get address info: %s", gai_strerror(status));
|
||||
}
|
||||
|
||||
struct addrinfo *rp = NULL;
|
||||
for (rp = ai; rp != NULL; rp = rp->ai_next) {
|
||||
#ifdef JANET_WINDOWS
|
||||
sfd = WSASocketW(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
|
||||
#else
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
|
||||
#endif
|
||||
if (JSOCKVALID(sfd)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
|
||||
if (!JSOCKVALID(sfd)) {
|
||||
Janet v = janet_ev_lasterr();
|
||||
janet_panicf("could not create socket: %V", v);
|
||||
}
|
||||
|
||||
/* Wrap socket in abstract type JanetStream */
|
||||
uint32_t udp_flag = 0;
|
||||
if (socktype == SOCK_DGRAM) udp_flag = JANET_STREAM_UDPSERVER;
|
||||
JanetStream *stream = make_stream(sfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE | udp_flag);
|
||||
|
||||
/* Set up the socket for non-blocking IO */
|
||||
janet_net_socknoblock(sfd);
|
||||
|
||||
return janet_wrap_abstract(stream);
|
||||
}
|
||||
|
||||
static const char *serverify_socket(JSock sfd, int reuse_addr, int reuse_port) {
|
||||
/* Set various socket options */
|
||||
int enable = 1;
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
|
||||
return "setsockopt(SO_REUSEADDR) failed";
|
||||
if (reuse_addr) {
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
|
||||
return "setsockopt(SO_REUSEADDR) failed";
|
||||
}
|
||||
}
|
||||
if (reuse_port) {
|
||||
#ifdef SO_REUSEPORT
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
|
||||
return "setsockopt(SO_REUSEPORT) failed";
|
||||
}
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
|
||||
return "setsockopt(SO_REUSEPORT) failed";
|
||||
}
|
||||
#else
|
||||
(void) reuse_port;
|
||||
#endif
|
||||
}
|
||||
janet_net_socknoblock(sfd);
|
||||
return NULL;
|
||||
}
|
||||
@@ -578,19 +707,22 @@ JANET_CORE_FN(cfun_net_shutdown,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_net_listen,
|
||||
"(net/listen host port &opt type)",
|
||||
"(net/listen host port &opt type no-reuse)",
|
||||
"Creates a server. Returns a new stream that is neither readable nor "
|
||||
"writeable. Use net/accept or net/accept-loop be to handle connections and start the server. "
|
||||
"The type parameter specifies the type of network connection, either "
|
||||
"a :stream (usually tcp), or :datagram (usually udp). If not specified, the default is "
|
||||
":stream. The host and port arguments are the same as in net/address.") {
|
||||
":stream. The host and port arguments are the same as in net/address. The last boolean parameter `no-reuse` will "
|
||||
"disable the use of `SO_REUSEADDR` and `SO_REUSEPORT` when creating a server on some operating systems.") {
|
||||
janet_sandbox_assert(JANET_SANDBOX_NET_LISTEN);
|
||||
janet_arity(argc, 2, 3);
|
||||
janet_arity(argc, 2, 4);
|
||||
|
||||
/* Get host, port, and handler*/
|
||||
int socktype = janet_get_sockettype(argv, argc, 2);
|
||||
int is_unix = 0;
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix);
|
||||
socklen_t addrlen = 0;
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix, &addrlen);
|
||||
int reuse = !(argc >= 4 && janet_truthy(argv[3]));
|
||||
|
||||
JSock sfd = JSOCKDEFAULT;
|
||||
#ifndef JANET_WINDOWS
|
||||
@@ -600,8 +732,8 @@ JANET_CORE_FN(cfun_net_listen,
|
||||
janet_free(ai);
|
||||
janet_panicf("could not create socket: %V", janet_ev_lasterr());
|
||||
}
|
||||
const char *err = serverify_socket(sfd);
|
||||
if (NULL != err || bind(sfd, (struct sockaddr *)ai, sizeof(struct sockaddr_un))) {
|
||||
const char *err = serverify_socket(sfd, reuse, 0);
|
||||
if (NULL != err || bind(sfd, (struct sockaddr *)ai, addrlen)) {
|
||||
JSOCKCLOSE(sfd);
|
||||
janet_free(ai);
|
||||
if (err) {
|
||||
@@ -623,7 +755,7 @@ JANET_CORE_FN(cfun_net_listen,
|
||||
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
|
||||
#endif
|
||||
if (!JSOCKVALID(sfd)) continue;
|
||||
const char *err = serverify_socket(sfd);
|
||||
const char *err = serverify_socket(sfd, reuse, reuse);
|
||||
if (NULL != err) {
|
||||
JSOCKCLOSE(sfd);
|
||||
continue;
|
||||
@@ -682,6 +814,7 @@ static Janet janet_so_getname(const void *sa_any) {
|
||||
Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai->sin_port))};
|
||||
return janet_wrap_tuple(janet_tuple_n(pair, 2));
|
||||
}
|
||||
#ifndef JANET_NO_IPV6
|
||||
case AF_INET6: {
|
||||
const struct sockaddr_in6 *sai6 = sa_any;
|
||||
if (!inet_ntop(AF_INET6, &(sai6->sin6_addr), buffer, sizeof(buffer))) {
|
||||
@@ -690,6 +823,7 @@ static Janet janet_so_getname(const void *sa_any) {
|
||||
Janet pair[2] = {janet_cstringv(buffer), janet_wrap_integer(ntohs(sai6->sin6_port))};
|
||||
return janet_wrap_tuple(janet_tuple_n(pair, 2));
|
||||
}
|
||||
#endif
|
||||
#ifndef JANET_WINDOWS
|
||||
case AF_UNIX: {
|
||||
const struct sockaddr_un *sun = sa_any;
|
||||
@@ -756,13 +890,14 @@ JANET_CORE_FN(cfun_stream_accept_loop,
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
janet_stream_flags(stream, JANET_STREAM_ACCEPTABLE | JANET_STREAM_SOCKET);
|
||||
JanetFunction *fun = janet_getfunction(argv, 1);
|
||||
if (fun->def->min_arity < 1) janet_panic("handler function must take at least 1 argument");
|
||||
janet_sched_accept(stream, fun);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_accept,
|
||||
"(net/accept stream &opt timeout)",
|
||||
"Get the next connection on a server stream. This would usually be called in a loop in a dedicated fiber. "
|
||||
"Takes an optional timeout in seconds, after which will return nil. "
|
||||
"Takes an optional timeout in seconds, after which will raise an error. "
|
||||
"Returns a new duplex stream which represents a connection to the client.") {
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
@@ -777,7 +912,7 @@ JANET_CORE_FN(cfun_stream_read,
|
||||
"Read up to n bytes from a stream, suspending the current fiber until the bytes are available. "
|
||||
"`n` can also be the keyword `:all` to read into the buffer until end of stream. "
|
||||
"If less than n bytes are available (and more than 0), will push those bytes and return early. "
|
||||
"Takes an optional timeout in seconds, after which will return nil. "
|
||||
"Takes an optional timeout in seconds, after which will raise an error. "
|
||||
"Returns a buffer with up to n more bytes in it, or raises an error if the read failed.") {
|
||||
janet_arity(argc, 2, 4);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
@@ -792,13 +927,12 @@ JANET_CORE_FN(cfun_stream_read,
|
||||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_recv(stream, buffer, n, MSG_NOSIGNAL);
|
||||
}
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_chunk,
|
||||
"(net/chunk stream nbytes &opt buf timeout)",
|
||||
"Same a net/read, but will wait for all n bytes to arrive rather than return early. "
|
||||
"Takes an optional timeout in seconds, after which will return nil.") {
|
||||
"Takes an optional timeout in seconds, after which will raise an error.") {
|
||||
janet_arity(argc, 2, 4);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
janet_stream_flags(stream, JANET_STREAM_READABLE | JANET_STREAM_SOCKET);
|
||||
@@ -807,13 +941,12 @@ JANET_CORE_FN(cfun_stream_chunk,
|
||||
double to = janet_optnumber(argv, argc, 3, INFINITY);
|
||||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_recvchunk(stream, buffer, n, MSG_NOSIGNAL);
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_recv_from,
|
||||
"(net/recv-from stream nbytes buf &opt timeout)",
|
||||
"Receives data from a server stream and puts it into a buffer. Returns the socket-address the "
|
||||
"packet came from. Takes an optional timeout in seconds, after which will return nil.") {
|
||||
"packet came from. Takes an optional timeout in seconds, after which will raise an error.") {
|
||||
janet_arity(argc, 3, 4);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
janet_stream_flags(stream, JANET_STREAM_UDPSERVER | JANET_STREAM_SOCKET);
|
||||
@@ -822,13 +955,12 @@ JANET_CORE_FN(cfun_stream_recv_from,
|
||||
double to = janet_optnumber(argv, argc, 3, INFINITY);
|
||||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_recvfrom(stream, buffer, n, MSG_NOSIGNAL);
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_write,
|
||||
"(net/write stream data &opt timeout)",
|
||||
"Write data to a stream, suspending the current fiber until the write "
|
||||
"completes. Takes an optional timeout in seconds, after which will return nil. "
|
||||
"completes. Takes an optional timeout in seconds, after which will raise an error. "
|
||||
"Returns nil, or raises an error if the write failed.") {
|
||||
janet_arity(argc, 2, 3);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
@@ -842,13 +974,12 @@ JANET_CORE_FN(cfun_stream_write,
|
||||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_send_string(stream, bytes.bytes, MSG_NOSIGNAL);
|
||||
}
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_send_to,
|
||||
"(net/send-to stream dest data &opt timeout)",
|
||||
"Writes a datagram to a server stream. dest is a the destination address of the packet. "
|
||||
"Takes an optional timeout in seconds, after which will return nil. "
|
||||
"Takes an optional timeout in seconds, after which will raise an error. "
|
||||
"Returns stream.") {
|
||||
janet_arity(argc, 3, 4);
|
||||
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
@@ -863,7 +994,6 @@ JANET_CORE_FN(cfun_stream_send_to,
|
||||
if (to != INFINITY) janet_addtimeout(to);
|
||||
janet_ev_sendto_string(stream, bytes.bytes, dest, MSG_NOSIGNAL);
|
||||
}
|
||||
janet_await();
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_flush,
|
||||
@@ -891,14 +1021,17 @@ struct sockopt_type {
|
||||
/* List of supported socket options; The type JANET_POINTER is used
|
||||
* for options that require special handling depending on the type. */
|
||||
static const struct sockopt_type sockopt_type_list[] = {
|
||||
{ "tcp-nodelay", IPPROTO_TCP, TCP_NODELAY, JANET_BOOLEAN },
|
||||
{ "so-broadcast", SOL_SOCKET, SO_BROADCAST, JANET_BOOLEAN },
|
||||
{ "so-reuseaddr", SOL_SOCKET, SO_REUSEADDR, JANET_BOOLEAN },
|
||||
{ "so-keepalive", SOL_SOCKET, SO_KEEPALIVE, JANET_BOOLEAN },
|
||||
{ "ip-multicast-ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER },
|
||||
{ "ip-add-membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER },
|
||||
{ "ip-drop-membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER },
|
||||
#ifndef JANET_NO_IPV6
|
||||
{ "ipv6-join-group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
|
||||
{ "ipv6-leave-group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
|
||||
#endif
|
||||
{ NULL, 0, 0, JANET_POINTER }
|
||||
};
|
||||
|
||||
@@ -910,6 +1043,7 @@ JANET_CORE_FN(cfun_net_setsockopt,
|
||||
"- :so-broadcast boolean\n"
|
||||
"- :so-reuseaddr boolean\n"
|
||||
"- :so-keepalive boolean\n"
|
||||
"- :tcp-nodelay boolean\n"
|
||||
"- :ip-multicast-ttl number\n"
|
||||
"- :ip-add-membership string\n"
|
||||
"- :ip-drop-membership string\n"
|
||||
@@ -935,7 +1069,9 @@ JANET_CORE_FN(cfun_net_setsockopt,
|
||||
union {
|
||||
int v_int;
|
||||
struct ip_mreq v_mreq;
|
||||
#ifndef JANET_NO_IPV6
|
||||
struct ipv6_mreq v_mreq6;
|
||||
#endif
|
||||
} val;
|
||||
|
||||
void *optval = (void *)&val;
|
||||
@@ -953,12 +1089,14 @@ JANET_CORE_FN(cfun_net_setsockopt,
|
||||
val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
inet_pton(AF_INET, addr, &val.v_mreq.imr_multiaddr.s_addr);
|
||||
optlen = sizeof(val.v_mreq);
|
||||
#ifndef JANET_NO_IPV6
|
||||
} else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) {
|
||||
const char *addr = janet_getcstring(argv, 2);
|
||||
memset(&val.v_mreq6, 0, sizeof val.v_mreq6);
|
||||
val.v_mreq6.ipv6mr_interface = 0;
|
||||
inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr);
|
||||
optlen = sizeof(val.v_mreq6);
|
||||
#endif
|
||||
} else {
|
||||
janet_panicf("invalid socket option type");
|
||||
}
|
||||
@@ -967,7 +1105,7 @@ JANET_CORE_FN(cfun_net_setsockopt,
|
||||
|
||||
int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen);
|
||||
if (r == -1) {
|
||||
janet_panicf("setsockopt(%q): %s", argv[1], strerror(errno));
|
||||
janet_panicf("setsockopt(%q): %s", argv[1], janet_strerror(errno));
|
||||
}
|
||||
|
||||
return janet_wrap_nil();
|
||||
@@ -999,6 +1137,7 @@ void janet_lib_net(JanetTable *env) {
|
||||
JanetRegExt net_cfuns[] = {
|
||||
JANET_CORE_REG("net/address", cfun_net_sockaddr),
|
||||
JANET_CORE_REG("net/listen", cfun_net_listen),
|
||||
JANET_CORE_REG("net/socket", cfun_net_socket),
|
||||
JANET_CORE_REG("net/accept", cfun_stream_accept),
|
||||
JANET_CORE_REG("net/accept-loop", cfun_stream_accept_loop),
|
||||
JANET_CORE_REG("net/read", cfun_stream_read),
|
||||
|
||||
613
src/core/os.c
613
src/core/os.c
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -231,7 +231,7 @@ static void delim_error(JanetParser *parser, size_t stack_index, char c, const c
|
||||
janet_buffer_push_u8(buffer, '`');
|
||||
}
|
||||
}
|
||||
janet_formatb(buffer, " opened at line %d, column %d", s->line, s->column);
|
||||
janet_formatb(buffer, " opened at line %d, column %d", (int32_t) s->line, (int32_t) s->column);
|
||||
}
|
||||
parser->error = (const char *) janet_string(buffer->data, buffer->count);
|
||||
parser->flag |= JANET_PARSER_GENERATED_ERROR;
|
||||
@@ -363,8 +363,7 @@ static int stringend(JanetParser *p, JanetParseState *state) {
|
||||
JanetParseState top = p->states[p->statecount - 1];
|
||||
int32_t indent_col = (int32_t) top.column - 1;
|
||||
uint8_t *r = bufstart, *end = r + buflen;
|
||||
/* Check if there are any characters before the start column -
|
||||
* if so, do not reindent. */
|
||||
/* Unless there are only spaces before EOLs, disable reindenting */
|
||||
int reindent = 1;
|
||||
while (reindent && (r < end)) {
|
||||
if (*r++ == '\n') {
|
||||
@@ -374,34 +373,36 @@ static int stringend(JanetParser *p, JanetParseState *state) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((r + 1) < end && *r == '\r' && *(r + 1) == '\n') reindent = 1;
|
||||
}
|
||||
}
|
||||
/* Now reindent if able to, otherwise just drop leading newline. */
|
||||
if (!reindent) {
|
||||
if (buflen > 0 && bufstart[0] == '\n') {
|
||||
buflen--;
|
||||
bufstart++;
|
||||
}
|
||||
} else {
|
||||
/* Now reindent if able */
|
||||
if (reindent) {
|
||||
uint8_t *w = bufstart;
|
||||
r = bufstart;
|
||||
while (r < end) {
|
||||
if (*r == '\n') {
|
||||
if (r == bufstart) {
|
||||
/* Skip leading newline */
|
||||
r++;
|
||||
} else {
|
||||
*w++ = *r++;
|
||||
}
|
||||
*w++ = *r++;
|
||||
for (int32_t j = 0; (r < end) && (*r != '\n') && (j < indent_col); j++, r++);
|
||||
if ((r + 1) < end && *r == '\r' && *(r + 1) == '\n') *w++ = *r++;
|
||||
} else {
|
||||
*w++ = *r++;
|
||||
}
|
||||
}
|
||||
buflen = (int32_t)(w - bufstart);
|
||||
}
|
||||
/* Check for trailing newline character so we can remove it */
|
||||
if (buflen > 0 && bufstart[buflen - 1] == '\n') {
|
||||
/* Check for leading EOL so we can remove it */
|
||||
if (buflen > 1 && bufstart[0] == '\r' && bufstart[1] == '\n') { /* Windows EOL */
|
||||
buflen = buflen - 2;
|
||||
bufstart = bufstart + 2;
|
||||
} else if (buflen > 0 && bufstart[0] == '\n') { /* Unix EOL */
|
||||
buflen--;
|
||||
bufstart++;
|
||||
}
|
||||
/* Check for trailing EOL so we can remove it */
|
||||
if (buflen > 1 && bufstart[buflen - 2] == '\r' && bufstart[buflen - 1] == '\n') { /* Windows EOL */
|
||||
buflen = buflen - 2;
|
||||
} else if (buflen > 0 && bufstart[buflen - 1] == '\n') { /* Unix EOL */
|
||||
buflen--;
|
||||
}
|
||||
}
|
||||
@@ -467,8 +468,13 @@ static int tokenchar(JanetParser *p, JanetParseState *state, uint8_t c) {
|
||||
return 0;
|
||||
}
|
||||
ret = janet_keywordv(p->buf + 1, blen - 1);
|
||||
#ifdef JANET_INT_TYPES
|
||||
} else if (start_num && !janet_scan_numeric(p->buf, blen, &ret)) {
|
||||
(void) numval;
|
||||
#else
|
||||
} else if (start_num && !janet_scan_number(p->buf, blen, &numval)) {
|
||||
ret = janet_wrap_number(numval);
|
||||
#endif
|
||||
} else if (!check_str_const("nil", p->buf, blen)) {
|
||||
ret = janet_wrap_nil();
|
||||
} else if (!check_str_const("false", p->buf, blen)) {
|
||||
|
||||
229
src/core/peg.c
229
src/core/peg.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -39,6 +39,10 @@
|
||||
typedef struct {
|
||||
const uint8_t *text_start;
|
||||
const uint8_t *text_end;
|
||||
/* text_end can be restricted by some rules, but
|
||||
outer_text_end will always contain the real end of
|
||||
input, which we need to generate a line mapping */
|
||||
const uint8_t *outer_text_end;
|
||||
const uint32_t *bytecode;
|
||||
const Janet *constants;
|
||||
JanetArray *captures;
|
||||
@@ -114,12 +118,12 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) {
|
||||
/* Generate if not made yet */
|
||||
if (s->linemaplen < 0) {
|
||||
int32_t newline_count = 0;
|
||||
for (const uint8_t *c = s->text_start; c < s->text_end; c++) {
|
||||
for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
|
||||
if (*c == '\n') newline_count++;
|
||||
}
|
||||
int32_t *mem = janet_smalloc(sizeof(int32_t) * newline_count);
|
||||
size_t index = 0;
|
||||
for (const uint8_t *c = s->text_start; c < s->text_end; c++) {
|
||||
for (const uint8_t *c = s->text_start; c < s->outer_text_end; c++) {
|
||||
if (*c == '\n') mem[index++] = (int32_t)(c - s->text_start);
|
||||
}
|
||||
s->linemaplen = newline_count;
|
||||
@@ -130,7 +134,7 @@ static LineCol get_linecol_from_position(PegState *s, int32_t position) {
|
||||
* a newline character is consider to be on the same line as the character before
|
||||
* (\n is line terminator, not line separator).
|
||||
* - in the not-found case, we still want to find the greatest-indexed newline that
|
||||
* is before position. we use that to calcuate the line and column.
|
||||
* is before position. we use that to calculate the line and column.
|
||||
* - in the case that lo = 0 and s->linemap[0] is still greater than position, we
|
||||
* are on the first line and our column is position + 1. */
|
||||
int32_t hi = s->linemaplen; /* hi is greater than the actual line */
|
||||
@@ -179,7 +183,7 @@ static const uint8_t *peg_rule(
|
||||
const uint32_t *rule,
|
||||
const uint8_t *text) {
|
||||
tail:
|
||||
switch (*rule & 0x1F) {
|
||||
switch (*rule) {
|
||||
default:
|
||||
janet_panic("unexpected opcode");
|
||||
return NULL;
|
||||
@@ -338,7 +342,7 @@ tail:
|
||||
while (captured < hi) {
|
||||
CapState cs2 = cap_save(s);
|
||||
next_text = peg_rule(s, rule_a, text);
|
||||
if (!next_text || next_text == text) {
|
||||
if (!next_text || ((next_text == text) && (hi == UINT32_MAX))) {
|
||||
cap_load(s, cs2);
|
||||
break;
|
||||
}
|
||||
@@ -461,6 +465,16 @@ tail:
|
||||
return result;
|
||||
}
|
||||
|
||||
case RULE_ONLY_TAGS: {
|
||||
CapState cs = cap_save(s);
|
||||
down1(s);
|
||||
const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
|
||||
up1(s);
|
||||
if (!result) return NULL;
|
||||
cap_load_keept(s, cs);
|
||||
return result;
|
||||
}
|
||||
|
||||
case RULE_GROUP: {
|
||||
uint32_t tag = rule[2];
|
||||
int oldmode = s->mode;
|
||||
@@ -482,6 +496,131 @@ tail:
|
||||
return result;
|
||||
}
|
||||
|
||||
case RULE_NTH: {
|
||||
uint32_t nth = rule[1];
|
||||
if (nth > INT32_MAX) nth = INT32_MAX;
|
||||
uint32_t tag = rule[3];
|
||||
int oldmode = s->mode;
|
||||
CapState cs = cap_save(s);
|
||||
s->mode = PEG_MODE_NORMAL;
|
||||
down1(s);
|
||||
const uint8_t *result = peg_rule(s, s->bytecode + rule[2], text);
|
||||
up1(s);
|
||||
s->mode = oldmode;
|
||||
if (!result) return NULL;
|
||||
int32_t num_sub_captures = s->captures->count - cs.cap;
|
||||
Janet cap;
|
||||
if (num_sub_captures > (int32_t) nth) {
|
||||
cap = s->captures->data[cs.cap + nth];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
cap_load_keept(s, cs);
|
||||
pushcap(s, cap, tag);
|
||||
return result;
|
||||
}
|
||||
|
||||
case RULE_SUB: {
|
||||
const uint8_t *text_start = text;
|
||||
const uint32_t *rule_window = s->bytecode + rule[1];
|
||||
const uint32_t *rule_subpattern = s->bytecode + rule[2];
|
||||
down1(s);
|
||||
const uint8_t *window_end = peg_rule(s, rule_window, text);
|
||||
up1(s);
|
||||
if (!window_end) {
|
||||
return NULL;
|
||||
}
|
||||
const uint8_t *saved_end = s->text_end;
|
||||
s->text_end = window_end;
|
||||
down1(s);
|
||||
const uint8_t *next_text = peg_rule(s, rule_subpattern, text_start);
|
||||
up1(s);
|
||||
s->text_end = saved_end;
|
||||
|
||||
if (!next_text) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window_end;
|
||||
}
|
||||
|
||||
case RULE_TIL: {
|
||||
const uint32_t *rule_terminus = s->bytecode + rule[1];
|
||||
const uint32_t *rule_subpattern = s->bytecode + rule[2];
|
||||
|
||||
const uint8_t *terminus_start = text;
|
||||
const uint8_t *terminus_end = NULL;
|
||||
down1(s);
|
||||
while (terminus_start <= s->text_end) {
|
||||
CapState cs2 = cap_save(s);
|
||||
terminus_end = peg_rule(s, rule_terminus, terminus_start);
|
||||
cap_load(s, cs2);
|
||||
if (terminus_end) {
|
||||
break;
|
||||
}
|
||||
terminus_start++;
|
||||
}
|
||||
up1(s);
|
||||
|
||||
if (!terminus_end) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const uint8_t *saved_end = s->text_end;
|
||||
s->text_end = terminus_start;
|
||||
down1(s);
|
||||
const uint8_t *matched = peg_rule(s, rule_subpattern, text);
|
||||
up1(s);
|
||||
s->text_end = saved_end;
|
||||
|
||||
if (!matched) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return terminus_end;
|
||||
}
|
||||
|
||||
case RULE_SPLIT: {
|
||||
const uint8_t *saved_end = s->text_end;
|
||||
const uint32_t *rule_separator = s->bytecode + rule[1];
|
||||
const uint32_t *rule_subpattern = s->bytecode + rule[2];
|
||||
|
||||
const uint8_t *chunk_start = text;
|
||||
const uint8_t *chunk_end = NULL;
|
||||
|
||||
while (text <= saved_end) {
|
||||
/* Find next split (or end of text) */
|
||||
CapState cs = cap_save(s);
|
||||
down1(s);
|
||||
while (text <= saved_end) {
|
||||
chunk_end = text;
|
||||
const uint8_t *check = peg_rule(s, rule_separator, text);
|
||||
cap_load(s, cs);
|
||||
if (check) {
|
||||
text = check;
|
||||
break;
|
||||
}
|
||||
text++;
|
||||
}
|
||||
up1(s);
|
||||
|
||||
/* Match between splits */
|
||||
s->text_end = chunk_end;
|
||||
down1(s);
|
||||
const uint8_t *subpattern_end = peg_rule(s, rule_subpattern, chunk_start);
|
||||
up1(s);
|
||||
s->text_end = saved_end;
|
||||
if (!subpattern_end) return NULL; /* Don't match anything */
|
||||
|
||||
/* Ensure forward progress */
|
||||
if (text == chunk_start) return NULL;
|
||||
chunk_start = text;
|
||||
}
|
||||
|
||||
s->text_end = saved_end;
|
||||
return s->text_end;
|
||||
}
|
||||
|
||||
case RULE_REPLACE:
|
||||
case RULE_MATCHTIME: {
|
||||
uint32_t tag = rule[3];
|
||||
@@ -601,11 +740,11 @@ tail:
|
||||
case RULE_READINT: {
|
||||
uint32_t tag = rule[2];
|
||||
uint32_t signedness = rule[1] & 0x10;
|
||||
uint32_t endianess = rule[1] & 0x20;
|
||||
uint32_t endianness = rule[1] & 0x20;
|
||||
int width = (int)(rule[1] & 0xF);
|
||||
if (text + width > s->text_end) return NULL;
|
||||
uint64_t accum = 0;
|
||||
if (endianess) {
|
||||
if (endianness) {
|
||||
/* BE */
|
||||
for (int i = 0; i < width; i++) accum = (accum << 8) | text[i];
|
||||
} else {
|
||||
@@ -995,6 +1134,9 @@ static void spec_thru(Builder *b, int32_t argc, const Janet *argv) {
|
||||
static void spec_drop(Builder *b, int32_t argc, const Janet *argv) {
|
||||
spec_onerule(b, argc, argv, RULE_DROP);
|
||||
}
|
||||
static void spec_only_tags(Builder *b, int32_t argc, const Janet *argv) {
|
||||
spec_onerule(b, argc, argv, RULE_ONLY_TAGS);
|
||||
}
|
||||
|
||||
/* Rule of the form [rule, tag] */
|
||||
static void spec_cap1(Builder *b, int32_t argc, const Janet *argv, uint32_t op) {
|
||||
@@ -1018,6 +1160,15 @@ static void spec_unref(Builder *b, int32_t argc, const Janet *argv) {
|
||||
spec_cap1(b, argc, argv, RULE_UNREF);
|
||||
}
|
||||
|
||||
static void spec_nth(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_arity(b, argc, 2, 3);
|
||||
Reserve r = reserve(b, 4);
|
||||
uint32_t nth = peg_getnat(b, argv[0]);
|
||||
uint32_t rule = peg_compile1(b, argv[1]);
|
||||
uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
|
||||
emit_3(r, RULE_NTH, nth, rule, tag);
|
||||
}
|
||||
|
||||
static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_arity(b, argc, 1, 3);
|
||||
Reserve r = reserve(b, 4);
|
||||
@@ -1107,6 +1258,30 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
|
||||
emit_3(r, RULE_MATCHTIME, subrule, cindex, tag);
|
||||
}
|
||||
|
||||
static void spec_sub(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_fixarity(b, argc, 2);
|
||||
Reserve r = reserve(b, 3);
|
||||
uint32_t subrule1 = peg_compile1(b, argv[0]);
|
||||
uint32_t subrule2 = peg_compile1(b, argv[1]);
|
||||
emit_2(r, RULE_SUB, subrule1, subrule2);
|
||||
}
|
||||
|
||||
static void spec_til(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_fixarity(b, argc, 2);
|
||||
Reserve r = reserve(b, 3);
|
||||
uint32_t subrule1 = peg_compile1(b, argv[0]);
|
||||
uint32_t subrule2 = peg_compile1(b, argv[1]);
|
||||
emit_2(r, RULE_TIL, subrule1, subrule2);
|
||||
}
|
||||
|
||||
static void spec_split(Builder *b, int32_t argc, const Janet *argv) {
|
||||
peg_fixarity(b, argc, 2);
|
||||
Reserve r = reserve(b, 3);
|
||||
uint32_t subrule1 = peg_compile1(b, argv[0]);
|
||||
uint32_t subrule2 = peg_compile1(b, argv[1]);
|
||||
emit_2(r, RULE_SPLIT, subrule1, subrule2);
|
||||
}
|
||||
|
||||
#ifdef JANET_INT_TYPES
|
||||
#define JANET_MAX_READINT_WIDTH 8
|
||||
#else
|
||||
@@ -1180,7 +1355,9 @@ static const SpecialPair peg_specials[] = {
|
||||
{"line", spec_line},
|
||||
{"look", spec_look},
|
||||
{"not", spec_not},
|
||||
{"nth", spec_nth},
|
||||
{"number", spec_capture_number},
|
||||
{"only-tags", spec_only_tags},
|
||||
{"opt", spec_opt},
|
||||
{"position", spec_position},
|
||||
{"quote", spec_capture},
|
||||
@@ -1190,7 +1367,10 @@ static const SpecialPair peg_specials[] = {
|
||||
{"sequence", spec_sequence},
|
||||
{"set", spec_set},
|
||||
{"some", spec_some},
|
||||
{"split", spec_split},
|
||||
{"sub", spec_sub},
|
||||
{"thru", spec_thru},
|
||||
{"til", spec_til},
|
||||
{"to", spec_to},
|
||||
{"uint", spec_uint_le},
|
||||
{"uint-be", spec_uint_be},
|
||||
@@ -1284,6 +1464,11 @@ static uint32_t peg_compile1(Builder *b, Janet peg) {
|
||||
emit_bytes(b, RULE_LITERAL, len, str);
|
||||
break;
|
||||
}
|
||||
case JANET_BUFFER: {
|
||||
const JanetBuffer *buf = janet_unwrap_buffer(peg);
|
||||
emit_bytes(b, RULE_LITERAL, buf->count, buf->data);
|
||||
break;
|
||||
}
|
||||
case JANET_TABLE: {
|
||||
/* Build grammar table */
|
||||
JanetTable *new_grammar = janet_table_clone(janet_unwrap_table(peg));
|
||||
@@ -1431,7 +1616,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
||||
uint32_t instr = bytecode[i];
|
||||
uint32_t *rule = bytecode + i;
|
||||
op_flags[i] |= 0x02;
|
||||
switch (instr & 0x1F) {
|
||||
switch (instr) {
|
||||
case RULE_LITERAL:
|
||||
i += 2 + ((rule[1] + 3) >> 2);
|
||||
break;
|
||||
@@ -1524,8 +1709,19 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
||||
op_flags[rule[1]] |= 0x01;
|
||||
i += 4;
|
||||
break;
|
||||
case RULE_SUB:
|
||||
case RULE_TIL:
|
||||
case RULE_SPLIT:
|
||||
/* [rule, rule] */
|
||||
if (rule[1] >= blen) goto bad;
|
||||
if (rule[2] >= blen) goto bad;
|
||||
op_flags[rule[1]] |= 0x01;
|
||||
op_flags[rule[2]] |= 0x01;
|
||||
i += 3;
|
||||
break;
|
||||
case RULE_ERROR:
|
||||
case RULE_DROP:
|
||||
case RULE_ONLY_TAGS:
|
||||
case RULE_NOT:
|
||||
case RULE_TO:
|
||||
case RULE_THRU:
|
||||
@@ -1535,10 +1731,16 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
|
||||
i += 2;
|
||||
break;
|
||||
case RULE_READINT:
|
||||
/* [ width | (endianess << 5) | (signedness << 6), tag ] */
|
||||
/* [ width | (endianness << 5) | (signedness << 6), tag ] */
|
||||
if (rule[1] > JANET_MAX_READINT_WIDTH) goto bad;
|
||||
i += 3;
|
||||
break;
|
||||
case RULE_NTH:
|
||||
/* [nth, rule, tag] */
|
||||
if (rule[2] >= blen) goto bad;
|
||||
op_flags[rule[2]] |= 0x01;
|
||||
i += 4;
|
||||
break;
|
||||
default:
|
||||
goto bad;
|
||||
}
|
||||
@@ -1632,7 +1834,7 @@ static JanetPeg *compile_peg(Janet x) {
|
||||
JANET_CORE_FN(cfun_peg_compile,
|
||||
"(peg/compile peg)",
|
||||
"Compiles a peg source data structure into a <core/peg>. This will speed up matching "
|
||||
"if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to suppliment "
|
||||
"if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to supplement "
|
||||
"the grammar of the peg for otherwise undefined peg keywords.") {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetPeg *peg = compile_peg(argv[0]);
|
||||
@@ -1652,7 +1854,7 @@ typedef struct {
|
||||
static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
|
||||
PegCall ret;
|
||||
int32_t min = get_replace ? 3 : 2;
|
||||
janet_arity(argc, get_replace, -1);
|
||||
janet_arity(argc, min, -1);
|
||||
if (janet_checktype(argv[0], JANET_ABSTRACT) &&
|
||||
janet_abstract_type(janet_unwrap_abstract(argv[0])) == &janet_peg_type) {
|
||||
ret.peg = janet_unwrap_abstract(argv[0]);
|
||||
@@ -1677,6 +1879,7 @@ static PegCall peg_cfun_init(int32_t argc, Janet *argv, int get_replace) {
|
||||
ret.s.mode = PEG_MODE_NORMAL;
|
||||
ret.s.text_start = ret.bytes.bytes;
|
||||
ret.s.text_end = ret.bytes.bytes + ret.bytes.len;
|
||||
ret.s.outer_text_end = ret.s.text_end;
|
||||
ret.s.depth = JANET_RECURSION_GUARD;
|
||||
ret.s.captures = janet_array(0);
|
||||
ret.s.tagged_captures = janet_array(0);
|
||||
@@ -1771,7 +1974,7 @@ JANET_CORE_FN(cfun_peg_replace_all,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_peg_replace,
|
||||
"(peg/replace peg repl text &opt start & args)",
|
||||
"(peg/replace peg subst text &opt start & args)",
|
||||
"Replace first match of `peg` in `text` with `subst`, returning a new buffer. "
|
||||
"The peg does not need to make captures to do replacement. "
|
||||
"If `subst` is a function, it will be called with the "
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <float.h>
|
||||
|
||||
/* Implements a pretty printer for Janet. The pretty printer
|
||||
* is simple and not that flexible, but fast. */
|
||||
@@ -38,11 +39,15 @@
|
||||
/* Temporary buffer size */
|
||||
#define BUFSIZE 64
|
||||
|
||||
/* Preprocessor hacks */
|
||||
#define STR_HELPER(x) #x
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
|
||||
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
|
||||
const char *fmt = (x == floor(x) &&
|
||||
x <= JANET_INTMAX_DOUBLE &&
|
||||
x >= JANET_INTMIN_DOUBLE) ? "%.0f" : "%g";
|
||||
x >= JANET_INTMIN_DOUBLE) ? "%.0f" : ("%." STR(DBL_DIG) "g");
|
||||
int count;
|
||||
if (x == 0.0) {
|
||||
/* Prevent printing of '-0' */
|
||||
@@ -374,8 +379,10 @@ static int print_jdn_one(struct pretty *S, Janet x, int depth) {
|
||||
break;
|
||||
case JANET_NUMBER:
|
||||
janet_buffer_ensure(S->buffer, S->buffer->count + BUFSIZE, 2);
|
||||
int count = snprintf((char *) S->buffer->data + S->buffer->count, BUFSIZE, "%.17g", janet_unwrap_number(x));
|
||||
S->buffer->count += count;
|
||||
double num = janet_unwrap_number(x);
|
||||
if (isnan(num)) return 1;
|
||||
if (isinf(num)) return 1;
|
||||
janet_buffer_dtostr(S->buffer, num);
|
||||
break;
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
@@ -772,6 +779,8 @@ struct FmtMapping {
|
||||
/* Janet uses fixed width integer types for most things, so map
|
||||
* format specifiers to these fixed sizes */
|
||||
static const struct FmtMapping format_mappings[] = {
|
||||
{'D', PRId64},
|
||||
{'I', PRIi64},
|
||||
{'d', PRId64},
|
||||
{'i', PRIi64},
|
||||
{'o', PRIo64},
|
||||
@@ -823,7 +832,7 @@ static const char *scanformat(
|
||||
if (loc != NULL && *loc != '\0') {
|
||||
const char *mapping = get_fmt_mapping(*p2++);
|
||||
size_t len = strlen(mapping);
|
||||
strcpy(form, mapping);
|
||||
memcpy(form, mapping, len);
|
||||
form += len;
|
||||
} else {
|
||||
*(form++) = *(p2++);
|
||||
@@ -850,13 +859,19 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
|
||||
c = scanformat(c, form, width, precision);
|
||||
switch (*c++) {
|
||||
case 'c': {
|
||||
int n = va_arg(args, long);
|
||||
int n = va_arg(args, int);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'i': {
|
||||
int64_t n = va_arg(args, int);
|
||||
int64_t n = (int64_t) va_arg(args, int32_t);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
case 'I': {
|
||||
int64_t n = va_arg(args, int64_t);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
@@ -864,7 +879,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
|
||||
case 'X':
|
||||
case 'o':
|
||||
case 'u': {
|
||||
uint64_t n = va_arg(args, unsigned int);
|
||||
uint64_t n = va_arg(args, uint64_t);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
@@ -908,7 +923,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
|
||||
janet_buffer_push_cstring(b, typestr(va_arg(args, Janet)));
|
||||
break;
|
||||
case 'T': {
|
||||
int types = va_arg(args, long);
|
||||
int types = va_arg(args, int);
|
||||
pushtypes(b, types);
|
||||
break;
|
||||
}
|
||||
@@ -1017,6 +1032,8 @@ void janet_buffer_format(
|
||||
janet_getinteger(argv, arg));
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
case 'I':
|
||||
case 'd':
|
||||
case 'i': {
|
||||
int64_t n = janet_getinteger64(argv, arg);
|
||||
@@ -1043,19 +1060,11 @@ void janet_buffer_format(
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
JanetByteView bytes = janet_getbytes(argv, arg);
|
||||
const uint8_t *s = bytes.bytes;
|
||||
int32_t l = bytes.len;
|
||||
const char *s = janet_getcbytes(argv, arg);
|
||||
if (form[2] == '\0')
|
||||
janet_buffer_push_bytes(b, s, l);
|
||||
janet_buffer_push_cstring(b, s);
|
||||
else {
|
||||
if (l != (int32_t) strlen((const char *) s))
|
||||
janet_panic("string contains zeros");
|
||||
if (!strchr(form, '.') && l >= 100) {
|
||||
janet_panic("no precision and string is too long to be formatted");
|
||||
} else {
|
||||
nb = snprintf(item, MAX_ITEM, form, s);
|
||||
}
|
||||
nb = snprintf(item, MAX_ITEM, form, s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -26,45 +26,57 @@
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Run a string */
|
||||
/* Run a string of code. The return value is a set of error flags, JANET_DO_ERROR_RUNTIME, JANET_DO_ERROR_COMPILE, and JANET_DOR_ERROR_PARSE if
|
||||
* any errors were encountered in those phases. More information is printed to stderr. */
|
||||
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out) {
|
||||
JanetParser parser;
|
||||
JanetParser *parser;
|
||||
int errflags = 0, done = 0;
|
||||
int32_t index = 0;
|
||||
Janet ret = janet_wrap_nil();
|
||||
JanetFiber *fiber = NULL;
|
||||
const uint8_t *where = sourcePath ? janet_cstring(sourcePath) : NULL;
|
||||
|
||||
if (where) janet_gcroot(janet_wrap_string(where));
|
||||
if (NULL == sourcePath) sourcePath = "<unknown>";
|
||||
janet_parser_init(&parser);
|
||||
parser = janet_abstract(&janet_parser_type, sizeof(JanetParser));
|
||||
janet_parser_init(parser);
|
||||
janet_gcroot(janet_wrap_abstract(parser));
|
||||
|
||||
/* While we haven't seen an error */
|
||||
while (!done) {
|
||||
|
||||
/* Evaluate parsed values */
|
||||
while (janet_parser_has_more(&parser)) {
|
||||
Janet form = janet_parser_produce(&parser);
|
||||
while (janet_parser_has_more(parser)) {
|
||||
Janet form = janet_parser_produce(parser);
|
||||
JanetCompileResult cres = janet_compile(form, env, where);
|
||||
if (cres.status == JANET_COMPILE_OK) {
|
||||
JanetFunction *f = janet_thunk(cres.funcdef);
|
||||
JanetFiber *fiber = janet_fiber(f, 64, 0, NULL);
|
||||
fiber = janet_fiber(f, 64, 0, NULL);
|
||||
fiber->env = env;
|
||||
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
|
||||
if (status != JANET_SIGNAL_OK && status != JANET_SIGNAL_EVENT) {
|
||||
janet_stacktrace_ext(fiber, ret, "");
|
||||
errflags |= 0x01;
|
||||
errflags |= JANET_DO_ERROR_RUNTIME;
|
||||
done = 1;
|
||||
}
|
||||
} else {
|
||||
ret = janet_wrap_string(cres.error);
|
||||
int32_t line = (int32_t) parser->line;
|
||||
int32_t col = (int32_t) parser->column;
|
||||
if ((cres.error_mapping.line > 0) &&
|
||||
(cres.error_mapping.column > 0)) {
|
||||
line = cres.error_mapping.line;
|
||||
col = cres.error_mapping.column;
|
||||
}
|
||||
if (cres.macrofiber) {
|
||||
janet_eprintf("compile error in %s: ", sourcePath);
|
||||
janet_eprintf("%s:%d:%d: compile error", sourcePath,
|
||||
line, col);
|
||||
janet_stacktrace_ext(cres.macrofiber, ret, "");
|
||||
} else {
|
||||
janet_eprintf("compile error in %s: %s\n", sourcePath,
|
||||
(const char *)cres.error);
|
||||
janet_eprintf("%s:%d:%d: compile error: %s\n", sourcePath,
|
||||
line, col, (const char *)cres.error);
|
||||
}
|
||||
errflags |= 0x02;
|
||||
errflags |= JANET_DO_ERROR_COMPILE;
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
@@ -72,16 +84,16 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
if (done) break;
|
||||
|
||||
/* Dispatch based on parse state */
|
||||
switch (janet_parser_status(&parser)) {
|
||||
switch (janet_parser_status(parser)) {
|
||||
case JANET_PARSE_DEAD:
|
||||
done = 1;
|
||||
break;
|
||||
case JANET_PARSE_ERROR: {
|
||||
const char *e = janet_parser_error(&parser);
|
||||
errflags |= 0x04;
|
||||
const char *e = janet_parser_error(parser);
|
||||
errflags |= JANET_DO_ERROR_PARSE;
|
||||
ret = janet_cstringv(e);
|
||||
int32_t line = (int32_t) parser.line;
|
||||
int32_t col = (int32_t) parser.column;
|
||||
int32_t line = (int32_t) parser->line;
|
||||
int32_t col = (int32_t) parser->column;
|
||||
janet_eprintf("%s:%d:%d: parse error: %s\n", sourcePath, line, col, e);
|
||||
done = 1;
|
||||
break;
|
||||
@@ -89,9 +101,9 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
case JANET_PARSE_ROOT:
|
||||
case JANET_PARSE_PENDING:
|
||||
if (index >= len) {
|
||||
janet_parser_eof(&parser);
|
||||
janet_parser_eof(parser);
|
||||
} else {
|
||||
janet_parser_consume(&parser, bytes[index++]);
|
||||
janet_parser_consume(parser, bytes[index++]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -99,14 +111,19 @@ int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char
|
||||
}
|
||||
|
||||
/* Clean up and return errors */
|
||||
janet_parser_deinit(&parser);
|
||||
janet_gcunroot(janet_wrap_abstract(parser));
|
||||
if (where) janet_gcunroot(janet_wrap_string(where));
|
||||
#ifdef JANET_EV
|
||||
/* Enter the event loop if we are not already in it */
|
||||
if (janet_vm.stackn == 0) {
|
||||
janet_gcroot(ret);
|
||||
if (fiber) {
|
||||
janet_gcroot(janet_wrap_fiber(fiber));
|
||||
}
|
||||
janet_loop();
|
||||
janet_gcunroot(ret);
|
||||
if (fiber) {
|
||||
janet_gcunroot(janet_wrap_fiber(fiber));
|
||||
ret = fiber->last_value;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (out) *out = ret;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -149,7 +149,7 @@ static int destructure(JanetCompiler *c,
|
||||
JanetTable *attr) {
|
||||
switch (janet_type(left)) {
|
||||
default:
|
||||
janetc_error(c, janet_formatc("unexpected type in destruction, got %v", left));
|
||||
janetc_error(c, janet_formatc("unexpected type in destructuring, got %v", left));
|
||||
return 1;
|
||||
case JANET_SYMBOL:
|
||||
/* Leaf, assign right to left */
|
||||
@@ -530,6 +530,26 @@ static JanetSlot janetc_def(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check if a form matches the pattern (= nil _) or (not= nil _) */
|
||||
static int janetc_check_nil_form(Janet x, Janet *capture, uint32_t fun_tag) {
|
||||
if (!janet_checktype(x, JANET_TUPLE)) return 0;
|
||||
JanetTuple tup = janet_unwrap_tuple(x);
|
||||
if (3 != janet_tuple_length(tup)) return 0;
|
||||
Janet op1 = tup[0];
|
||||
if (!janet_checktype(op1, JANET_FUNCTION)) return 0;
|
||||
JanetFunction *fun = janet_unwrap_function(op1);
|
||||
uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
|
||||
if (tag != fun_tag) return 0;
|
||||
if (janet_checktype(tup[1], JANET_NIL)) {
|
||||
*capture = tup[2];
|
||||
return 1;
|
||||
} else if (janet_checktype(tup[2], JANET_NIL)) {
|
||||
*capture = tup[1];
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* :condition
|
||||
* ...
|
||||
@@ -550,6 +570,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
JanetScope condscope, tempscope;
|
||||
const int tail = opts.flags & JANET_FOPTS_TAIL;
|
||||
const int drop = opts.flags & JANET_FOPTS_DROP;
|
||||
uint8_t ifnjmp = JOP_JUMP_IF_NOT;
|
||||
|
||||
if (argn < 2 || argn > 3) {
|
||||
janetc_cerror(c, "expected 2 or 3 arguments to if");
|
||||
@@ -572,12 +593,24 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
|
||||
/* Compile condition */
|
||||
janetc_scope(&condscope, c, 0, "if");
|
||||
cond = janetc_value(condopts, argv[0]);
|
||||
|
||||
Janet condform = argv[0];
|
||||
if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
|
||||
ifnjmp = JOP_JUMP_IF_NOT_NIL;
|
||||
} else if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
|
||||
ifnjmp = JOP_JUMP_IF_NIL;
|
||||
}
|
||||
|
||||
cond = janetc_value(condopts, condform);
|
||||
|
||||
/* Check constant condition. */
|
||||
/* TODO: Use type info for more short circuits */
|
||||
if (cond.flags & JANET_SLOT_CONSTANT) {
|
||||
if (!janet_truthy(cond.constant)) {
|
||||
int swap_condition = 0;
|
||||
if (ifnjmp == JOP_JUMP_IF_NOT && !janet_truthy(cond.constant)) swap_condition = 1;
|
||||
if (ifnjmp == JOP_JUMP_IF_NIL && janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
|
||||
if (ifnjmp == JOP_JUMP_IF_NOT_NIL && !janet_checktype(cond.constant, JANET_NIL)) swap_condition = 1;
|
||||
if (swap_condition) {
|
||||
/* Swap the true and false bodies */
|
||||
Janet temp = falsebody;
|
||||
falsebody = truebody;
|
||||
@@ -595,7 +628,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
}
|
||||
|
||||
/* Compile jump to right */
|
||||
labeljr = janetc_emit_si(c, JOP_JUMP_IF_NOT, cond, 0, 0);
|
||||
labeljr = janetc_emit_si(c, ifnjmp, cond, 0, 0);
|
||||
|
||||
/* Condition left body */
|
||||
janetc_scope(&tempscope, c, 0, "if-true");
|
||||
@@ -605,7 +638,7 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
|
||||
/* Compile jump to done */
|
||||
labeljd = janet_v_count(c->buffer);
|
||||
if (!tail) janetc_emit(c, JOP_JUMP);
|
||||
if (!tail && !(drop && janet_checktype(falsebody, JANET_NIL))) janetc_emit(c, JOP_JUMP);
|
||||
|
||||
/* Compile right body */
|
||||
labelr = janet_v_count(c->buffer);
|
||||
@@ -716,9 +749,8 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
if (!(scope->flags & JANET_SCOPE_WHILE) && argn) {
|
||||
/* Closure body with return argument */
|
||||
subopts.flags |= JANET_FOPTS_TAIL;
|
||||
JanetSlot ret = janetc_value(subopts, argv[0]);
|
||||
ret.flags |= JANET_SLOT_RETURNED;
|
||||
return ret;
|
||||
janetc_value(subopts, argv[0]);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
} else {
|
||||
/* while loop IIFE or no argument */
|
||||
if (argn) {
|
||||
@@ -726,9 +758,7 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
janetc_value(subopts, argv[0]);
|
||||
}
|
||||
janetc_emit(c, JOP_RETURN_NIL);
|
||||
JanetSlot s = janetc_cslot(janet_wrap_nil());
|
||||
s.flags |= JANET_SLOT_RETURNED;
|
||||
return s;
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
} else {
|
||||
if (argn) {
|
||||
@@ -741,20 +771,6 @@ static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if a form matches the pattern (not= nil _) */
|
||||
static int janetc_check_notnil_form(Janet x, Janet *capture) {
|
||||
if (!janet_checktype(x, JANET_TUPLE)) return 0;
|
||||
JanetTuple tup = janet_unwrap_tuple(x);
|
||||
if (!janet_checktype(tup[0], JANET_FUNCTION)) return 0;
|
||||
if (3 != janet_tuple_length(tup)) return 0;
|
||||
JanetFunction *fun = janet_unwrap_function(tup[0]);
|
||||
uint32_t tag = fun->def->flags & JANET_FUNCDEF_FLAG_TAG;
|
||||
if (tag != JANET_FUN_NEQ) return 0;
|
||||
if (!janet_checktype(tup[1], JANET_NIL)) return 0;
|
||||
*capture = tup[2];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* :whiletop
|
||||
* ...
|
||||
@@ -771,6 +787,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
JanetScope tempscope;
|
||||
int32_t labelwt, labeld, labeljt, labelc, i;
|
||||
int infinite = 0;
|
||||
int is_nil_form = 0;
|
||||
int is_notnil_form = 0;
|
||||
uint8_t ifjmp = JOP_JUMP_IF;
|
||||
uint8_t ifnjmp = JOP_JUMP_IF_NOT;
|
||||
@@ -784,11 +801,16 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
|
||||
janetc_scope(&tempscope, c, JANET_SCOPE_WHILE, "while");
|
||||
|
||||
/* Check for `(not= nil _)` in condition, and if so, use the
|
||||
/* Check for `(= nil _)` or `(not= nil _)` in condition, and if so, use the
|
||||
* jmpnl or jmpnn instructions. This let's us implement `(each ...)`
|
||||
* more efficiently. */
|
||||
Janet condform = argv[0];
|
||||
if (janetc_check_notnil_form(condform, &condform)) {
|
||||
if (janetc_check_nil_form(condform, &condform, JANET_FUN_EQ)) {
|
||||
is_nil_form = 1;
|
||||
ifjmp = JOP_JUMP_IF_NIL;
|
||||
ifnjmp = JOP_JUMP_IF_NOT_NIL;
|
||||
}
|
||||
if (janetc_check_nil_form(condform, &condform, JANET_FUN_NEQ)) {
|
||||
is_notnil_form = 1;
|
||||
ifjmp = JOP_JUMP_IF_NOT_NIL;
|
||||
ifnjmp = JOP_JUMP_IF_NIL;
|
||||
@@ -800,7 +822,9 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
/* Check for constant condition */
|
||||
if (cond.flags & JANET_SLOT_CONSTANT) {
|
||||
/* Loop never executes */
|
||||
int never_executes = is_notnil_form
|
||||
int never_executes = is_nil_form
|
||||
? !janet_checktype(cond.constant, JANET_NIL)
|
||||
: is_notnil_form
|
||||
? janet_checktype(cond.constant, JANET_NIL)
|
||||
: !janet_truthy(cond.constant);
|
||||
if (never_executes) {
|
||||
@@ -901,6 +925,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
int structarg = 0;
|
||||
int allow_extra = 0;
|
||||
int selfref = 0;
|
||||
int hasname = 0;
|
||||
int seenamp = 0;
|
||||
int seenopt = 0;
|
||||
int namedargs = 0;
|
||||
@@ -919,6 +944,10 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
head = argv[0];
|
||||
if (janet_checktype(head, JANET_SYMBOL)) {
|
||||
selfref = 1;
|
||||
hasname = 1;
|
||||
parami = 1;
|
||||
} else if (janet_checktype(head, JANET_KEYWORD)) {
|
||||
hasname = 1;
|
||||
parami = 1;
|
||||
}
|
||||
if (parami >= argn || !janet_checktype(argv[parami], JANET_TUPLE)) {
|
||||
@@ -1079,7 +1108,7 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
|
||||
if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
|
||||
if (selfref) def->name = janet_unwrap_symbol(head);
|
||||
if (hasname) def->name = janet_unwrap_symbol(head); /* Also correctly unwraps keyword */
|
||||
janet_def_addflags(def);
|
||||
defindex = janetc_addfuncdef(c, def);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -24,6 +24,11 @@
|
||||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
JANET_THREAD_LOCAL JanetVM janet_vm;
|
||||
@@ -53,9 +58,14 @@ void janet_vm_load(JanetVM *from) {
|
||||
}
|
||||
|
||||
/* Trigger suspension of the Janet vm by trying to
|
||||
* exit the interpeter loop when convenient. You can optionally
|
||||
* exit the interpreter loop when convenient. You can optionally
|
||||
* use NULL to interrupt the current VM when convenient */
|
||||
void janet_interpreter_interrupt(JanetVM *vm) {
|
||||
vm = vm ? vm : &janet_vm;
|
||||
vm->auto_suspend = 1;
|
||||
janet_atomic_inc(&vm->auto_suspend);
|
||||
}
|
||||
|
||||
void janet_interpreter_interrupt_handled(JanetVM *vm) {
|
||||
vm = vm ? vm : &janet_vm;
|
||||
janet_atomic_dec(&vm->auto_suspend);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -23,11 +23,16 @@
|
||||
#ifndef JANET_STATE_H_defined
|
||||
#define JANET_STATE_H_defined
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include "features.h"
|
||||
#include <janet.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef JANET_EV
|
||||
#ifndef JANET_WINDOWS
|
||||
#ifdef JANET_WINDOWS
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#endif
|
||||
@@ -53,13 +58,22 @@ typedef struct {
|
||||
void *data;
|
||||
} JanetQueue;
|
||||
|
||||
#ifdef JANET_EV
|
||||
typedef struct {
|
||||
JanetTimestamp when;
|
||||
JanetFiber *fiber;
|
||||
JanetFiber *curr_fiber;
|
||||
uint32_t sched_id;
|
||||
int is_error;
|
||||
int has_worker;
|
||||
#ifdef JANET_WINDOWS
|
||||
HANDLE worker;
|
||||
HANDLE worker_event;
|
||||
#else
|
||||
pthread_t worker;
|
||||
#endif
|
||||
} JanetTimeout;
|
||||
#endif
|
||||
|
||||
/* Registry table for C functions - contains metadata that can
|
||||
* be looked up by cfunction pointer. All strings here are pointing to
|
||||
@@ -89,7 +103,7 @@ struct JanetVM {
|
||||
|
||||
/* If this flag is true, suspend on function calls and backwards jumps.
|
||||
* When this occurs, this flag will be reset to 0. */
|
||||
int auto_suspend;
|
||||
volatile JanetAtomicInt auto_suspend;
|
||||
|
||||
/* The current running fiber on the current thread.
|
||||
* Set and unset by functions in vm.c */
|
||||
@@ -100,6 +114,7 @@ struct JanetVM {
|
||||
* return point for panics. */
|
||||
jmp_buf *signal_buf;
|
||||
Janet *return_reg;
|
||||
int coerce_error;
|
||||
|
||||
/* The global registry for c functions. Used to store meta-data
|
||||
* along with otherwise bare c function pointers. */
|
||||
@@ -121,10 +136,12 @@ struct JanetVM {
|
||||
|
||||
/* Garbage collection */
|
||||
void *blocks;
|
||||
void *weak_blocks;
|
||||
size_t gc_interval;
|
||||
size_t next_collection;
|
||||
size_t block_count;
|
||||
int gc_suspend;
|
||||
int gc_mark_phase;
|
||||
|
||||
/* GC roots */
|
||||
Janet *roots;
|
||||
@@ -147,6 +164,11 @@ struct JanetVM {
|
||||
JanetTraversalNode *traversal_top;
|
||||
JanetTraversalNode *traversal_base;
|
||||
|
||||
/* Thread safe strerror error buffer - for janet_strerror */
|
||||
#ifndef JANET_WINDOWS
|
||||
char strerror_buf[256];
|
||||
#endif
|
||||
|
||||
/* Event loop and scheduler globals */
|
||||
#ifdef JANET_EV
|
||||
size_t tq_count;
|
||||
@@ -154,12 +176,10 @@ struct JanetVM {
|
||||
JanetQueue spawn;
|
||||
JanetTimeout *tq;
|
||||
JanetRNG ev_rng;
|
||||
JanetListenerState **listeners;
|
||||
size_t listener_count;
|
||||
size_t listener_cap;
|
||||
size_t extra_listeners;
|
||||
volatile JanetAtomicInt listener_count; /* used in signal handler, must be volatile */
|
||||
JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */
|
||||
JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */
|
||||
JanetTable signal_handlers;
|
||||
#ifdef JANET_WINDOWS
|
||||
void **iocp;
|
||||
#elif defined(JANET_EV_EPOLL)
|
||||
@@ -175,6 +195,9 @@ struct JanetVM {
|
||||
int timer;
|
||||
int timer_enabled;
|
||||
#else
|
||||
JanetStream **streams;
|
||||
size_t stream_count;
|
||||
size_t stream_capacity;
|
||||
pthread_attr_t new_thread_attr;
|
||||
JanetHandle selfpipe[2];
|
||||
struct pollfd *fds;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -71,10 +71,10 @@ int janet_string_compare(const uint8_t *lhs, const uint8_t *rhs) {
|
||||
int janet_string_equalconst(const uint8_t *lhs, const uint8_t *rhs, int32_t rlen, int32_t rhash) {
|
||||
int32_t lhash = janet_string_hash(lhs);
|
||||
int32_t llen = janet_string_length(lhs);
|
||||
if (lhs == rhs)
|
||||
return 1;
|
||||
if (lhash != rhash || llen != rlen)
|
||||
return 0;
|
||||
if (lhs == rhs)
|
||||
return 1;
|
||||
return !memcmp(lhs, rhs, rlen);
|
||||
}
|
||||
|
||||
@@ -175,8 +175,9 @@ JANET_CORE_FN(cfun_string_slice,
|
||||
"Returns a substring from a byte sequence. The substring is from "
|
||||
"index `start` inclusive to index `end`, exclusive. All indexing "
|
||||
"is from 0. `start` and `end` can also be negative to indicate indexing "
|
||||
"from the end of the string. Note that index -1 is synonymous with "
|
||||
"index `(length bytes)` to allow a full negative slice range. ") {
|
||||
"from the end of the string. Note that if `start` is negative it is "
|
||||
"exclusive, and if `end` is negative it is inclusive, to allow a full "
|
||||
"negative slice range.") {
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
return janet_stringv(view.bytes + range.start, range.end - range.start);
|
||||
@@ -548,12 +549,12 @@ JANET_CORE_FN(cfun_string_format,
|
||||
"- `a`, `A`: floating point number, formatted as a hexadecimal number.\n"
|
||||
"- `s`: formatted as a string, precision indicates padding and maximum length.\n"
|
||||
"- `t`: emit the type of the given value.\n"
|
||||
"- `v`: format with (describe x)"
|
||||
"- `V`: format with (string x)"
|
||||
"- `v`: format with (describe x)\n"
|
||||
"- `V`: format with (string x)\n"
|
||||
"- `j`: format to jdn (Janet data notation).\n"
|
||||
"\n"
|
||||
"The following conversion specifiers are used for \"pretty-printing\", where the upper-case "
|
||||
"variants generate colored output. These speficiers can take a precision "
|
||||
"variants generate colored output. These specifiers can take a precision "
|
||||
"argument to specify the maximum nesting depth to print.\n"
|
||||
"- `p`, `P`: pretty format, truncating if necessary\n"
|
||||
"- `m`, `M`: pretty format without truncating.\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -34,9 +34,9 @@
|
||||
* because E is a valid digit in bases 15 or greater. For bases greater than
|
||||
* 10, the letters are used as digits. A through Z correspond to the digits 10
|
||||
* through 35, and the lowercase letters have the same values. The radix number
|
||||
* is always in base 10. For example, a hexidecimal number could be written
|
||||
* is always in base 10. For example, a hexadecimal number could be written
|
||||
* '16rdeadbeef'. janet_scan_number also supports some c style syntax for
|
||||
* hexidecimal literals. The previous number could also be written
|
||||
* hexadecimal literals. The previous number could also be written
|
||||
* '0xdeadbeef'.
|
||||
*/
|
||||
|
||||
@@ -301,6 +301,7 @@ int janet_scan_number_base(
|
||||
if (base == 0) {
|
||||
base = 10;
|
||||
}
|
||||
int exp_base = base;
|
||||
|
||||
/* Skip leading zeros */
|
||||
while (str < end && (*str == '0' || *str == '.')) {
|
||||
@@ -322,6 +323,12 @@ int janet_scan_number_base(
|
||||
} else if (*str == '&') {
|
||||
foundexp = 1;
|
||||
break;
|
||||
} else if (base == 16 && (*str == 'P' || *str == 'p')) { /* IEEE hex float */
|
||||
foundexp = 1;
|
||||
exp_base = 10;
|
||||
base = 2;
|
||||
ex *= 4; /* We need to correct the current exponent after we change the base */
|
||||
break;
|
||||
} else if (base == 10 && (*str == 'E' || *str == 'e')) {
|
||||
foundexp = 1;
|
||||
break;
|
||||
@@ -360,9 +367,9 @@ int janet_scan_number_base(
|
||||
}
|
||||
while (str < end) {
|
||||
int digit = digit_lookup[*str & 0x7F];
|
||||
if (*str > 127 || digit >= base) goto error;
|
||||
if (*str > 127 || digit >= exp_base) goto error;
|
||||
if (ee < (INT32_MAX / 40)) {
|
||||
ee = base * ee + digit;
|
||||
ee = exp_base * ee + digit;
|
||||
}
|
||||
str++;
|
||||
seenadigit = 1;
|
||||
@@ -489,4 +496,53 @@ int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Similar to janet_scan_number but allows for
|
||||
* more numeric types with a given suffix. */
|
||||
int janet_scan_numeric(
|
||||
const uint8_t *str,
|
||||
int32_t len,
|
||||
Janet *out) {
|
||||
int result;
|
||||
double num;
|
||||
int64_t i64 = 0;
|
||||
uint64_t u64 = 0;
|
||||
if (len < 2 || str[len - 2] != ':') {
|
||||
result = janet_scan_number_base(str, len, 0, &num);
|
||||
*out = janet_wrap_number(num);
|
||||
return result;
|
||||
}
|
||||
switch (str[len - 1]) {
|
||||
default:
|
||||
return 1;
|
||||
case 'n':
|
||||
result = janet_scan_number_base(str, len - 2, 0, &num);
|
||||
*out = janet_wrap_number(num);
|
||||
return result;
|
||||
/* Condition is inverted janet_scan_int64 and janet_scan_uint64 */
|
||||
case 's':
|
||||
result = !janet_scan_int64(str, len - 2, &i64);
|
||||
*out = janet_wrap_s64(i64);
|
||||
return result;
|
||||
case 'u':
|
||||
result = !janet_scan_uint64(str, len - 2, &u64);
|
||||
*out = janet_wrap_u64(u64);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void janet_buffer_dtostr(JanetBuffer *buffer, double x) {
|
||||
#define BUFSIZE 32
|
||||
janet_buffer_extra(buffer, BUFSIZE);
|
||||
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%.17g", x);
|
||||
#undef BUFSIZE
|
||||
/* fix locale issues with commas */
|
||||
for (int i = 0; i < count; i++) {
|
||||
char c = buffer->data[buffer->count + i];
|
||||
if (c == ',') {
|
||||
buffer->data[buffer->count + i] = '.';
|
||||
}
|
||||
}
|
||||
buffer->count += count;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -294,6 +294,16 @@ JANET_CORE_FN(cfun_struct_to_table,
|
||||
return janet_wrap_table(tab);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_struct_rawget,
|
||||
"(struct/rawget st key)",
|
||||
"Gets a value from a struct `st` without looking at the prototype struct. "
|
||||
"If `st` does not contain the key directly, the function will return "
|
||||
"nil without checking the prototype. Returns the value in the struct.") {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetStruct st = janet_getstruct(argv, 0);
|
||||
return janet_struct_rawget(st, argv[1]);
|
||||
}
|
||||
|
||||
/* Load the struct module */
|
||||
void janet_lib_struct(JanetTable *env) {
|
||||
JanetRegExt struct_cfuns[] = {
|
||||
@@ -301,6 +311,7 @@ void janet_lib_struct(JanetTable *env) {
|
||||
JANET_CORE_REG("struct/getproto", cfun_struct_getproto),
|
||||
JANET_CORE_REG("struct/proto-flatten", cfun_struct_flatten),
|
||||
JANET_CORE_REG("struct/to-table", cfun_struct_to_table),
|
||||
JANET_CORE_REG("struct/rawget", cfun_struct_rawget),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, struct_cfuns);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -234,6 +234,7 @@ const uint8_t *janet_symbol_gen(void) {
|
||||
head->hash = hash;
|
||||
sym = (uint8_t *)(head->data);
|
||||
memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter));
|
||||
sym[head->length] = 0;
|
||||
janet_symcache_put((const uint8_t *)sym, bucket);
|
||||
return (const uint8_t *)sym;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Calvin Rose
|
||||
* Copyright (c) 2025 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
||||
1915
src/core/sysir.c
Normal file
1915
src/core/sysir.c
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user