mirror of
https://github.com/janet-lang/janet
synced 2025-11-07 19:13:02 +00:00
Compare commits
687 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3928136670 | ||
|
|
0dcae6c3d6 | ||
|
|
b639ccdad1 | ||
|
|
affcb5b459 | ||
|
|
70c80d7899 | ||
|
|
fb7914a3c8 | ||
|
|
6099d2a45d | ||
|
|
044fc7c461 | ||
|
|
7c4670c3de | ||
|
|
c1113d61d6 | ||
|
|
2c4366dd71 | ||
|
|
d66f8333c1 | ||
|
|
1588359ebc | ||
|
|
a861399ecb | ||
|
|
a7f3d3436f | ||
|
|
75f1bb6a7c | ||
|
|
0384b83c31 | ||
|
|
c68361a03f | ||
|
|
0bda455cad | ||
|
|
bb7bef7188 | ||
|
|
b8032ec61d | ||
|
|
8d1e6ddffc | ||
|
|
f7f2f5e84f | ||
|
|
bedd9ccaa1 | ||
|
|
a29e717fd7 | ||
|
|
522545287e | ||
|
|
4b4fe80404 | ||
|
|
cf05ff610f | ||
|
|
300124961f | ||
|
|
7eb78c8028 | ||
|
|
1a7691dade | ||
|
|
3b51501847 | ||
|
|
fc46030e7d | ||
|
|
ff3bb66272 | ||
|
|
1ceaceada4 | ||
|
|
19a0444f41 | ||
|
|
0102a72538 | ||
|
|
9943bdd907 | ||
|
|
264c5bc02b | ||
|
|
9ba8728176 | ||
|
|
8839731951 | ||
|
|
e88a9af2f6 | ||
|
|
a5e50a0f65 | ||
|
|
7c35acca75 | ||
|
|
4bb57550c8 | ||
|
|
446ab037b0 | ||
|
|
4adfb9f2d3 | ||
|
|
9c89d1c658 | ||
|
|
3598f056bb | ||
|
|
779fcf2d54 | ||
|
|
3bbc121c6a | ||
|
|
82edc19137 | ||
|
|
5689ef1af1 | ||
|
|
b4e25e5597 | ||
|
|
647139cdf9 | ||
|
|
6225f8d334 | ||
|
|
95eb54045f | ||
|
|
43520ac67d | ||
|
|
802a2d6b71 | ||
|
|
d9a4ef05ac | ||
|
|
f00a2770ef | ||
|
|
b83fe146fa | ||
|
|
6249f03367 | ||
|
|
bfc00b67bd | ||
|
|
2b7428ed2b | ||
|
|
64a80c57e3 | ||
|
|
efb2ab06cb | ||
|
|
b082c8123e | ||
|
|
cc1ff9125a | ||
|
|
5734e02034 | ||
|
|
6e8beff0a0 | ||
|
|
c21eaa5474 | ||
|
|
13667292c6 | ||
|
|
22eb8372dd | ||
|
|
1b7a9def25 | ||
|
|
d7954e6fe3 | ||
|
|
c20c9cd5d7 | ||
|
|
46531d9a60 | ||
|
|
d9a366fbed | ||
|
|
64bf52372a | ||
|
|
0a9715a94c | ||
|
|
c82aac1365 | ||
|
|
e697cc3811 | ||
|
|
c150f2f2c1 | ||
|
|
0a54e1ed62 | ||
|
|
b9daf41327 | ||
|
|
2d2bc436e6 | ||
|
|
3d76d988c3 | ||
|
|
bea6dbbf3d | ||
|
|
e1bd24c2ab | ||
|
|
1f30ea66e9 | ||
|
|
c43aaf8986 | ||
|
|
2acc81d1c5 | ||
|
|
26513a7a16 | ||
|
|
d005ac6888 | ||
|
|
7fdb098a20 | ||
|
|
a4a200e037 | ||
|
|
15d95d8803 | ||
|
|
46950a8cb3 | ||
|
|
4867cab569 | ||
|
|
c8cf7c2445 | ||
|
|
1b63215aad | ||
|
|
bcbe42ab23 | ||
|
|
c8c6419013 | ||
|
|
e8516c29e0 | ||
|
|
12247bd958 | ||
|
|
9d30d5f6e3 | ||
|
|
ba0956488d | ||
|
|
31f502b508 | ||
|
|
efaaead378 | ||
|
|
4d47d92a4a | ||
|
|
b39ad97a87 | ||
|
|
af23040d9c | ||
|
|
fd2d706e33 | ||
|
|
178d175bcf | ||
|
|
7a7f586094 | ||
|
|
5124587c96 | ||
|
|
6c897b1a37 | ||
|
|
c6ac53f4be | ||
|
|
2d7812a06c | ||
|
|
db55277b58 | ||
|
|
75818217a6 | ||
|
|
486b80fa7b | ||
|
|
873054d055 | ||
|
|
f12f896020 | ||
|
|
09ab391d13 | ||
|
|
7569930b0c | ||
|
|
e7189438dd | ||
|
|
3c304ddc35 | ||
|
|
1696de233c | ||
|
|
ce9cd4fcef | ||
|
|
698e89aba4 | ||
|
|
4c8dd4b96c | ||
|
|
11998b3913 | ||
|
|
840610facf | ||
|
|
0280deccae | ||
|
|
4d5a95784a | ||
|
|
b43d93cf55 | ||
|
|
3f137ed0b1 | ||
|
|
5deb13d73e | ||
|
|
82a1c8635e | ||
|
|
010e2e4652 | ||
|
|
ddedae6831 | ||
|
|
6c63c4f129 | ||
|
|
802686e3df | ||
|
|
3be79e8735 | ||
|
|
a303704a7d | ||
|
|
b5e6c0b8fc | ||
|
|
98c46fcfb1 | ||
|
|
409da697dd | ||
|
|
91c3685705 | ||
|
|
411fc77ecf | ||
|
|
0378ba78cc | ||
|
|
55d8e8b56b | ||
|
|
97ad4c4f89 | ||
|
|
8de999c8f7 | ||
|
|
f444bd25ef | ||
|
|
43c0db4b0e | ||
|
|
8f168c600d | ||
|
|
ec43afb426 | ||
|
|
880049c0ee | ||
|
|
2b7ac16784 | ||
|
|
56d903d75b | ||
|
|
7054e878fb | ||
|
|
dde5351d11 | ||
|
|
7d49e3e6f1 | ||
|
|
30cb01e2f0 | ||
|
|
018e836ef5 | ||
|
|
7b25125431 | ||
|
|
0aa2f68793 | ||
|
|
516e031f67 | ||
|
|
3331f2fa02 | ||
|
|
dd1a199ebd | ||
|
|
f35b5765d6 | ||
|
|
8359044408 | ||
|
|
9f3dde3cc7 | ||
|
|
ad0f7d9b0d | ||
|
|
f647ac5631 | ||
|
|
e4c5eb4c76 | ||
|
|
dc9fc9c3f5 | ||
|
|
3b6a51df24 | ||
|
|
f2313b9959 | ||
|
|
805b3bbb88 | ||
|
|
232ea22dc5 | ||
|
|
3388acd2db | ||
|
|
52ab9fb475 | ||
|
|
c7dc3611bc | ||
|
|
7a313f6038 | ||
|
|
bbcfaf1289 | ||
|
|
bfb0cb331e | ||
|
|
1759252071 | ||
|
|
fff60b053b | ||
|
|
65ac17986a | ||
|
|
ff720f1320 | ||
|
|
5a28d8d1fa | ||
|
|
ea25766374 | ||
|
|
88b8418253 | ||
|
|
4fa1b28cad | ||
|
|
c70d59edee | ||
|
|
5694998382 | ||
|
|
1cfc7b3b0d | ||
|
|
03e3ecb0a1 | ||
|
|
f8935b0692 | ||
|
|
702b50b7a1 | ||
|
|
e7baa2ae3d | ||
|
|
bfb354b469 | ||
|
|
3c0f12ea4d | ||
|
|
25a93ac4a6 | ||
|
|
0bad523913 | ||
|
|
5b36199aea | ||
|
|
a474a640be | ||
|
|
f10028d41a | ||
|
|
eb4684a64d | ||
|
|
73b81e0253 | ||
|
|
027f106a56 | ||
|
|
20e94adb61 | ||
|
|
9100794cea | ||
|
|
4ddf90e301 | ||
|
|
d1eca1cf52 | ||
|
|
7918add47d | ||
|
|
513d551df6 | ||
|
|
ddaa5e34e6 | ||
|
|
208eb7520a | ||
|
|
2d7df6b78e | ||
|
|
7527142549 | ||
|
|
4e6193b67e | ||
|
|
4ded5e10a2 | ||
|
|
1596511175 | ||
|
|
d514eab627 | ||
|
|
5287007cd6 | ||
|
|
e5a56174e2 | ||
|
|
6c68c7a35f | ||
|
|
675c1030fd | ||
|
|
ed65d04b81 | ||
|
|
fa1c5c85b5 | ||
|
|
59c69e6896 | ||
|
|
ee35786c8f | ||
|
|
ec6e2cfd62 | ||
|
|
7d48e7fd1f | ||
|
|
0063e3a69d | ||
|
|
cd6c009c03 | ||
|
|
b15cf193a0 | ||
|
|
429dc70374 | ||
|
|
e50e77e5f9 | ||
|
|
2fdd6aa0f7 | ||
|
|
cc55364b21 | ||
|
|
71526d1d9b | ||
|
|
e239980da7 | ||
|
|
1709bce77e | ||
|
|
d6ba2de888 | ||
|
|
61c0a4bc87 | ||
|
|
8af28d3fa5 | ||
|
|
970923d0e5 | ||
|
|
5d7dc0a57c | ||
|
|
c5090606a4 | ||
|
|
bf2d9ae634 | ||
|
|
871a58e1db | ||
|
|
53c7f2eedd | ||
|
|
bfd3845218 | ||
|
|
22d75d017f | ||
|
|
37e6ea0a23 | ||
|
|
10769f6f2e | ||
|
|
082639319e | ||
|
|
f20ad34c76 | ||
|
|
c045eadefa | ||
|
|
e2337b2ec4 | ||
|
|
90c5d12613 | ||
|
|
6016662807 | ||
|
|
2c9195b507 | ||
|
|
b47c48b59a | ||
|
|
98758b68ab | ||
|
|
7f1b5d4d70 | ||
|
|
25aa7a26c5 | ||
|
|
cb2caecbb3 | ||
|
|
1e299632e4 | ||
|
|
94a2084723 | ||
|
|
22e24fb47b | ||
|
|
93f0d5f626 | ||
|
|
bad040665f | ||
|
|
a07d76b264 | ||
|
|
1db6d0e0bc | ||
|
|
34849ea7b3 | ||
|
|
5a9f7c3a85 | ||
|
|
15c6300608 | ||
|
|
c6a4485623 | ||
|
|
090c6ac975 | ||
|
|
319575c864 | ||
|
|
42a0af3b1b | ||
|
|
9bc899ccf2 | ||
|
|
d29e3a1199 | ||
|
|
41bb6a9833 | ||
|
|
95e54c66b6 | ||
|
|
31e2415bbb | ||
|
|
2a5234b390 | ||
|
|
ad5b0a371e | ||
|
|
ba4dd9b5bb | ||
|
|
d42bdf2443 | ||
|
|
a246877c1e | ||
|
|
98e68a5cb4 | ||
|
|
e12aace02c | ||
|
|
51a9c7104d | ||
|
|
75dc08ff21 | ||
|
|
6fa60820a3 | ||
|
|
609a9621af | ||
|
|
8ba1121161 | ||
|
|
9a080197e7 | ||
|
|
e65375277a | ||
|
|
4a111b38b1 | ||
|
|
a363dce943 | ||
|
|
687a3c91f5 | ||
|
|
951aa0d8cd | ||
|
|
a61b59be87 | ||
|
|
91f3c17a5b | ||
|
|
0382dc976b | ||
|
|
69dcab2b55 | ||
|
|
c4f6f1d256 | ||
|
|
b57e530553 | ||
|
|
021b71ad62 | ||
|
|
0ee2ff1b05 | ||
|
|
adaa014d7c | ||
|
|
dc9dc98e80 | ||
|
|
4a2d4f52b5 | ||
|
|
8d37e544ab | ||
|
|
b07adce2b9 | ||
|
|
624be87c97 | ||
|
|
1b9591b5e3 | ||
|
|
a4cc23971f | ||
|
|
9ed1c35d30 | ||
|
|
6158ec0ce5 | ||
|
|
009bed158b | ||
|
|
402dc2a767 | ||
|
|
b5eb888af6 | ||
|
|
172261b89f | ||
|
|
8cc2c964c1 | ||
|
|
efbb704247 | ||
|
|
7fef5be3af | ||
|
|
1753f8bc18 | ||
|
|
235019ec39 | ||
|
|
7d17159ae4 | ||
|
|
56d7d4ef39 | ||
|
|
77c379faa8 | ||
|
|
3014a59c3e | ||
|
|
d70049dbb1 | ||
|
|
4713219317 | ||
|
|
36f92db61e | ||
|
|
59393fc73b | ||
|
|
3eb44f1f79 | ||
|
|
fb5119bf43 | ||
|
|
febfefa4b2 | ||
|
|
632b920e97 | ||
|
|
c81bf42f6b | ||
|
|
4147c0ce1f | ||
|
|
602e30a421 | ||
|
|
92a5567b4a | ||
|
|
9495be328c | ||
|
|
0eae75a5c2 | ||
|
|
8e0d7f2539 | ||
|
|
9c1c7fb384 | ||
|
|
af48912f11 | ||
|
|
327d2ed849 | ||
|
|
db64a682be | ||
|
|
4d3c655058 | ||
|
|
2becebce92 | ||
|
|
0cc6c6ff33 | ||
|
|
115bc6140b | ||
|
|
b14fcb068b | ||
|
|
2ea28f29b0 | ||
|
|
7cb1c7cef2 | ||
|
|
9d60e8b343 | ||
|
|
340a6c4d8d | ||
|
|
e5a4c6fc2b | ||
|
|
db9ac6dba5 | ||
|
|
d570aae817 | ||
|
|
59e4b15fad | ||
|
|
b3401381fa | ||
|
|
beed839d12 | ||
|
|
f4908ebc41 | ||
|
|
1147482e62 | ||
|
|
4d07176f1c | ||
|
|
8c67bf82f6 | ||
|
|
0823eb7327 | ||
|
|
8cff3dd2c3 | ||
|
|
df550efb6b | ||
|
|
00a47dc0cb | ||
|
|
811b1825cb | ||
|
|
2ca252bc0e | ||
|
|
6054858359 | ||
|
|
1d50fd9485 | ||
|
|
a982f351d7 | ||
|
|
27a274b686 | ||
|
|
cb002e7b84 | ||
|
|
c022a1cf1a | ||
|
|
9d4effc02e | ||
|
|
7c19ed8a48 | ||
|
|
ef5f80ad38 | ||
|
|
dbcbb4466d | ||
|
|
7927078b49 | ||
|
|
b61c9eb991 | ||
|
|
ed72dcf82d | ||
|
|
9480ad24cc | ||
|
|
a9574b692f | ||
|
|
8d9a88e759 | ||
|
|
732de8f88d | ||
|
|
6af5800d21 | ||
|
|
540b326c54 | ||
|
|
660a2b41ae | ||
|
|
d2d502b9ae | ||
|
|
3aae524964 | ||
|
|
07912f5ab2 | ||
|
|
ffc14f6019 | ||
|
|
1e70c97ef0 | ||
|
|
54227ebff1 | ||
|
|
33087fe9de | ||
|
|
6d5ff43de7 | ||
|
|
c715912ea3 | ||
|
|
3b6ff3c09a | ||
|
|
efab484fff | ||
|
|
4ba7fbb8bb | ||
|
|
53cc7ebd29 | ||
|
|
c6f032340a | ||
|
|
0ce5acec89 | ||
|
|
44e31cac5d | ||
|
|
029394db31 | ||
|
|
00020ba8ab | ||
|
|
1f91ee30fe | ||
|
|
0f0c415bcf | ||
|
|
a6f022a73d | ||
|
|
ec02d55145 | ||
|
|
cb1a773ca8 | ||
|
|
0dc1217d69 | ||
|
|
06f38d3380 | ||
|
|
2e1ec3700d | ||
|
|
9e6b1d1b16 | ||
|
|
bdf03b4706 | ||
|
|
4d96ba3ba9 | ||
|
|
f161002390 | ||
|
|
eb576d6caf | ||
|
|
e0d26629e0 | ||
|
|
17783c3c3e | ||
|
|
c64e92a5de | ||
|
|
291c13bafc | ||
|
|
c6672e62ac | ||
|
|
eb9bd38256 | ||
|
|
3ac6b2335a | ||
|
|
c6edf03ae8 | ||
|
|
5020a1bae9 | ||
|
|
86ba69c16b | ||
|
|
5f70024f87 | ||
|
|
9ff819a4a1 | ||
|
|
1244e2e93b | ||
|
|
b61d1a0a0e | ||
|
|
89ef4eb634 | ||
|
|
114a45306d | ||
|
|
fe27df528c | ||
|
|
8ab60e475a | ||
|
|
6321c30cb1 | ||
|
|
8343c9edd1 | ||
|
|
74e1a3273f | ||
|
|
1394dbbd57 | ||
|
|
f6a3853131 | ||
|
|
49465f71f3 | ||
|
|
960cf76eb5 | ||
|
|
1b735564fa | ||
|
|
7ae01d25dd | ||
|
|
cb5263d2d8 | ||
|
|
602092f6d5 | ||
|
|
d3a067a665 | ||
|
|
98a26f5ce3 | ||
|
|
09d9dca5f5 | ||
|
|
8a3f512746 | ||
|
|
19e59705b9 | ||
|
|
367c9da856 | ||
|
|
4bcf6565cd | ||
|
|
0c950d0846 | ||
|
|
7ba925c50a | ||
|
|
cb3b9dd76f | ||
|
|
f4fa55027b | ||
|
|
0fe11adb9c | ||
|
|
b138ee6e8e | ||
|
|
a66f19f636 | ||
|
|
c76f4e89d8 | ||
|
|
85a211b26b | ||
|
|
fe3620529f | ||
|
|
a7551e9b4e | ||
|
|
46c540b93e | ||
|
|
32c209ede9 | ||
|
|
0d293cd3f5 | ||
|
|
f284776490 | ||
|
|
38a7e4faf1 | ||
|
|
c333cbfa55 | ||
|
|
f72aa64f41 | ||
|
|
d85892edc8 | ||
|
|
56383b2ecc | ||
|
|
0d729eaab1 | ||
|
|
17ab654ccb | ||
|
|
872d03ae1d | ||
|
|
ee5fa54134 | ||
|
|
68e00cdb7a | ||
|
|
5bf9e4fc89 | ||
|
|
7350bf5dd9 | ||
|
|
e755f98300 | ||
|
|
8ee2f0a1d6 | ||
|
|
0726de34ff | ||
|
|
00301ad26b | ||
|
|
611543c48b | ||
|
|
4d81fbc238 | ||
|
|
c5012ca4c1 | ||
|
|
e68a889fa9 | ||
|
|
795e7a9de8 | ||
|
|
090a6a8c5c | ||
|
|
2bbf9fdcc5 | ||
|
|
0025f6ac87 | ||
|
|
737b2449f0 | ||
|
|
f7a0133eb1 | ||
|
|
48b179d67e | ||
|
|
d1a075b2a6 | ||
|
|
2bad24371d | ||
|
|
bf8d5da3dc | ||
|
|
4a6fcb5e23 | ||
|
|
5ba969f91d | ||
|
|
26818a5e5c | ||
|
|
b84b0e4828 | ||
|
|
b4934ceddc | ||
|
|
c4114fbcdb | ||
|
|
95f2bbe0a0 | ||
|
|
63137b8107 | ||
|
|
2c1b506213 | ||
|
|
612a245961 | ||
|
|
4b8edef58c | ||
|
|
82cddef5bb | ||
|
|
d0fc29338c | ||
|
|
4eeadd7463 | ||
|
|
f0fcdf6bc5 | ||
|
|
2a333f8359 | ||
|
|
0dd867d508 | ||
|
|
e3f902cb8a | ||
|
|
c651b6f67c | ||
|
|
3a9b50ea4a | ||
|
|
1304f9263b | ||
|
|
90313afd40 | ||
|
|
99f176f37b | ||
|
|
d0ec89c7c1 | ||
|
|
170e785b72 | ||
|
|
e53778d5d8 | ||
|
|
192705113e | ||
|
|
97a42ea17b | ||
|
|
2cd489b9d4 | ||
|
|
ff0d3a0081 | ||
|
|
282c02c475 | ||
|
|
798c88b4c8 | ||
|
|
83f4a11bf3 | ||
|
|
d7626f8c57 | ||
|
|
1efca2ebe7 | ||
|
|
40845b5c1b | ||
|
|
84fb07dd5a | ||
|
|
62cb3f81fe | ||
|
|
16ebb11181 | ||
|
|
115ed9cbb9 | ||
|
|
3ae6f64de5 | ||
|
|
ff3f7487a4 | ||
|
|
f0afb3c311 | ||
|
|
5b1a3b8208 | ||
|
|
b1e0849a2f | ||
|
|
67f26b7d72 | ||
|
|
d5bab72620 | ||
|
|
aa079e3145 | ||
|
|
d64a57297d | ||
|
|
be85196de8 | ||
|
|
eae4e0dede | ||
|
|
92e9e64945 | ||
|
|
63dd6d03f4 | ||
|
|
2a79d2e749 | ||
|
|
6f3bc3d577 | ||
|
|
ef5eed2c21 | ||
|
|
5865692401 | ||
|
|
b626e73d19 | ||
|
|
b535c91ee1 | ||
|
|
7b28032f5c | ||
|
|
0fdd404a71 | ||
|
|
1f98eff33a | ||
|
|
338b31f5a2 | ||
|
|
b60e3e302a | ||
|
|
5b62c8e6db | ||
|
|
cd6a7793e8 | ||
|
|
5afb00859a | ||
|
|
001917f8d9 | ||
|
|
b9c0fc8201 | ||
|
|
d8b0a5ed01 | ||
|
|
5fa96a6f8c | ||
|
|
dd3fc24a1e | ||
|
|
ddba0010b0 | ||
|
|
337a498edb | ||
|
|
5fff36d047 | ||
|
|
a679f60e07 | ||
|
|
58d480539c | ||
|
|
6afaacf2af | ||
|
|
e9c94598e6 | ||
|
|
29ec30c79f | ||
|
|
122312dbf6 | ||
|
|
618f8d6818 | ||
|
|
0d4ab7dee0 | ||
|
|
6b4824c2ab | ||
|
|
8dde89126e | ||
|
|
56927e1b81 | ||
|
|
9e6254bf56 | ||
|
|
fe22a8db39 | ||
|
|
d724c5b959 | ||
|
|
ca9c017ec4 | ||
|
|
65be318306 | ||
|
|
7c4671d98f | ||
|
|
7880d73201 | ||
|
|
00f0f628e8 | ||
|
|
21b7583a7c | ||
|
|
42c6aca526 | ||
|
|
52b8781684 | ||
|
|
5d39570ec9 | ||
|
|
28331ad6ab | ||
|
|
129ec1e3c5 | ||
|
|
bdcd3a3dbf | ||
|
|
6c8f49206d | ||
|
|
b06f7226c4 | ||
|
|
2bcedd5920 | ||
|
|
5c84f0f5d9 | ||
|
|
424073bbb8 | ||
|
|
e9a80d4e4a | ||
|
|
1ec7f04642 | ||
|
|
59f6c335ad | ||
|
|
6b95326d7c | ||
|
|
5a3190d471 | ||
|
|
e7a8958c63 | ||
|
|
017ee2b0d1 | ||
|
|
a7933f5f08 | ||
|
|
be7fc79b6f | ||
|
|
6c8da9fe5c | ||
|
|
17283241ab | ||
|
|
2c94aa1a6a | ||
|
|
e5d2752329 | ||
|
|
70b4c8ae84 | ||
|
|
876a68f620 | ||
|
|
6c91e5fae0 | ||
|
|
55c091e898 | ||
|
|
3f5ba64f30 | ||
|
|
3bd7787efa | ||
|
|
39198de60a | ||
|
|
9723ddb96b | ||
|
|
02673dd791 | ||
|
|
ac9935c95f | ||
|
|
cc5b4eac0a | ||
|
|
77ea11c603 | ||
|
|
003472354d | ||
|
|
131ee29190 | ||
|
|
05a957c524 | ||
|
|
99e14a9b70 | ||
|
|
03dbd79165 | ||
|
|
f7a25ecae3 | ||
|
|
22e49bc0fc | ||
|
|
696866ae51 | ||
|
|
8333c22e8a | ||
|
|
e286e82144 | ||
|
|
8a5ede21f7 | ||
|
|
a412eecd36 | ||
|
|
24b9ae7820 | ||
|
|
7484a396ac | ||
|
|
79184ab05d | ||
|
|
fb6dd2c83f | ||
|
|
b2146a4c1d | ||
|
|
df13a8b967 | ||
|
|
dfb771700a | ||
|
|
56e5c19aa9 | ||
|
|
e8c0dcd14e | ||
|
|
f444bdd052 | ||
|
|
090068c784 | ||
|
|
85a190b971 | ||
|
|
3437880c78 | ||
|
|
4b01409d2d | ||
|
|
f0e125b304 | ||
|
|
e4503df8b6 | ||
|
|
db0313b379 | ||
|
|
cd16888beb | ||
|
|
49dd75b0e5 | ||
|
|
2abd97d095 | ||
|
|
34a69d0318 | ||
|
|
a77ce76928 | ||
|
|
c971d8ab6e | ||
|
|
0a15539d7b | ||
|
|
21d4b8fe1f | ||
|
|
6f64b0c152 |
@@ -1,11 +1,11 @@
|
||||
image: freebsd
|
||||
image: freebsd/latest
|
||||
packages:
|
||||
- gmake
|
||||
- gcc
|
||||
sources:
|
||||
- https://github.com/bakpakin/janet.git
|
||||
tasks:
|
||||
- build: |
|
||||
cd janet
|
||||
gmake CC=gcc
|
||||
gmake test CC=gcc
|
||||
gmake
|
||||
gmake test
|
||||
sudo gmake install
|
||||
gmake test-install
|
||||
gmake test-amalg
|
||||
|
||||
11
.builds/.openbsd.yaml
Normal file
11
.builds/.openbsd.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
image: openbsd/6.5
|
||||
packages:
|
||||
- gmake
|
||||
tasks:
|
||||
- build: |
|
||||
cd janet
|
||||
gmake
|
||||
gmake test
|
||||
doas gmake install
|
||||
gmake test-install
|
||||
gmake test-amalg
|
||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Use an approximate language for syntax highlighting (clojure is pretty close)
|
||||
*.janet linguist-language=clojure
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -4,12 +4,24 @@ dst
|
||||
janet
|
||||
!*/**/janet
|
||||
/build
|
||||
/builddir
|
||||
/Build
|
||||
/Release
|
||||
/Debug
|
||||
/Emscripten
|
||||
/src/include/generated/*.h
|
||||
janet-*.tar.gz
|
||||
dist
|
||||
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
# Eclipse
|
||||
.project
|
||||
.cproject
|
||||
|
||||
# Local directory for testing
|
||||
local
|
||||
|
||||
# Emscripten
|
||||
*.bc
|
||||
@@ -38,6 +50,7 @@ tags
|
||||
|
||||
# Valgrind files
|
||||
vgcore.*
|
||||
*.out.*
|
||||
|
||||
# Created by https://www.gitignore.io/api/c
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ language: c
|
||||
script:
|
||||
- make
|
||||
- make test
|
||||
- make janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME}.tar.gz
|
||||
- sudo make install
|
||||
- make test-install
|
||||
- make test-amalg
|
||||
- make build/janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME}.tar.gz
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
@@ -14,10 +17,10 @@ deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: JSqAOTH1jmfVlbOuPO3BbY1BhPq+ddiBNPCxuAyKHoVwfO4eNAmq9COI+UwCMWY3dg+YlspufRwkHj//B7QQ6hPbSsKu+Mapu6gr/CAE/jxbfO/E98LkIkUwbGjplwtzw2kiBkHN/Bu6J5X76cwo4D8nwQ1JIcV3nWtoG87t7H4W0R4AYQkbLGAPylgUFr11YMPx2cRBBqCdLAGIrny7kQ/0cRBfkN81R/gUJv/q3OjmUvY7sALXp7mFdZb75QPSilKIDuVUU5hLvPYTeRl6cWI/M+m5SmGZx1rjv5S9Qaw070XoNyt9JAADtbOUnADKvDguDZIP1FCuT1Gb+cnJPzrvk6+OBU9s8UjCTFtgV+LKlhmRZcwV5YQBE94PKRMJNC6VvIWM7UeQ8Zhm1jmQS6ONNWbuoUAlkZP57NtDQa2x0GT2wkubNSQKlaY+6/gwTD9KAJIzaZG7HYXH7b+4g7VbccCyhDAtDZtXgrOIS4WAkNc8rWezRO4H0qHMyON9aCEb0eTE8hWIufbx6ymG4gUxnYO+AkrEYMCwQvU6lS8BsevkaMTVtSShqlQtJ9FRlmJA3MA2ONyqzQXJENqRydyVbpFrKSv+0HbMyhEc5BoKbt0QcTh/slouNV4eASNar/GKN7aP8XKGUeMwIoCcRpP+3ehmwX9SUw7Ah5S42pA=
|
||||
file: janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME}.tar.gz
|
||||
file: build/janet-${TRAVIS_TAG}-${TRAVIS_OS_NAME}.tar.gz
|
||||
draft: true
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
repo: bakpakin/janet
|
||||
repo: janet-lang/janet
|
||||
condition: "$CC = clang"
|
||||
|
||||
129
CHANGELOG.md
Normal file
129
CHANGELOG.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 1.1.0 - 2019-07-08
|
||||
- Change semantics of `-l` flag to be import rather than dofile.
|
||||
- Fix compiler regression in top level defs with destructuring.
|
||||
- Add `table/clone`.
|
||||
- Improve `jpm` tool with git and dependency capabilities, as well as better
|
||||
module uninstalls.
|
||||
|
||||
## 1.0.0 - 2019-07-01
|
||||
- Add `with` macro for resource handling.
|
||||
- Add `propagate` function so we can "rethrow" signals after they are
|
||||
intercepted. This makes signals even more flexible.
|
||||
- Add `JANET_NO_DOCSTRINGS` and `JANET_NO_SOURCEMAPS` defines in janetconf.h
|
||||
for shrinking binary size.
|
||||
This seems to save about 50kB in most builds, so it's not usually worth it.
|
||||
- Update module system to allow relative imports. The `:cur:` pattern
|
||||
in `module/expand-path` will expand to the directory part of the current file, or
|
||||
whatever the value of `(dyn :current-file)` is. The `:dir:` pattern gets
|
||||
the directory part of the input path name.
|
||||
- Remove `:native:` pattern in `module/paths`.
|
||||
- Add `module/expand-path`
|
||||
- Remove `module/*syspath*` and `module/*headerpath*` in favor of dynamic
|
||||
bindings `:syspath` and `:headerpath`.
|
||||
- Compiled PEGs can now be marshaled and unmarshaled.
|
||||
- Change signature to `parser/state`
|
||||
- Add `:until` verb to loop.
|
||||
- Add `:p` flag to `fiber/new`.
|
||||
- Add `file/{fdopen,fileno}` functions.
|
||||
- Add `parser/clone` function.
|
||||
- Add optional argument to `parser/where` to set parser byte index.
|
||||
- Add optional `env` argument to `all-bindings` and `all-dynamics`.
|
||||
- Add scratch memory C API functions for auto-released memory on next gc.
|
||||
Scratch memory differs from normal GCed memory as it can also be freed normally
|
||||
for better performance.
|
||||
- Add API compatibility checking for modules. This will let native modules not load
|
||||
when the host program is not of a compatible version or configuration.
|
||||
- Change signature of `os/execute` to be much more flexible.
|
||||
|
||||
## 0.6.0 - 2019-05-29
|
||||
- `file/close` returns exit code when closing file opened with `file/popen`.
|
||||
- Add `os/rename`
|
||||
- Update windows installer to include tools like `jpm`.
|
||||
- Add `jpm` tool for building and managing projects.
|
||||
- Change interface to `cook` tool.
|
||||
- Add optional filters to `module/paths` to further refine import methods.
|
||||
- Add keyword arguments via `&keys` in parameter list.
|
||||
- Add `-k` flag for flychecking source.
|
||||
- Change signature to `compile` function.
|
||||
- Add `module/loaders` for custom loading functions.
|
||||
- Add external unification to `match` macro.
|
||||
- Add static library to main build.
|
||||
- Add `janet/*headerpath*` and change location of installed headers.
|
||||
- Let `partition` take strings.
|
||||
- Haiku OS support
|
||||
- Add `string/trim`, `string/trimr`, and `string/triml`.
|
||||
- Add `dofile` function.
|
||||
- Numbers require at least 1 significant digit.
|
||||
- `file/read` will return nil on end of file.
|
||||
- Fix various bugs.
|
||||
|
||||
## 0.5.0 - 2019-05-09
|
||||
- Fix some bugs with buffers.
|
||||
- Add `trace` and `untrace` to the core library.
|
||||
- Add `string/has-prefix?` and `string/has-suffix?` to string module.
|
||||
- Add simple debugger to repl that activates on errors or debug signal
|
||||
- Remove `*env*` and `*doc-width*`.
|
||||
- Add `fiber/getenv`, `fiber/setenv`, and `dyn`, and `setdyn`.
|
||||
- Add support for dynamic bindings (via the `dyn` and `setdyn` functions).
|
||||
- Change signatures of some functions like `eval` which no longer takes
|
||||
an optional environment.
|
||||
- Add printf function
|
||||
- Make `pp` configurable with dynamic binding `:pretty-format`.
|
||||
- Remove the `meta` function.
|
||||
- Add `with-dyns` for blocks with dynamic bindings assigned.
|
||||
- Allow leading and trailing newlines in backtick-delimited string (long strings).
|
||||
These newlines will not be included in the actual string value.
|
||||
|
||||
## 0.4.1 - 2019-04-14
|
||||
- Squash some bugs
|
||||
- Peg patterns can now make captures in any position in a grammar.
|
||||
- Add color to repl output
|
||||
- Add array/remove function
|
||||
- Add meson build support
|
||||
- Add int module for int types
|
||||
- Add meson build option
|
||||
- Add (break) special form and improve loop macro
|
||||
- Allow abstract types to specify custom tostring method
|
||||
- Extend C API for marshalling abstract types and other values
|
||||
- Add functions to `os` module.
|
||||
|
||||
## 0.4.0 - 2019-03-08
|
||||
- Fix a number of smaller bugs
|
||||
- Added :export option to import and require
|
||||
- Added typed arrays
|
||||
- Remove `callable?`.
|
||||
- Remove `tuple/append` and `tuple/prepend`, which may have seemed like `O(1)`
|
||||
operations. Instead, use the `splice` special to extend tuples.
|
||||
- Add `-m` flag to main client to allow specifying where to load
|
||||
system modules from.
|
||||
- Add `-c` flag to main client to allow compiling Janet modules to images.
|
||||
- Add `string/format` and `buffer/format`.
|
||||
- Remove `string/pretty` and `string/number`.
|
||||
- `make-image` function creates pre compiled images for janet. These images
|
||||
link to the core library. They can be loaded via require or manually via
|
||||
`load-image`.
|
||||
- Add bracketed tuples as tuple constructor.
|
||||
- Add partition function to core library.
|
||||
- Pre-compile core library into an image for faster startup.
|
||||
- Add methods to parser values that mirror the api.
|
||||
- Add janet\_getmethod to CAPI for easier use of method like syntax.
|
||||
- Add get/set to abstract types to allow them to behave more
|
||||
like objects with methods.
|
||||
- Add parser/insert to modify parser state programmatically
|
||||
- Add debug/stacktrace for easy, pretty stacktraces
|
||||
- Remove the status-pp function
|
||||
- Update API to run-context to be much more sane
|
||||
- Add :lflags option to cook/make-native
|
||||
- Disallow NaNs as table or struct keys
|
||||
- Update module resolution paths and format
|
||||
|
||||
## 0.3.0 - 2019-01-26
|
||||
- Add amalgamated build to janet for easier embedding.
|
||||
- Add os/date function
|
||||
- Add slurp and spit to core library.
|
||||
- Added this changelog.
|
||||
- Added peg module (Parsing Expression Grammars)
|
||||
- Move hand written documentation into website repository.
|
||||
@@ -29,10 +29,42 @@ may require changes before being merged.
|
||||
run tests with `make test`. If you want to add a new test suite, simply add a file to
|
||||
the test folder and make sure it is run when`make test` is invoked.
|
||||
* Be consistent with the style. For C this means follow the indentation and style in
|
||||
other files (files have MIT license at top, 4 spaces indentation, no trailing whitespace, cuddled brackets, etc.)
|
||||
other files (files have MIT license at top, 4 spaces indentation, no trailing
|
||||
whitespace, cuddled brackets, etc.) Use `make format` to
|
||||
automatically format your C code with
|
||||
[astyle](http://astyle.sourceforge.net/astyle.html). You will probably need
|
||||
to install this, but it can be installed with most package managers.
|
||||
|
||||
For janet code, the use lisp indentation with 2 spaces. One can use janet.vim to
|
||||
do this indentation, or approximate as close as possible.
|
||||
|
||||
## C style
|
||||
|
||||
For changes to the VM and Core code, you will probably need to know C. Janet is programmed with
|
||||
a subset of C99 that works with Microsoft Visual C++. This means most of C99 but with the following
|
||||
omissions.
|
||||
|
||||
* No `restrict`
|
||||
* Certain functions in the standard library are not always available
|
||||
|
||||
In practice, this means programming for both MSVC on one hand and everything else on the other.
|
||||
The code must also build with emscripten, even if some features are not available, although
|
||||
this is not a priority.
|
||||
|
||||
Code should compile warning free and run valgrind clean. I find that these two criteria are some
|
||||
of the easiest ways to protect against a large number of bugs in an unsafe language like C. To check for
|
||||
valgrind errors, run `make valtest` and check the output for undefined or flagged behavior.
|
||||
|
||||
### Formatting
|
||||
|
||||
Use [astyle](http://astyle.sourceforge.net/astyle.html) via `make format` to
|
||||
ensure a consistent code style for C.
|
||||
|
||||
## Janet style
|
||||
|
||||
All janet code in the project should be formatted similar to the code in core.janet.
|
||||
The auto formatting from janet.vim will work well.
|
||||
|
||||
## Suggesting Changes
|
||||
|
||||
To suggest changes, open an issue on GitHub. Check GitHub for other issues
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018 Calvin Rose
|
||||
Copyright (c) 2019 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
|
||||
|
||||
344
Makefile
344
Makefile
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2018 Calvin Rose
|
||||
# Copyright (c) 2019 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,174 +24,322 @@
|
||||
|
||||
PREFIX?=/usr/local
|
||||
|
||||
INCLUDEDIR=$(PREFIX)/include/janet
|
||||
LIBDIR=$(PREFIX)/lib
|
||||
INCLUDEDIR=$(PREFIX)/include
|
||||
BINDIR=$(PREFIX)/bin
|
||||
LIBDIR=$(PREFIX)/lib
|
||||
JANET_BUILD?="\"$(shell git log --pretty=format:'%h' -n 1)\""
|
||||
|
||||
CFLAGS=-std=c99 -Wall -Wextra -Isrc/include -fpic -O2 -fvisibility=hidden \
|
||||
-DJANET_BUILD=$(JANET_BUILD)
|
||||
CLIBS=-lm -ldl
|
||||
JANET_TARGET=janet
|
||||
JANET_LIBRARY=libjanet.so
|
||||
JANET_PATH?=/usr/local/lib/janet
|
||||
CLIBS=-lm
|
||||
JANET_TARGET=build/janet
|
||||
JANET_LIBRARY=build/libjanet.so
|
||||
JANET_STATIC_LIBRARY=build/libjanet.a
|
||||
JANET_PATH?=$(PREFIX)/lib/janet
|
||||
MANPATH?=$(PREFIX)/share/man/man1/
|
||||
PKG_CONFIG_PATH?=$(PREFIX)/lib/pkgconfig
|
||||
DEBUGGER=gdb
|
||||
|
||||
CFLAGS=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -fpic -O2 -fvisibility=hidden \
|
||||
-DJANET_BUILD=$(JANET_BUILD)
|
||||
LDFLAGS=-rdynamic
|
||||
|
||||
# Check OS
|
||||
UNAME:=$(shell uname -s)
|
||||
LDCONFIG:=ldconfig
|
||||
ifeq ($(UNAME), Darwin)
|
||||
# Add other macos/clang flags
|
||||
LDCONFIG:=
|
||||
else
|
||||
CFLAGS:=$(CFLAGS) -rdynamic
|
||||
CLIBS:=$(CLIBS) -lrt
|
||||
CLIBS:=$(CLIBS) -ldl
|
||||
else ifeq ($(UNAME), Linux)
|
||||
CLIBS:=$(CLIBS) -lrt -ldl
|
||||
endif
|
||||
# For other unix likes, add flags here!
|
||||
ifeq ($(UNAME),Haiku)
|
||||
LDFLAGS=-Wl,--export-dynamic
|
||||
endif
|
||||
|
||||
# Source headers
|
||||
JANET_HEADERS=$(sort $(wildcard src/include/janet/*.h))
|
||||
JANET_LOCAL_HEADERS=$(sort $(wildcard src/*/*.h))
|
||||
$(shell mkdir -p build/core build/mainclient build/webclient build/boot)
|
||||
all: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY)
|
||||
|
||||
# Source files
|
||||
JANET_CORE_SOURCES=$(sort $(wildcard src/core/*.c)) src/core/core.gen.c
|
||||
JANET_MAINCLIENT_SOURCES=$(sort $(wildcard src/mainclient/*.c)) src/mainclient/init.gen.c
|
||||
JANET_WEBCLIENT_SOURCES=$(sort $(wildcard src/webclient/*.c)) src/webclient/webinit.gen.c
|
||||
######################
|
||||
##### Name Files #####
|
||||
######################
|
||||
|
||||
all: $(JANET_TARGET) $(JANET_LIBRARY)
|
||||
JANET_HEADERS=src/include/janet.h src/conf/janetconf.h
|
||||
|
||||
JANET_LOCAL_HEADERS=src/core/util.h \
|
||||
src/core/state.h \
|
||||
src/core/gc.h \
|
||||
src/core/vector.h \
|
||||
src/core/fiber.h \
|
||||
src/core/regalloc.h \
|
||||
src/core/compile.h \
|
||||
src/core/emit.h \
|
||||
src/core/symcache.h
|
||||
|
||||
JANET_CORE_SOURCES=src/core/abstract.c \
|
||||
src/core/array.c \
|
||||
src/core/asm.c \
|
||||
src/core/buffer.c \
|
||||
src/core/bytecode.c \
|
||||
src/core/capi.c \
|
||||
src/core/cfuns.c \
|
||||
src/core/compile.c \
|
||||
src/core/corelib.c \
|
||||
src/core/debug.c \
|
||||
src/core/emit.c \
|
||||
src/core/fiber.c \
|
||||
src/core/gc.c \
|
||||
src/core/inttypes.c \
|
||||
src/core/io.c \
|
||||
src/core/marsh.c \
|
||||
src/core/math.c \
|
||||
src/core/os.c \
|
||||
src/core/parse.c \
|
||||
src/core/peg.c \
|
||||
src/core/pp.c \
|
||||
src/core/regalloc.c \
|
||||
src/core/run.c \
|
||||
src/core/specials.c \
|
||||
src/core/string.c \
|
||||
src/core/strtod.c \
|
||||
src/core/struct.c \
|
||||
src/core/symcache.c \
|
||||
src/core/table.c \
|
||||
src/core/tuple.c \
|
||||
src/core/typedarray.c \
|
||||
src/core/util.c \
|
||||
src/core/value.c \
|
||||
src/core/vector.c \
|
||||
src/core/vm.c \
|
||||
src/core/wrap.c
|
||||
|
||||
JANET_BOOT_SOURCES=src/boot/array_test.c \
|
||||
src/boot/boot.c \
|
||||
src/boot/buffer_test.c \
|
||||
src/boot/number_test.c \
|
||||
src/boot/system_test.c \
|
||||
src/boot/table_test.c
|
||||
|
||||
JANET_MAINCLIENT_SOURCES=src/mainclient/line.c src/mainclient/main.c
|
||||
|
||||
JANET_WEBCLIENT_SOURCES=src/webclient/main.c
|
||||
|
||||
##################################################################
|
||||
##### The bootstrap interpreter that compiles the core image #####
|
||||
##################################################################
|
||||
|
||||
JANET_BOOT_OBJECTS=$(patsubst src/%.c,build/%.boot.o,$(JANET_CORE_SOURCES) $(JANET_BOOT_SOURCES)) \
|
||||
build/boot.gen.o
|
||||
|
||||
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(CC) $(CFLAGS) -DJANET_BOOTSTRAP -o $@ -c $<
|
||||
|
||||
build/janet_boot: $(JANET_BOOT_OBJECTS)
|
||||
$(CC) $(CFLAGS) -DJANET_BOOTSTRAP -o $@ $^ $(CLIBS)
|
||||
|
||||
# Now the reason we bootstrap in the first place
|
||||
build/core_image.c: build/janet_boot
|
||||
build/janet_boot $@ JANET_PATH $(JANET_PATH) JANET_HEADERPATH $(INCLUDEDIR)/janet
|
||||
|
||||
##########################################################
|
||||
##### The main interpreter program and shared object #####
|
||||
##########################################################
|
||||
|
||||
JANET_ALL_SOURCES=$(JANET_CORE_SOURCES) \
|
||||
$(JANET_MAINCLIENT_SOURCES)
|
||||
JANET_CORE_OBJECTS=$(patsubst src/%.c,build/%.o,$(JANET_CORE_SOURCES)) build/core_image.o
|
||||
JANET_MAINCLIENT_OBJECTS=$(patsubst src/%.c,build/%.o,$(JANET_MAINCLIENT_SOURCES)) build/init.gen.o
|
||||
|
||||
JANET_CORE_OBJECTS=$(patsubst %.c,%.o,$(JANET_CORE_SOURCES))
|
||||
JANET_ALL_OBJECTS=$(patsubst %.c,%.o,$(JANET_ALL_SOURCES))
|
||||
|
||||
%.o: %.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
# Compile the core image generated by the bootstrap build
|
||||
build/core_image.o: build/core_image.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
$(JANET_TARGET): $(JANET_ALL_OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(CLIBS)
|
||||
build/%.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
$(JANET_TARGET): $(JANET_CORE_OBJECTS) $(JANET_MAINCLIENT_OBJECTS)
|
||||
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ $(CLIBS)
|
||||
|
||||
$(JANET_LIBRARY): $(JANET_CORE_OBJECTS)
|
||||
$(CC) $(CFLAGS) -shared -o $@ $^ $(CLIBS)
|
||||
$(CC) $(LDFLAGS) $(CFLAGS) -shared -o $@ $^ $(CLIBS)
|
||||
|
||||
$(JANET_STATIC_LIBRARY): $(JANET_CORE_OBJECTS)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
######################
|
||||
##### Emscripten #####
|
||||
######################
|
||||
|
||||
EMCC=emcc
|
||||
EMCCFLAGS=-std=c99 -Wall -Wextra -Isrc/include -O2 \
|
||||
EMCFLAGS=-std=c99 -Wall -Wextra -Isrc/include -Isrc/conf -O2 \
|
||||
-s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap"]' \
|
||||
-s ALLOW_MEMORY_GROWTH=1 \
|
||||
-s AGGRESSIVE_VARIABLE_ELIMINATION=1 \
|
||||
-DJANET_BUILD=$(JANET_BUILD)
|
||||
JANET_EMTARGET=janet.js
|
||||
JANET_EMTARGET=build/janet.js
|
||||
JANET_WEB_SOURCES=$(JANET_CORE_SOURCES) $(JANET_WEBCLIENT_SOURCES)
|
||||
JANET_EMOBJECTS=$(patsubst %.c,%.bc,$(JANET_WEB_SOURCES))
|
||||
JANET_EMOBJECTS=$(patsubst src/%.c,build/%.bc,$(JANET_WEB_SOURCES)) \
|
||||
build/webinit.gen.bc build/core_image.bc
|
||||
|
||||
%.bc: %.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(EMCC) $(EMCCFLAGS) -o $@ -c $<
|
||||
%.gen.bc: %.gen.c
|
||||
$(EMCC) $(EMCFLAGS) -o $@ -c $<
|
||||
|
||||
build/core_image.bc: build/core_image.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(EMCC) $(EMCFLAGS) -o $@ -c $<
|
||||
|
||||
build/%.bc: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
|
||||
$(EMCC) $(EMCFLAGS) -o $@ -c $<
|
||||
|
||||
$(JANET_EMTARGET): $(JANET_EMOBJECTS)
|
||||
$(EMCC) $(EMCCFLAGS) -shared -o $@ $^
|
||||
$(EMCC) $(EMCFLAGS) -shared -o $@ $^
|
||||
|
||||
emscripten: $(JANET_EMTARGET)
|
||||
|
||||
#############################
|
||||
##### Generated C files #####
|
||||
#############################
|
||||
|
||||
xxd: src/tools/xxd.c
|
||||
%.gen.o: %.gen.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
build/xxd: tools/xxd.c
|
||||
$(CC) $< -o $@
|
||||
|
||||
%.gen.c: %.janet xxd
|
||||
./xxd $< $@ janet_gen_$(*F)
|
||||
build/init.gen.c: src/mainclient/init.janet build/xxd
|
||||
build/xxd $< $@ janet_gen_init
|
||||
build/webinit.gen.c: src/webclient/webinit.janet build/xxd
|
||||
build/xxd $< $@ janet_gen_webinit
|
||||
build/boot.gen.c: src/boot/boot.janet build/xxd
|
||||
build/xxd $< $@ janet_gen_boot
|
||||
|
||||
########################
|
||||
##### Amalgamation #####
|
||||
########################
|
||||
|
||||
amalg: build/janet.c build/janet.h build/core_image.c
|
||||
|
||||
AMALG_SOURCE=$(JANET_LOCAL_HEADERS) $(JANET_CORE_SOURCES) build/core_image.c
|
||||
build/janet.c: $(AMALG_SOURCE) tools/amalg.janet $(JANET_TARGET)
|
||||
$(JANET_TARGET) tools/amalg.janet $(AMALG_SOURCE) > $@
|
||||
|
||||
build/janet.h: src/include/janet.h
|
||||
cp $< $@
|
||||
|
||||
###################
|
||||
##### Testing #####
|
||||
###################
|
||||
|
||||
TEST_SOURCES=$(wildcard ctest/*.c)
|
||||
TEST_OBJECTS=$(patsubst %.c,%.o,$(TEST_SOURCES))
|
||||
TEST_PROGRAMS=$(patsubst %.c,%.out,$(TEST_SOURCES))
|
||||
TEST_SCRIPTS=$(wildcard test/suite*.janet)
|
||||
|
||||
ctest/%.o: ctest/%.c $(JANET_HEADERS)
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
ctest/%.out: ctest/%.o $(JANET_CORE_OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(CLIBS)
|
||||
|
||||
repl: $(JANET_TARGET)
|
||||
./$(JANET_TARGET)
|
||||
|
||||
debug: $(JANET_TARGET)
|
||||
$(DEBUGGER) ./$(JANET_TARGET)
|
||||
|
||||
VALGRIND_COMMAND=valgrind --leak-check=full
|
||||
|
||||
valgrind: $(JANET_TARGET)
|
||||
valgrind --leak-check=full -v ./$(JANET_TARGET)
|
||||
$(VALGRIND_COMMAND) ./$(JANET_TARGET)
|
||||
|
||||
test: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
for f in ctest/*.out; do "$$f" || exit; done
|
||||
for f in test/*.janet; do ./$(JANET_TARGET) "$$f" || exit; done
|
||||
|
||||
VALGRIND_COMMAND=valgrind --leak-check=full -v
|
||||
for f in test/suite*.janet; do ./$(JANET_TARGET) "$$f" || exit; done
|
||||
|
||||
valtest: $(JANET_TARGET) $(TEST_PROGRAMS)
|
||||
for f in ctest/*.out; do $(VALGRIND_COMMAND) "$$f" || exit; done
|
||||
for f in test/*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
for f in test/suite*.janet; do $(VALGRIND_COMMAND) ./$(JANET_TARGET) "$$f" || exit; done
|
||||
|
||||
###################
|
||||
##### Natives #####
|
||||
###################
|
||||
callgrind: $(JANET_TARGET)
|
||||
for f in test/suite*.janet; do valgrind --tool=callgrind ./$(JANET_TARGET) "$$f" || exit; done
|
||||
|
||||
natives: $(JANET_TARGET)
|
||||
$(MAKE) -C natives/json
|
||||
$(MAKE) -j 8 -C natives/sqlite3
|
||||
########################
|
||||
##### Distribution #####
|
||||
########################
|
||||
|
||||
clean-natives:
|
||||
$(MAKE) -C natives/json clean
|
||||
$(MAKE) -C natives/sqlite3 clean
|
||||
dist: build/janet-dist.tar.gz
|
||||
|
||||
build/janet-%.tar.gz: $(JANET_TARGET) \
|
||||
src/include/janet.h src/conf/janetconf.h \
|
||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) \
|
||||
build/doc.html README.md build/janet.c
|
||||
tar -czvf $@ $^
|
||||
|
||||
#########################
|
||||
##### Documentation #####
|
||||
#########################
|
||||
|
||||
docs: build/doc.html
|
||||
|
||||
build/doc.html: $(JANET_TARGET) tools/gendoc.janet
|
||||
$(JANET_TARGET) tools/gendoc.janet > build/doc.html
|
||||
|
||||
########################
|
||||
##### Installation #####
|
||||
########################
|
||||
|
||||
SONAME=libjanet.so.1
|
||||
|
||||
.PHONY: $(PKG_CONFIG_PATH)/janet.pc
|
||||
$(PKG_CONFIG_PATH)/janet.pc: $(JANET_TARGET)
|
||||
mkdir -p $(PKG_CONFIG_PATH)
|
||||
echo 'prefix=$(PREFIX)' > $@
|
||||
echo 'exec_prefix=$${prefix}' >> $@
|
||||
echo 'includedir=$(INCLUDEDIR)/janet' >> $@
|
||||
echo 'libdir=$(LIBDIR)' >> $@
|
||||
echo "" >> $@
|
||||
echo "Name: janet" >> $@
|
||||
echo "Url: https://janet-lang.org" >> $@
|
||||
echo "Description: Library for the Janet programming language." >> $@
|
||||
$(JANET_TARGET) -e '(print "Version: " janet/version)' >> $@
|
||||
echo 'Cflags: -I$${includedir}' >> $@
|
||||
echo 'Libs: -L$${libdir} -ljanet $(LDFLAGS)' >> $@
|
||||
echo 'Libs.private: $(CLIBS)' >> $@
|
||||
|
||||
install: $(JANET_TARGET) $(PKG_CONFIG_PATH)/janet.pc
|
||||
mkdir -p $(BINDIR)
|
||||
cp $(JANET_TARGET) $(BINDIR)/janet
|
||||
mkdir -p $(INCLUDEDIR)/janet
|
||||
cp -rf $(JANET_HEADERS) $(INCLUDEDIR)/janet
|
||||
mkdir -p $(JANET_PATH)
|
||||
mkdir -p $(LIBDIR)
|
||||
cp $(JANET_LIBRARY) $(LIBDIR)/libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)')
|
||||
cp $(JANET_STATIC_LIBRARY) $(LIBDIR)/libjanet.a
|
||||
ln -sf $(SONAME) $(LIBDIR)/libjanet.so
|
||||
ln -sf libjanet.so.$(shell $(JANET_TARGET) -e '(print janet/version)') $(LIBDIR)/$(SONAME)
|
||||
cp -rf auxlib/* $(JANET_PATH)
|
||||
cp -rf auxbin/* $(BINDIR)
|
||||
mkdir -p $(MANPATH)
|
||||
cp janet.1 $(MANPATH)
|
||||
-ldconfig $(LIBDIR)
|
||||
|
||||
uninstall:
|
||||
-rm $(BINDIR)/janet
|
||||
-rm $(BINDIR)/jpm
|
||||
-rm -rf $(INCLUDEDIR)/janet
|
||||
-rm -rf $(LIBDIR)/libjanet.*
|
||||
-rm $(PKG_CONFIG_PATH)/janet.pc
|
||||
-rm $(MANPATH)/janet.1
|
||||
# -rm -rf $(JANET_PATH)/* - err on the side of correctness here
|
||||
|
||||
#################
|
||||
##### Other #####
|
||||
#################
|
||||
|
||||
dist: janet-dist.tar.gz
|
||||
format:
|
||||
tools/format.sh
|
||||
|
||||
janet-%.tar.gz: $(JANET_TARGET) src/include/janet/janet.h \
|
||||
janet.1 LICENSE CONTRIBUTING.md $(JANET_LIBRARY) README.md
|
||||
tar -czvf $@ $^
|
||||
grammar: build/janet.tmLanguage
|
||||
build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
|
||||
$(JANET_TARGET) $< > $@
|
||||
|
||||
clean:
|
||||
-rm $(JANET_TARGET)
|
||||
-rm $(JANET_LIBRARY)
|
||||
-rm ctest/*.o ctest/*.out
|
||||
-rm src/**/*.o src/**/*.bc vgcore.* *.js *.wasm *.html
|
||||
-rm src/**/*.gen.c
|
||||
-rm janet-*.tar.gz
|
||||
-rm -rf build vgcore.* callgrind.*
|
||||
|
||||
install: $(JANET_TARGET)
|
||||
mkdir -p $(BINDIR)
|
||||
cp $(JANET_TARGET) $(BINDIR)/$(JANET_TARGET)
|
||||
mkdir -p $(INCLUDEDIR)
|
||||
cp $(JANET_HEADERS) $(INCLUDEDIR)
|
||||
mkdir -p $(LIBDIR)
|
||||
cp $(JANET_LIBRARY) $(LIBDIR)/$(JANET_LIBRARY)
|
||||
cp janet.1 /usr/local/share/man/man1/
|
||||
mandb
|
||||
$(LDCONFIG)
|
||||
test-install:
|
||||
cd test/install && rm -rf build && jpm build && jpm test
|
||||
|
||||
install-libs: natives
|
||||
mkdir -p $(JANET_PATH)
|
||||
cp -r lib $(JANET_PATH)
|
||||
cp natives/*/*.so $(JANET_PATH)
|
||||
build/embed_janet.o: build/janet.c $(JANET_HEADERS)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
build/embed_main.o: test/amalg/main.c $(JANET_HEADERS)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
build/embed_test: build/embed_janet.o build/embed_main.o
|
||||
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ $(CLIBS)
|
||||
|
||||
uninstall:
|
||||
-rm $(BINDIR)/$(JANET_TARGET)
|
||||
-rm $(LIBDIR)/$(JANET_LIBRARY)
|
||||
-rm -rf $(INCLUDEDIR)
|
||||
$(LDCONFIG)
|
||||
test-amalg: build/embed_test
|
||||
./build/embed_test
|
||||
|
||||
.PHONY: clean install repl debug valgrind test valtest dist install uninstall \
|
||||
$(TEST_PROGRAM_PHONIES) $(TEST_PROGRAM_VALPHONIES)
|
||||
.PHONY: clean install repl debug valgrind test amalg \
|
||||
valtest emscripten dist uninstall docs grammar format
|
||||
|
||||
211
README.md
211
README.md
@@ -1,24 +1,24 @@
|
||||
# janet
|
||||
[](https://gitter.im/janet-language/community)
|
||||
|
||||
[](https://ci.appveyor.com/project/bakpakin/janet/branch/master)
|
||||
[](https://travis-ci.org/janet-lang/janet)
|
||||
[](https://builds.sr.ht/~bakpakin/janet/.freebsd.yaml?)
|
||||
[](https://builds.sr.ht/~bakpakin/janet/.openbsd.yaml?)
|
||||
<noscript><a href="https://liberapay.com/Janet-Language/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
||||
|
||||
[](https://travis-ci.org/bakpakin/janet)
|
||||
[](https://ci.appveyor.com/project/bakpakin/janet)
|
||||
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-w200.png" alt="Janet logo" width=200 align="left">
|
||||
|
||||
Janet is a functional and imperative programming language and bytecode interpreter. It is a
|
||||
**Janet** is a functional and imperative programming language and bytecode interpreter. It is a
|
||||
modern lisp, but lists are replaced
|
||||
by other data structures with better utility and performance (arrays, tables, structs, tuples).
|
||||
The language also bridging bridging to native code written in C, meta-programming with macros, and bytecode assembly.
|
||||
The language also supports bridging to native code written in C, meta-programming with macros, and bytecode assembly.
|
||||
|
||||
There is a repl for trying out the language, as well as the ability
|
||||
to run script files. This client program is separate from the core runtime, so
|
||||
janet could be embedded into other programs. Try janet in your browser at
|
||||
janet could be embedded into other programs. Try janet in your browser at
|
||||
[https://janet-lang.org](https://janet-lang.org).
|
||||
|
||||
Implemented in mostly standard C99, janet runs on Windows, Linux and macOS.
|
||||
The few features that are not standard C (dynamic library loading, compiler specific optimizations),
|
||||
are fairly straight forward. Janet can be easily ported to new platforms.
|
||||
|
||||
For syntax highlighting, there is some preliminary vim syntax highlighting in [janet.vim](https://github.com/bakpakin/janet.vim).
|
||||
Generic lisp syntax highlighting should, however, provide good results.
|
||||
<br>
|
||||
|
||||
## Use Cases
|
||||
|
||||
@@ -43,30 +43,99 @@ Janet makes a good system scripting language, or a language to embed in other pr
|
||||
* Lexical scoping
|
||||
* Imperative programming as well as functional
|
||||
* REPL
|
||||
* Parsing Expression Grammars built in to the core library
|
||||
* 300+ functions and macros in the core library
|
||||
* Embedding Janet in other programs
|
||||
* Interactive environment with detailed stack traces
|
||||
|
||||
## Documentation
|
||||
|
||||
API documentation and design documents can be found in the
|
||||
[wiki](https://github.com/bakpakin/janet/wiki). There is an introduction
|
||||
section in the wiki that contains a good overview of the language.
|
||||
* For a quick tutorial, see [the introduction](https://janet-lang.org/docs/index.html) for more details.
|
||||
* For the full API for all functions in the core library, see [the core API doc](https://janet-lang.org/api/index.html)
|
||||
|
||||
For individual bindings, use the `(doc symbol-name)` macro to get API
|
||||
documentation for the core library. For example,
|
||||
Documentation is also available locally in the repl.
|
||||
Use the `(doc symbol-name)` macro to get API
|
||||
documentation for symbols in the core library. For example,
|
||||
```
|
||||
(doc doc)
|
||||
```
|
||||
Shows documentation for the doc macro.
|
||||
|
||||
|
||||
To get a list of all bindings in the default
|
||||
environment, use the `(all-symbols)` function.
|
||||
|
||||
## Source
|
||||
|
||||
You can get the source on [GitHub](https://github.com/janet-lang/janet) or
|
||||
[SourceHut](https://git.sr.ht/~bakpakin/janet). While the GitHub repo is the official repo,
|
||||
the SourceHut mirror is actively maintained.
|
||||
|
||||
## Building
|
||||
|
||||
### macos and Unix-like
|
||||
|
||||
The Makefile is non-portable and requires GNU-flavored make.
|
||||
|
||||
```
|
||||
cd somewhere/my/projects/janet
|
||||
make
|
||||
make test
|
||||
make repl
|
||||
```
|
||||
|
||||
### 32-bit Haiku
|
||||
|
||||
32-bit Haiku build instructions are the same as the unix-like build instructions,
|
||||
but you need to specify an alternative compiler, such as `gcc-x86`.
|
||||
|
||||
```
|
||||
cd somewhere/my/projects/janet
|
||||
make CC=gcc-x86
|
||||
make test
|
||||
make repl
|
||||
```
|
||||
|
||||
### FreeBSD
|
||||
|
||||
FreeBSD build instructions are the same as the unix-like build instuctions,
|
||||
but you need `gmake` to compile. Alternatively, install directly from
|
||||
packages, using `pkg install lang/janet`.
|
||||
|
||||
```
|
||||
cd somewhere/my/projects/janet
|
||||
gmake
|
||||
gmake test
|
||||
gmake repl
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#) or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#)
|
||||
2. Run a Visual Studio Command Prompt (cl.exe and link.exe need to be on the PATH) and cd to the directory with janet.
|
||||
3. Run `build_win` to compile janet.
|
||||
4. Run `build_win test` to make sure everything is working.
|
||||
|
||||
### Emscripten
|
||||
|
||||
To build janet for the web via [Emscripten](https://kripken.github.io/emscripten-site/), make sure you
|
||||
have `emcc` installed and on your path. On a linux or macOS system, use `make emscripten` to build
|
||||
`janet.js` and `janet.wasm` - both are needed to run janet in a browser or in node.
|
||||
The JavaScript build is what runs the repl on the main website,
|
||||
but really serves mainly as a proof of concept. Janet will run slower in a browser.
|
||||
Building with emscripten on windows is currently unsupported.
|
||||
|
||||
### Meson
|
||||
|
||||
Janet also has a build file for [Meson](https://mesonbuild.com/), a cross platform build
|
||||
system. Although Meson has a python dependency, Meson is a very complete build system that
|
||||
is maybe more convenient and flexible for integrating into existing pipelines.
|
||||
Meson also provides much better IDE integration than Make or batch files, as well as support
|
||||
for cross compilation.
|
||||
|
||||
## Installation
|
||||
|
||||
Install a stable version of janet from the [releases page](https://github.com/bakpakin/janet/releases).
|
||||
Janet is prebuilt for a few systems, but if you want to develop janet, run janet on a non-x86 system, or
|
||||
get the latest, you must build janet from source.
|
||||
See [the Introduction](https://janet-lang.org/introduction.html) for more details. If you just want
|
||||
to try out the language, you don't need to install anything. You can also simply move the `janet` executable wherever you want on your system and run it.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -74,7 +143,7 @@ A repl is launched when the binary is invoked with no arguments. Pass the -h fla
|
||||
to display the usage information. Individual scripts can be run with `./janet myscript.janet`
|
||||
|
||||
If you are looking to explore, you can print a list of all available macros, functions, and constants
|
||||
by entering the command `(all-symbols)` into the repl.
|
||||
by entering the command `(all-bindings)` into the repl.
|
||||
|
||||
```
|
||||
$ ./janet
|
||||
@@ -84,7 +153,7 @@ janet:1:> (+ 1 2 3)
|
||||
janet:2:> (print "Hello, World!")
|
||||
Hello, World!
|
||||
nil
|
||||
janet:3:> (os.exit)
|
||||
janet:3:> (os/exit)
|
||||
$ ./janet -h
|
||||
usage: ./janet [options] scripts...
|
||||
Options are:
|
||||
@@ -98,87 +167,43 @@ Options are:
|
||||
$
|
||||
```
|
||||
|
||||
## Compiling and Running
|
||||
If installed, you can also run `man janet` to get usage information.
|
||||
|
||||
Janet only uses Make and batch files to compile on Posix and windows
|
||||
respectively. To configure janet, edit the header file src/include/janet/janet.h
|
||||
before compilation.
|
||||
## Embedding
|
||||
|
||||
### Unix-like
|
||||
The C API for Janet is not yet documented but coming soon.
|
||||
|
||||
On most platforms, use Make to build janet.
|
||||
|
||||
```sh
|
||||
cd somewhere/my/projects/janet
|
||||
make
|
||||
make test
|
||||
```
|
||||
|
||||
After building, run `make install` to install the janet binary and libs.
|
||||
Will install in `/usr/local` by default, see the Makefile to customize.
|
||||
|
||||
It's also recommended to set the `JANET_PATH` variable in your profile.
|
||||
This is where janet will look for imported libraries after the current directory.
|
||||
|
||||
### FreeBSD
|
||||
|
||||
FreeBSD build instructions are the same as the unix-like build instuctions,
|
||||
but you need `gmake` and `gcc` to compile.
|
||||
|
||||
```
|
||||
cd somewhere/my/projects/janet
|
||||
gmake CC=gcc
|
||||
gmake test CC=gcc
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install [Visual Studio](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=15#)
|
||||
or [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15#)
|
||||
2. Run a Visual Studio Command Prompt (cl.exe and link.exe need to be on the PATH) and cd to the directory with janet.
|
||||
3. Run `build_win` to compile janet.
|
||||
4. Run `build_win test` to make sure everything is working.
|
||||
|
||||
### Emscripten
|
||||
|
||||
To build janet for the web via [Emscripten](https://kripken.github.io/emscripten-site/), make sure you
|
||||
have `emcc` installed and on your path. On a linux or macOS system, use `make janet.js` to build
|
||||
`janet.js` and `janet.wasm` - both are needed to run janet in a browser or in node.
|
||||
The JavaScript build is what runs the repl on the main website,
|
||||
but really serves mainly as a proof of concept. Janet will run much slower in a browser.
|
||||
Building with emscripten on windows is currently unsupported.
|
||||
Janet can be embedded in a host program very easily. There is a make target
|
||||
`make amalg` which creates the file `build/janet.c`, which is a single C file
|
||||
that contains all the source to Janet. This file, along with
|
||||
`src/include/janet.h` and `src/include/janetconf.h` can dragged into any C
|
||||
project and compiled into the project. Janet should be compiled with `-std=c99`
|
||||
on most compilers, and will need to be linked to the math library, `-lm`, and
|
||||
the dynamic linker, `-ldl`, if one wants to be able to load dynamic modules. If
|
||||
there is no need for dynamic modules, add the define
|
||||
`-DJANET_NO_DYNAMIC_MODULES` to the compiler options.
|
||||
|
||||
## Examples
|
||||
|
||||
See the examples directory for some example janet code.
|
||||
|
||||
## SQLite bindings
|
||||
## Discussion
|
||||
|
||||
There are some sqlite3 bindings in the directory natives/sqlite3. They serve mostly as a
|
||||
proof of concept external c library. To use, first compile the module with Make.
|
||||
Feel free to ask questions and join discussion on the [Janet Gitter Channel](https://gitter.im/janet-language/community).
|
||||
Alternatively, check out [the #janet channel on Freenode](https://webchat.freenode.net/)
|
||||
|
||||
```sh
|
||||
make natives
|
||||
```
|
||||
## FAQ
|
||||
|
||||
Next, enter the repl and create a database and a table.
|
||||
### Why is my terminal is spitting out junk when I run the repl?
|
||||
|
||||
```
|
||||
janet:1:> (import natives/sqlite3 :as sql)
|
||||
nil
|
||||
janet:2:> (def db (sql/open "test.db"))
|
||||
<sqlite3.connection 0x5561A138C470>
|
||||
janet:3:> (sql/eval db `CREATE TABLE customers(id INTEGER PRIMARY KEY, name TEXT);`)
|
||||
@[]
|
||||
janet:4:> (sql/eval db `INSERT INTO customers VALUES(:id, :name);` {:name "John" :id 12345})
|
||||
@[]
|
||||
janet:5:> (sql/eval db `SELECT * FROM customers;`)
|
||||
@[{"id" 12345 "name" "John"}]
|
||||
```
|
||||
Make sure your terminal supports ANSI escape codes. Most modern terminals will
|
||||
support these, but some older terminals, windows consoles, or embedded terminals
|
||||
will not. If your terminal does not support ANSI escape codes, run the repl with
|
||||
the `-n` flag, which disables color output. You can also try the `-s` if further issues
|
||||
ensue.
|
||||
|
||||
Finally, close the database connection when done with it.
|
||||
## Why Janet
|
||||
|
||||
```
|
||||
janet:6:> (sql/close db)
|
||||
nil
|
||||
```
|
||||
Janet is named after the almost omniscient and friendly artificial being in [The Good Place](https://en.wikipedia.org/wiki/The_Good_Place).
|
||||
|
||||
<img src="https://raw.githubusercontent.com/janet-lang/janet/master/assets/janet-the-good-place.gif" alt="Janet logo" width="115px" align="left">
|
||||
|
||||
10
appveyor.yml
10
appveyor.yml
@@ -20,7 +20,9 @@ init:
|
||||
install:
|
||||
- build_win
|
||||
- build_win test
|
||||
- choco install nsis -y -pre
|
||||
- build_win dist
|
||||
- call "C:\Program Files (x86)\NSIS\makensis.exe" janet-installer.nsi
|
||||
|
||||
build: off
|
||||
|
||||
@@ -30,9 +32,9 @@ only_commits:
|
||||
- src/
|
||||
|
||||
artifacts:
|
||||
- path: dist
|
||||
name: janet-windows
|
||||
type: Zip
|
||||
- path: janet-installer.exe
|
||||
name: janet-v1.1.0-windows-installer.exe
|
||||
type: File
|
||||
|
||||
deploy:
|
||||
description: 'The Janet Programming Language.'
|
||||
@@ -42,4 +44,4 @@ deploy:
|
||||
artifact: janet-windows
|
||||
draft: true
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
APPVEYOR_REPO_TAG: true
|
||||
|
||||
BIN
assets/icon.ico
Normal file
BIN
assets/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
11
assets/icon_svg.svg
Normal file
11
assets/icon_svg.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 640 640" preserveAspectRatio="xMidYMid meet">
|
||||
<g id="layer101" fill="#d45500" stroke="none">
|
||||
<path d="M145 531 c-46 -31 -58 -75 -30 -118 21 -32 30 -22 44 47 7 30 19 62 27 71 26 29 1 29 -41 0z"/>
|
||||
<path d="M341 534 c-23 -29 -26 -50 -11 -88 10 -28 64 -60 86 -52 12 5 12 2 0 -22 -24 -47 -51 -64 -116 -71 -51 -6 -65 -12 -85 -37 -14 -16 -24 -32 -25 -36 0 -12 -35 -9 -48 4 -7 7 -12 24 -12 38 0 41 -11 43 -47 8 -47 -46 -46 -90 5 -138 20 -19 49 -51 63 -70 l27 -35 88 0 c49 0 106 4 127 8 46 10 106 62 143 125 25 42 28 58 30 142 0 52 4 103 9 113 11 27 -14 75 -49 93 -41 21 -115 44 -143 44 -12 0 -31 -12 -42 -26z m89 -119 c0 -3 -2 -5 -5 -5 -3 0 -5 2 -5 5 0 3 2 5 5 5 3 0 5 -2 5 -5z"/>
|
||||
</g>
|
||||
<g id="layer102" fill="#deaa87" stroke="none">
|
||||
<path d="M186 549 c-33 -31 -38 -43 -56 -137 -26 -135 -26 -163 3 -190 33 -31 49 -28 85 17 28 35 36 39 87 43 46 4 61 10 90 38 18 18 39 46 46 62 10 25 9 32 -5 46 -17 16 -19 16 -29 1 -8 -14 -15 -15 -34 -6 -27 12 -40 65 -24 96 10 17 8 23 -12 36 -13 8 -44 18 -69 21 -42 6 -49 4 -82 -27z"/>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/janet-big.png
Normal file
BIN
assets/janet-big.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
BIN
assets/janet-the-good-place.gif
Normal file
BIN
assets/janet-the-good-place.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
BIN
assets/janet-w200.png
Normal file
BIN
assets/janet-w200.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
104
auxbin/jpm
Executable file
104
auxbin/jpm
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env janet
|
||||
|
||||
# CLI tool for building janet projects. Wraps cook.
|
||||
|
||||
(import cook)
|
||||
|
||||
(def- argpeg
|
||||
(peg/compile
|
||||
'(* "--" '(some (if-not "=" 1)) "=" '(any 1))))
|
||||
|
||||
(defn- local-rule
|
||||
[rule]
|
||||
(cook/import-rules "./project.janet")
|
||||
(cook/do-rule rule))
|
||||
|
||||
(defn- help
|
||||
[]
|
||||
(print `
|
||||
usage: jpm --key=value ... [subcommand] [args]...
|
||||
|
||||
Subcommands are:
|
||||
build : build all artifacts
|
||||
install (repo) : install artifacts. If a repo is given, install the contents of that
|
||||
git repository, assuming that the repository is a jpm project. If not, build
|
||||
and install the current project.
|
||||
uninstall (module) : uninstall a module. If no module is given, uninstall the module
|
||||
defined by the current directory.
|
||||
clean : remove any generated files or artifacts
|
||||
test : run tests
|
||||
deps : install dependencies.
|
||||
clear-cache : clear the git cache. Useful for updating dependencies.
|
||||
|
||||
Keys are:
|
||||
--modpath : The directory to install modules to. Defaults to $JANET_MODPATH or (dyn :syspath)
|
||||
--headerpath : The directory containing janet headers. Defaults to $JANET_HEADERPATH or (dyn :headerpath)
|
||||
--binpath : The directory to install binaries and scripts. Defaults to $JANET_BINPATH.
|
||||
--optimize : Optimization level for natives. Defaults to $OPTIMIZE or 2.
|
||||
--compiler : C compiler to use for natives. Defaults to $COMPILER or cc.
|
||||
--linker : C linker to use for linking natives. Defaults to $LINKER or cc.
|
||||
--cflags : Extra compiler flags for native modules. Defaults to $CFLAGS if set.
|
||||
--lflags : Extra linker flags for native modules. Defaults to $LFLAGS if set.
|
||||
`))
|
||||
|
||||
(defn build
|
||||
[]
|
||||
(local-rule "build"))
|
||||
|
||||
(defn clean
|
||||
[]
|
||||
(local-rule "clean"))
|
||||
|
||||
(defn install
|
||||
[&opt repo]
|
||||
(if repo
|
||||
(cook/install-git repo)
|
||||
(local-rule "install")))
|
||||
|
||||
(defn test
|
||||
[]
|
||||
(local-rule "test"))
|
||||
|
||||
(defn uninstall
|
||||
[&opt what]
|
||||
(if what
|
||||
(cook/uninstall what)
|
||||
(local-rule "uninstall")))
|
||||
|
||||
(defn deps
|
||||
[]
|
||||
(local-rule "install-deps"))
|
||||
|
||||
(def subcommands
|
||||
{"build" build
|
||||
"clean" clean
|
||||
"install" install
|
||||
"test" test
|
||||
"help" help
|
||||
"deps" deps
|
||||
"clear-cache" cook/clear-cache
|
||||
"uninstall" uninstall})
|
||||
|
||||
(def args (tuple/slice process/args 2))
|
||||
(def len (length args))
|
||||
(var i 0)
|
||||
|
||||
# Get flags
|
||||
(while (< i len)
|
||||
(def arg (args i))
|
||||
(unless (string/has-prefix? "--" arg) (break))
|
||||
(if-let [m (peg/match argpeg arg)]
|
||||
(let [[key value] m]
|
||||
(setdyn (keyword key) value))
|
||||
(print "invalid argument " arg))
|
||||
(++ i))
|
||||
|
||||
# Run subcommand
|
||||
(if (= i len)
|
||||
(help)
|
||||
(do
|
||||
(if-let [com (subcommands (args i))]
|
||||
(com ;(tuple/slice args (+ i 1)))
|
||||
(do
|
||||
(print "invalid command " (args i))
|
||||
(help)))))
|
||||
496
auxlib/cook.janet
Normal file
496
auxlib/cook.janet
Normal file
@@ -0,0 +1,496 @@
|
||||
### cook.janet
|
||||
###
|
||||
### Library to help build janet natives and other
|
||||
### build artifacts.
|
||||
###
|
||||
### Copyright 2019 © Calvin Rose
|
||||
|
||||
#
|
||||
# Basic Path Settings
|
||||
#
|
||||
|
||||
# Windows is the OS outlier
|
||||
(def- is-win (= (os/which) :windows))
|
||||
(def- is-mac (= (os/which) :macos))
|
||||
(def- sep (if is-win "\\" "/"))
|
||||
(def- objext (if is-win ".obj" ".o"))
|
||||
(def- modext (if is-win ".dll" ".so"))
|
||||
(def- absprefix (if is-win "C:\\" "/"))
|
||||
|
||||
#
|
||||
# Rule Engine
|
||||
#
|
||||
|
||||
(defn- getrules []
|
||||
(if-let [rules (dyn :rules)] rules (setdyn :rules @{})))
|
||||
|
||||
(defn- gettarget [target]
|
||||
(def item ((getrules) target))
|
||||
(unless item (error (string "No rule for target " target)))
|
||||
item)
|
||||
|
||||
(defn- rule-impl
|
||||
[target deps thunk &opt phony]
|
||||
(put (getrules) target @[(array/slice deps) thunk phony]))
|
||||
|
||||
(defmacro rule
|
||||
"Add a rule to the rule graph."
|
||||
[target deps & body]
|
||||
~(,rule-impl ,target ,deps (fn [] nil ,;body)))
|
||||
|
||||
(defmacro phony
|
||||
"Add a phony rule to the rule graph. A phony rule will run every time
|
||||
(it is always considered out of date). Phony rules are good for defining
|
||||
user facing tasks."
|
||||
[target deps & body]
|
||||
~(,rule-impl ,target ,deps (fn [] nil ,;body) true))
|
||||
|
||||
(defn add-dep
|
||||
"Add a dependency to an existing rule. Useful for extending phony
|
||||
rules or extending the dependency graph of existing rules."
|
||||
[target dep]
|
||||
(def [deps] (gettarget target))
|
||||
(array/push deps dep))
|
||||
|
||||
(defn- add-thunk
|
||||
[target more]
|
||||
(def item (gettarget target))
|
||||
(def [_ thunk] item)
|
||||
(put item 1 (fn [] (more) (thunk))))
|
||||
|
||||
(defmacro add-body
|
||||
"Add recipe code to an existing rule. This makes existing rules do more but
|
||||
does not modify the dependency graph."
|
||||
[target & body]
|
||||
~(,add-thunk ,target (fn [] ,;body)))
|
||||
|
||||
(defn- needs-build
|
||||
[dest src]
|
||||
(let [mod-dest (os/stat dest :modified)
|
||||
mod-src (os/stat src :modified)]
|
||||
(< mod-dest mod-src)))
|
||||
|
||||
(defn- needs-build-some
|
||||
[dest sources]
|
||||
(def f (file/open dest))
|
||||
(if (not f) (break true))
|
||||
(file/close f)
|
||||
(some (partial needs-build dest) sources))
|
||||
|
||||
(defn do-rule
|
||||
"Evaluate a given rule."
|
||||
[target]
|
||||
(def item ((getrules) target))
|
||||
(unless item
|
||||
(if (os/stat target :mode)
|
||||
(break target)
|
||||
(error (string "No rule for file " target " found."))))
|
||||
(def [deps thunk phony] item)
|
||||
(def realdeps (seq [dep :in deps :let [x (do-rule dep)] :when x] x))
|
||||
(when (or phony (needs-build-some target realdeps))
|
||||
(thunk))
|
||||
(unless phony target))
|
||||
|
||||
(def- _env (fiber/getenv (fiber/current)))
|
||||
|
||||
(defn import-rules
|
||||
"Import another file that defines more cook rules. This ruleset
|
||||
is merged into the current ruleset."
|
||||
[path]
|
||||
(def env (make-env))
|
||||
(unless (os/stat path :mode)
|
||||
(error (string "cannot open " path)))
|
||||
(loop [k :keys _env :when (symbol? k)]
|
||||
(unless ((_env k) :private) (put env k (_env k))))
|
||||
(def currenv (fiber/getenv (fiber/current)))
|
||||
(loop [k :keys currenv :when (keyword? k)]
|
||||
(put env k (currenv k)))
|
||||
(dofile path :env env)
|
||||
(when-let [rules (env :rules)] (merge-into (getrules) rules)))
|
||||
|
||||
#
|
||||
# Configuration
|
||||
#
|
||||
|
||||
# Installation settings
|
||||
(def JANET_MODPATH (or (os/getenv "JANET_MODPATH") (dyn :syspath)))
|
||||
(def JANET_HEADERPATH (os/getenv "JANET_HEADERPATH"))
|
||||
(def JANET_BINPATH (or (os/getenv "JANET_BINPATH") (unless is-win "/usr/local/bin")))
|
||||
|
||||
# Compilation settings
|
||||
(def- OPTIMIZE (or (os/getenv "OPTIMIZE") 2))
|
||||
(def- COMPILER (or (os/getenv "COMPILER") (if is-win "cl" "cc")))
|
||||
(def- LINKER (or (os/getenv "LINKER") (if is-win "link" COMPILER)))
|
||||
(def- LFLAGS
|
||||
(if-let [lflags (os/getenv "LFLAGS")]
|
||||
(string/split " " lflags)
|
||||
(if is-win ["/nologo" "/DLL"]
|
||||
(if is-mac
|
||||
["-shared" "-undefined" "dynamic_lookup"]
|
||||
["-shared"]))))
|
||||
(def- CFLAGS
|
||||
(if-let [cflags (os/getenv "CFLAGS")]
|
||||
(string/split " " cflags)
|
||||
(if is-win
|
||||
["/nologo"]
|
||||
["-std=c99" "-Wall" "-Wextra" "-fpic"])))
|
||||
|
||||
# Some defaults
|
||||
(def default-cflags CFLAGS)
|
||||
(def default-lflags LFLAGS)
|
||||
(def default-cc COMPILER)
|
||||
(def default-ld LINKER)
|
||||
|
||||
(defn- opt
|
||||
"Get an option, allowing overrides via dynamic bindings AND some
|
||||
default value dflt if no dynamic binding is set."
|
||||
[opts key dflt]
|
||||
(def ret (or (opts key) (dyn key dflt)))
|
||||
(if (= nil ret)
|
||||
(error (string "option :" key " not set")))
|
||||
ret)
|
||||
|
||||
#
|
||||
# OS and shell helpers
|
||||
#
|
||||
|
||||
(defn shell
|
||||
"Do a shell command"
|
||||
[& args]
|
||||
(def res (os/execute args :p))
|
||||
(unless (zero? res)
|
||||
(error (string "command exited with status " res))))
|
||||
|
||||
(defn rm
|
||||
"Remove a directory and all sub directories."
|
||||
[path]
|
||||
(if (= (os/stat path :mode) :directory)
|
||||
(do
|
||||
(each subpath (os/dir path)
|
||||
(rm (string path sep subpath)))
|
||||
(os/rmdir path))
|
||||
(os/rm path)))
|
||||
|
||||
(defn copy
|
||||
"Copy a file or directory recursively from one location to another."
|
||||
[src dest]
|
||||
(print "copying " src " to " dest "...")
|
||||
(if is-win
|
||||
(shell "xcopy" src dest "/y" "/e")
|
||||
(shell "cp" "-rf" src dest)))
|
||||
|
||||
#
|
||||
# C Compilation
|
||||
#
|
||||
|
||||
(defn- embed-name
|
||||
"Rename a janet symbol for embedding."
|
||||
[path]
|
||||
(->> path
|
||||
(string/replace-all sep "___")
|
||||
(string/replace-all ".janet" "")))
|
||||
|
||||
(defn- embed-c-name
|
||||
"Rename a janet file for embedding."
|
||||
[path]
|
||||
(->> path
|
||||
(string/replace-all sep "___")
|
||||
(string/replace-all ".janet" ".janet.c")
|
||||
(string "build" sep)))
|
||||
|
||||
(defn- embed-o-name
|
||||
"Get object file for c file."
|
||||
[path]
|
||||
(->> path
|
||||
(string/replace-all sep "___")
|
||||
(string/replace-all ".janet" (string ".janet" objext))
|
||||
(string "build" sep)))
|
||||
|
||||
(defn- object-name
|
||||
"Rename a source file so it can be built in a flat source tree."
|
||||
[path]
|
||||
(->> path
|
||||
(string/replace-all sep "___")
|
||||
(string/replace-all ".c" (if is-win ".obj" ".o"))
|
||||
(string "build" sep)))
|
||||
|
||||
(defn- lib-name
|
||||
"Generate name for dynamic library."
|
||||
[name]
|
||||
(string "build" sep name modext))
|
||||
|
||||
(defn- make-define
|
||||
"Generate strings for adding custom defines to the compiler."
|
||||
[define value]
|
||||
(def pre (if is-win "/D" "-D"))
|
||||
(if value
|
||||
(string pre define "=" value)
|
||||
(string pre define)))
|
||||
|
||||
(defn- make-defines
|
||||
"Generate many defines. Takes a dictionary of defines. If a value is
|
||||
true, generates -DNAME (/DNAME on windows), otherwise -DNAME=value."
|
||||
[defines]
|
||||
(seq [[d v] :pairs defines] (make-define d (if (not= v true) v))))
|
||||
|
||||
(defn- getcflags
|
||||
"Generate the c flags from the input options."
|
||||
[opts]
|
||||
@[;(opt opts :cflags CFLAGS)
|
||||
(string (if is-win "/I" "-I") (opt opts :headerpath JANET_HEADERPATH))
|
||||
(string (if is-win "/O" "-O") (opt opts :optimize OPTIMIZE))])
|
||||
|
||||
(defn- compile-c
|
||||
"Compile a C file into an object file."
|
||||
[opts src dest]
|
||||
(def cc (opt opts :compiler COMPILER))
|
||||
(def cflags (getcflags opts))
|
||||
(def defines (interpose " " (make-defines (opt opts :defines {}))))
|
||||
(def headers (or (opts :headers) []))
|
||||
(rule dest [src ;headers]
|
||||
(print "compiling " dest "...")
|
||||
(if is-win
|
||||
(shell cc ;defines "/c" ;cflags (string "/Fo" dest) src)
|
||||
(shell cc "-c" src ;defines ;cflags "-o" dest))))
|
||||
|
||||
(defn- link-c
|
||||
"Link a number of object files together."
|
||||
[opts target & objects]
|
||||
(def ld (opt opts :linker LINKER))
|
||||
(def cflags (getcflags opts))
|
||||
(def lflags (opt opts :lflags LFLAGS))
|
||||
(rule target objects
|
||||
(print "linking " target "...")
|
||||
(if is-win
|
||||
(shell ld ;lflags (string "/OUT:" target) ;objects (string (opt opts :headerpath JANET_HEADERPATH) `\\janet.lib`))
|
||||
(shell ld ;cflags `-o` target ;objects ;lflags))))
|
||||
|
||||
(defn- create-buffer-c
|
||||
"Inline raw byte file as a c file."
|
||||
[source dest name]
|
||||
(rule dest [source]
|
||||
(print "generating " dest "...")
|
||||
(def f (file/open source :r))
|
||||
(if (not f) (error (string "file " f " not found")))
|
||||
(def out (file/open dest :w))
|
||||
(def chunks (seq [b :in (file/read f :all)] (string b)))
|
||||
(file/write out
|
||||
"#include <janet.h>\n"
|
||||
"static const unsigned char bytes[] = {"
|
||||
;(interpose ", " chunks)
|
||||
"};\n\n"
|
||||
"const unsigned char *" name "_embed = bytes;\n"
|
||||
"size_t " name "_embed_size = sizeof(bytes);\n")
|
||||
(file/close out)
|
||||
(file/close f)))
|
||||
|
||||
(defn- abspath
|
||||
"Create an absolute path. Does not resolve . and .. (useful for
|
||||
generating entries in install manifest file)."
|
||||
[path]
|
||||
(if (string/has-prefix? absprefix)
|
||||
path
|
||||
(string (os/cwd) sep path)))
|
||||
|
||||
#
|
||||
# Public utilities
|
||||
#
|
||||
|
||||
(defn repo-id
|
||||
"Convert a repo url into a path component that serves as its id."
|
||||
[repo]
|
||||
(string/replace-all "\\" "_" (string/replace-all "/" "_" repo)))
|
||||
|
||||
(defn find-manifest-dir
|
||||
"Get the path to the directory containing manifests for installed
|
||||
packages."
|
||||
[&opt opts]
|
||||
(string (opt (or opts @{}) :modpath JANET_MODPATH) sep ".manifests"))
|
||||
|
||||
(defn find-manifest
|
||||
"Get the full path of a manifest file given a package name."
|
||||
[name &opt opts]
|
||||
(string (find-manifest-dir opts) sep name ".txt"))
|
||||
|
||||
(defn find-cache
|
||||
"Return the path to the global cache."
|
||||
[&opt opts]
|
||||
(def path (opt (or opts @{}) :modpath JANET_MODPATH))
|
||||
(string path sep ".cache"))
|
||||
|
||||
(defn uninstall
|
||||
"Uninstall bundle named name"
|
||||
[name &opt opts]
|
||||
(def manifest (find-manifest name opts))
|
||||
(def f (file/open manifest :r))
|
||||
(unless f (print manifest " does not exist") (break))
|
||||
(loop [line :iterate (:read f :line)]
|
||||
(def path ((string/split "\n" line) 0))
|
||||
(print "removing " path)
|
||||
(try (rm path) ([err]
|
||||
(unless (= err "No such file or directory")
|
||||
(error err)))))
|
||||
(print "removing " manifest)
|
||||
(rm manifest)
|
||||
(:close f)
|
||||
(print "Uninstalled."))
|
||||
|
||||
(defn clear-cache
|
||||
"Clear the global git cache."
|
||||
[&opt opts]
|
||||
(rm (find-cache opts)))
|
||||
|
||||
(defn install-git
|
||||
"Install a bundle from git. If the bundle is already installed, the bundle
|
||||
is reinistalled (but not rebuilt if artifacts are cached)."
|
||||
[repo &opt opts]
|
||||
(def cache (find-cache opts))
|
||||
(os/mkdir cache)
|
||||
(def id (repo-id repo))
|
||||
(def module-dir (string cache sep id))
|
||||
(when (os/mkdir module-dir)
|
||||
(os/execute ["git" "clone" repo module-dir] :p))
|
||||
(def olddir (os/cwd))
|
||||
(os/cd module-dir)
|
||||
(try
|
||||
(with-dyns [:rules @{}]
|
||||
(import-rules "./project.janet")
|
||||
(do-rule "install-deps")
|
||||
(do-rule "build")
|
||||
(do-rule "install"))
|
||||
([err] nil))
|
||||
(os/cd olddir))
|
||||
|
||||
(defn install-rule
|
||||
"Add install and uninstall rule for moving file from src into destdir."
|
||||
[src destdir]
|
||||
(def parts (string/split sep src))
|
||||
(def name (last parts))
|
||||
(def path (string destdir sep name))
|
||||
(array/push (dyn :installed-files) path)
|
||||
(add-body "install"
|
||||
(try (os/mkdir destdir) ([err] nil))
|
||||
(copy src destdir)))
|
||||
|
||||
#
|
||||
# Declaring Artifacts - used in project.janet, targets specifically
|
||||
# tailored for janet.
|
||||
#
|
||||
|
||||
(defn declare-native
|
||||
"Declare a native binary. This is a shared library that can be loaded
|
||||
dynamically by a janet runtime."
|
||||
[&keys opts]
|
||||
(def sources (opts :source))
|
||||
(def name (opts :name))
|
||||
(def lname (lib-name name))
|
||||
(loop [src :in sources]
|
||||
(compile-c opts src (object-name src)))
|
||||
(def objects (map object-name sources))
|
||||
(when-let [embedded (opts :embedded)]
|
||||
(loop [src :in embedded]
|
||||
(def c-src (embed-c-name src))
|
||||
(def o-src (embed-o-name src))
|
||||
(array/push objects o-src)
|
||||
(create-buffer-c src c-src (embed-name src))
|
||||
(compile-c opts c-src o-src)))
|
||||
(link-c opts lname ;objects)
|
||||
(add-dep "build" lname)
|
||||
(def path (opt opts :modpath JANET_MODPATH))
|
||||
(install-rule lname path))
|
||||
|
||||
(defn declare-source
|
||||
"Create a Janet modules. This does not actually build the module(s),
|
||||
but registers it for packaging and installation."
|
||||
[&keys opts]
|
||||
(def sources (opts :source))
|
||||
(def path (opt opts :modpath JANET_MODPATH))
|
||||
(each s sources
|
||||
(install-rule s path)))
|
||||
|
||||
(defn declare-bin
|
||||
"Declare a generic file to be installed as an executable."
|
||||
[&keys opts]
|
||||
(def main (opts :main))
|
||||
(def binpath (opt opts :binpath JANET_BINPATH))
|
||||
(install-rule main binpath))
|
||||
|
||||
(defn declare-binscript
|
||||
"Declare a janet file to be installed as an executable script. Creates
|
||||
a shim on windows."
|
||||
[&keys opts]
|
||||
(def main (opts :main))
|
||||
(def binpath (opt opts :binpath JANET_BINPATH))
|
||||
(install-rule main binpath)
|
||||
# Create a dud batch file when on windows.
|
||||
(when is-win
|
||||
(def name (last (string/split sep main)))
|
||||
(def bat (string "@echo off\r\njanet %~dp0\\" name "%*"))
|
||||
(def newname (string binpath sep name ".bat"))
|
||||
(add-body "install"
|
||||
(spit newname bat))
|
||||
(add-body "uninstall"
|
||||
(os/rm newname))))
|
||||
|
||||
(defn declare-archive
|
||||
"Build a janet archive. This is a file that bundles together many janet
|
||||
scripts into a janet image. This file can the be moved to any machine with
|
||||
a janet vm and the required dependencies and run there."
|
||||
[&keys opts]
|
||||
(def entry (opts :entry))
|
||||
(def name (opts :name))
|
||||
(def iname (string "build" sep name ".jimage"))
|
||||
(rule iname (or (opts :deps) [])
|
||||
(spit iname (make-image (require entry))))
|
||||
(def path (opt opts :modpath JANET_MODPATH))
|
||||
(install-rule iname path))
|
||||
|
||||
(defn declare-project
|
||||
"Define your project metadata. This should
|
||||
be the first declaration in a project.janet file.
|
||||
Also sets up basic phony targets like clean, build, test, etc."
|
||||
[&keys meta]
|
||||
(setdyn :project meta)
|
||||
|
||||
(def installed-files @[])
|
||||
(def manifests (find-manifest-dir))
|
||||
(def manifest (find-manifest (meta :name)))
|
||||
(setdyn :manifest manifest)
|
||||
(setdyn :manifest-dir manifests)
|
||||
(setdyn :installed-files installed-files)
|
||||
|
||||
(rule "./build" [] (os/mkdir "build"))
|
||||
(phony "build" ["./build"])
|
||||
|
||||
(phony "manifest" []
|
||||
(print "generating " manifest "...")
|
||||
(os/mkdir manifests)
|
||||
(spit manifest (string (string/join installed-files "\n") "\n")))
|
||||
(phony "install" ["uninstall" "build" "manifest"]
|
||||
(print "Installed as '" (meta :name) "'."))
|
||||
|
||||
(phony "install-deps" []
|
||||
(if-let [deps (meta :dependencies)]
|
||||
(each dep deps
|
||||
(install-git dep))
|
||||
(print "no dependencies found")))
|
||||
|
||||
(phony "uninstall" []
|
||||
(uninstall (meta :name)))
|
||||
|
||||
(phony "clean" []
|
||||
(rm "build")
|
||||
(print "Deleted build directory."))
|
||||
|
||||
(phony "test" ["build"]
|
||||
(defn dodir
|
||||
[dir]
|
||||
(each sub (os/dir dir)
|
||||
(def ndir (string dir sep sub))
|
||||
(case (os/stat ndir :mode)
|
||||
:file (when (string/has-suffix? ".janet" ndir)
|
||||
(print "running " ndir " ...")
|
||||
(dofile ndir :exit true))
|
||||
:directory (dodir ndir))))
|
||||
(dodir "test")
|
||||
(print "All tests passed.")))
|
||||
149
auxlib/path.janet
Normal file
149
auxlib/path.janet
Normal file
@@ -0,0 +1,149 @@
|
||||
### path.janet
|
||||
###
|
||||
### A library for path manipulation.
|
||||
###
|
||||
### Copyright 2019 © Calvin Rose
|
||||
|
||||
#
|
||||
# Common
|
||||
#
|
||||
|
||||
(def- ext-peg
|
||||
(peg/compile ~{:back (> -1 (+ (* ($) (set "\\/.")) :back))
|
||||
:main :back}))
|
||||
|
||||
(defn ext
|
||||
"Get the file extension for a path."
|
||||
[path]
|
||||
(if-let [m (peg/match ext-peg path (length path))]
|
||||
(let [i (m 0)]
|
||||
(if (= (path i) 46)
|
||||
(string/slice path (m 0) -1)))))
|
||||
|
||||
(defn- redef
|
||||
"Redef a value, keeping all metadata."
|
||||
[from to]
|
||||
(setdyn (symbol to) (dyn (symbol from))))
|
||||
|
||||
#
|
||||
# Generating Macros
|
||||
#
|
||||
|
||||
(defmacro- decl-sep [pre sep] ~(def ,(symbol pre "/sep") ,sep))
|
||||
(defmacro- decl-delim [pre d] ~(def ,(symbol pre "/delim") ,d))
|
||||
|
||||
(defmacro- decl-last-sep
|
||||
[pre sep]
|
||||
~(def- ,(symbol pre "/last-sep-peg")
|
||||
(peg/compile ~{:back (> -1 (+ (* ,sep ($)) :back))
|
||||
:main :back})))
|
||||
|
||||
(defmacro- decl-basename
|
||||
[pre]
|
||||
~(defn ,(symbol pre "/basename")
|
||||
"Gets the base file name of a path."
|
||||
[path]
|
||||
(if-let [m (peg/match
|
||||
,(symbol pre "/last-sep-peg")
|
||||
path
|
||||
(length path))]
|
||||
(let [[p] m]
|
||||
(string/slice path p -1))
|
||||
path)))
|
||||
|
||||
(defmacro- decl-parts
|
||||
[pre sep]
|
||||
~(defn ,(symbol pre "/parts")
|
||||
"Split a path into its parts."
|
||||
[path]
|
||||
(string/split ,sep path)))
|
||||
|
||||
(defmacro- decl-normalize
|
||||
[pre sep lead]
|
||||
~(defn ,(symbol pre "/normalize")
|
||||
"Normalize a path. This removes . and .. in the
|
||||
path, as well as empty path elements."
|
||||
[path]
|
||||
(def els (string/split ,sep path))
|
||||
(def newparts @[])
|
||||
(if (,(symbol pre "/abspath?") path) (array/push newparts ,lead))
|
||||
(each part els
|
||||
(case part
|
||||
"" nil
|
||||
"." nil
|
||||
".." (array/pop newparts)
|
||||
(array/push newparts part)))
|
||||
(string/join newparts ,sep)))
|
||||
|
||||
(defmacro- decl-join
|
||||
[pre sep]
|
||||
~(defn ,(symbol pre "/join")
|
||||
"Join path elements together."
|
||||
[& els]
|
||||
(,(symbol pre "/normalize") (string/join els ,sep))))
|
||||
|
||||
(defmacro- decl-abspath
|
||||
[pre]
|
||||
~(defn ,(symbol pre "/abspath")
|
||||
"Coerce a path to be absolute."
|
||||
[path]
|
||||
(if (,(symbol pre "/abspath?") path)
|
||||
path
|
||||
(,(symbol pre "/join") (os/cwd) path))))
|
||||
|
||||
#
|
||||
# Posix
|
||||
#
|
||||
|
||||
(defn posix/abspath?
|
||||
"Check if a path is absolute."
|
||||
[path]
|
||||
(string/has-prefix? "/" path))
|
||||
|
||||
(redef "ext" "posix/ext")
|
||||
(decl-sep "posix" "/")
|
||||
(decl-delim "posix" ":")
|
||||
(decl-last-sep "posix" "/")
|
||||
(decl-basename "posix")
|
||||
(decl-parts "posix" "/")
|
||||
(decl-normalize "posix" "/" "")
|
||||
(decl-join "posix" "/")
|
||||
(decl-abspath "posix")
|
||||
|
||||
#
|
||||
# Windows
|
||||
#
|
||||
|
||||
(def- abs-peg (peg/compile '(* (range "AZ") ":\\")))
|
||||
(defn win32/abspath?
|
||||
"Check if a path is absolute."
|
||||
[path]
|
||||
(peg/match abs-peg path))
|
||||
|
||||
(redef "ext" "win32/ext")
|
||||
(decl-sep "win32" "\\")
|
||||
(decl-delim "win32" ";")
|
||||
(decl-last-sep "win32" "\\")
|
||||
(decl-basename "win32")
|
||||
(decl-parts "win32" "\\")
|
||||
(decl-normalize "win32" "\\" "C:")
|
||||
(decl-join "win32" "\\")
|
||||
(decl-abspath "win32")
|
||||
|
||||
#
|
||||
# Specialize for current OS
|
||||
#
|
||||
|
||||
(def- syms
|
||||
["ext"
|
||||
"sep"
|
||||
"delim"
|
||||
"basename"
|
||||
"abspath?"
|
||||
"abspath"
|
||||
"parts"
|
||||
"normalize"
|
||||
"join"])
|
||||
(let [pre (if (= :windows (os/which)) "win32" "posix")]
|
||||
(each sym syms
|
||||
(redef (string pre "/" sym) sym)))
|
||||
@@ -16,47 +16,76 @@
|
||||
|
||||
@rem Set compile and link options here
|
||||
@setlocal
|
||||
@set JANET_COMPILE=cl /nologo /Isrc\include /c /O2 /W3 /LD /D_CRT_SECURE_NO_WARNINGS
|
||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /LD /D_CRT_SECURE_NO_WARNINGS
|
||||
@set JANET_LINK=link /nologo
|
||||
|
||||
mkdir build
|
||||
mkdir build\core
|
||||
mkdir build\mainclient
|
||||
mkdir build\boot
|
||||
|
||||
@rem Build the xxd tool for generating sources
|
||||
@cl /nologo /c src/tools/xxd.c /Fobuild\xxd.obj
|
||||
@cl /nologo /c tools/xxd.c /Fobuild\xxd.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@link /nologo /out:build\xxd.exe build\xxd.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Generate the embedded sources
|
||||
@build\xxd.exe src\core\core.janet build\core\core.gen.c janet_gen_core
|
||||
@build\xxd.exe src\mainclient\init.janet build\init.gen.c janet_gen_init
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@build\xxd.exe src\mainclient\init.janet build\mainclient\init.gen.c janet_gen_init
|
||||
@build\xxd.exe src\boot\boot.janet build\boot.gen.c janet_gen_boot
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the generated sources
|
||||
@%JANET_COMPILE% /Fobuild\core\core.gen.obj build\core\core.gen.c
|
||||
@%JANET_COMPILE% /Fobuild\mainclient\init.gen.obj build\init.gen.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
@%JANET_COMPILE% /Fobuild\mainclient\init.gen.obj build\mainclient\init.gen.c
|
||||
@%JANET_COMPILE% /Fobuild\boot\boot.gen.obj build\boot.gen.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the bootstrap interpretter
|
||||
for %%f in (src\core\*.c) do (
|
||||
@%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
for %%f in (src\boot\*.c) do (
|
||||
@%JANET_COMPILE% /DJANET_BOOTSTRAP /Fobuild\boot\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
%JANET_LINK% /out:build\janet_boot.exe build\boot\*.obj
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
build\janet_boot build\core_image.c
|
||||
|
||||
@rem Build the core image
|
||||
@%JANET_COMPILE% /Fobuild\core_image.obj build\core_image.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Build the sources
|
||||
for %%f in (src\core\*.c) do (
|
||||
@%JANET_COMPILE% /Fobuild\core\%%~nf.obj %%f
|
||||
@%JANET_COMPILE% /Fobuild\core\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
|
||||
@rem Build the resources
|
||||
rc /nologo /fobuild\janet_win.res janet_win.rc
|
||||
|
||||
@rem Build the main client
|
||||
for %%f in (src\mainclient\*.c) do (
|
||||
@%JANET_COMPILE% /Fobuild\mainclient\%%~nf.obj %%f
|
||||
@%JANET_COMPILE% /Fobuild\mainclient\%%~nf.obj %%f
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
)
|
||||
|
||||
@rem Link everything to main client
|
||||
%JANET_LINK% /out:janet.exe build\core\*.obj build\mainclient\*.obj
|
||||
%JANET_LINK% /out:janet.exe build\core\*.obj build\mainclient\*.obj build\core_image.obj build\janet_win.res
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@rem Gen amlag
|
||||
setlocal enabledelayedexpansion
|
||||
set "amalg_files="
|
||||
for %%f in (src\core\*.c) do (
|
||||
set "amalg_files=!amalg_files! %%f"
|
||||
)
|
||||
janet.exe tools\amalg.janet src\core\util.h src\core\state.h src\core\gc.h src\core\vector.h src\core\fiber.h src\core\regalloc.h src\core\compile.h src\core\emit.h src\core\symcache.h %amalg_files% build\core_image.c > build\janet.c
|
||||
|
||||
echo === Successfully built janet.exe for Windows ===
|
||||
echo === Run 'build_win test' to run tests. ==
|
||||
echo === Run 'build_win clean' to delete build artifacts. ===
|
||||
@@ -70,7 +99,7 @@ exit /b 1
|
||||
@rem Show help
|
||||
:HELP
|
||||
@echo.
|
||||
@echo Usage: build_windows [subcommand=clean,help,test]
|
||||
@echo Usage: build_windows [subcommand=clean,help,test,dist]
|
||||
@echo.
|
||||
@echo Script to build janet on windows. Must be run from the Visual Studio
|
||||
@echo command prompt.
|
||||
@@ -85,20 +114,30 @@ exit /b 0
|
||||
@rem Run tests
|
||||
:TEST
|
||||
for %%f in (test/suite*.janet) do (
|
||||
janet.exe test\%%f
|
||||
@if errorlevel 1 goto :TESTFAIL
|
||||
janet.exe test\%%f
|
||||
@if errorlevel 1 goto :TESTFAIL
|
||||
)
|
||||
exit /b 0
|
||||
|
||||
@rem Build a dist directory
|
||||
:DIST
|
||||
mkdir dist
|
||||
janet.exe tools\gendoc.janet > dist\doc.html
|
||||
|
||||
copy build\janet.c dist\janet.c
|
||||
copy janet.exe dist\janet.exe
|
||||
copy LICENSE dist\LICENSE
|
||||
copy README.md dist\README.md
|
||||
|
||||
copy janet.lib dist\janet.lib
|
||||
copy janet.exp dist\janet.exp
|
||||
copy src\include\janet\janet.h dist\janet.h
|
||||
copy src\include\janet.h dist\janet.h
|
||||
copy src\conf\janetconf.h dist\janetconf.h
|
||||
|
||||
copy auxlib\cook.janet dist\cook.janet
|
||||
|
||||
copy auxbin\jpm dist\jpm
|
||||
copy tools\jpm.bat dist\jpm.bat
|
||||
exit /b 0
|
||||
|
||||
:TESTFAIL
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
(def solutions @{})
|
||||
(def len (length s))
|
||||
(for k 0 len
|
||||
(put tab s@k k))
|
||||
(put tab (s k) k))
|
||||
(for i 0 len
|
||||
(for j 0 len
|
||||
(def k (get tab (- 0 s@i s@j)))
|
||||
(def k (get tab (- 0 (s i) (s j))))
|
||||
(when (and k (not= k i) (not= k j) (not= i j))
|
||||
(put solutions {i true j true k true} true))))
|
||||
(map keys (keys solution)))
|
||||
(map keys (keys solutions)))
|
||||
|
||||
(def arr @[2 4 1 3 8 7 -3 -1 12 -5 -8])
|
||||
(printf "3sum of %P: " arr)
|
||||
(printf "%P\n" (sum3 arr))
|
||||
|
||||
@@ -13,8 +13,16 @@
|
||||
(addim 0 0 -0x1) # $0 = $0 - 1
|
||||
(push 0) # push($0)
|
||||
(call 0 1) # $0 = call($1)
|
||||
(addi 0 0 2) # $0 = $0 + $2 (integers)
|
||||
(add 0 0 2) # $0 = $0 + $2 (integers)
|
||||
:done
|
||||
(ret 0) # return $0
|
||||
]
|
||||
}))
|
||||
|
||||
# Test it
|
||||
|
||||
(defn testn
|
||||
[n]
|
||||
(print "fibasm(" n ") = " (fibasm n)))
|
||||
|
||||
(for i 0 10 (testn i))
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
:bright-white 97
|
||||
:bg-bright-white 107})
|
||||
|
||||
(loop [[name color] :in (pairs colormap)]
|
||||
(defglobal (string.slice name 1)
|
||||
(fn color-wrapper [& pieces]
|
||||
(string "\e[" color "m" (apply string pieces) "\e[0m"))))
|
||||
(defn color
|
||||
"Take a string made by concatenating xs and colorize it for an ANSI terminal."
|
||||
[c & xs]
|
||||
(def code (get colormap c))
|
||||
(if (not code) (error (string "color " c " unknown")))
|
||||
(string "\e[" code "m" ;xs "\e[0m"))
|
||||
|
||||
# Print all colors
|
||||
|
||||
(loop [c :keys colormap] (print (color c c)))
|
||||
@@ -18,12 +18,12 @@
|
||||
(if ,loaded
|
||||
,state
|
||||
(do
|
||||
(:= ,loaded true)
|
||||
(:= ,state (do ;forms)))))))
|
||||
(set ,loaded true)
|
||||
(set ,state (do ,;forms)))))))
|
||||
|
||||
# Use tuples instead of structs to save memory
|
||||
(def HEAD :private 0)
|
||||
(def TAIL :private 1)
|
||||
(def- HEAD 0)
|
||||
(def- TAIL 1)
|
||||
|
||||
(defn empty-seq
|
||||
"The empty sequence."
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
(defn lazy-range
|
||||
"Return a sequence of integers [start, end)."
|
||||
@[start end]
|
||||
[start end &]
|
||||
(if end
|
||||
(if (< start end)
|
||||
(delay (tuple start (lazy-range (+ 1 start) end)))
|
||||
@@ -94,7 +94,7 @@
|
||||
(defn randseq
|
||||
"Return a sequence of random numbers."
|
||||
[]
|
||||
(delay (tuple (math.random) (randseq))))
|
||||
(delay (tuple (math/random) (randseq))))
|
||||
|
||||
(defn take-while
|
||||
"Returns a sequence of values until the predicate is false."
|
||||
@@ -4,11 +4,11 @@
|
||||
(seq [x :range [-1 2]
|
||||
y :range [-1 2]
|
||||
:when (not (and (zero? x) (zero? y)))]
|
||||
(tuple x y)))
|
||||
[x y]))
|
||||
|
||||
(defn- neighbors
|
||||
[[x y]]
|
||||
(map (fn [[x1 y1]] (tuple (+ x x1) (+ y y1))) window))
|
||||
(map (fn [[x1 y1]] [(+ x x1) (+ y y1)]) window))
|
||||
|
||||
(defn tick
|
||||
"Get the next state in the Game Of Life."
|
||||
@@ -16,7 +16,7 @@
|
||||
(def cell-set (frequencies state))
|
||||
(def neighbor-set (frequencies (mapcat neighbors state)))
|
||||
(seq [coord :keys neighbor-set
|
||||
:let [count neighbor-set@coord]
|
||||
:let [count (get neighbor-set coord)]
|
||||
:when (or (= count 3) (and (get cell-set coord) (= count 2)))]
|
||||
coord))
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
"Draw cells in the game of life from (x1, y1) to (x2, y2)"
|
||||
[state x1 y1 x2 y2]
|
||||
(def cellset @{})
|
||||
(each cell state (:= cellset@cell true))
|
||||
(each cell state (put cellset cell true))
|
||||
(loop [x :range [x1 (+ 1 x2)]
|
||||
:after (print)
|
||||
y :range [y1 (+ 1 y2)]]
|
||||
(file/write stdout (if (get cellset (tuple x y)) "X " ". ")))
|
||||
(file/write stdout (if (get cellset [x y]) "X " ". ")))
|
||||
(print))
|
||||
|
||||
#
|
||||
@@ -40,4 +40,4 @@
|
||||
(for i 0 20
|
||||
(print "generation " i)
|
||||
(draw *state* -7 -7 7 7)
|
||||
(:= *state* (tick *state*)))
|
||||
(set *state* (tick *state*)))
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
# of the triangle to the leaves of the triangle.
|
||||
|
||||
(defn myfold [xs ys]
|
||||
(let [xs1 (tuple/prepend xs 0)
|
||||
xs2 (tuple/append xs 0)
|
||||
m1 (map + xs1 ys)
|
||||
m2 (map + xs2 ys)]
|
||||
(let [m1 (map + [;xs 0] ys)
|
||||
m2 (map + [0 ;xs] ys)]
|
||||
(map max m1 m2)))
|
||||
|
||||
(defn maxpath [t]
|
||||
|
||||
1
examples/numarray/.gitignore
vendored
Normal file
1
examples/numarray/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
23
examples/numarray/build.janet
Normal file
23
examples/numarray/build.janet
Normal file
@@ -0,0 +1,23 @@
|
||||
(import cook)
|
||||
|
||||
(cook/make-native
|
||||
:name "numarray"
|
||||
:source @["numarray.c"])
|
||||
|
||||
(import build/numarray :as numarray)
|
||||
|
||||
(def a (numarray/new 30))
|
||||
(print (get a 20))
|
||||
(print (a 20))
|
||||
|
||||
(put a 5 3.14)
|
||||
(print (a 5))
|
||||
(set (a 5) 100)
|
||||
(print (a 5))
|
||||
|
||||
# (numarray/scale a 5))
|
||||
# ((a :scale) a 5)
|
||||
(:scale a 5)
|
||||
(for i 0 10 (print (a i)))
|
||||
|
||||
(print "sum=" (:sum a))
|
||||
117
examples/numarray/numarray.c
Normal file
117
examples/numarray/numarray.c
Normal file
@@ -0,0 +1,117 @@
|
||||
#include <stdlib.h>
|
||||
#include <janet.h>
|
||||
|
||||
typedef struct {
|
||||
double *data;
|
||||
size_t size;
|
||||
} num_array;
|
||||
|
||||
static num_array *num_array_init(num_array *array, size_t size) {
|
||||
array->data = (double *)calloc(size, sizeof(double));
|
||||
array->size = size;
|
||||
return array;
|
||||
}
|
||||
|
||||
static void num_array_deinit(num_array *array) {
|
||||
free(array->data);
|
||||
}
|
||||
|
||||
static int num_array_gc(void *p, size_t s) {
|
||||
(void) s;
|
||||
num_array *array = (num_array *)p;
|
||||
num_array_deinit(array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Janet num_array_get(void *p, Janet key);
|
||||
void num_array_put(void *p, Janet key, Janet value);
|
||||
|
||||
static const JanetAbstractType num_array_type = {
|
||||
"numarray",
|
||||
num_array_gc,
|
||||
NULL,
|
||||
num_array_get,
|
||||
num_array_put
|
||||
};
|
||||
|
||||
static Janet num_array_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t size = janet_getinteger(argv, 0);
|
||||
num_array *array = (num_array *)janet_abstract(&num_array_type, sizeof(num_array));
|
||||
num_array_init(array, size);
|
||||
return janet_wrap_abstract(array);
|
||||
}
|
||||
|
||||
static Janet num_array_scale(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
num_array *array = (num_array *)janet_getabstract(argv, 0, &num_array_type);
|
||||
double factor = janet_getnumber(argv, 1);
|
||||
size_t i;
|
||||
for (i = 0; i < array->size; i++) {
|
||||
array->data[i] *= factor;
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet num_array_sum(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
num_array *array = (num_array *)janet_getabstract(argv, 0, &num_array_type);
|
||||
double sum = 0;
|
||||
for (size_t i = 0; i < array->size; i++) sum += array->data[i];
|
||||
return janet_wrap_number(sum);
|
||||
}
|
||||
|
||||
void num_array_put(void *p, Janet key, Janet value) {
|
||||
size_t index;
|
||||
num_array *array = (num_array *)p;
|
||||
if (!janet_checkint(key))
|
||||
janet_panic("expected integer key");
|
||||
if (!janet_checktype(value, JANET_NUMBER))
|
||||
janet_panic("expected number value");
|
||||
|
||||
index = (size_t)janet_unwrap_integer(key);
|
||||
if (index < array->size) {
|
||||
array->data[index] = janet_unwrap_number(value);
|
||||
}
|
||||
}
|
||||
|
||||
static const JanetMethod methods[] = {
|
||||
{"scale", num_array_scale},
|
||||
{"sum", num_array_sum},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
Janet num_array_get(void *p, Janet key) {
|
||||
size_t index;
|
||||
Janet value;
|
||||
num_array *array = (num_array *)p;
|
||||
if (janet_checktype(key, JANET_KEYWORD))
|
||||
return janet_getmethod(janet_unwrap_keyword(key), methods);
|
||||
if (!janet_checkint(key))
|
||||
janet_panic("expected integer key");
|
||||
index = (size_t)janet_unwrap_integer(key);
|
||||
if (index >= array->size) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = janet_wrap_number(array->data[index]);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{
|
||||
"new", num_array_new,
|
||||
"(numarray/new size)\n\n"
|
||||
"Create new numarray"
|
||||
},
|
||||
{
|
||||
"scale", num_array_scale,
|
||||
"(numarray/scale numarray factor)\n\n"
|
||||
"scale numarray by factor"
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
JANET_MODULE_ENTRY(JanetTable *env) {
|
||||
janet_cfuns(env, "numarray", cfuns);
|
||||
}
|
||||
@@ -9,6 +9,8 @@
|
||||
(def len (length list))
|
||||
(for j 0 len
|
||||
(def trial (get list j))
|
||||
(if (zero? (% i trial)) (:= isprime? false)))
|
||||
(if (zero? (% i trial)) (set isprime? false)))
|
||||
(if isprime? (array/push list i)))
|
||||
list)
|
||||
|
||||
(pp (primes 100))
|
||||
|
||||
83
examples/tarray.janet
Normal file
83
examples/tarray.janet
Normal file
@@ -0,0 +1,83 @@
|
||||
# naive matrix implementation for testing typed array
|
||||
|
||||
(defmacro printf [& xs] ['print ['string/format (splice xs)]])
|
||||
|
||||
(defn matrix [nrow ncol] {:nrow nrow :ncol ncol :array (tarray/new :float64 (* nrow ncol))})
|
||||
|
||||
(defn matrix/row [mat i]
|
||||
(def {:nrow nrow :ncol ncol :array array} mat)
|
||||
(tarray/new :float64 ncol 1 (* i ncol) array))
|
||||
|
||||
(defn matrix/column [mat j]
|
||||
(def {:nrow nrow :ncol ncol :array array} mat)
|
||||
(tarray/new :float64 nrow ncol j array))
|
||||
|
||||
(defn matrix/set [mat i j value]
|
||||
(def {:nrow nrow :ncol ncol :array array} mat)
|
||||
(set (array (+ (* i ncol) j)) value))
|
||||
|
||||
(defn matrix/get [mat i j value]
|
||||
(def {:nrow nrow :ncol ncol :array array} mat)
|
||||
(array (+ (* i ncol) j)))
|
||||
|
||||
|
||||
# other variants to test rows and cols views
|
||||
|
||||
(defn matrix/set* [mat i j value]
|
||||
(set ((matrix/row mat i) j) value))
|
||||
|
||||
(defn matrix/set** [mat i j value]
|
||||
(set ((matrix/column mat j) i) value))
|
||||
|
||||
|
||||
(defn matrix/get* [mat i j value]
|
||||
((matrix/row mat i) j))
|
||||
|
||||
(defn matrix/get** [mat i j value]
|
||||
((matrix/column j) i))
|
||||
|
||||
|
||||
(defn tarray/print [array]
|
||||
(def size (tarray/length array))
|
||||
(def buf @"")
|
||||
(buffer/format buf "[%2i]" size)
|
||||
(for i 0 size
|
||||
(buffer/format buf " %+6.3f " (array i)))
|
||||
(print buf))
|
||||
|
||||
(defn matrix/print [mat]
|
||||
(def {:nrow nrow :ncol ncol :array tarray} mat)
|
||||
(printf "matrix %iX%i %p" nrow ncol tarray)
|
||||
(for i 0 nrow
|
||||
(tarray/print (matrix/row mat i))))
|
||||
|
||||
|
||||
(def nr 5)
|
||||
(def nc 4)
|
||||
(def A (matrix nr nc))
|
||||
|
||||
(loop (i :range (0 nr) j :range (0 nc))
|
||||
(matrix/set A i j i))
|
||||
(matrix/print A)
|
||||
|
||||
(loop (i :range (0 nr) j :range (0 nc))
|
||||
(matrix/set* A i j i))
|
||||
(matrix/print A)
|
||||
|
||||
(loop (i :range (0 nr) j :range (0 nc))
|
||||
(matrix/set** A i j i))
|
||||
(matrix/print A)
|
||||
|
||||
|
||||
(printf "properties:\n%p" (tarray/properties (A :array)))
|
||||
(for i 0 nr
|
||||
(printf "row properties:[%i]\n%p" i (tarray/properties (matrix/row A i))))
|
||||
(for i 0 nc
|
||||
(printf "col properties:[%i]\n%p" i (tarray/properties (matrix/column A i))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
29
examples/urlloader.janet
Normal file
29
examples/urlloader.janet
Normal file
@@ -0,0 +1,29 @@
|
||||
# An example of using Janet's extensible module system
|
||||
# to import files from URL. To try this, run `janet -l examples/urlloader.janet`
|
||||
# from the repl, and then:
|
||||
#
|
||||
# (import https://raw.githubusercontent.com/janet-lang/janet/master/examples/colors.janet :as c)
|
||||
#
|
||||
# This will import a file using curl. You can then try
|
||||
#
|
||||
# (print (c/color :green "Hello!"))
|
||||
#
|
||||
# This is a bit of a toy example (it just shells out to curl), but it is very
|
||||
# powerful and will work well in many cases.
|
||||
|
||||
(defn- load-url
|
||||
[url args]
|
||||
(def f (file/popen (string "curl " url)))
|
||||
(def res (dofile f :source url ;args))
|
||||
(try (file/close f) ([err] nil))
|
||||
res)
|
||||
|
||||
(defn- check-http-url
|
||||
[path]
|
||||
(if (or (string/has-prefix? "http://" path)
|
||||
(string/has-prefix? "https://" path))
|
||||
path))
|
||||
|
||||
# Add the module loader and path tuple to right places
|
||||
(array/push module/paths [check-http-url :janet-http])
|
||||
(put module/loaders :janet-http load-url)
|
||||
180
janet-installer.nsi
Normal file
180
janet-installer.nsi
Normal file
@@ -0,0 +1,180 @@
|
||||
# Version
|
||||
!define VERSION "1.1.0"
|
||||
!define PRODUCT_VERSION "${VERSION}.0"
|
||||
VIProductVersion "${PRODUCT_VERSION}"
|
||||
VIFileVersion "${PRODUCT_VERSION}"
|
||||
|
||||
# Use the modern UI
|
||||
!define MULTIUSER_EXECUTIONLEVEL Highest
|
||||
!define MULTIUSER_MUI
|
||||
!define MULTIUSER_INSTALLMODE_COMMANDLINE
|
||||
!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY "Software\Janet\${VERSION}"
|
||||
!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME ""
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "Software\Janet\${VERSION}"
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME ""
|
||||
!define MULTIUSER_INSTALLMODE_INSTDIR "Janet-${VERSION}"
|
||||
|
||||
# Includes
|
||||
!include "MultiUser.nsh"
|
||||
!include "MUI2.nsh"
|
||||
!include ".\tools\EnvVarUpdate.nsh"
|
||||
!include "LogicLib.nsh"
|
||||
|
||||
# Basics
|
||||
Name "Janet"
|
||||
OutFile "janet-v${VERSION}-windows-installer.exe"
|
||||
|
||||
# Some Configuration
|
||||
!define APPNAME "Janet"
|
||||
!define DESCRIPTION "The Janet Programming Language"
|
||||
!define HELPURL "http://janet-lang.org"
|
||||
BrandingText "The Janet Programming Language"
|
||||
|
||||
# Macros for setting registry values
|
||||
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\Janet-${VERSION}"
|
||||
!macro WriteEnv key value
|
||||
${If} $MultiUser.InstallMode == "AllUsers"
|
||||
WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "${key}" "${value}"
|
||||
${Else}
|
||||
WriteRegExpandStr HKCU "Environment" "${key}" "${value}"
|
||||
${EndIf}
|
||||
!macroend
|
||||
!macro DelEnv key
|
||||
${If} $MultiUser.InstallMode == "AllUsers"
|
||||
DeleteRegValue HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "${key}"
|
||||
${Else}
|
||||
DeleteRegValue HKCU "Environment" "${key}"
|
||||
${EndIf}
|
||||
!macroend
|
||||
|
||||
# MUI Configuration
|
||||
!define MUI_ICON "assets\icon.ico"
|
||||
!define MUI_UNICON "assets\icon.ico"
|
||||
!define MUI_HEADERIMAGE
|
||||
!define MUI_HEADERIMAGE_BITMAP "assets\janet-w200.png"
|
||||
!define MUI_HEADERIMAGE_RIGHT
|
||||
!define MUI_ABORTWARNING
|
||||
|
||||
# Show a welcome page first
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_LICENSE "LICENSE"
|
||||
|
||||
# Pick Install Directory
|
||||
!insertmacro MULTIUSER_PAGE_INSTALLMODE
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
# Done
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
# Need to set a language.
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
function .onInit
|
||||
!insertmacro MULTIUSER_INIT
|
||||
functionEnd
|
||||
|
||||
section "Janet" BfWSection
|
||||
createDirectory "$INSTDIR\Library"
|
||||
createDirectory "$INSTDIR\C"
|
||||
createDirectory "$INSTDIR\bin"
|
||||
createDirectory "$INSTDIR\docs"
|
||||
setOutPath "$INSTDIR"
|
||||
|
||||
# Bin files
|
||||
file /oname=bin\janet.exe dist\janet.exe
|
||||
file /oname=logo.ico assets\icon.ico
|
||||
file /oname=bin\jpm.janet auxbin\jpm
|
||||
file /oname=bin\jpm.bat tools\jpm.bat
|
||||
|
||||
# Modules
|
||||
file /oname=Library\cook.janet auxlib\cook.janet
|
||||
file /oname=Library\path.janet auxlib\path.janet
|
||||
|
||||
# C headers
|
||||
file /oname=C\janet.h dist\janet.h
|
||||
file /oname=C\janetconf.h dist\janetconf.h
|
||||
file /oname=C\janet.lib dist\janet.lib
|
||||
file /oname=C\janet.exp dist\janet.exp
|
||||
file /oname=C\janet.c dist\janet.c
|
||||
|
||||
# Documentation
|
||||
file /oname=docs\docs.html dist\doc.html
|
||||
|
||||
# Other
|
||||
file README.md
|
||||
file LICENSE
|
||||
|
||||
# Uninstaller - See function un.onInit and section "uninstall" for configuration
|
||||
writeUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
# Start Menu
|
||||
createShortCut "$SMPROGRAMS\Janet.lnk" "$INSTDIR\bin\janet.exe" "" "$INSTDIR\logo.ico"
|
||||
|
||||
# Set up Environment variables
|
||||
!insertmacro WriteEnv JANET_PATH "$INSTDIR\Library"
|
||||
!insertmacro WriteEnv JANET_HEADERPATH "$INSTDIR\C"
|
||||
!insertmacro WriteEnv JANET_BINPATH "$INSTDIR\bin"
|
||||
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
# Update path
|
||||
${EnvVarUpdate} $0 "PATH" "A" "HKCU" "$INSTDIR\bin" ; Append
|
||||
${EnvVarUpdate} $0 "PATH" "A" "HKLM" "$INSTDIR\bin" ; Append
|
||||
|
||||
# Registry information for add/remove programs
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayName" "Janet"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\logo.ico"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "Publisher" "Janet-Lang.org"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "HelpLink" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "URLUpdateInfo" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "URLInfoAbout" "${HELPURL}"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "DisplayVersion" "${VERSION}"
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "NoModify" 1
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "NoRepair" 1
|
||||
WriteRegDWORD SHCTX "${UNINST_KEY}" "EstimatedSize" 1000
|
||||
# Add uninstall
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\" /$MultiUser.InstallMode"
|
||||
WriteRegStr SHCTX "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /$MultiUser.InstallMode /S"
|
||||
|
||||
sectionEnd
|
||||
|
||||
# Uninstaller
|
||||
|
||||
function un.onInit
|
||||
!insertmacro MULTIUSER_UNINIT
|
||||
functionEnd
|
||||
|
||||
section "uninstall"
|
||||
|
||||
# Remove Start Menu launcher
|
||||
delete "$SMPROGRAMS\Janet.lnk"
|
||||
|
||||
# Remove files
|
||||
delete "$INSTDIR\logo.ico"
|
||||
delete "$INSTDIR\README.md"
|
||||
delete "$INSTDIR\LICENSE"
|
||||
rmdir /r "$INSTDIR\Library"
|
||||
rmdir /r "$INSTDIR\bin"
|
||||
rmdir /r "$INSTDIR\C"
|
||||
rmdir /r "$INSTDIR\docs"
|
||||
|
||||
# Remove env vars
|
||||
!insertmacro DelEnv JANET_PATH
|
||||
!insertmacro DelEnv JANET_HEADERPATH
|
||||
!insertmacro DelEnv JANET_BINPATH
|
||||
|
||||
# Unset PATH
|
||||
${un.EnvVarUpdate} $0 "PATH" "R" "HKCU" "$INSTDIR\bin" ; Remove
|
||||
${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" "$INSTDIR\bin" ; Remove
|
||||
|
||||
# make sure windows knows about the change
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
# Always delete uninstaller as the last action
|
||||
delete "$INSTDIR\uninstall.exe"
|
||||
|
||||
# Remove uninstaller information from the registry
|
||||
DeleteRegKey SHCTX "${UNINST_KEY}"
|
||||
sectionEnd
|
||||
77
janet.1
77
janet.1
@@ -1,25 +1,29 @@
|
||||
.TH JANET 1
|
||||
.SH NAME
|
||||
janet \- run the janet language abstract machine
|
||||
janet \- run the Janet language abstract machine
|
||||
.SH SYNOPSIS
|
||||
.B janet
|
||||
[\fB\-hvsrp\fR]
|
||||
[\fB\-e\fR \fIJANET SOURCE\fR]
|
||||
[\fB\-hvsrpnqk\fR]
|
||||
[\fB\-e\fR \fISOURCE\fR]
|
||||
[\fB\-l\fR \fIMODULE\fR]
|
||||
[\fB\-m\fR \fIPATH\fR]
|
||||
[\fB\-c\fR \fIMODULE JIMAGE\fR]
|
||||
[\fB\-\-\fR]
|
||||
.IR files ...
|
||||
.IR script
|
||||
.IR args ...
|
||||
.SH DESCRIPTION
|
||||
Janet is a functional and imperative programming language and bytecode interpreter.
|
||||
Janet is a functional and imperative programming language and bytecode interpreter.
|
||||
It is a modern lisp, but lists are replaced by other data structures with better utility
|
||||
and performance (arrays, tables, structs, tuples). The language also bridging bridging
|
||||
to native code written in C, meta-programming with macros, and bytecode assembly.
|
||||
|
||||
There is a repl for trying out the language, as well as the ability to run script files.
|
||||
This client program is separate from the core runtime, so janet could be embedded
|
||||
into other programs. Try janet in your browser at https://janet-lang.org.
|
||||
This client program is separate from the core runtime, so Janet could be embedded
|
||||
into other programs. Try Janet in your browser at https://janet-lang.org.
|
||||
|
||||
Implemented in mostly standard C99, janet runs on Windows, Linux and macOS.
|
||||
Implemented in mostly standard C99, Janet runs on Windows, Linux and macOS.
|
||||
The few features that are not standard C99 (dynamic library loading, compiler
|
||||
specific optimizations), are fairly straight forward. Janet can be easily ported to
|
||||
specific optimizations), are fairly straight forward. Janet can be easily ported to
|
||||
most new platforms.
|
||||
.SH DOCUMENTATION
|
||||
|
||||
@@ -37,37 +41,66 @@ Shows the version text and exits immediately.
|
||||
|
||||
.TP
|
||||
.BR \-s
|
||||
Read raw input from stdin, such as from a pipe without printing a prompt.
|
||||
Read raw input from stdin and forgo prompt history and other readline-like features.
|
||||
|
||||
.TP
|
||||
.BR \-e\ code
|
||||
Execute a string of Janet source. Source code is executed in the order it is encountered, so earlier
|
||||
arguments are executed before later ones.
|
||||
|
||||
.TP
|
||||
.BR \-n
|
||||
Disable ANSI colors in the repl. Has no effect if no repl is run.
|
||||
|
||||
.TP
|
||||
.BR \-r
|
||||
Open a REPL (Read Eval Print Loop) after executing all sources. By default, if janet is called with no
|
||||
Open a REPL (Read Eval Print Loop) after executing all sources. By default, if Janet is called with no
|
||||
arguments, a REPL is opened.
|
||||
|
||||
.TP
|
||||
.BR \-p
|
||||
Turn on the persistent flag. By default, when janet is executing commands from a file and encounters an error,
|
||||
it will immediately exit after printing the error message. In persistent mode, janet will keep executing commands
|
||||
Turn on the persistent flag. By default, when Janet is executing commands from a file and encounters an error,
|
||||
it will immediately exit after printing the error message. In persistent mode, Janet will keep executing commands
|
||||
after an error. Persistent mode can be good for debugging and testing.
|
||||
|
||||
.TP
|
||||
.BR \-e
|
||||
Execute a string of janet source. Source code is executed in the order it is encountered, so earlier
|
||||
arguments are executed before later ones.
|
||||
.BR \-q
|
||||
Quiet output. Don't print a repl prompt or expression results to stdout.
|
||||
|
||||
.TP
|
||||
.BR \-k
|
||||
Don't execute a script, only compile it to check for errors. Useful for linting scripts.
|
||||
|
||||
.TP
|
||||
.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.
|
||||
|
||||
.TP
|
||||
.BR \-c\ source\ output
|
||||
Precompiles Janet source code into an image, a binary dump that can be efficiently loaded later.
|
||||
Source should be a path to the Janet module to compile, and output should be the file path of
|
||||
resulting image. Output should usually end with the .jimage extension.
|
||||
|
||||
.TP
|
||||
.BR \-l\ path
|
||||
Load a Janet file before running a script or repl. Multiple files can be loaded
|
||||
in this manner, and exports from each file will be made available to the script
|
||||
or repl.
|
||||
|
||||
.TP
|
||||
.BR \-\-
|
||||
Stop parsing command line arguments. All arguments after this one will be considered file names.
|
||||
Stop parsing command line arguments. All arguments after this one will be considered file names
|
||||
and then arguments to the script.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
|
||||
.B JANET_PATH
|
||||
.RS
|
||||
The location to look for janet libraries. This is the only environment variable janet needs to
|
||||
find native and source code modules. If no JANET_PATH is set, janet will look in
|
||||
/usr/local/lib/janet for modules.
|
||||
To make janet search multiple locations, modify the module.paths
|
||||
array in janet.
|
||||
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.
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
|
||||
1
janet_win.rc
Normal file
1
janet_win.rc
Normal file
@@ -0,0 +1 @@
|
||||
IDI_MYICON ICON "assets\icon.ico"
|
||||
233
meson.build
Normal file
233
meson.build
Normal file
@@ -0,0 +1,233 @@
|
||||
# Copyright (c) 2019 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 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.
|
||||
|
||||
project('janet', 'c',
|
||||
default_options : ['c_std=c99', 'b_lundef=false', 'default_library=both'],
|
||||
version : '1.1.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
|
||||
cc = meson.get_compiler('c')
|
||||
m_dep = cc.find_library('m', required : false)
|
||||
dl_dep = cc.find_library('dl', required : false)
|
||||
|
||||
# Link options
|
||||
if build_machine.system() != 'windows'
|
||||
add_project_link_arguments('-rdynamic', language : 'c')
|
||||
endif
|
||||
|
||||
# Generate custom janetconf.h
|
||||
conf = configuration_data()
|
||||
version_parts = meson.project_version().split('.')
|
||||
last_parts = version_parts[2].split('-')
|
||||
if last_parts.length() > 1
|
||||
conf.set_quoted('JANET_VERSION_EXTRA', '-' + last_parts[1])
|
||||
else
|
||||
conf.set_quoted('JANET_VERSION_EXTRA', '')
|
||||
endif
|
||||
conf.set('JANET_VERSION_MAJOR', version_parts[0].to_int())
|
||||
conf.set('JANET_VERSION_MINOR', version_parts[1].to_int())
|
||||
conf.set('JANET_VERSION_PATCH', last_parts[0].to_int())
|
||||
conf.set_quoted('JANET_VERSION', meson.project_version())
|
||||
# Use options
|
||||
conf.set_quoted('JANET_BUILD', get_option('git_hash'))
|
||||
conf.set('JANET_NO_NANBOX', not get_option('nanbox'))
|
||||
conf.set('JANET_SINGLE_THREADED', not get_option('single_threaded'))
|
||||
conf.set('JANET_NO_DYNAMIC_MODULES', not get_option('dynamic_modules'))
|
||||
conf.set('JANET_NO_DOCSTRINGS', not get_option('docstrings'))
|
||||
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_REDUCED_OS', get_option('reduced_os'))
|
||||
conf.set('JANET_NO_TYPED_ARRAY', not get_option('typed_array'))
|
||||
conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
|
||||
conf.set('JANET_RECURSION_GUARD', get_option('recursion_guard'))
|
||||
conf.set('JANET_MAX_PROTO_DEPTH', get_option('max_proto_depth'))
|
||||
conf.set('JANET_MAX_MACRO_EXPAND', get_option('max_macro_expand'))
|
||||
conf.set('JANET_STACK_MAX', get_option('stack_max'))
|
||||
jconf = configure_file(output : 'janetconf.h',
|
||||
configuration : conf)
|
||||
|
||||
# Include directories
|
||||
incdir = include_directories(['src/include', '.'])
|
||||
|
||||
# Building generated sources
|
||||
xxd = executable('xxd', 'tools/xxd.c', native : true)
|
||||
gen = generator(xxd,
|
||||
output : '@BASENAME@.gen.c',
|
||||
arguments : ['@INPUT@', '@OUTPUT@', '@EXTRA_ARGS@'])
|
||||
boot_gen = gen.process('src/boot/boot.janet', extra_args: 'janet_gen_boot')
|
||||
init_gen = gen.process('src/mainclient/init.janet', extra_args: 'janet_gen_init')
|
||||
|
||||
# Order is important here, as some headers
|
||||
# depend on other headers for the amalg target
|
||||
core_headers = [
|
||||
'src/core/util.h',
|
||||
'src/core/state.h',
|
||||
'src/core/gc.h',
|
||||
'src/core/vector.h',
|
||||
'src/core/fiber.h',
|
||||
'src/core/regalloc.h',
|
||||
'src/core/compile.h',
|
||||
'src/core/emit.h',
|
||||
'src/core/symcache.h'
|
||||
]
|
||||
|
||||
core_src = [
|
||||
'src/core/abstract.c',
|
||||
'src/core/array.c',
|
||||
'src/core/asm.c',
|
||||
'src/core/buffer.c',
|
||||
'src/core/bytecode.c',
|
||||
'src/core/capi.c',
|
||||
'src/core/cfuns.c',
|
||||
'src/core/compile.c',
|
||||
'src/core/corelib.c',
|
||||
'src/core/debug.c',
|
||||
'src/core/emit.c',
|
||||
'src/core/fiber.c',
|
||||
'src/core/gc.c',
|
||||
'src/core/inttypes.c',
|
||||
'src/core/io.c',
|
||||
'src/core/marsh.c',
|
||||
'src/core/math.c',
|
||||
'src/core/os.c',
|
||||
'src/core/parse.c',
|
||||
'src/core/peg.c',
|
||||
'src/core/pp.c',
|
||||
'src/core/regalloc.c',
|
||||
'src/core/run.c',
|
||||
'src/core/specials.c',
|
||||
'src/core/string.c',
|
||||
'src/core/strtod.c',
|
||||
'src/core/struct.c',
|
||||
'src/core/symcache.c',
|
||||
'src/core/table.c',
|
||||
'src/core/tuple.c',
|
||||
'src/core/typedarray.c',
|
||||
'src/core/util.c',
|
||||
'src/core/value.c',
|
||||
'src/core/vector.c',
|
||||
'src/core/vm.c',
|
||||
'src/core/wrap.c'
|
||||
]
|
||||
|
||||
boot_src = [
|
||||
'src/boot/array_test.c',
|
||||
'src/boot/boot.c',
|
||||
'src/boot/buffer_test.c',
|
||||
'src/boot/number_test.c',
|
||||
'src/boot/system_test.c',
|
||||
'src/boot/table_test.c',
|
||||
]
|
||||
|
||||
mainclient_src = [
|
||||
'src/mainclient/line.c',
|
||||
'src/mainclient/main.c'
|
||||
]
|
||||
|
||||
# Build boot binary
|
||||
janet_boot = executable('janet-boot', core_src, boot_src, boot_gen,
|
||||
include_directories : incdir,
|
||||
c_args : '-DJANET_BOOTSTRAP',
|
||||
dependencies : [m_dep, dl_dep],
|
||||
native : true)
|
||||
|
||||
# Build core image
|
||||
core_image = custom_target('core_image',
|
||||
input : [janet_boot],
|
||||
output : 'core_image.gen.c',
|
||||
command : [janet_boot, '@OUTPUT@', 'JANET_PATH', janet_path, 'JANET_HEADERPATH', header_path])
|
||||
|
||||
libjanet = library('janet', core_src, core_image,
|
||||
include_directories : incdir,
|
||||
dependencies : [m_dep, dl_dep],
|
||||
install : true)
|
||||
|
||||
janet_mainclient = executable('janet', core_src, core_image, init_gen, mainclient_src,
|
||||
include_directories : incdir,
|
||||
dependencies : [m_dep, dl_dep],
|
||||
install : true)
|
||||
|
||||
if meson.is_cross_build()
|
||||
janet_nativeclient = executable('janet-native', core_src, core_image, init_gen, mainclient_src,
|
||||
include_directories : incdir,
|
||||
dependencies : [m_dep, dl_dep],
|
||||
native : true)
|
||||
else
|
||||
janet_nativeclient = janet_mainclient
|
||||
endif
|
||||
|
||||
# Documentation
|
||||
docs = custom_target('docs',
|
||||
input : ['tools/gendoc.janet'],
|
||||
output : ['doc.html'],
|
||||
capture : true,
|
||||
command : [janet_nativeclient, '@INPUT@'])
|
||||
|
||||
# Amalgamated source
|
||||
amalg = custom_target('amalg',
|
||||
input : ['tools/amalg.janet', core_headers, core_src, core_image],
|
||||
output : ['janet.c'],
|
||||
capture : true,
|
||||
command : [janet_nativeclient, '@INPUT@'])
|
||||
|
||||
# Amalgamated client
|
||||
janet_amalgclient = executable('janet-amalg', amalg, init_gen, mainclient_src,
|
||||
include_directories : incdir,
|
||||
dependencies : [m_dep, dl_dep],
|
||||
build_by_default : false)
|
||||
|
||||
# Tests
|
||||
test_files = [
|
||||
'test/suite0.janet',
|
||||
'test/suite1.janet',
|
||||
'test/suite2.janet',
|
||||
'test/suite3.janet',
|
||||
'test/suite4.janet',
|
||||
'test/suite5.janet',
|
||||
'test/suite6.janet'
|
||||
]
|
||||
foreach t : test_files
|
||||
test(t, janet_nativeclient, args : files([t]), workdir : meson.current_source_dir())
|
||||
endforeach
|
||||
|
||||
# Repl
|
||||
run_target('repl', command : [janet_nativeclient])
|
||||
|
||||
# For use as meson subproject (wrap)
|
||||
janet_dep = declare_dependency(include_directories : incdir,
|
||||
link_with : libjanet)
|
||||
|
||||
# Installation
|
||||
install_man('janet.1')
|
||||
install_headers(['src/include/janet.h', jconf], subdir: 'janet')
|
||||
janet_libs = [
|
||||
'auxlib/cook.janet',
|
||||
'auxlib/path.janet'
|
||||
]
|
||||
janet_binscripts = [
|
||||
'auxbin/jpm'
|
||||
]
|
||||
install_data(sources : janet_libs, install_dir : janet_path)
|
||||
install_data(sources : janet_binscripts, install_dir : 'bin')
|
||||
17
meson_options.txt
Normal file
17
meson_options.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
option('git_hash', type : 'string', value : 'meson')
|
||||
|
||||
option('single_threaded', type : 'boolean', value : false)
|
||||
option('nanbox', type : 'boolean', value : true)
|
||||
option('dynamic_modules', type : 'boolean', value : true)
|
||||
option('docstrings', type : 'boolean', value : true)
|
||||
option('sourcemaps', type : 'boolean', value : true)
|
||||
option('reduced_os', type : 'boolean', value : false)
|
||||
option('assembler', type : 'boolean', value : true)
|
||||
option('peg', type : 'boolean', value : true)
|
||||
option('typed_array', type : 'boolean', value : true)
|
||||
option('int_types', type : 'boolean', value : true)
|
||||
|
||||
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
|
||||
option('max_proto_depth', type : 'integer', min : 10, max : 8000, value : 200)
|
||||
option('max_macro_expand', type : 'integer', min : 1, max : 8000, value : 200)
|
||||
option('stack_max', type : 'integer', min : 8096, max : 1000000000, value : 16384)
|
||||
@@ -1,44 +0,0 @@
|
||||
# Copyright (c) 2018 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.
|
||||
|
||||
CFLAGS:=-std=c99 -Wall -Wextra -O2 -shared -fpic
|
||||
CFLAGS=-std=c99 -Wall -Wextra -I../../src/include -O2 -shared -fpic
|
||||
OBJECTS:=json.o
|
||||
TARGET:=json.so
|
||||
|
||||
# MacOS specifics
|
||||
UNAME:=$(shell uname -s)
|
||||
ifeq ($(UNAME), Darwin)
|
||||
CFLAGS:=$(CFLAGS) -undefined dynamic_lookup
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
%.o: %.c $(HEADERS)
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm $(OBJECTS)
|
||||
rm $(TARGET)
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -1,25 +0,0 @@
|
||||
@rem Generated batch script, run in 'Visual Studio Developer Prompt'
|
||||
|
||||
@rem
|
||||
|
||||
@echo off
|
||||
|
||||
cl /nologo /I..\..\src\include /c /O2 /W3 json.c
|
||||
@if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
link /nologo /dll ..\..\janet.lib /out:json.dll *.obj
|
||||
if errorlevel 1 goto :BUILDFAIL
|
||||
|
||||
@echo .
|
||||
@echo ======
|
||||
@echo Build Succeeded.
|
||||
@echo =====
|
||||
exit /b 0
|
||||
|
||||
:BUILDFAIL
|
||||
@echo .
|
||||
@echo =====
|
||||
@echo BUILD FAILED. See Output For Details.
|
||||
@echo =====
|
||||
@echo .
|
||||
exit /b 1
|
||||
@@ -1,605 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*****************/
|
||||
/* JSON Decoding */
|
||||
/*****************/
|
||||
|
||||
/* Check if a character is whitespace */
|
||||
static int white(uint8_t c) {
|
||||
return c == '\t' || c == '\n' || c == ' ' || c == '\r';
|
||||
}
|
||||
|
||||
/* Skip whitespace */
|
||||
static void skipwhite(const char **p) {
|
||||
const char *cp = *p;
|
||||
for (;;) {
|
||||
if (white(*cp))
|
||||
cp++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
*p = cp;
|
||||
}
|
||||
|
||||
/* Get a hex digit value */
|
||||
static int hexdig(char dig) {
|
||||
if (dig >= '0' && dig <= '9')
|
||||
return dig - '0';
|
||||
if (dig >= 'a' && dig <= 'f')
|
||||
return 10 + dig - 'a';
|
||||
if (dig >= 'A' && dig <= 'F')
|
||||
return 10 + dig - 'A';
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read the hex value for a unicode escape */
|
||||
static const char *decode_utf16_escape(const char *p, uint32_t *outpoint) {
|
||||
if (!p[0] || !p[1] || !p[2] || !p[3])
|
||||
return "unexpected end of source";
|
||||
int d1 = hexdig(p[0]);
|
||||
int d2 = hexdig(p[1]);
|
||||
int d3 = hexdig(p[2]);
|
||||
int d4 = hexdig(p[3]);
|
||||
if (d1 < 0 || d2 < 0 || d3 < 0 || d4 < 0)
|
||||
return "invalid hex digit";
|
||||
*outpoint = d4 | (d3 << 4) | (d2 << 8) | (d1 << 12);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Parse a string. Also handles the conversion of utf-16 to
|
||||
* utf-8. */
|
||||
static const char *decode_string(const char **p, Janet *out) {
|
||||
JanetBuffer *buffer = janet_buffer(0);
|
||||
const char *cp = *p;
|
||||
while (*cp != '"') {
|
||||
uint8_t b = (uint8_t) *cp;
|
||||
if (b < 32) return "invalid character in string";
|
||||
if (b == '\\') {
|
||||
cp++;
|
||||
switch(*cp) {
|
||||
default:
|
||||
return "unknown string escape";
|
||||
case 'b':
|
||||
b = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
b = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
b = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
b = '\r';
|
||||
break;
|
||||
case 't':
|
||||
b = '\t';
|
||||
break;
|
||||
case '"':
|
||||
b = '"';
|
||||
break;
|
||||
case '\\':
|
||||
b = '\\';
|
||||
break;
|
||||
case 'u':
|
||||
{
|
||||
/* Get codepoint and check for surrogate pair */
|
||||
uint32_t codepoint;
|
||||
const char *err = decode_utf16_escape(cp + 1, &codepoint);
|
||||
if (err) return err;
|
||||
if (codepoint >= 0xDC00 && codepoint <= 0xDFFF) {
|
||||
return "unexpected utf-16 low surrogate";
|
||||
} else if (codepoint >= 0xD800 && codepoint <= 0xDBFF) {
|
||||
if (cp[5] != '\\') return "expected utf-16 low surrogate pair";
|
||||
if (cp[6] != 'u') return "expected utf-16 low surrogate pair";
|
||||
uint32_t lowsur;
|
||||
const char *err = decode_utf16_escape(cp + 7, &lowsur);
|
||||
if (err) return err;
|
||||
if (lowsur < 0xDC00 || lowsur > 0xDFFF)
|
||||
return "expected utf-16 low surrogate pair";
|
||||
codepoint = ((codepoint - 0xD800) << 10) +
|
||||
(lowsur - 0xDC00) + 0x10000;
|
||||
cp += 11;
|
||||
} else {
|
||||
cp += 5;
|
||||
}
|
||||
/* Write codepoint */
|
||||
if (codepoint <= 0x7F) {
|
||||
janet_buffer_push_u8(buffer, codepoint);
|
||||
} else if (codepoint <= 0x7FF) {
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 6) & 0x1F) | 0xC0);
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 0) & 0x3F) | 0x80);
|
||||
} else if (codepoint <= 0xFFFF) {
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 12) & 0x0F) | 0xE0);
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 6) & 0x3F) | 0x80);
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 0) & 0x3F) | 0x80);
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 18) & 0x07) | 0xF0);
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 12) & 0x3F) | 0x80);
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 6) & 0x3F) | 0x80);
|
||||
janet_buffer_push_u8(buffer, ((codepoint >> 0) & 0x3F) | 0x80);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
janet_buffer_push_u8(buffer, b);
|
||||
cp++;
|
||||
}
|
||||
*out = janet_stringv(buffer->data, buffer->count);
|
||||
*p = cp + 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *decode_one(const char **p, Janet *out, int depth) {
|
||||
|
||||
/* Prevent stack overflow */
|
||||
if (depth > JANET_RECURSION_GUARD) goto recurdepth;
|
||||
|
||||
/* Skip leading whitepspace */
|
||||
skipwhite(p);
|
||||
|
||||
/* Main switch */
|
||||
switch (**p) {
|
||||
default:
|
||||
goto badchar;
|
||||
case '\0':
|
||||
goto eos;
|
||||
/* Numbers */
|
||||
case '-': case '0': case '1' : case '2': case '3' : case '4':
|
||||
case '5': case '6': case '7' : case '8': case '9':
|
||||
{
|
||||
errno = 0;
|
||||
char *end = NULL;
|
||||
double x = strtod(*p, &end);
|
||||
if (end == *p) goto badnum;
|
||||
*p = end;
|
||||
*out = janet_wrap_real(x);
|
||||
break;
|
||||
}
|
||||
/* false, null, true */
|
||||
case 'f':
|
||||
{
|
||||
const char *cp = *p;
|
||||
if (cp[1] != 'a' || cp[2] != 'l' || cp[3] != 's' || cp[4] != 'e')
|
||||
goto badident;
|
||||
*out = janet_wrap_false();
|
||||
*p = cp + 5;
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
{
|
||||
const char *cp = *p;
|
||||
|
||||
if (cp[1] != 'u' || cp[2] != 'l' || cp[3] != 'l')
|
||||
goto badident;
|
||||
*out = janet_wrap_nil();
|
||||
*p = cp + 4;
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
{
|
||||
const char *cp = *p;
|
||||
if (cp[1] != 'r' || cp[2] != 'u' || cp[3] != 'e')
|
||||
goto badident;
|
||||
*out = janet_wrap_true();
|
||||
*p = cp + 4;
|
||||
break;
|
||||
}
|
||||
/* String */
|
||||
case '"':
|
||||
{
|
||||
const char *cp = *p + 1;
|
||||
const char *start = cp;
|
||||
while (*cp >= 32 && *cp != '"' && *cp != '\\')
|
||||
cp++;
|
||||
/* Only use a buffer for strings with escapes, else just copy
|
||||
* memory from source */
|
||||
if (*cp == '\\') {
|
||||
*p = *p + 1;
|
||||
const char *err = decode_string(p, out);
|
||||
if (err) return err;
|
||||
break;
|
||||
}
|
||||
if (*cp != '"') goto badchar;
|
||||
*p = cp + 1;
|
||||
*out = janet_stringv((const uint8_t *)start, cp - start);
|
||||
break;
|
||||
}
|
||||
/* Array */
|
||||
case '[':
|
||||
{
|
||||
*p = *p + 1;
|
||||
JanetArray *array = janet_array(0);
|
||||
const char *err;
|
||||
Janet subval;
|
||||
skipwhite(p);
|
||||
while (**p != ']') {
|
||||
err = decode_one(p, &subval, depth + 1);
|
||||
if (err) return err;
|
||||
janet_array_push(array, subval);
|
||||
skipwhite(p);
|
||||
if (**p == ']') break;
|
||||
if (**p != ',') goto wantcomma;
|
||||
*p = *p + 1;
|
||||
}
|
||||
*p = *p + 1;
|
||||
*out = janet_wrap_array(array);
|
||||
}
|
||||
break;
|
||||
/* Object */
|
||||
case '{':
|
||||
{
|
||||
*p = *p + 1;
|
||||
JanetTable *table = janet_table(0);
|
||||
const char *err;
|
||||
Janet subkey, subval;
|
||||
skipwhite(p);
|
||||
while (**p != '}') {
|
||||
skipwhite(p);
|
||||
if (**p != '"') goto wantstring;
|
||||
err = decode_one(p, &subkey, depth + 1);
|
||||
if (err) return err;
|
||||
skipwhite(p);
|
||||
if (**p != ':') goto wantcolon;
|
||||
*p = *p + 1;
|
||||
err = decode_one(p, &subval, depth + 1);
|
||||
if (err) return err;
|
||||
janet_table_put(table, subkey, subval);
|
||||
skipwhite(p);
|
||||
if (**p == '}') break;
|
||||
if (**p != ',') goto wantcomma;
|
||||
*p = *p + 1;
|
||||
}
|
||||
*p = *p + 1;
|
||||
*out = janet_wrap_table(table);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Good return */
|
||||
return NULL;
|
||||
|
||||
/* Errors */
|
||||
recurdepth:
|
||||
return "recured too deeply";
|
||||
eos:
|
||||
return "unexpected end of source";
|
||||
badident:
|
||||
return "bad identifier";
|
||||
badnum:
|
||||
return "bad number";
|
||||
wantcomma:
|
||||
return "expected comma";
|
||||
wantcolon:
|
||||
return "expected colon";
|
||||
badchar:
|
||||
return "unexpected character";
|
||||
wantstring:
|
||||
return "expected json string";
|
||||
}
|
||||
|
||||
static int json_decode(JanetArgs args) {
|
||||
Janet ret;
|
||||
JANET_FIXARITY(args, 1);
|
||||
const char *err;
|
||||
const char *start;
|
||||
const char *p;
|
||||
if (janet_checktype(args.v[0], JANET_BUFFER)) {
|
||||
JanetBuffer *buffer = janet_unwrap_buffer(args.v[0]);
|
||||
/* Ensure 0 padded */
|
||||
janet_buffer_push_u8(buffer, 0);
|
||||
start = p = (const char *)buffer->data;
|
||||
err = decode_one(&p, &ret, 0);
|
||||
buffer->count--;
|
||||
} else {
|
||||
const uint8_t *bytes;
|
||||
int32_t len;
|
||||
JANET_ARG_BYTES(bytes, len, args, 0);
|
||||
start = p = (const char *)bytes;
|
||||
err = decode_one(&p, &ret, 0);
|
||||
}
|
||||
/* Check trailing values */
|
||||
if (!err) {
|
||||
skipwhite(&p);
|
||||
if (*p)
|
||||
err = "unexpected extra token";
|
||||
}
|
||||
if (err) {
|
||||
JANET_THROWV(args, janet_wrap_string(janet_formatc(
|
||||
"decode error at postion %d: %s",
|
||||
p - start,
|
||||
err)));
|
||||
}
|
||||
JANET_RETURN(args, ret);
|
||||
}
|
||||
|
||||
/*****************/
|
||||
/* JSON Encoding */
|
||||
/*****************/
|
||||
|
||||
typedef struct {
|
||||
JanetBuffer *buffer;
|
||||
int32_t indent;
|
||||
const uint8_t *tab;
|
||||
const uint8_t *newline;
|
||||
int32_t tablen;
|
||||
int32_t newlinelen;
|
||||
} Encoder;
|
||||
|
||||
static const char *encode_newline(Encoder *e) {
|
||||
if (janet_buffer_push_bytes(e->buffer, e->newline, e->newlinelen))
|
||||
return "buffer overflow";
|
||||
/* Skip loop if no tab string */
|
||||
if (e->tablen) {
|
||||
for (int32_t i = 0; i < e->indent; i++)
|
||||
if (janet_buffer_push_bytes(e->buffer, e->tab, e->tablen))
|
||||
return "buffer overflow";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *encode_one(Encoder *e, Janet x, int depth) {
|
||||
switch(janet_type(x)) {
|
||||
default:
|
||||
goto badtype;
|
||||
case JANET_NIL:
|
||||
{
|
||||
if (janet_buffer_push_cstring(e->buffer, "null"))
|
||||
goto overflow;
|
||||
}
|
||||
break;
|
||||
case JANET_FALSE:
|
||||
{
|
||||
if (janet_buffer_push_cstring(e->buffer, "false"))
|
||||
goto overflow;
|
||||
}
|
||||
break;
|
||||
case JANET_TRUE:
|
||||
{
|
||||
if (janet_buffer_push_cstring(e->buffer, "true"))
|
||||
goto overflow;
|
||||
}
|
||||
break;
|
||||
case JANET_INTEGER:
|
||||
{
|
||||
char cbuf[20];
|
||||
sprintf(cbuf, "%d", janet_unwrap_integer(x));
|
||||
if (janet_buffer_push_cstring(e->buffer, cbuf))
|
||||
goto overflow;
|
||||
}
|
||||
break;
|
||||
case JANET_REAL:
|
||||
{
|
||||
char cbuf[25];
|
||||
sprintf(cbuf, "%.17g", janet_unwrap_real(x));
|
||||
if (janet_buffer_push_cstring(e->buffer, cbuf))
|
||||
goto overflow;
|
||||
}
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_BUFFER:
|
||||
{
|
||||
const uint8_t *bytes;
|
||||
const uint8_t *c;
|
||||
const uint8_t *end;
|
||||
int32_t len;
|
||||
janet_bytes_view(x, &bytes, &len);
|
||||
if (janet_buffer_push_u8(e->buffer, '"')) goto overflow;
|
||||
c = bytes;
|
||||
end = bytes + len;
|
||||
while (c < end) {
|
||||
|
||||
/* get codepoint */
|
||||
uint32_t codepoint;
|
||||
if (*c < 0x80) {
|
||||
/* one byte */
|
||||
codepoint = *c++;
|
||||
} else if (*c < 0xE0) {
|
||||
/* two bytes */
|
||||
if (c + 2 > end) goto overflow;
|
||||
codepoint = ((c[0] & 0x1F) << 6) |
|
||||
(c[1] & 0x3F);
|
||||
c += 2;
|
||||
} else if (*c < 0xF0) {
|
||||
/* three bytes */
|
||||
if (c + 3 > end) goto overflow;
|
||||
codepoint = ((c[0] & 0x0F) << 12) |
|
||||
((c[1] & 0x3F) << 6) |
|
||||
(c[2] & 0x3F);
|
||||
c += 3;
|
||||
} else if (*c < 0xF8) {
|
||||
/* four bytes */
|
||||
if (c + 4 > end) goto overflow;
|
||||
codepoint = ((c[0] & 0x07) << 18) |
|
||||
((c[1] & 0x3F) << 12) |
|
||||
((c[3] & 0x3F) << 6) |
|
||||
(c[3] & 0x3F);
|
||||
c += 4;
|
||||
} else {
|
||||
/* invalid */
|
||||
goto invalidutf8;
|
||||
}
|
||||
|
||||
/* write codepoint */
|
||||
if (codepoint > 0x1F && codepoint < 0x80) {
|
||||
/* Normal, no escape */
|
||||
if (codepoint == '\\' || codepoint == '"')
|
||||
if (janet_buffer_push_u8(e->buffer, '\\'))
|
||||
goto overflow;
|
||||
if (janet_buffer_push_u8(e->buffer, (uint8_t) codepoint))
|
||||
goto overflow;
|
||||
} else if (codepoint < 0x10000) {
|
||||
/* One unicode escape */
|
||||
uint8_t buf[6];
|
||||
buf[0] = '\\';
|
||||
buf[1] = 'u';
|
||||
buf[2] = (codepoint >> 12) & 0xF;
|
||||
buf[3] = (codepoint >> 8) & 0xF;
|
||||
buf[4] = (codepoint >> 4) & 0xF;
|
||||
buf[5] = codepoint & 0xF;
|
||||
if (janet_buffer_push_bytes(e->buffer, buf, sizeof(buf)))
|
||||
goto overflow;
|
||||
} else {
|
||||
/* Two unicode escapes (surrogate pair) */
|
||||
uint32_t hi, lo;
|
||||
uint8_t buf[12];
|
||||
hi = ((codepoint - 0x10000) >> 10) + 0xD800;
|
||||
lo = ((codepoint - 0x10000) & 0x3FF) + 0xDC00;
|
||||
buf[0] = '\\';
|
||||
buf[1] = 'u';
|
||||
buf[2] = (hi >> 12) & 0xF;
|
||||
buf[3] = (hi >> 8) & 0xF;
|
||||
buf[4] = (hi >> 4) & 0xF;
|
||||
buf[5] = hi & 0xF;
|
||||
buf[6] = '\\';
|
||||
buf[7] = 'u';
|
||||
buf[8] = (lo >> 12) & 0xF;
|
||||
buf[9] = (lo >> 8) & 0xF;
|
||||
buf[10] = (lo >> 4) & 0xF;
|
||||
buf[11] = lo & 0xF;
|
||||
if (janet_buffer_push_bytes(e->buffer, buf, sizeof(buf)))
|
||||
goto overflow;
|
||||
}
|
||||
}
|
||||
if (janet_buffer_push_u8(e->buffer, '"')) goto overflow;
|
||||
}
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
case JANET_ARRAY:
|
||||
{
|
||||
const char *err;
|
||||
const Janet *items;
|
||||
int32_t len;
|
||||
janet_indexed_view(x, &items, &len);
|
||||
if (janet_buffer_push_u8(e->buffer, '[')) goto overflow;
|
||||
e->indent++;
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
if ((err = encode_newline(e))) return err;
|
||||
if ((err = encode_one(e, items[i], depth + 1)))
|
||||
return err;
|
||||
if (janet_buffer_push_u8(e->buffer, ','))
|
||||
goto overflow;
|
||||
}
|
||||
e->indent--;
|
||||
if (e->buffer->data[e->buffer->count - 1] == ',') {
|
||||
e->buffer->count--;
|
||||
if ((err = encode_newline(e))) return err;
|
||||
}
|
||||
if (janet_buffer_push_u8(e->buffer, ']')) goto overflow;
|
||||
}
|
||||
break;
|
||||
case JANET_TABLE:
|
||||
case JANET_STRUCT:
|
||||
{
|
||||
const char *err;
|
||||
const JanetKV *kvs;
|
||||
int32_t count, capacity;
|
||||
janet_dictionary_view(x, &kvs, &count, &capacity);
|
||||
if (janet_buffer_push_u8(e->buffer, '{')) goto overflow;
|
||||
e->indent++;
|
||||
for (int32_t i = 0; i < capacity; i++) {
|
||||
if (janet_checktype(kvs[i].key, JANET_NIL))
|
||||
continue;
|
||||
if (!janet_checktype(kvs[i].key, JANET_STRING))
|
||||
return "only strings keys are allowed in objects";
|
||||
if ((err = encode_newline(e))) return err;
|
||||
if ((err = encode_one(e, kvs[i].key, depth + 1)))
|
||||
return err;
|
||||
const char *sep = e->tablen ? ": " : ":";
|
||||
if (janet_buffer_push_cstring(e->buffer, sep))
|
||||
goto overflow;
|
||||
if ((err = encode_one(e, kvs[i].value, depth + 1)))
|
||||
return err;
|
||||
if (janet_buffer_push_u8(e->buffer, ','))
|
||||
goto overflow;
|
||||
}
|
||||
e->indent--;
|
||||
if (e->buffer->data[e->buffer->count - 1] == ',') {
|
||||
e->buffer->count--;
|
||||
if ((err = encode_newline(e))) return err;
|
||||
}
|
||||
if (janet_buffer_push_u8(e->buffer, '}')) goto overflow;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
/* Errors */
|
||||
overflow:
|
||||
return "buffer overflow";
|
||||
badtype:
|
||||
return "type not supported";
|
||||
invalidutf8:
|
||||
return "string contains invalid utf-8";
|
||||
}
|
||||
|
||||
static int json_encode(JanetArgs args) {
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_MAXARITY(args, 3);
|
||||
Encoder e;
|
||||
e.indent = 0;
|
||||
e.buffer = janet_buffer(10);
|
||||
e.tab = NULL;
|
||||
e.newline = NULL;
|
||||
e.tablen = 0;
|
||||
e.newlinelen = 0;
|
||||
if (args.n >= 2) {
|
||||
JANET_ARG_BYTES(e.tab, e.tablen, args, 1);
|
||||
if (args.n >= 3) {
|
||||
JANET_ARG_BYTES(e.newline, e.newlinelen, args, 2);
|
||||
} else {
|
||||
e.newline = (const uint8_t *)"\r\n";
|
||||
e.newlinelen = 2;
|
||||
}
|
||||
}
|
||||
const char *err = encode_one(&e, args.v[0], 0);
|
||||
if (err) JANET_THROW(args, err);
|
||||
JANET_RETURN_BUFFER(args, e.buffer);
|
||||
}
|
||||
|
||||
/****************/
|
||||
/* Module Entry */
|
||||
/****************/
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"encode", json_encode,
|
||||
"(json/encode x)\n\n"
|
||||
"Encodes a janet value in JSON (utf-8)."
|
||||
},
|
||||
{"decode", json_decode,
|
||||
"(json/decode json-source)\n\n"
|
||||
"Returns a janet object after parsing JSON."
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
JANET_MODULE_ENTRY(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, "json", cfuns);
|
||||
return 0;
|
||||
}
|
||||
62
natives/sqlite3/.gitignore
vendored
62
natives/sqlite3/.gitignore
vendored
@@ -1,62 +0,0 @@
|
||||
# Created by https://www.gitignore.io/api/c
|
||||
|
||||
### C ###
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
|
||||
# End of https://www.gitignore.io/api/c
|
||||
|
||||
sqlite3.c
|
||||
sqlite3.h
|
||||
sqlite-autoconf-3230100
|
||||
@@ -1,60 +0,0 @@
|
||||
# Copyright (c) 2018 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.
|
||||
|
||||
CFLAGS=-std=c99 -Wall -Wextra -I../../src/include -O2 -shared -fpic \
|
||||
-DSQLITE_THREADSAFE=0 \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION
|
||||
TARGET=sqlite3.so
|
||||
|
||||
# MacOS specifics
|
||||
UNAME:=$(shell uname -s)
|
||||
ifeq ($(UNAME), Darwin)
|
||||
CFLAGS:=$(CFLAGS) -undefined dynamic_lookup
|
||||
endif
|
||||
|
||||
# Default target
|
||||
all: $(TARGET)
|
||||
|
||||
OBJECTS:=main.o sqlite3.o
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
sqlite-autoconf-3230100/sqlite3.%:
|
||||
curl https://www.sqlite.org/2018/sqlite-autoconf-3230100.tar.gz | tar -xvz
|
||||
|
||||
sqlite3.c: sqlite-autoconf-3230100/sqlite3.c
|
||||
cp $< $@
|
||||
sqlite3.h: sqlite-autoconf-3230100/sqlite3.h
|
||||
cp $< $@
|
||||
|
||||
%.o: %.c sqlite3.h
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
clean:
|
||||
rm -rf sqlite-autoconf-3230100
|
||||
rm *.o
|
||||
rm sqlite3.c
|
||||
rm sqlite3.h
|
||||
rm $(TARGET)
|
||||
|
||||
install:
|
||||
cp $(TARGET) $(DST_PATH)
|
||||
|
||||
.PHONY: clean all
|
||||
@@ -1,428 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include <janet/janet.h>
|
||||
|
||||
#define FLAG_CLOSED 1
|
||||
|
||||
#define MSG_DB_CLOSED "database already closed"
|
||||
|
||||
typedef struct {
|
||||
sqlite3* handle;
|
||||
int flags;
|
||||
} Db;
|
||||
|
||||
/* Close a db, noop if already closed */
|
||||
static void closedb(Db *db) {
|
||||
if (!(db->flags & FLAG_CLOSED)) {
|
||||
db->flags |= FLAG_CLOSED;
|
||||
sqlite3_close_v2(db->handle);
|
||||
}
|
||||
}
|
||||
|
||||
/* Called to garbage collect a sqlite3 connection */
|
||||
static int gcsqlite(void *p, size_t s) {
|
||||
(void) s;
|
||||
Db *db = (Db *)p;
|
||||
closedb(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const JanetAbstractType sql_conn_type = {
|
||||
":sqlite3.connection",
|
||||
gcsqlite,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Open a new database connection */
|
||||
static int sql_open(JanetArgs args) {
|
||||
sqlite3 *conn;
|
||||
const uint8_t *filename;
|
||||
int status;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_STRING(filename, args, 0);
|
||||
status = sqlite3_open((const char *)filename, &conn);
|
||||
if (status == SQLITE_OK) {
|
||||
Db *db = (Db *) janet_abstract(&sql_conn_type, sizeof(Db));
|
||||
db->handle = conn;
|
||||
db->flags = 0;
|
||||
JANET_RETURN_ABSTRACT(args, db);
|
||||
} else {
|
||||
const char *err = sqlite3_errmsg(conn);
|
||||
JANET_THROW(args, err);
|
||||
}
|
||||
}
|
||||
|
||||
/* Close a database connection */
|
||||
static int sql_close(JanetArgs args) {
|
||||
Db *db;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_ABSTRACT(db, args, 0, &sql_conn_type);
|
||||
closedb(db);
|
||||
JANET_RETURN_NIL(args);
|
||||
}
|
||||
|
||||
/* Check for embedded NULL bytes */
|
||||
static int has_null(const uint8_t *str, int32_t len) {
|
||||
while (len--) {
|
||||
if (!str[len])
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bind a single parameter */
|
||||
static const char *bind1(sqlite3_stmt *stmt, int index, Janet value) {
|
||||
int res;
|
||||
switch (janet_type(value)) {
|
||||
default:
|
||||
return "invalid sql value";
|
||||
case JANET_NIL:
|
||||
res = sqlite3_bind_null(stmt, index);
|
||||
break;
|
||||
case JANET_FALSE:
|
||||
res = sqlite3_bind_int(stmt, index, 0);
|
||||
break;
|
||||
case JANET_TRUE:
|
||||
res = sqlite3_bind_int(stmt, index, 1);
|
||||
break;
|
||||
case JANET_REAL:
|
||||
res = sqlite3_bind_double(stmt, index, janet_unwrap_real(value));
|
||||
break;
|
||||
case JANET_INTEGER:
|
||||
res = sqlite3_bind_int64(stmt, index, janet_unwrap_integer(value));
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
{
|
||||
const uint8_t *str = janet_unwrap_string(value);
|
||||
int32_t len = janet_string_length(str);
|
||||
if (has_null(str, len)) {
|
||||
return "cannot have embedded nulls in text values";
|
||||
} else {
|
||||
res = sqlite3_bind_text(stmt, index, (const char *)str, len + 1, SQLITE_STATIC);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JANET_BUFFER:
|
||||
{
|
||||
JanetBuffer *buffer = janet_unwrap_buffer(value);
|
||||
res = sqlite3_bind_blob(stmt, index, buffer->data, buffer->count, SQLITE_STATIC);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (res != SQLITE_OK) {
|
||||
sqlite3 *db = sqlite3_db_handle(stmt);
|
||||
return sqlite3_errmsg(db);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Bind many parameters */
|
||||
static const char *bindmany(sqlite3_stmt *stmt, Janet params) {
|
||||
/* parameters */
|
||||
const Janet *seq;
|
||||
const JanetKV *kvs;
|
||||
int32_t len, cap;
|
||||
int limitindex = sqlite3_bind_parameter_count(stmt);
|
||||
if (janet_indexed_view(params, &seq, &len)) {
|
||||
if (len > limitindex + 1) {
|
||||
return "invalid index in sql parameters";
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
const char *err = bind1(stmt, i + 1, seq[i]);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else if (janet_dictionary_view(params, &kvs, &len, &cap)) {
|
||||
for (int i = 0; i < cap; i++) {
|
||||
int index = 0;
|
||||
switch (janet_type(kvs[i].key)) {
|
||||
default:
|
||||
/* Will fail */
|
||||
break;
|
||||
case JANET_NIL:
|
||||
/* Will skip as nil keys indicate empty hash table slot */
|
||||
continue;
|
||||
case JANET_INTEGER:
|
||||
index = janet_unwrap_integer(kvs[i].key);
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
{
|
||||
const uint8_t *s = janet_unwrap_string(kvs[i].key);
|
||||
index = sqlite3_bind_parameter_index(
|
||||
stmt,
|
||||
(const char *)s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (index <= 0 || index > limitindex) {
|
||||
return "invalid index in sql parameters";
|
||||
}
|
||||
const char *err = bind1(stmt, index, kvs[i].value);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return "invalid type for sql parameters";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Execute a statement but don't collect results */
|
||||
static const char *execute(sqlite3_stmt *stmt) {
|
||||
int status;
|
||||
const char *ret = NULL;
|
||||
do {
|
||||
status = sqlite3_step(stmt);
|
||||
} while (status == SQLITE_ROW);
|
||||
/* Check for errors */
|
||||
if (status != SQLITE_DONE) {
|
||||
sqlite3 *db = sqlite3_db_handle(stmt);
|
||||
ret = sqlite3_errmsg(db);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Execute and return values from prepared statement */
|
||||
static const char *execute_collect(sqlite3_stmt *stmt, JanetArray *rows) {
|
||||
/* Count number of columns in result */
|
||||
int ncol = sqlite3_column_count(stmt);
|
||||
int status;
|
||||
const char *ret = NULL;
|
||||
|
||||
/* Get column names */
|
||||
Janet *tupstart = janet_tuple_begin(ncol);
|
||||
for (int i = 0; i < ncol; i++) {
|
||||
tupstart[i] = janet_cstringv(sqlite3_column_name(stmt, i));
|
||||
}
|
||||
const Janet *colnames = janet_tuple_end(tupstart);
|
||||
|
||||
do {
|
||||
status = sqlite3_step(stmt);
|
||||
if (status == SQLITE_ROW) {
|
||||
JanetKV *row = janet_struct_begin(ncol);
|
||||
for (int i = 0; i < ncol; i++) {
|
||||
int t = sqlite3_column_type(stmt, i);
|
||||
Janet value;
|
||||
switch (t) {
|
||||
case SQLITE_NULL:
|
||||
value = janet_wrap_nil();
|
||||
break;
|
||||
case SQLITE_INTEGER:
|
||||
value = janet_wrap_integer(sqlite3_column_int(stmt, i));
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
value = janet_wrap_real(sqlite3_column_double(stmt, i));
|
||||
break;
|
||||
case SQLITE_TEXT:
|
||||
{
|
||||
int nbytes = sqlite3_column_bytes(stmt, i);
|
||||
uint8_t *str = janet_string_begin(nbytes);
|
||||
memcpy(str, sqlite3_column_text(stmt, i), nbytes);
|
||||
value = janet_wrap_string(janet_string_end(str));
|
||||
}
|
||||
break;
|
||||
case SQLITE_BLOB:
|
||||
{
|
||||
int nbytes = sqlite3_column_bytes(stmt, i);
|
||||
JanetBuffer *b = janet_buffer(nbytes);
|
||||
memcpy(b->data, sqlite3_column_blob(stmt, i), nbytes);
|
||||
b->count = nbytes;
|
||||
value = janet_wrap_buffer(b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
janet_struct_put(row, colnames[i], value);
|
||||
}
|
||||
janet_array_push(rows, janet_wrap_struct(janet_struct_end(row)));
|
||||
}
|
||||
} while (status == SQLITE_ROW);
|
||||
|
||||
/* Check for errors */
|
||||
if (status != SQLITE_DONE) {
|
||||
sqlite3 *db = sqlite3_db_handle(stmt);
|
||||
ret = sqlite3_errmsg(db);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Evaluate a string of sql */
|
||||
static int sql_eval(JanetArgs args) {
|
||||
const char *err;
|
||||
sqlite3_stmt *stmt = NULL, *stmt_next = NULL;
|
||||
const uint8_t *query;
|
||||
|
||||
JANET_MINARITY(args, 2);
|
||||
JANET_MAXARITY(args, 3);
|
||||
JANET_CHECKABSTRACT(args, 0, &sql_conn_type);
|
||||
Db *db = (Db *)janet_unwrap_abstract(args.v[0]);
|
||||
if (db->flags & FLAG_CLOSED) {
|
||||
JANET_THROW(args, MSG_DB_CLOSED);
|
||||
}
|
||||
JANET_ARG_STRING(query, args, 1);
|
||||
if (has_null(query, janet_string_length(query))) {
|
||||
err = "cannot have embedded NULL in sql statememts";
|
||||
goto error;
|
||||
}
|
||||
JanetArray *rows = janet_array(10);
|
||||
const char *c = (const char *)query;
|
||||
|
||||
/* Evaluate all statements in a loop */
|
||||
do {
|
||||
/* Compile the next statement */
|
||||
if (sqlite3_prepare_v2(db->handle, c, -1, &stmt_next, &c) != SQLITE_OK) {
|
||||
err = sqlite3_errmsg(db->handle);
|
||||
goto error;
|
||||
}
|
||||
/* Check if we have found last statement */
|
||||
if (NULL == stmt_next) {
|
||||
/* Execute current statement and collect results */
|
||||
if (stmt) {
|
||||
err = execute_collect(stmt, rows);
|
||||
if (err) goto error;
|
||||
}
|
||||
} else {
|
||||
/* Execute current statement but don't collect results. */
|
||||
if (stmt) {
|
||||
err = execute(stmt);
|
||||
if (err) goto error;
|
||||
}
|
||||
/* Bind params to next statement*/
|
||||
if (args.n == 3) {
|
||||
/* parameters */
|
||||
err = bindmany(stmt_next, args.v[2]);
|
||||
if (err) goto error;
|
||||
}
|
||||
}
|
||||
/* rotate stmt and stmt_next */
|
||||
if (stmt) sqlite3_finalize(stmt);
|
||||
stmt = stmt_next;
|
||||
stmt_next = NULL;
|
||||
} while (NULL != stmt);
|
||||
|
||||
/* Good return path */
|
||||
JANET_RETURN_ARRAY(args, rows);
|
||||
|
||||
error:
|
||||
if (stmt) sqlite3_finalize(stmt);
|
||||
if (stmt_next) sqlite3_finalize(stmt_next);
|
||||
JANET_THROW(args, err);
|
||||
}
|
||||
|
||||
/* Convert int64_t to a string */
|
||||
static const uint8_t *coerce_int64(int64_t x) {
|
||||
uint8_t bytes[40];
|
||||
int i = 0;
|
||||
/* Edge cases */
|
||||
if (x == 0) return janet_cstring("0");
|
||||
if (x == INT64_MIN) return janet_cstring("-9,223,372,036,854,775,808");
|
||||
/* Negative becomes pos */
|
||||
if (x < 0) {
|
||||
bytes[i++] = '-';
|
||||
x = -x;
|
||||
}
|
||||
while (x) {
|
||||
bytes[i++] = x % 10;
|
||||
x = x / 10;
|
||||
}
|
||||
bytes[i] = '\0';
|
||||
return janet_string(bytes, i);
|
||||
}
|
||||
|
||||
/* Gets the last inserted row id */
|
||||
static int sql_last_insert_rowid(JanetArgs args) {
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_CHECKABSTRACT(args, 0, &sql_conn_type);
|
||||
Db *db = (Db *)janet_unwrap_abstract(args.v[0]);
|
||||
if (db->flags & FLAG_CLOSED) {
|
||||
JANET_THROW(args, MSG_DB_CLOSED);
|
||||
}
|
||||
sqlite3_int64 id = sqlite3_last_insert_rowid(db->handle);
|
||||
if (id >= INT32_MIN && id <= INT32_MAX) {
|
||||
JANET_RETURN_INTEGER(args, (int32_t) id);
|
||||
}
|
||||
/* Convert to string */
|
||||
JANET_RETURN_STRING(args, coerce_int64(id));
|
||||
}
|
||||
|
||||
/* Get the sqlite3 errcode */
|
||||
static int sql_error_code(JanetArgs args) {
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_CHECKABSTRACT(args, 0, &sql_conn_type);
|
||||
Db *db = (Db *)janet_unwrap_abstract(args.v[0]);
|
||||
if (db->flags & FLAG_CLOSED) {
|
||||
JANET_THROW(args, MSG_DB_CLOSED);
|
||||
}
|
||||
int errcode = sqlite3_errcode(db->handle);
|
||||
JANET_RETURN_INTEGER(args, errcode);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"open", sql_open,
|
||||
"(sqlite3/open path)\n\n"
|
||||
"Opens a sqlite3 database on disk. Returns the database handle if the database was opened "
|
||||
"successfully, and otheriwse throws an error."
|
||||
},
|
||||
{"close", sql_close,
|
||||
"(sqlite3/close db)\n\n"
|
||||
"Closes a database. Use this to free a database after use. Returns nil."
|
||||
},
|
||||
{"eval", sql_eval,
|
||||
"(sqlite3/eval db sql [,params])\n\n"
|
||||
"Evaluate sql in the context of database db. Multiple sql statements "
|
||||
"can be changed together, and optionally parameters maybe passed in. "
|
||||
"The optional parameters maybe either an indexed data type (tuple or array), or a dictionary "
|
||||
"data type (struct or table). If params is a tuple or array, then sqlite "
|
||||
"parameters are substituted using indices. For example:\n\n"
|
||||
"\t(sqlite3/eval db `SELECT * FROM tab WHERE id = ?;` [123])\n\n"
|
||||
"Will select rows from tab where id is equal to 123. Alternatively, "
|
||||
"the programmer can use named parameters with tables or structs, like so:\n\n"
|
||||
"\t(sqlite3/eval db `SELECT * FROM tab WHERE id = :id;` {:id 123})\n\n"
|
||||
"Will return an array of rows, where each row contains a table where columns names "
|
||||
"are keys for column values."
|
||||
},
|
||||
{"last-insert-rowid", sql_last_insert_rowid,
|
||||
"(sqlite3/last-insert-rowid db)\n\n"
|
||||
"Returns the id of the last inserted row. If the id will fit into a 32-bit"
|
||||
"signed integer, will returned an integer, otherwise will return a string representation "
|
||||
"of the id (an 8 bytes string containing a long integer)."
|
||||
},
|
||||
{"error-code", sql_error_code,
|
||||
"(sqlite3/error-code db)\n\n"
|
||||
"Returns the error number of the last sqlite3 command that threw an error. Cross "
|
||||
"check these numbers with the SQLite documentation for more information."
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
JANET_MODULE_ENTRY(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, "sqlite3", cfuns);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,16 +20,16 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <janet.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main() {
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
int array_test() {
|
||||
|
||||
int i;
|
||||
JanetArray *array1, *array2;
|
||||
|
||||
janet_init();
|
||||
|
||||
array1 = janet_array(10);
|
||||
array2 = janet_array(0);
|
||||
|
||||
@@ -62,7 +62,5 @@ int main() {
|
||||
|
||||
assert(array1->count == 5);
|
||||
|
||||
janet_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
78
src/boot/boot.c
Normal file
78
src/boot/boot.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#include <janet.h>
|
||||
#include "tests.h"
|
||||
|
||||
extern const unsigned char *janet_gen_boot;
|
||||
extern int32_t janet_gen_boot_size;
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
|
||||
/* Init janet */
|
||||
janet_init();
|
||||
|
||||
/* Run tests */
|
||||
array_test();
|
||||
buffer_test();
|
||||
number_test();
|
||||
system_test();
|
||||
table_test();
|
||||
|
||||
/* C tests passed */
|
||||
|
||||
/* Set up VM */
|
||||
int status;
|
||||
JanetTable *env;
|
||||
|
||||
env = janet_core_env(NULL);
|
||||
|
||||
/* Create args tuple */
|
||||
JanetArray *args = janet_array(argc);
|
||||
for (int i = 0; i < argc; i++)
|
||||
janet_array_push(args, janet_cstringv(argv[i]));
|
||||
janet_def(env, "process/args", janet_wrap_array(args), "Command line arguments.");
|
||||
|
||||
/* Add in options from janetconf.h so boot.janet can configure the image as needed. */
|
||||
JanetTable *opts = janet_table(0);
|
||||
#ifdef JANET_NO_DOCSTRINGS
|
||||
janet_table_put(opts, janet_ckeywordv("no-docstrings"), janet_wrap_true());
|
||||
#endif
|
||||
#ifdef JANET_NO_SOURCEMAPS
|
||||
janet_table_put(opts, janet_ckeywordv("no-sourcemaps"), janet_wrap_true());
|
||||
#endif
|
||||
janet_def(env, "process/config", janet_wrap_table(opts), "Boot options");
|
||||
|
||||
/* Run bootstrap script to generate core image */
|
||||
const char *boot_file;
|
||||
#ifdef JANET_NO_SOURCEMAPS
|
||||
boot_file = NULL;
|
||||
#else
|
||||
boot_file = "boot.janet";
|
||||
#endif
|
||||
status = janet_dobytes(env, janet_gen_boot, janet_gen_boot_size, boot_file, NULL);
|
||||
|
||||
/* Deinitialize vm */
|
||||
janet_deinit();
|
||||
|
||||
return status;
|
||||
}
|
||||
1907
src/boot/boot.janet
Normal file
1907
src/boot/boot.janet
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,16 +20,16 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <janet.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main() {
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
int buffer_test() {
|
||||
|
||||
int i;
|
||||
JanetBuffer *buffer1, *buffer2;
|
||||
|
||||
janet_init();
|
||||
|
||||
buffer1 = janet_buffer(100);
|
||||
buffer2 = janet_buffer(0);
|
||||
|
||||
@@ -58,7 +58,5 @@ int main() {
|
||||
assert(buffer1->data[i] == buffer2->data[i]);
|
||||
}
|
||||
|
||||
janet_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
67
src/boot/number_test.c
Normal file
67
src/boot/number_test.c
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#include <janet.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
/* Check a subset of numbers against system implementation.
|
||||
* Note that this depends on the system implementation being correct,
|
||||
* which may not be the case for old or non compliant systems. Also,
|
||||
* we cannot check against bases other 10. */
|
||||
|
||||
/* Compare valid c numbers to system implementation. */
|
||||
static void test_valid_str(const char *str) {
|
||||
int err;
|
||||
double cnum, jnum;
|
||||
jnum = 0.0;
|
||||
cnum = atof(str);
|
||||
err = janet_scan_number((const uint8_t *) str, (int32_t) strlen(str), &jnum);
|
||||
assert(!err);
|
||||
assert(cnum == jnum);
|
||||
}
|
||||
|
||||
int number_test() {
|
||||
|
||||
test_valid_str("1.0");
|
||||
test_valid_str("1");
|
||||
test_valid_str("2.1");
|
||||
test_valid_str("1e10");
|
||||
test_valid_str("2e10");
|
||||
test_valid_str("1e-10");
|
||||
test_valid_str("2e-10");
|
||||
test_valid_str("1.123123e10");
|
||||
test_valid_str("1.123123e-10");
|
||||
test_valid_str("-1.23e2");
|
||||
test_valid_str("-4.5e15");
|
||||
test_valid_str("-4.5e151");
|
||||
test_valid_str("-4.5e200");
|
||||
test_valid_str("-4.5e123");
|
||||
test_valid_str("123123123123123123132123");
|
||||
test_valid_str("0000000011111111111111111111111111");
|
||||
test_valid_str(".112312333333323123123123123123123");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -21,11 +20,13 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <janet.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
#include "tests.h"
|
||||
|
||||
int system_test() {
|
||||
|
||||
#ifdef JANET_32
|
||||
assert(sizeof(void *) == 4);
|
||||
@@ -33,8 +34,6 @@ int main() {
|
||||
assert(sizeof(void *) == 8);
|
||||
#endif
|
||||
|
||||
janet_init();
|
||||
|
||||
/* Reflexive testing and nanbox testing */
|
||||
assert(janet_equals(janet_wrap_nil(), janet_wrap_nil()));
|
||||
assert(janet_equals(janet_wrap_false(), janet_wrap_false()));
|
||||
@@ -43,13 +42,13 @@ int main() {
|
||||
assert(janet_equals(janet_wrap_integer(INT32_MAX), janet_wrap_integer(INT32_MAX)));
|
||||
assert(janet_equals(janet_wrap_integer(-2), janet_wrap_integer(-2)));
|
||||
assert(janet_equals(janet_wrap_integer(INT32_MIN), janet_wrap_integer(INT32_MIN)));
|
||||
assert(janet_equals(janet_wrap_real(1.4), janet_wrap_real(1.4)));
|
||||
assert(janet_equals(janet_wrap_real(3.14159265), janet_wrap_real(3.14159265)));
|
||||
assert(janet_equals(janet_wrap_number(1.4), janet_wrap_number(1.4)));
|
||||
assert(janet_equals(janet_wrap_number(3.14159265), janet_wrap_number(3.14159265)));
|
||||
|
||||
assert(NULL != &janet_wrap_nil);
|
||||
|
||||
assert(janet_equals(janet_cstringv("a string."), janet_cstringv("a string.")));
|
||||
assert(janet_equals(janet_csymbolv("sym"), janet_csymbolv("sym")));
|
||||
|
||||
janet_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,14 +20,14 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <janet.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main() {
|
||||
|
||||
JanetTable *t1, *t2;
|
||||
#include "tests.h"
|
||||
|
||||
janet_init();
|
||||
int table_test() {
|
||||
|
||||
JanetTable *t1, *t2;
|
||||
|
||||
t1 = janet_table(10);
|
||||
t2 = janet_table(0);
|
||||
@@ -39,7 +39,7 @@ int main() {
|
||||
|
||||
assert(t1->count == 4);
|
||||
assert(t1->capacity >= t1->count);
|
||||
|
||||
|
||||
assert(janet_equals(janet_table_get(t1, janet_cstringv("hello")), janet_wrap_integer(2)));
|
||||
assert(janet_equals(janet_table_get(t1, janet_cstringv("akey")), janet_wrap_integer(5)));
|
||||
assert(janet_equals(janet_table_get(t1, janet_cstringv("box")), janet_wrap_boolean(0)));
|
||||
@@ -61,7 +61,5 @@ int main() {
|
||||
assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key1")), janet_wrap_integer(10)));
|
||||
assert(janet_equals(janet_table_get(t2, janet_csymbolv("t2key2")), janet_wrap_integer(100)));
|
||||
|
||||
janet_deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
src/boot/tests.h
Normal file
11
src/boot/tests.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef TESTS_H_DNMBUYYL
|
||||
#define TESTS_H_DNMBUYYL
|
||||
|
||||
/* Tests */
|
||||
extern int array_test();
|
||||
extern int buffer_test();
|
||||
extern int number_test();
|
||||
extern int system_test();
|
||||
extern int table_test();
|
||||
|
||||
#endif /* end of include guard: TESTS_H_DNMBUYYL */
|
||||
60
src/conf/janetconf.h
Normal file
60
src/conf/janetconf.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* This is an example janetconf.h file. This will be usually generated
|
||||
* by the build system. */
|
||||
|
||||
#ifndef JANETCONF_H
|
||||
#define JANETCONF_H
|
||||
|
||||
#define JANET_VERSION_MAJOR 1
|
||||
#define JANET_VERSION_MINOR 1
|
||||
#define JANET_VERSION_PATCH 0
|
||||
#define JANET_VERSION_EXTRA "-dev"
|
||||
#define JANET_VERSION "1.1.0-dev"
|
||||
|
||||
/* #define JANET_BUILD "local" */
|
||||
|
||||
/* These settings all affect linking, so use cautiously. */
|
||||
/* #define JANET_SINGLE_THREADED */
|
||||
/* #define JANET_NO_DYNAMIC_MODULES */
|
||||
/* #define JANET_NO_NANBOX */
|
||||
/* #define JANET_API __attribute__((visibility ("default"))) */
|
||||
|
||||
/* These settings should be specified before amalgamation is
|
||||
* built. */
|
||||
/* #define JANET_NO_DOCSTRINGS */
|
||||
/* #define JANET_NO_SOURCEMAPS */
|
||||
/* #define JANET_REDUCED_OS */
|
||||
|
||||
/* Other settings */
|
||||
/* #define JANET_NO_ASSEMBLER */
|
||||
/* #define JANET_NO_PEG */
|
||||
/* #define JANET_NO_TYPED_ARRAY */
|
||||
/* #define JANET_NO_INT_TYPES */
|
||||
/* #define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0) */
|
||||
/* #define JANET_RECURSION_GUARD 1024 */
|
||||
/* #define JANET_MAX_PROTO_DEPTH 200 */
|
||||
/* #define JANET_MAX_MACRO_EXPAND 200 */
|
||||
/* #define JANET_STACK_MAX 16384 */
|
||||
|
||||
#endif /* end of include guard: JANETCONF_H */
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,15 +20,25 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#endif
|
||||
|
||||
/* Create new userdata */
|
||||
void *janet_abstract(const JanetAbstractType *atype, size_t size) {
|
||||
char *data = janet_gcalloc(JANET_MEMORY_ABSTRACT, sizeof(JanetAbstractHeader) + size);
|
||||
JanetAbstractHeader *header = (JanetAbstractHeader *)data;
|
||||
void *a = data + sizeof(JanetAbstractHeader);
|
||||
void *janet_abstract_begin(const JanetAbstractType *atype, size_t size) {
|
||||
JanetAbstractHead *header = janet_gcalloc(JANET_MEMORY_NONE,
|
||||
sizeof(JanetAbstractHead) + size);
|
||||
header->size = size;
|
||||
header->type = atype;
|
||||
return a;
|
||||
return (void *) & (header->data);
|
||||
}
|
||||
|
||||
void *janet_abstract_end(void *x) {
|
||||
janet_gc_settype((void *)(janet_abstract_head(x)), JANET_MEMORY_ABSTRACT);
|
||||
return x;
|
||||
}
|
||||
|
||||
void *janet_abstract(const JanetAbstractType *atype, size_t size) {
|
||||
return janet_abstract_end(janet_abstract_begin(atype, size));
|
||||
}
|
||||
|
||||
312
src/core/array.c
312
src/core/array.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,12 +20,17 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Initializes an array */
|
||||
JanetArray *janet_array_init(JanetArray *array, int32_t capacity) {
|
||||
/* Creates a new array */
|
||||
JanetArray *janet_array(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
|
||||
Janet *data = NULL;
|
||||
if (capacity > 0) {
|
||||
data = (Janet *) malloc(sizeof(Janet) * capacity);
|
||||
@@ -39,16 +44,6 @@ JanetArray *janet_array_init(JanetArray *array, int32_t capacity) {
|
||||
return array;
|
||||
}
|
||||
|
||||
void janet_array_deinit(JanetArray *array) {
|
||||
free(array->data);
|
||||
}
|
||||
|
||||
/* Creates a new array */
|
||||
JanetArray *janet_array(int32_t capacity) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
|
||||
return janet_array_init(array, capacity);
|
||||
}
|
||||
|
||||
/* Creates a new array from n elements. */
|
||||
JanetArray *janet_array_n(const Janet *elements, int32_t n) {
|
||||
JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray));
|
||||
@@ -118,194 +113,191 @@ Janet janet_array_peek(JanetArray *array) {
|
||||
|
||||
/* C Functions */
|
||||
|
||||
static int cfun_new(JanetArgs args) {
|
||||
int32_t cap;
|
||||
JanetArray *array;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_INTEGER(cap, args, 0);
|
||||
array = janet_array(cap);
|
||||
JANET_RETURN_ARRAY(args, array);
|
||||
static Janet cfun_array_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
JanetArray *array = janet_array(cap);
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static int cfun_pop(JanetArgs args) {
|
||||
JanetArray *array;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_ARRAY(array, args, 0);
|
||||
JANET_RETURN(args, janet_array_pop(array));
|
||||
static Janet cfun_array_pop(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
return janet_array_pop(array);
|
||||
}
|
||||
|
||||
static int cfun_peek(JanetArgs args) {
|
||||
JanetArray *array;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_ARRAY(array, args, 0);
|
||||
JANET_RETURN(args, janet_array_peek(array));
|
||||
static Janet cfun_array_peek(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
return janet_array_peek(array);
|
||||
}
|
||||
|
||||
static int cfun_push(JanetArgs args) {
|
||||
JanetArray *array;
|
||||
int32_t newcount;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_ARG_ARRAY(array, args, 0);
|
||||
newcount = array->count - 1 + args.n;
|
||||
static Janet cfun_array_push(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
int32_t newcount = array->count - 1 + argc;
|
||||
janet_array_ensure(array, newcount, 2);
|
||||
if (args.n > 1) memcpy(array->data + array->count, args.v + 1, (args.n - 1) * sizeof(Janet));
|
||||
if (argc > 1) memcpy(array->data + array->count, argv + 1, (argc - 1) * sizeof(Janet));
|
||||
array->count = newcount;
|
||||
JANET_RETURN(args, args.v[0]);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static int cfun_ensure(JanetArgs args) {
|
||||
JanetArray *array;
|
||||
int32_t newcount;
|
||||
int32_t growth;
|
||||
JANET_FIXARITY(args, 3);
|
||||
JANET_ARG_ARRAY(array, args, 0);
|
||||
JANET_ARG_INTEGER(newcount, args, 1);
|
||||
JANET_ARG_INTEGER(growth, args, 2);
|
||||
if (newcount < 0) JANET_THROW(args, "expected positive integer");
|
||||
static Janet cfun_array_ensure(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 3);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
int32_t newcount = janet_getinteger(argv, 1);
|
||||
int32_t growth = janet_getinteger(argv, 2);
|
||||
if (newcount < 1) janet_panic("expected positive integer");
|
||||
janet_array_ensure(array, newcount, growth);
|
||||
JANET_RETURN(args, args.v[0]);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static int cfun_slice(JanetArgs args) {
|
||||
const Janet *vals;
|
||||
int32_t len;
|
||||
JanetArray *ret;
|
||||
int32_t start, end;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_MAXARITY(args, 3);
|
||||
if (!janet_indexed_view(args.v[0], &vals, &len))
|
||||
JANET_THROW(args, "expected array|tuple");
|
||||
/* Get start */
|
||||
if (args.n < 2) {
|
||||
start = 0;
|
||||
} else if (janet_checktype(args.v[1], JANET_INTEGER)) {
|
||||
start = janet_unwrap_integer(args.v[1]);
|
||||
} else {
|
||||
JANET_THROW(args, "expected integer");
|
||||
}
|
||||
/* Get end */
|
||||
if (args.n < 3) {
|
||||
end = -1;
|
||||
} else if (janet_checktype(args.v[2], JANET_INTEGER)) {
|
||||
end = janet_unwrap_integer(args.v[2]);
|
||||
} else {
|
||||
JANET_THROW(args, "expected integer");
|
||||
}
|
||||
if (start < 0) start = len + start;
|
||||
if (end < 0) end = len + end + 1;
|
||||
if (end < 0 || start < 0 || end > len || start > len)
|
||||
JANET_THROW(args, "slice range out of bounds");
|
||||
if (end >= start) {
|
||||
ret = janet_array(end - start);
|
||||
memcpy(ret->data, vals + start, sizeof(Janet) * (end - start));
|
||||
ret->count = end - start;
|
||||
} else {
|
||||
ret = janet_array(0);
|
||||
}
|
||||
JANET_RETURN_ARRAY(args, ret);
|
||||
static Janet cfun_array_slice(int32_t argc, Janet *argv) {
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
JanetArray *array = janet_array(range.end - range.start);
|
||||
if (array->data)
|
||||
memcpy(array->data, view.items + range.start, sizeof(Janet) * (range.end - range.start));
|
||||
array->count = range.end - range.start;
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static int cfun_concat(JanetArgs args) {
|
||||
static Janet cfun_array_concat(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
JanetArray *array;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_ARG_ARRAY(array, args, 0);
|
||||
for (i = 1; i < args.n; i++) {
|
||||
switch (janet_type(args.v[i])) {
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
for (i = 1; i < argc; i++) {
|
||||
switch (janet_type(argv[i])) {
|
||||
default:
|
||||
janet_array_push(array, args.v[i]);
|
||||
janet_array_push(array, argv[i]);
|
||||
break;
|
||||
case JANET_ARRAY:
|
||||
case JANET_TUPLE:
|
||||
{
|
||||
int32_t j, len;
|
||||
const Janet *vals;
|
||||
janet_indexed_view(args.v[i], &vals, &len);
|
||||
for (j = 0; j < len; j++)
|
||||
janet_array_push(array, vals[j]);
|
||||
}
|
||||
break;
|
||||
case JANET_TUPLE: {
|
||||
int32_t j, len = 0;
|
||||
const Janet *vals = NULL;
|
||||
janet_indexed_view(argv[i], &vals, &len);
|
||||
for (j = 0; j < len; j++)
|
||||
janet_array_push(array, vals[j]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
JANET_RETURN_ARRAY(args, array);
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static int cfun_insert(JanetArgs args) {
|
||||
int32_t at;
|
||||
static Janet cfun_array_insert(int32_t argc, Janet *argv) {
|
||||
size_t chunksize, restsize;
|
||||
JanetArray *array;
|
||||
JANET_MINARITY(args, 2);
|
||||
JANET_ARG_ARRAY(array, args, 0);
|
||||
JANET_ARG_INTEGER(at, args, 1);
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
int32_t at = janet_getinteger(argv, 1);
|
||||
if (at < 0) {
|
||||
at = array->count + at + 1;
|
||||
at = array->count + at + 1;
|
||||
}
|
||||
if (at < 0 || at > array->count)
|
||||
JANET_THROW(args, "insertion index out of bounds");
|
||||
chunksize = (args.n - 2) * sizeof(Janet);
|
||||
janet_panicf("insertion index %d out of range [0,%d]", at, array->count);
|
||||
chunksize = (argc - 2) * sizeof(Janet);
|
||||
restsize = (array->count - at) * sizeof(Janet);
|
||||
janet_array_ensure(array, array->count + args.n - 2, 2);
|
||||
memmove(array->data + at + args.n - 2,
|
||||
janet_array_ensure(array, array->count + argc - 2, 2);
|
||||
memmove(array->data + at + argc - 2,
|
||||
array->data + at,
|
||||
restsize);
|
||||
memcpy(array->data + at, args.v + 2, chunksize);
|
||||
array->count += (args.n - 2);
|
||||
JANET_RETURN_ARRAY(args, array);
|
||||
memcpy(array->data + at, argv + 2, chunksize);
|
||||
array->count += (argc - 2);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"array/new", cfun_new,
|
||||
"(array/new capacity)\n\n"
|
||||
"Creates a new empty array with a preallocated capacity. The same as "
|
||||
"(array) but can be more efficient if the maximum size of an array is known."
|
||||
static Janet cfun_array_remove(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 3);
|
||||
JanetArray *array = janet_getarray(argv, 0);
|
||||
int32_t at = janet_getinteger(argv, 1);
|
||||
int32_t n = 1;
|
||||
if (at < 0) {
|
||||
at = array->count + at + 1;
|
||||
}
|
||||
if (at < 0 || at > array->count)
|
||||
janet_panicf("removal index %d out of range [0,%d]", at, array->count);
|
||||
if (argc == 3) {
|
||||
n = janet_getinteger(argv, 2);
|
||||
if (n < 0)
|
||||
janet_panicf("expected non-negative integer for argument n, got %v", argv[2]);
|
||||
}
|
||||
if (at + n > array->count) {
|
||||
n = array->count - at;
|
||||
}
|
||||
memmove(array->data + at,
|
||||
array->data + at + n,
|
||||
(array->count - at - n) * sizeof(Janet));
|
||||
array->count -= n;
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg array_cfuns[] = {
|
||||
{
|
||||
"array/new", cfun_array_new,
|
||||
JDOC("(array/new capacity)\n\n"
|
||||
"Creates a new empty array with a pre-allocated capacity. The same as "
|
||||
"(array) but can be more efficient if the maximum size of an array is known.")
|
||||
},
|
||||
{"array/pop", cfun_pop,
|
||||
"(array/pop arr)\n\n"
|
||||
"Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
|
||||
"the input array."
|
||||
{
|
||||
"array/pop", cfun_array_pop,
|
||||
JDOC("(array/pop arr)\n\n"
|
||||
"Remove the last element of the array and return it. If the array is empty, will return nil. Modifies "
|
||||
"the input array.")
|
||||
},
|
||||
{"array/peek", cfun_peek,
|
||||
"(array/peek arr)\n\n"
|
||||
"Returns the last element of the array. Does not modify the array."
|
||||
{
|
||||
"array/peek", cfun_array_peek,
|
||||
JDOC("(array/peek arr)\n\n"
|
||||
"Returns the last element of the array. Does not modify the array.")
|
||||
},
|
||||
{"array/push", cfun_push,
|
||||
"(array/push arr x)\n\n"
|
||||
"Insert an element in the end of an array. Modifies the input array and returns it."
|
||||
{
|
||||
"array/push", cfun_array_push,
|
||||
JDOC("(array/push arr x)\n\n"
|
||||
"Insert an element in the end of an array. Modifies the input array and returns it.")
|
||||
},
|
||||
{"array/ensure", cfun_ensure,
|
||||
"(array/ensure arr capacity)\n\n"
|
||||
"Ensures that the memory backing the array has enough memory for capacity "
|
||||
"items. Capacity must be an integer. If the backing capacity is already enough, "
|
||||
"then this function does nothing. Otherwise, the backing memory will be reallocated "
|
||||
"so that there is enough space."
|
||||
{
|
||||
"array/ensure", cfun_array_ensure,
|
||||
JDOC("(array/ensure arr capacity)\n\n"
|
||||
"Ensures that the memory backing the array is large enough for capacity "
|
||||
"items. Capacity must be an integer. If the backing capacity is already enough, "
|
||||
"then this function does nothing. Otherwise, the backing memory will be reallocated "
|
||||
"so that there is enough space.")
|
||||
},
|
||||
{"array/slice", cfun_slice,
|
||||
"(array/slice arrtup [, start=0 [, end=(length arrtup)]])\n\n"
|
||||
"Takes a slice of array or tuple from start to end. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the end of the "
|
||||
"end of the array. By default, start is 0 and end is the length of the array. "
|
||||
"Returns a new array."
|
||||
{
|
||||
"array/slice", cfun_array_slice,
|
||||
JDOC("(array/slice arrtup &opt start end)\n\n"
|
||||
"Takes a slice of array or tuple from start to end. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the end of the "
|
||||
"end of the array. By default, start is 0 and end is the length of the array. "
|
||||
"Returns a new array.")
|
||||
},
|
||||
{"array/concat", cfun_concat,
|
||||
"(array/concat arr & parts)\n\n"
|
||||
"Concatenates a variadic number of arrays (and tuples) into the first argument "
|
||||
"which must an array. If any of the parts are arrays or tuples, their elements will "
|
||||
"be inserted into the array. Otherwise, each part in parts will be appended to arr in order. "
|
||||
"Return the modified array arr."
|
||||
{
|
||||
"array/concat", cfun_array_concat,
|
||||
JDOC("(array/concat arr & parts)\n\n"
|
||||
"Concatenates a variadic number of arrays (and tuples) into the first argument "
|
||||
"which must an array. If any of the parts are arrays or tuples, their elements will "
|
||||
"be inserted into the array. Otherwise, each part in parts will be appended to arr in order. "
|
||||
"Return the modified array arr.")
|
||||
},
|
||||
{"array/insert", cfun_insert,
|
||||
"(array/insert arr at & xs)\n\n"
|
||||
"Insert all of xs into array arr at index at. at should be an integer "
|
||||
"0 and the length of the array. A negative value for at will index from "
|
||||
"the end of the array, such that inserting at -1 appends to the array. "
|
||||
"Returns the array."
|
||||
{
|
||||
"array/insert", cfun_array_insert,
|
||||
JDOC("(array/insert arr at & xs)\n\n"
|
||||
"Insert all of xs into array arr at index at. at should be an integer "
|
||||
"0 and the length of the array. A negative value for at will index from "
|
||||
"the end of the array, such that inserting at -1 appends to the array. "
|
||||
"Returns the array.")
|
||||
},
|
||||
{
|
||||
"array/remove", cfun_array_remove,
|
||||
JDOC("(array/remove arr at &opt n)\n\n"
|
||||
"Remove up to n elements starting at index at in array arr. at can index from "
|
||||
"the end of the array with a negative index, and n must be a non-negative integer. "
|
||||
"By default, n is 1. "
|
||||
"Returns the array.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Load the array module */
|
||||
int janet_lib_array(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
return 0;
|
||||
void janet_lib_array(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, array_cfuns);
|
||||
}
|
||||
|
||||
356
src/core/asm.c
356
src/core/asm.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,9 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#include <setjmp.h>
|
||||
|
||||
/* Conditionally compile this file */
|
||||
#ifdef JANET_ASSEMBLER
|
||||
@@ -48,23 +51,21 @@ struct JanetAssembler {
|
||||
int32_t bytecode_count; /* Used for calculating labels */
|
||||
|
||||
Janet name;
|
||||
JanetTable labels; /* symbol -> bytecode index */
|
||||
JanetTable labels; /* keyword -> bytecode index */
|
||||
JanetTable constants; /* symbol -> constant index */
|
||||
JanetTable slots; /* symbol -> slot index */
|
||||
JanetTable envs; /* symbol -> environment index */
|
||||
JanetTable defs; /* symbol -> funcdefs index */
|
||||
};
|
||||
|
||||
/* Janet opcode descriptions in lexographic order. This
|
||||
/* Janet opcode descriptions in lexicographic order. This
|
||||
* allows a binary search over the elements to find the
|
||||
* correct opcode given a name. This works in reasonable
|
||||
* time and is easier to setup statically than a hash table or
|
||||
* prefix tree. */
|
||||
static const JanetInstructionDef janet_ops[] = {
|
||||
{"add", JOP_ADD},
|
||||
{"addi", JOP_ADD_INTEGER},
|
||||
{"addim", JOP_ADD_IMMEDIATE},
|
||||
{"addr", JOP_ADD_REAL},
|
||||
{"band", JOP_BAND},
|
||||
{"bnot", JOP_BNOT},
|
||||
{"bor", JOP_BOR},
|
||||
@@ -73,24 +74,17 @@ static const JanetInstructionDef janet_ops[] = {
|
||||
{"clo", JOP_CLOSURE},
|
||||
{"cmp", JOP_COMPARE},
|
||||
{"div", JOP_DIVIDE},
|
||||
{"divi", JOP_DIVIDE_INTEGER},
|
||||
{"divim", JOP_DIVIDE_IMMEDIATE},
|
||||
{"divr", JOP_DIVIDE_REAL},
|
||||
{"eq", JOP_EQUALS},
|
||||
{"eqi", JOP_EQUALS_INTEGER},
|
||||
{"eqim", JOP_EQUALS_IMMEDIATE},
|
||||
{"eqn", JOP_NUMERIC_EQUAL},
|
||||
{"eqr", JOP_EQUALS_REAL},
|
||||
{"err", JOP_ERROR},
|
||||
{"get", JOP_GET},
|
||||
{"geti", JOP_GET_INDEX},
|
||||
{"gt", JOP_GREATER_THAN},
|
||||
{"gti", JOP_GREATER_THAN_INTEGER},
|
||||
{"gten", JOP_NUMERIC_GREATER_THAN_EQUAL},
|
||||
{"gtim", JOP_GREATER_THAN_IMMEDIATE},
|
||||
{"gtn", JOP_NUMERIC_GREATER_THAN},
|
||||
{"gtr", JOP_GREATER_THAN_REAL},
|
||||
{"gten", JOP_NUMERIC_GREATER_THAN_EQUAL},
|
||||
{"gter", JOP_GREATER_THAN_EQUAL_REAL},
|
||||
{"jmp", JOP_JUMP},
|
||||
{"jmpif", JOP_JUMP_IF},
|
||||
{"jmpno", JOP_JUMP_IF_NOT},
|
||||
@@ -104,12 +98,10 @@ static const JanetInstructionDef janet_ops[] = {
|
||||
{"len", JOP_LENGTH},
|
||||
{"lt", JOP_LESS_THAN},
|
||||
{"lten", JOP_NUMERIC_LESS_THAN_EQUAL},
|
||||
{"lter", JOP_LESS_THAN_EQUAL_REAL},
|
||||
{"lti", JOP_LESS_THAN_INTEGER},
|
||||
{"ltim", JOP_LESS_THAN_IMMEDIATE},
|
||||
{"ltn", JOP_NUMERIC_LESS_THAN},
|
||||
{"ltr", JOP_LESS_THAN_REAL},
|
||||
{"mkarr", JOP_MAKE_ARRAY},
|
||||
{"mkbtp", JOP_MAKE_BRACKET_TUPLE},
|
||||
{"mkbuf", JOP_MAKE_BUFFER},
|
||||
{"mkstr", JOP_MAKE_STRING},
|
||||
{"mkstu", JOP_MAKE_STRUCT},
|
||||
@@ -118,10 +110,9 @@ static const JanetInstructionDef janet_ops[] = {
|
||||
{"movf", JOP_MOVE_FAR},
|
||||
{"movn", JOP_MOVE_NEAR},
|
||||
{"mul", JOP_MULTIPLY},
|
||||
{"muli", JOP_MULTIPLY_INTEGER},
|
||||
{"mulim", JOP_MULTIPLY_IMMEDIATE},
|
||||
{"mulr", JOP_MULTIPLY_REAL},
|
||||
{"noop", JOP_NOOP},
|
||||
{"prop", JOP_PROPAGATE},
|
||||
{"push", JOP_PUSH},
|
||||
{"push2", JOP_PUSH_2},
|
||||
{"push3", JOP_PUSH_3},
|
||||
@@ -151,27 +142,25 @@ typedef struct TypeAlias {
|
||||
} TypeAlias;
|
||||
|
||||
static const TypeAlias type_aliases[] = {
|
||||
{":abstract", JANET_TFLAG_ABSTRACT},
|
||||
{":array", JANET_TFLAG_ARRAY},
|
||||
{":boolean", JANET_TFLAG_BOOLEAN},
|
||||
{":buffer", JANET_TFLAG_BUFFER},
|
||||
{":callable", JANET_TFLAG_CALLABLE},
|
||||
{":cfunction", JANET_TFLAG_CFUNCTION},
|
||||
{":dictionary", JANET_TFLAG_DICTIONARY},
|
||||
{":false", JANET_TFLAG_FALSE},
|
||||
{":fiber", JANET_TFLAG_FIBER},
|
||||
{":function", JANET_TFLAG_FUNCTION},
|
||||
{":indexed", JANET_TFLAG_INDEXED},
|
||||
{":integer", JANET_TFLAG_INTEGER},
|
||||
{":nil", JANET_TFLAG_NIL},
|
||||
{":number", JANET_TFLAG_NUMBER},
|
||||
{":real", JANET_TFLAG_REAL},
|
||||
{":string", JANET_TFLAG_STRING},
|
||||
{":struct", JANET_TFLAG_STRUCT},
|
||||
{":symbol", JANET_TFLAG_SYMBOL},
|
||||
{":table", JANET_TFLAG_BOOLEAN},
|
||||
{":true", JANET_TFLAG_TRUE},
|
||||
{":tuple", JANET_TFLAG_BOOLEAN}
|
||||
{"abstract", JANET_TFLAG_ABSTRACT},
|
||||
{"array", JANET_TFLAG_ARRAY},
|
||||
{"boolean", JANET_TFLAG_BOOLEAN},
|
||||
{"buffer", JANET_TFLAG_BUFFER},
|
||||
{"callable", JANET_TFLAG_CALLABLE},
|
||||
{"cfunction", JANET_TFLAG_CFUNCTION},
|
||||
{"dictionary", JANET_TFLAG_DICTIONARY},
|
||||
{"fiber", JANET_TFLAG_FIBER},
|
||||
{"function", JANET_TFLAG_FUNCTION},
|
||||
{"indexed", JANET_TFLAG_INDEXED},
|
||||
{"keyword", JANET_TFLAG_KEYWORD},
|
||||
{"nil", JANET_TFLAG_NIL},
|
||||
{"number", JANET_TFLAG_NUMBER},
|
||||
{"pointer", JANET_TFLAG_POINTER},
|
||||
{"string", JANET_TFLAG_STRING},
|
||||
{"struct", JANET_TFLAG_STRUCT},
|
||||
{"symbol", JANET_TFLAG_SYMBOL},
|
||||
{"table", JANET_TFLAG_TABLE},
|
||||
{"tuple", JANET_TFLAG_TUPLE}
|
||||
};
|
||||
|
||||
/* Deinitialize an Assembler. Does not deinitialize the parents. */
|
||||
@@ -199,7 +188,7 @@ static void janet_asm_errorv(JanetAssembler *a, const uint8_t *m) {
|
||||
/* Add a closure environment to the assembler. Sub funcdefs may need
|
||||
* to reference outer function environments, and may change the outer environment.
|
||||
* Returns the index of the environment in the assembler's environments, or -1
|
||||
* if not found. */
|
||||
* if not found. */
|
||||
static int32_t janet_asm_addenv(JanetAssembler *a, Janet envname) {
|
||||
Janet check;
|
||||
JanetFuncDef *def = a->def;
|
||||
@@ -210,8 +199,8 @@ static int32_t janet_asm_addenv(JanetAssembler *a, Janet envname) {
|
||||
}
|
||||
/* Check for memoized value */
|
||||
check = janet_table_get(&a->envs, envname);
|
||||
if (janet_checktype(check, JANET_INTEGER)) {
|
||||
return janet_unwrap_integer(check);
|
||||
if (janet_checktype(check, JANET_NUMBER)) {
|
||||
return (int32_t) janet_unwrap_number(check);
|
||||
}
|
||||
if (NULL == a->parent) return -2;
|
||||
res = janet_asm_addenv(a->parent, envname);
|
||||
@@ -219,7 +208,7 @@ static int32_t janet_asm_addenv(JanetAssembler *a, Janet envname) {
|
||||
return res;
|
||||
}
|
||||
envindex = def->environments_length;
|
||||
janet_table_put(&a->envs, envname, janet_wrap_integer(envindex));
|
||||
janet_table_put(&a->envs, envname, janet_wrap_number(envindex));
|
||||
if (envindex >= a->environments_capacity) {
|
||||
int32_t newcap = 2 * envindex;
|
||||
def->environments = realloc(def->environments, newcap * sizeof(int32_t));
|
||||
@@ -236,9 +225,9 @@ static int32_t janet_asm_addenv(JanetAssembler *a, Janet envname) {
|
||||
/* Parse an argument to an assembly instruction, and return the result as an
|
||||
* integer. This integer will need to be bounds checked. */
|
||||
static int32_t doarg_1(
|
||||
JanetAssembler *a,
|
||||
enum JanetOpArgType argtype,
|
||||
Janet x) {
|
||||
JanetAssembler *a,
|
||||
enum JanetOpArgType argtype,
|
||||
Janet x) {
|
||||
int32_t ret = -1;
|
||||
JanetTable *c;
|
||||
switch (argtype) {
|
||||
@@ -265,11 +254,16 @@ static int32_t doarg_1(
|
||||
default:
|
||||
goto error;
|
||||
break;
|
||||
case JANET_INTEGER:
|
||||
ret = janet_unwrap_integer(x);
|
||||
case JANET_NUMBER: {
|
||||
double y = janet_unwrap_number(x);
|
||||
if (janet_checkintrange(y)) {
|
||||
ret = (int32_t) y;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
{
|
||||
}
|
||||
case JANET_TUPLE: {
|
||||
const Janet *t = janet_unwrap_tuple(x);
|
||||
if (argtype == JANET_OAT_TYPE) {
|
||||
int32_t i = 0;
|
||||
@@ -282,25 +276,20 @@ static int32_t doarg_1(
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_SYMBOL:
|
||||
{
|
||||
if (NULL != c) {
|
||||
case JANET_KEYWORD: {
|
||||
if (NULL != c && argtype == JANET_OAT_LABEL) {
|
||||
Janet result = janet_table_get(c, x);
|
||||
if (janet_checktype(result, JANET_INTEGER)) {
|
||||
if (argtype == JANET_OAT_LABEL) {
|
||||
ret = janet_unwrap_integer(result) - a->bytecode_count;
|
||||
} else {
|
||||
ret = janet_unwrap_integer(result);
|
||||
}
|
||||
if (janet_checktype(result, JANET_NUMBER)) {
|
||||
ret = janet_unwrap_integer(result) - a->bytecode_count;
|
||||
} else {
|
||||
janet_asm_errorv(a, janet_formatc("unknown name %v", x));
|
||||
goto error;
|
||||
}
|
||||
} else if (argtype == JANET_OAT_TYPE || argtype == JANET_OAT_SIMPLETYPE) {
|
||||
const TypeAlias *alias = janet_strbinsearch(
|
||||
&type_aliases,
|
||||
sizeof(type_aliases)/sizeof(TypeAlias),
|
||||
sizeof(TypeAlias),
|
||||
janet_unwrap_symbol(x));
|
||||
&type_aliases,
|
||||
sizeof(type_aliases) / sizeof(TypeAlias),
|
||||
sizeof(TypeAlias),
|
||||
janet_unwrap_keyword(x));
|
||||
if (alias) {
|
||||
ret = alias->mask;
|
||||
} else {
|
||||
@@ -309,6 +298,19 @@ static int32_t doarg_1(
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_SYMBOL: {
|
||||
if (NULL != c) {
|
||||
Janet result = janet_table_get(c, x);
|
||||
if (janet_checktype(result, JANET_NUMBER)) {
|
||||
ret = (int32_t) janet_unwrap_number(result);
|
||||
} else {
|
||||
janet_asm_errorv(a, janet_formatc("unknown name %v", x));
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
if (argtype == JANET_OAT_ENVIRONMENT && ret == -1) {
|
||||
/* Add a new env */
|
||||
ret = janet_asm_addenv(a, x);
|
||||
@@ -323,7 +325,7 @@ static int32_t doarg_1(
|
||||
a->def->slotcount = (int32_t) ret + 1;
|
||||
return ret;
|
||||
|
||||
error:
|
||||
error:
|
||||
janet_asm_errorv(a, janet_formatc("error parsing instruction argument %v", x));
|
||||
return 0;
|
||||
}
|
||||
@@ -331,12 +333,12 @@ static int32_t doarg_1(
|
||||
/* Parse a single argument to an instruction. Trims it as well as
|
||||
* try to convert arguments to bit patterns */
|
||||
static uint32_t doarg(
|
||||
JanetAssembler *a,
|
||||
enum JanetOpArgType argtype,
|
||||
int nth,
|
||||
int nbytes,
|
||||
int hassign,
|
||||
Janet x) {
|
||||
JanetAssembler *a,
|
||||
enum JanetOpArgType argtype,
|
||||
int nth,
|
||||
int nbytes,
|
||||
int hassign,
|
||||
Janet x) {
|
||||
int32_t arg = doarg_1(a, argtype, x);
|
||||
/* Calculate the min and max values that can be stored given
|
||||
* nbytes, and whether or not the storage is signed */
|
||||
@@ -344,59 +346,53 @@ static uint32_t doarg(
|
||||
int32_t min = hassign ? -max - 1 : 0;
|
||||
if (arg < min)
|
||||
janet_asm_errorv(a, janet_formatc("instruction argument %v is too small, must be %d byte%s",
|
||||
x, nbytes, nbytes > 1 ? "s" : ""));
|
||||
x, nbytes, nbytes > 1 ? "s" : ""));
|
||||
if (arg > max)
|
||||
janet_asm_errorv(a, janet_formatc("instruction argument %v is too large, must be %d byte%s",
|
||||
x, nbytes, nbytes > 1 ? "s" : ""));
|
||||
x, nbytes, nbytes > 1 ? "s" : ""));
|
||||
return ((uint32_t) arg) << (nth << 3);
|
||||
}
|
||||
|
||||
/* Provide parsing methods for the different kinds of arguments */
|
||||
static uint32_t read_instruction(
|
||||
JanetAssembler *a,
|
||||
const JanetInstructionDef *idef,
|
||||
const Janet *argt) {
|
||||
JanetAssembler *a,
|
||||
const JanetInstructionDef *idef,
|
||||
const Janet *argt) {
|
||||
uint32_t instr = idef->opcode;
|
||||
enum JanetInstructionType type = janet_instructions[idef->opcode];
|
||||
switch (type) {
|
||||
case JINT_0:
|
||||
{
|
||||
case JINT_0: {
|
||||
if (janet_tuple_length(argt) != 1)
|
||||
janet_asm_error(a, "expected 0 arguments: (op)");
|
||||
break;
|
||||
}
|
||||
case JINT_S:
|
||||
{
|
||||
case JINT_S: {
|
||||
if (janet_tuple_length(argt) != 2)
|
||||
janet_asm_error(a, "expected 1 argument: (op, slot)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 2, 0, argt[1]);
|
||||
break;
|
||||
}
|
||||
case JINT_L:
|
||||
{
|
||||
case JINT_L: {
|
||||
if (janet_tuple_length(argt) != 2)
|
||||
janet_asm_error(a, "expected 1 argument: (op, label)");
|
||||
instr |= doarg(a, JANET_OAT_LABEL, 1, 3, 1, argt[1]);
|
||||
break;
|
||||
}
|
||||
case JINT_SS:
|
||||
{
|
||||
case JINT_SS: {
|
||||
if (janet_tuple_length(argt) != 3)
|
||||
janet_asm_error(a, "expected 2 arguments: (op, slot, slot)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 2, 2, 0, argt[2]);
|
||||
break;
|
||||
}
|
||||
case JINT_SL:
|
||||
{
|
||||
case JINT_SL: {
|
||||
if (janet_tuple_length(argt) != 3)
|
||||
janet_asm_error(a, "expected 2 arguments: (op, slot, label)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
instr |= doarg(a, JANET_OAT_LABEL, 2, 2, 1, argt[2]);
|
||||
break;
|
||||
}
|
||||
case JINT_ST:
|
||||
{
|
||||
case JINT_ST: {
|
||||
if (janet_tuple_length(argt) != 3)
|
||||
janet_asm_error(a, "expected 2 arguments: (op, slot, type)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
@@ -404,24 +400,21 @@ static uint32_t read_instruction(
|
||||
break;
|
||||
}
|
||||
case JINT_SI:
|
||||
case JINT_SU:
|
||||
{
|
||||
case JINT_SU: {
|
||||
if (janet_tuple_length(argt) != 3)
|
||||
janet_asm_error(a, "expected 2 arguments: (op, slot, integer)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
instr |= doarg(a, JANET_OAT_INTEGER, 2, 2, type == JINT_SI, argt[2]);
|
||||
break;
|
||||
}
|
||||
case JINT_SD:
|
||||
{
|
||||
case JINT_SD: {
|
||||
if (janet_tuple_length(argt) != 3)
|
||||
janet_asm_error(a, "expected 2 arguments: (op, slot, funcdef)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
instr |= doarg(a, JANET_OAT_FUNCDEF, 2, 2, 0, argt[2]);
|
||||
break;
|
||||
}
|
||||
case JINT_SSS:
|
||||
{
|
||||
case JINT_SSS: {
|
||||
if (janet_tuple_length(argt) != 4)
|
||||
janet_asm_error(a, "expected 3 arguments: (op, slot, slot, slot)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
@@ -430,8 +423,7 @@ static uint32_t read_instruction(
|
||||
break;
|
||||
}
|
||||
case JINT_SSI:
|
||||
case JINT_SSU:
|
||||
{
|
||||
case JINT_SSU: {
|
||||
if (janet_tuple_length(argt) != 4)
|
||||
janet_asm_error(a, "expected 3 arguments: (op, slot, slot, integer)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
@@ -439,8 +431,7 @@ static uint32_t read_instruction(
|
||||
instr |= doarg(a, JANET_OAT_INTEGER, 3, 1, type == JINT_SSI, argt[3]);
|
||||
break;
|
||||
}
|
||||
case JINT_SES:
|
||||
{
|
||||
case JINT_SES: {
|
||||
JanetAssembler *b = a;
|
||||
uint32_t env;
|
||||
if (janet_tuple_length(argt) != 4)
|
||||
@@ -456,8 +447,7 @@ static uint32_t read_instruction(
|
||||
instr |= doarg(b, JANET_OAT_SLOT, 3, 1, 0, argt[3]);
|
||||
break;
|
||||
}
|
||||
case JINT_SC:
|
||||
{
|
||||
case JINT_SC: {
|
||||
if (janet_tuple_length(argt) != 3)
|
||||
janet_asm_error(a, "expected 2 arguments: (op, slot, constant)");
|
||||
instr |= doarg(a, JANET_OAT_SLOT, 1, 1, 0, argt[1]);
|
||||
@@ -469,7 +459,7 @@ static uint32_t read_instruction(
|
||||
}
|
||||
|
||||
/* Helper to get from a structure */
|
||||
static Janet janet_get(Janet ds, Janet key) {
|
||||
static Janet janet_get1(Janet ds, Janet key) {
|
||||
switch (janet_type(ds)) {
|
||||
default:
|
||||
return janet_wrap_nil();
|
||||
@@ -523,34 +513,39 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
}
|
||||
|
||||
janet_asm_assert(&a,
|
||||
janet_checktype(s, JANET_STRUCT) ||
|
||||
janet_checktype(s, JANET_TABLE),
|
||||
"expected struct or table for assembly source");
|
||||
janet_checktype(s, JANET_STRUCT) ||
|
||||
janet_checktype(s, JANET_TABLE),
|
||||
"expected struct or table for assembly source");
|
||||
|
||||
/* Check for function name */
|
||||
a.name = janet_get(s, janet_csymbolv("name"));
|
||||
a.name = janet_get1(s, janet_csymbolv("name"));
|
||||
if (!janet_checktype(a.name, JANET_NIL)) {
|
||||
def->name = janet_to_string(a.name);
|
||||
}
|
||||
|
||||
/* Set function arity */
|
||||
x = janet_get(s, janet_csymbolv("arity"));
|
||||
def->arity = janet_checktype(x, JANET_INTEGER) ? janet_unwrap_integer(x) : 0;
|
||||
x = janet_get1(s, janet_csymbolv("arity"));
|
||||
def->arity = janet_checkint(x) ? janet_unwrap_integer(x) : 0;
|
||||
janet_asm_assert(&a, def->arity >= 0, "arity must be non-negative");
|
||||
|
||||
x = janet_get1(s, janet_csymbolv("max-arity"));
|
||||
def->max_arity = janet_checkint(x) ? janet_unwrap_integer(x) : def->arity;
|
||||
janet_asm_assert(&a, def->max_arity >= def->arity, "max-arity must be greater than or equal to arity");
|
||||
|
||||
x = janet_get1(s, janet_csymbolv("min-arity"));
|
||||
def->min_arity = janet_checkint(x) ? janet_unwrap_integer(x) : def->arity;
|
||||
janet_asm_assert(&a, def->min_arity <= def->arity, "min-arity must be less than or equal to arity");
|
||||
|
||||
/* Check vararg */
|
||||
x = janet_get(s, janet_csymbolv("vararg"));
|
||||
x = janet_get1(s, janet_csymbolv("vararg"));
|
||||
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
|
||||
|
||||
/* Check strict arity */
|
||||
x = janet_get(s, janet_csymbolv("fix-arity"));
|
||||
if (janet_truthy(x)) def->flags |= JANET_FUNCDEF_FLAG_FIXARITY;
|
||||
|
||||
/* Check source */
|
||||
x = janet_get(s, janet_csymbolv("source"));
|
||||
x = janet_get1(s, janet_csymbolv("source"));
|
||||
if (janet_checktype(x, JANET_STRING)) def->source = janet_unwrap_string(x);
|
||||
|
||||
/* Create slot aliases */
|
||||
x = janet_get(s, janet_csymbolv("slots"));
|
||||
x = janet_get1(s, janet_csymbolv("slots"));
|
||||
if (janet_indexed_view(x, &arr, &count)) {
|
||||
for (i = 0; i < count; i++) {
|
||||
Janet v = arr[i];
|
||||
@@ -571,7 +566,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
}
|
||||
|
||||
/* Parse constants */
|
||||
x = janet_get(s, janet_csymbolv("constants"));
|
||||
x = janet_get1(s, janet_csymbolv("constants"));
|
||||
if (janet_indexed_view(x, &arr, &count)) {
|
||||
def->constants_length = count;
|
||||
def->constants = malloc(sizeof(Janet) * count);
|
||||
@@ -581,16 +576,16 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
for (i = 0; i < count; i++) {
|
||||
Janet ct = arr[i];
|
||||
if (janet_checktype(ct, JANET_TUPLE) &&
|
||||
janet_tuple_length(janet_unwrap_tuple(ct)) > 1 &&
|
||||
janet_checktype(janet_unwrap_tuple(ct)[0], JANET_SYMBOL)) {
|
||||
janet_tuple_length(janet_unwrap_tuple(ct)) > 1 &&
|
||||
janet_checktype(janet_unwrap_tuple(ct)[0], JANET_SYMBOL)) {
|
||||
const Janet *t = janet_unwrap_tuple(ct);
|
||||
int32_t tcount = janet_tuple_length(t);
|
||||
const uint8_t *macro = janet_unwrap_symbol(t[0]);
|
||||
if (0 == janet_cstrcmp(macro, "quote")) {
|
||||
def->constants[i] = t[1];
|
||||
} else if (tcount == 3 &&
|
||||
janet_checktype(t[1], JANET_SYMBOL) &&
|
||||
0 == janet_cstrcmp(macro, "def")) {
|
||||
janet_checktype(t[1], JANET_SYMBOL) &&
|
||||
0 == janet_cstrcmp(macro, "def")) {
|
||||
def->constants[i] = t[2];
|
||||
janet_table_put(&a.constants, t[1], janet_wrap_integer(i));
|
||||
} else {
|
||||
@@ -606,7 +601,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
}
|
||||
|
||||
/* Parse sub funcdefs */
|
||||
x = janet_get(s, janet_csymbolv("closures"));
|
||||
x = janet_get1(s, janet_csymbolv("closures"));
|
||||
if (janet_indexed_view(x, &arr, &count)) {
|
||||
int32_t i;
|
||||
for (i = 0; i < count; i++) {
|
||||
@@ -617,7 +612,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
if (subres.status != JANET_ASSEMBLE_OK) {
|
||||
janet_asm_errorv(&a, subres.error);
|
||||
}
|
||||
subname = janet_get(arr[i], janet_csymbolv("name"));
|
||||
subname = janet_get1(arr[i], janet_csymbolv("name"));
|
||||
if (!janet_checktype(subname, JANET_NIL)) {
|
||||
janet_table_put(&a.defs, subname, janet_wrap_integer(def->defs_length));
|
||||
}
|
||||
@@ -636,13 +631,13 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
}
|
||||
|
||||
/* Parse bytecode and labels */
|
||||
x = janet_get(s, janet_csymbolv("bytecode"));
|
||||
x = janet_get1(s, janet_csymbolv("bytecode"));
|
||||
if (janet_indexed_view(x, &arr, &count)) {
|
||||
/* Do labels and find length */
|
||||
int32_t blength = 0;
|
||||
for (i = 0; i < count; ++i) {
|
||||
Janet instr = arr[i];
|
||||
if (janet_checktype(instr, JANET_SYMBOL)) {
|
||||
if (janet_checktype(instr, JANET_KEYWORD)) {
|
||||
janet_table_put(&a.labels, instr, janet_wrap_integer(blength));
|
||||
} else if (janet_checktype(instr, JANET_TUPLE)) {
|
||||
blength++;
|
||||
@@ -653,14 +648,14 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
}
|
||||
/* Allocate bytecode array */
|
||||
def->bytecode_length = blength;
|
||||
def->bytecode = malloc(sizeof(int32_t) * blength);
|
||||
def->bytecode = malloc(sizeof(uint32_t) * blength);
|
||||
if (NULL == def->bytecode) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
/* Do bytecode */
|
||||
for (i = 0; i < count; ++i) {
|
||||
Janet instr = arr[i];
|
||||
if (janet_checktype(instr, JANET_SYMBOL)) {
|
||||
if (janet_checktype(instr, JANET_KEYWORD)) {
|
||||
continue;
|
||||
} else {
|
||||
uint32_t op;
|
||||
@@ -673,12 +668,12 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
op = 0;
|
||||
} else {
|
||||
janet_asm_assert(&a, janet_checktype(t[0], JANET_SYMBOL),
|
||||
"expected symbol in assembly instruction");
|
||||
"expected symbol in assembly instruction");
|
||||
idef = janet_strbinsearch(
|
||||
&janet_ops,
|
||||
sizeof(janet_ops)/sizeof(JanetInstructionDef),
|
||||
sizeof(JanetInstructionDef),
|
||||
janet_unwrap_symbol(t[0]));
|
||||
&janet_ops,
|
||||
sizeof(janet_ops) / sizeof(JanetInstructionDef),
|
||||
sizeof(JanetInstructionDef),
|
||||
janet_unwrap_symbol(t[0]));
|
||||
if (NULL == idef)
|
||||
janet_asm_errorv(&a, janet_formatc("unknown instruction %v", t[0]));
|
||||
op = read_instruction(&a, idef, t);
|
||||
@@ -692,7 +687,7 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
a.errindex = -1;
|
||||
|
||||
/* Check for source mapping */
|
||||
x = janet_get(s, janet_csymbolv("sourcemap"));
|
||||
x = janet_get1(s, janet_csymbolv("sourcemap"));
|
||||
if (janet_indexed_view(x, &arr, &count)) {
|
||||
janet_asm_assert(&a, count == def->bytecode_length, "sourcemap must have the same length as the bytecode");
|
||||
def->sourcemap = malloc(sizeof(JanetSourceMapping) * count);
|
||||
@@ -704,14 +699,14 @@ static JanetAssembleResult janet_asm1(JanetAssembler *parent, Janet source, int
|
||||
janet_asm_error(&a, "expected tuple");
|
||||
}
|
||||
tup = janet_unwrap_tuple(entry);
|
||||
if (!janet_checktype(tup[0], JANET_INTEGER)) {
|
||||
if (!janet_checkint(tup[0])) {
|
||||
janet_asm_error(&a, "expected integer");
|
||||
}
|
||||
if (!janet_checktype(tup[1], JANET_INTEGER)) {
|
||||
if (!janet_checkint(tup[1])) {
|
||||
janet_asm_error(&a, "expected integer");
|
||||
}
|
||||
mapping.line = janet_unwrap_integer(tup[0]);
|
||||
mapping.column = janet_unwrap_integer(tup[1]);
|
||||
mapping.start = janet_unwrap_integer(tup[0]);
|
||||
mapping.end = janet_unwrap_integer(tup[1]);
|
||||
def->sourcemap[i] = mapping;
|
||||
}
|
||||
}
|
||||
@@ -740,12 +735,12 @@ JanetAssembleResult janet_asm(Janet source, int flags) {
|
||||
|
||||
/* Disassembly */
|
||||
|
||||
/* Find the deinfintion of an instruction given the instruction word. Return
|
||||
/* Find the definition of an instruction given the instruction word. Return
|
||||
* NULL if not found. */
|
||||
static const JanetInstructionDef *janet_asm_reverse_lookup(uint32_t instr) {
|
||||
size_t i;
|
||||
uint32_t opcode = instr & 0x7F;
|
||||
for (i = 0; i < sizeof(janet_ops)/sizeof(JanetInstructionDef); i++) {
|
||||
for (i = 0; i < sizeof(janet_ops) / sizeof(JanetInstructionDef); i++) {
|
||||
const JanetInstructionDef *def = janet_ops + i;
|
||||
if (def->opcode == opcode)
|
||||
return def;
|
||||
@@ -781,7 +776,7 @@ static Janet tup4(Janet w, Janet x, Janet y, Janet z) {
|
||||
return janet_wrap_tuple(janet_tuple_end(tup));
|
||||
}
|
||||
|
||||
/* Given an argument, convert it to the appriate integer or symbol */
|
||||
/* Given an argument, convert it to the appropriate integer or symbol */
|
||||
Janet janet_asm_decode_instruction(uint32_t instr) {
|
||||
const JanetInstructionDef *def = janet_asm_reverse_lookup(instr);
|
||||
Janet name;
|
||||
@@ -803,25 +798,25 @@ Janet janet_asm_decode_instruction(uint32_t instr) {
|
||||
case JINT_SU:
|
||||
case JINT_SD:
|
||||
return tup3(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFFFF)));
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFFFF)));
|
||||
case JINT_SI:
|
||||
case JINT_SL:
|
||||
return tup3(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer((int32_t)instr >> 16));
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer((int32_t)instr >> 16));
|
||||
case JINT_SSS:
|
||||
case JINT_SES:
|
||||
case JINT_SSU:
|
||||
return tup4(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFF)),
|
||||
janet_wrap_integer(oparg(3, 0xFF)));
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFF)),
|
||||
janet_wrap_integer(oparg(3, 0xFF)));
|
||||
case JINT_SSI:
|
||||
return tup4(name,
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFF)),
|
||||
janet_wrap_integer((int32_t)instr >> 24));
|
||||
janet_wrap_integer(oparg(1, 0xFF)),
|
||||
janet_wrap_integer(oparg(2, 0xFF)),
|
||||
janet_wrap_integer((int32_t)instr >> 24));
|
||||
}
|
||||
#undef oparg
|
||||
return janet_wrap_nil();
|
||||
@@ -833,6 +828,8 @@ Janet janet_disasm(JanetFuncDef *def) {
|
||||
JanetArray *constants;
|
||||
JanetTable *ret = janet_table(10);
|
||||
janet_table_put(ret, janet_csymbolv("arity"), janet_wrap_integer(def->arity));
|
||||
janet_table_put(ret, janet_csymbolv("min-arity"), janet_wrap_integer(def->min_arity));
|
||||
janet_table_put(ret, janet_csymbolv("max-arity"), janet_wrap_integer(def->max_arity));
|
||||
janet_table_put(ret, janet_csymbolv("bytecode"), janet_wrap_array(bcode));
|
||||
if (NULL != def->source) {
|
||||
janet_table_put(ret, janet_csymbolv("source"), janet_wrap_string(def->source));
|
||||
@@ -840,9 +837,6 @@ Janet janet_disasm(JanetFuncDef *def) {
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_VARARG) {
|
||||
janet_table_put(ret, janet_csymbolv("vararg"), janet_wrap_true());
|
||||
}
|
||||
if (def->flags & JANET_FUNCDEF_FLAG_FIXARITY) {
|
||||
janet_table_put(ret, janet_csymbolv("fix-arity"), janet_wrap_true());
|
||||
}
|
||||
if (NULL != def->name) {
|
||||
janet_table_put(ret, janet_csymbolv("name"), janet_wrap_string(def->name));
|
||||
}
|
||||
@@ -876,8 +870,8 @@ Janet janet_disasm(JanetFuncDef *def) {
|
||||
for (i = 0; i < def->bytecode_length; i++) {
|
||||
Janet *t = janet_tuple_begin(2);
|
||||
JanetSourceMapping mapping = def->sourcemap[i];
|
||||
t[0] = janet_wrap_integer(mapping.line);
|
||||
t[1] = janet_wrap_integer(mapping.column);
|
||||
t[0] = janet_wrap_integer(mapping.start);
|
||||
t[1] = janet_wrap_integer(mapping.end);
|
||||
sourcemap->data[i] = janet_wrap_tuple(janet_tuple_end(t));
|
||||
}
|
||||
sourcemap->count = def->bytecode_length;
|
||||
@@ -912,45 +906,43 @@ Janet janet_disasm(JanetFuncDef *def) {
|
||||
}
|
||||
|
||||
/* C Function for assembly */
|
||||
static int cfun_asm(JanetArgs args) {
|
||||
static Janet cfun_asm(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 1);
|
||||
JanetAssembleResult res;
|
||||
JANET_FIXARITY(args, 1);
|
||||
res = janet_asm(args.v[0], 0);
|
||||
if (res.status == JANET_ASSEMBLE_OK) {
|
||||
JANET_RETURN_FUNCTION(args, janet_thunk(res.funcdef));
|
||||
} else {
|
||||
JANET_THROWV(args, janet_wrap_string(res.error));
|
||||
res = janet_asm(argv[0], 0);
|
||||
if (res.status != JANET_ASSEMBLE_OK) {
|
||||
janet_panics(res.error);
|
||||
}
|
||||
return janet_wrap_function(janet_thunk(res.funcdef));
|
||||
}
|
||||
|
||||
static int cfun_disasm(JanetArgs args) {
|
||||
JanetFunction *f;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_FUNCTION(f, args, 0);
|
||||
JANET_RETURN(args, janet_disasm(f->def));
|
||||
static Janet cfun_disasm(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 1);
|
||||
JanetFunction *f = janet_getfunction(argv, 0);
|
||||
return janet_disasm(f->def);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"asm", cfun_asm,
|
||||
"(asm assembly)\n\n"
|
||||
"Returns a new function that is the compiled result of the assembly.\n"
|
||||
"The syntax for the assembly can be found on the janet wiki. Will throw an\n"
|
||||
"error on invalid assembly."
|
||||
static const JanetReg asm_cfuns[] = {
|
||||
{
|
||||
"asm", cfun_asm,
|
||||
JDOC("(asm assembly)\n\n"
|
||||
"Returns a new function that is the compiled result of the assembly.\n"
|
||||
"The syntax for the assembly can be found on the janet wiki. Will throw an\n"
|
||||
"error on invalid assembly.")
|
||||
},
|
||||
{"disasm", cfun_disasm,
|
||||
"(disasm func)\n\n"
|
||||
"Returns assembly that could be used be compile the given function.\n"
|
||||
"func must be a function, not a c function. Will throw on error on a badly\n"
|
||||
"typed argument."
|
||||
{
|
||||
"disasm", cfun_disasm,
|
||||
JDOC("(disasm func)\n\n"
|
||||
"Returns assembly that could be used be compile the given function.\n"
|
||||
"func must be a function, not a c function. Will throw on error on a badly\n"
|
||||
"typed argument.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Load the library */
|
||||
int janet_lib_asm(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
return 0;
|
||||
void janet_lib_asm(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, asm_cfuns);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,8 +20,11 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Initialize a buffer */
|
||||
JanetBuffer *janet_buffer_init(JanetBuffer *buffer, int32_t capacity) {
|
||||
@@ -54,7 +57,8 @@ void janet_buffer_ensure(JanetBuffer *buffer, int32_t capacity, int32_t growth)
|
||||
uint8_t *new_data;
|
||||
uint8_t *old = buffer->data;
|
||||
if (capacity <= buffer->capacity) return;
|
||||
capacity *= growth;
|
||||
int64_t big_capacity = capacity * growth;
|
||||
capacity = big_capacity > INT32_MAX ? INT32_MAX : (int32_t) big_capacity;
|
||||
new_data = realloc(old, capacity * sizeof(uint8_t));
|
||||
if (NULL == new_data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
@@ -77,10 +81,10 @@ void janet_buffer_setcount(JanetBuffer *buffer, int32_t count) {
|
||||
|
||||
/* Adds capacity for enough extra bytes to the buffer. Ensures that the
|
||||
* next n bytes pushed to the buffer will not cause a reallocation */
|
||||
int janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
|
||||
void janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
|
||||
/* Check for buffer overflow */
|
||||
if ((int64_t)n + buffer->count > INT32_MAX) {
|
||||
return -1;
|
||||
janet_panic("buffer overflow");
|
||||
}
|
||||
int32_t new_size = buffer->count + n;
|
||||
if (new_size > buffer->capacity) {
|
||||
@@ -92,59 +96,54 @@ int janet_buffer_extra(JanetBuffer *buffer, int32_t n) {
|
||||
buffer->data = new_data;
|
||||
buffer->capacity = new_capacity;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Push a cstring to buffer */
|
||||
int janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) {
|
||||
void janet_buffer_push_cstring(JanetBuffer *buffer, const char *cstring) {
|
||||
int32_t len = 0;
|
||||
while (cstring[len]) ++len;
|
||||
return janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len);
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *) cstring, len);
|
||||
}
|
||||
|
||||
/* Push multiple bytes into the buffer */
|
||||
int janet_buffer_push_bytes(JanetBuffer *buffer, const uint8_t *string, int32_t length) {
|
||||
if (janet_buffer_extra(buffer, length)) return -1;
|
||||
void janet_buffer_push_bytes(JanetBuffer *buffer, const uint8_t *string, int32_t length) {
|
||||
janet_buffer_extra(buffer, length);
|
||||
memcpy(buffer->data + buffer->count, string, length);
|
||||
buffer->count += length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int janet_buffer_push_string(JanetBuffer *buffer, const uint8_t *string) {
|
||||
return janet_buffer_push_bytes(buffer, string, janet_string_length(string));
|
||||
void janet_buffer_push_string(JanetBuffer *buffer, const uint8_t *string) {
|
||||
janet_buffer_push_bytes(buffer, string, janet_string_length(string));
|
||||
}
|
||||
|
||||
/* Push a single byte to the buffer */
|
||||
int janet_buffer_push_u8(JanetBuffer *buffer, uint8_t byte) {
|
||||
if (janet_buffer_extra(buffer, 1)) return -1;
|
||||
void janet_buffer_push_u8(JanetBuffer *buffer, uint8_t byte) {
|
||||
janet_buffer_extra(buffer, 1);
|
||||
buffer->data[buffer->count] = byte;
|
||||
buffer->count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Push a 16 bit unsigned integer to the buffer */
|
||||
int janet_buffer_push_u16(JanetBuffer *buffer, uint16_t x) {
|
||||
if (janet_buffer_extra(buffer, 2)) return -1;
|
||||
void janet_buffer_push_u16(JanetBuffer *buffer, uint16_t x) {
|
||||
janet_buffer_extra(buffer, 2);
|
||||
buffer->data[buffer->count] = x & 0xFF;
|
||||
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
|
||||
buffer->count += 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Push a 32 bit unsigned integer to the buffer */
|
||||
int janet_buffer_push_u32(JanetBuffer *buffer, uint32_t x) {
|
||||
if (janet_buffer_extra(buffer, 4)) return -1;
|
||||
void janet_buffer_push_u32(JanetBuffer *buffer, uint32_t x) {
|
||||
janet_buffer_extra(buffer, 4);
|
||||
buffer->data[buffer->count] = x & 0xFF;
|
||||
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
|
||||
buffer->data[buffer->count + 2] = (x >> 16) & 0xFF;
|
||||
buffer->data[buffer->count + 3] = (x >> 24) & 0xFF;
|
||||
buffer->count += 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Push a 64 bit unsigned integer to the buffer */
|
||||
int janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x) {
|
||||
if (janet_buffer_extra(buffer, 8)) return -1;
|
||||
void janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x) {
|
||||
janet_buffer_extra(buffer, 8);
|
||||
buffer->data[buffer->count] = x & 0xFF;
|
||||
buffer->data[buffer->count + 1] = (x >> 8) & 0xFF;
|
||||
buffer->data[buffer->count + 2] = (x >> 16) & 0xFF;
|
||||
@@ -154,165 +153,278 @@ int janet_buffer_push_u64(JanetBuffer *buffer, uint64_t x) {
|
||||
buffer->data[buffer->count + 6] = (x >> 48) & 0xFF;
|
||||
buffer->data[buffer->count + 7] = (x >> 56) & 0xFF;
|
||||
buffer->count += 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* C functions */
|
||||
|
||||
static int cfun_new(JanetArgs args) {
|
||||
int32_t cap;
|
||||
JanetBuffer *buffer;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_INTEGER(cap, args, 0);
|
||||
buffer = janet_buffer(cap);
|
||||
JANET_RETURN_BUFFER(args, buffer);
|
||||
static Janet cfun_buffer_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
JanetBuffer *buffer = janet_buffer(cap);
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
static int cfun_u8(JanetArgs args) {
|
||||
int32_t i;
|
||||
JanetBuffer *buffer;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_ARG_BUFFER(buffer, args, 0);
|
||||
for (i = 1; i < args.n; i++) {
|
||||
int32_t integer;
|
||||
JANET_ARG_INTEGER(integer, args, i);
|
||||
if (janet_buffer_push_u8(buffer, (uint8_t) (integer & 0xFF)))
|
||||
JANET_THROW(args, "buffer overflow");
|
||||
static Janet cfun_buffer_new_filled(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
int32_t count = janet_getinteger(argv, 0);
|
||||
int32_t byte = 0;
|
||||
if (argc == 2) {
|
||||
byte = janet_getinteger(argv, 1) & 0xFF;
|
||||
}
|
||||
JANET_RETURN(args, args.v[0]);
|
||||
JanetBuffer *buffer = janet_buffer(count);
|
||||
if (buffer->data)
|
||||
memset(buffer->data, byte, count);
|
||||
buffer->count = count;
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
static int cfun_int(JanetArgs args) {
|
||||
static Janet cfun_buffer_u8(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
JanetBuffer *buffer;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_ARG_BUFFER(buffer, args, 0);
|
||||
for (i = 1; i < args.n; i++) {
|
||||
int32_t integer;
|
||||
JANET_ARG_INTEGER(integer, args, i);
|
||||
if (janet_buffer_push_u32(buffer, (uint32_t) integer))
|
||||
JANET_THROW(args, "buffer overflow");
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
for (i = 1; i < argc; i++) {
|
||||
janet_buffer_push_u8(buffer, (uint8_t)(janet_getinteger(argv, i) & 0xFF));
|
||||
}
|
||||
JANET_RETURN(args, args.v[0]);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static int cfun_chars(JanetArgs args) {
|
||||
static Janet cfun_buffer_word(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
JanetBuffer *buffer;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_ARG_BUFFER(buffer, args, 0);
|
||||
for (i = 1; i < args.n; i++) {
|
||||
int32_t len;
|
||||
const uint8_t *str;
|
||||
JANET_ARG_BYTES(str, len, args, i);
|
||||
if (janet_buffer_push_bytes(buffer, str, len))
|
||||
JANET_THROW(args, "buffer overflow");
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
for (i = 1; i < argc; i++) {
|
||||
double number = janet_getnumber(argv, i);
|
||||
uint32_t word = (uint32_t) number;
|
||||
if (word != number)
|
||||
janet_panicf("cannot convert %v to machine word", argv[i]);
|
||||
janet_buffer_push_u32(buffer, word);
|
||||
}
|
||||
JANET_RETURN(args, args.v[0]);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static int cfun_clear(JanetArgs args) {
|
||||
JanetBuffer *buffer;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_BUFFER(buffer, args, 0);
|
||||
static Janet cfun_buffer_chars(int32_t argc, Janet *argv) {
|
||||
int32_t i;
|
||||
janet_arity(argc, 1, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
for (i = 1; i < argc; i++) {
|
||||
JanetByteView view = janet_getbytes(argv, i);
|
||||
if (view.bytes == buffer->data) {
|
||||
janet_buffer_ensure(buffer, buffer->count + view.len, 2);
|
||||
view.bytes = buffer->data;
|
||||
}
|
||||
janet_buffer_push_bytes(buffer, view.bytes, view.len);
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_clear(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
buffer->count = 0;
|
||||
JANET_RETURN(args, args.v[0]);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static int cfun_popn(JanetArgs args) {
|
||||
JanetBuffer *buffer;
|
||||
int32_t n;
|
||||
JANET_FIXARITY(args, 2);
|
||||
JANET_ARG_BUFFER(buffer, args, 0);
|
||||
JANET_ARG_INTEGER(n, args, 1);
|
||||
if (n < 0) JANET_THROW(args, "n must be non-negative");
|
||||
static Janet cfun_buffer_popn(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
int32_t n = janet_getinteger(argv, 1);
|
||||
if (n < 0) janet_panic("n must be non-negative");
|
||||
if (buffer->count < n) {
|
||||
buffer->count = 0;
|
||||
} else {
|
||||
buffer->count -= n;
|
||||
}
|
||||
JANET_RETURN(args, args.v[0]);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static int cfun_slice(JanetArgs args) {
|
||||
const uint8_t *data;
|
||||
int32_t len, start, end;
|
||||
JanetBuffer *ret;
|
||||
JANET_ARG_BYTES(data, len, args, 0);
|
||||
/* Get start */
|
||||
if (args.n < 2) {
|
||||
start = 0;
|
||||
} else if (janet_checktype(args.v[1], JANET_INTEGER)) {
|
||||
start = janet_unwrap_integer(args.v[1]);
|
||||
} else {
|
||||
JANET_THROW(args, "expected integer");
|
||||
}
|
||||
/* Get end */
|
||||
if (args.n < 3) {
|
||||
end = -1;
|
||||
} else if (janet_checktype(args.v[2], JANET_INTEGER)) {
|
||||
end = janet_unwrap_integer(args.v[2]);
|
||||
} else {
|
||||
JANET_THROW(args, "expected integer");
|
||||
}
|
||||
if (start < 0) start = len + start;
|
||||
if (end < 0) end = len + end + 1;
|
||||
if (end < 0 || start < 0 || end > len || start > len)
|
||||
JANET_THROW(args, "slice range out of bounds");
|
||||
if (end >= start) {
|
||||
ret = janet_buffer(end - start);
|
||||
memcpy(ret->data, data + start, end - start);
|
||||
ret->count = end - start;
|
||||
} else {
|
||||
ret = janet_buffer(0);
|
||||
}
|
||||
JANET_RETURN_BUFFER(args, ret);
|
||||
static Janet cfun_buffer_slice(int32_t argc, Janet *argv) {
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
JanetByteView view = janet_getbytes(argv, 0);
|
||||
JanetBuffer *buffer = janet_buffer(range.end - range.start);
|
||||
if (buffer->data)
|
||||
memcpy(buffer->data, view.bytes + range.start, range.end - range.start);
|
||||
buffer->count = range.end - range.start;
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"buffer/new", cfun_new,
|
||||
"(buffer/new capacity)\n\n"
|
||||
"Creates a new, empty buffer with enough memory for capacity bytes. "
|
||||
"Returns a new buffer."
|
||||
static void bitloc(int32_t argc, Janet *argv, JanetBuffer **b, int32_t *index, int *bit) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
double x = janet_getnumber(argv, 1);
|
||||
int64_t bitindex = (int64_t) x;
|
||||
int64_t byteindex = bitindex >> 3;
|
||||
int which_bit = bitindex & 7;
|
||||
if (bitindex != x || bitindex < 0 || byteindex >= buffer->count)
|
||||
janet_panicf("invalid bit index %v", argv[1]);
|
||||
*b = buffer;
|
||||
*index = (int32_t) byteindex;
|
||||
*bit = which_bit;
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_bitset(int32_t argc, Janet *argv) {
|
||||
int bit;
|
||||
int32_t index;
|
||||
JanetBuffer *buffer;
|
||||
bitloc(argc, argv, &buffer, &index, &bit);
|
||||
buffer->data[index] |= 1 << bit;
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_bitclear(int32_t argc, Janet *argv) {
|
||||
int bit;
|
||||
int32_t index;
|
||||
JanetBuffer *buffer;
|
||||
bitloc(argc, argv, &buffer, &index, &bit);
|
||||
buffer->data[index] &= ~(1 << bit);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_bitget(int32_t argc, Janet *argv) {
|
||||
int bit;
|
||||
int32_t index;
|
||||
JanetBuffer *buffer;
|
||||
bitloc(argc, argv, &buffer, &index, &bit);
|
||||
return janet_wrap_boolean(buffer->data[index] & (1 << bit));
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_bittoggle(int32_t argc, Janet *argv) {
|
||||
int bit;
|
||||
int32_t index;
|
||||
JanetBuffer *buffer;
|
||||
bitloc(argc, argv, &buffer, &index, &bit);
|
||||
buffer->data[index] ^= (1 << bit);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_blit(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 5);
|
||||
JanetBuffer *dest = janet_getbuffer(argv, 0);
|
||||
JanetByteView src = janet_getbytes(argv, 1);
|
||||
int same_buf = src.bytes == dest->data;
|
||||
int32_t offset_dest = 0;
|
||||
int32_t offset_src = 0;
|
||||
if (argc > 2)
|
||||
offset_dest = janet_gethalfrange(argv, 2, dest->count, "dest-start");
|
||||
if (argc > 3)
|
||||
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");
|
||||
length_src = src_end - offset_src;
|
||||
if (length_src < 0) length_src = 0;
|
||||
} else {
|
||||
length_src = src.len - offset_src;
|
||||
}
|
||||
int64_t last = ((int64_t) offset_dest - offset_src) + length_src;
|
||||
if (last > INT32_MAX)
|
||||
janet_panic("buffer blit out of range");
|
||||
janet_buffer_ensure(dest, (int32_t) last, 2);
|
||||
if (last > dest->count) dest->count = (int32_t) last;
|
||||
if (same_buf) {
|
||||
src.bytes = dest->data;
|
||||
memmove(dest->data + offset_dest, src.bytes + offset_src, length_src);
|
||||
} else {
|
||||
memcpy(dest->data + offset_dest, src.bytes + offset_src, length_src);
|
||||
}
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_buffer_format(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, -1);
|
||||
JanetBuffer *buffer = janet_getbuffer(argv, 0);
|
||||
const char *strfrmt = (const char *) janet_getstring(argv, 1);
|
||||
janet_buffer_format(buffer, strfrmt, 1, argc, argv);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg buffer_cfuns[] = {
|
||||
{
|
||||
"buffer/new", cfun_buffer_new,
|
||||
JDOC("(buffer/new capacity)\n\n"
|
||||
"Creates a new, empty buffer with enough memory for capacity bytes. "
|
||||
"Returns a new buffer.")
|
||||
},
|
||||
{"buffer/push-byte", cfun_u8,
|
||||
"(buffer/push-byte buffer x)\n\n"
|
||||
"Append a byte to a buffer. Will expand the buffer as necessary. "
|
||||
"Returns the modified buffer. Will throw an error if the buffer overflows."
|
||||
{
|
||||
"buffer/new-filled", cfun_buffer_new_filled,
|
||||
JDOC("(buffer/new-filled count &opt byte)\n\n"
|
||||
"Creates a new buffer of length count filled with byte. By default, byte is 0. "
|
||||
"Returns the new buffer.")
|
||||
},
|
||||
{"buffer/push-integer", cfun_int,
|
||||
"(buffer/push-integer buffer x)\n\n"
|
||||
"Append an integer to a buffer. The 4 bytes of the integer are appended "
|
||||
"in twos complement, big endian order. Returns the modified buffer. Will "
|
||||
"throw an error if the buffer overflows."
|
||||
{
|
||||
"buffer/push-byte", cfun_buffer_u8,
|
||||
JDOC("(buffer/push-byte buffer x)\n\n"
|
||||
"Append a byte to a buffer. Will expand the buffer as necessary. "
|
||||
"Returns the modified buffer. Will throw an error if the buffer overflows.")
|
||||
},
|
||||
{"buffer/push-string", cfun_chars,
|
||||
"(buffer/push-string buffer str)\n\n"
|
||||
"Push a string onto the end of a buffer. Non string values will be converted "
|
||||
"to strings before being pushed. Returns the modified buffer. "
|
||||
"Will throw an error if the buffer overflows."
|
||||
{
|
||||
"buffer/push-word", cfun_buffer_word,
|
||||
JDOC("(buffer/push-word buffer x)\n\n"
|
||||
"Append a machine word to a buffer. The 4 bytes of the integer are appended "
|
||||
"in twos complement, big endian order, unsigned. Returns the modified buffer. Will "
|
||||
"throw an error if the buffer overflows.")
|
||||
},
|
||||
{"buffer/popn", cfun_popn,
|
||||
"(buffer/popn buffer n)\n\n"
|
||||
"Removes the last n bytes from the buffer. Returns the modified buffer."
|
||||
{
|
||||
"buffer/push-string", cfun_buffer_chars,
|
||||
JDOC("(buffer/push-string buffer str)\n\n"
|
||||
"Push a string onto the end of a buffer. Non string values will be converted "
|
||||
"to strings before being pushed. Returns the modified buffer. "
|
||||
"Will throw an error if the buffer overflows.")
|
||||
},
|
||||
{"buffer/clear", cfun_clear,
|
||||
"(buffer/clear buffer)\n\n"
|
||||
"Sets the size of a buffer to 0 and empties it. The buffer retains "
|
||||
"its memory so it can be efficiently refilled. Returns the modified buffer."
|
||||
{
|
||||
"buffer/popn", cfun_buffer_popn,
|
||||
JDOC("(buffer/popn buffer n)\n\n"
|
||||
"Removes the last n bytes from the buffer. Returns the modified buffer.")
|
||||
},
|
||||
{"buffer/slice", cfun_slice,
|
||||
"(buffer/slice bytes [, start=0 [, end=(length bytes)]])\n\n"
|
||||
"Takes a slice of a byte sequence from start to end. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the end of the "
|
||||
"end of the array. By default, start is 0 and end is the length of the buffer. "
|
||||
"Returns a new buffer."
|
||||
{
|
||||
"buffer/clear", cfun_buffer_clear,
|
||||
JDOC("(buffer/clear buffer)\n\n"
|
||||
"Sets the size of a buffer to 0 and empties it. The buffer retains "
|
||||
"its memory so it can be efficiently refilled. Returns the modified buffer.")
|
||||
},
|
||||
{
|
||||
"buffer/slice", cfun_buffer_slice,
|
||||
JDOC("(buffer/slice bytes &opt start end)\n\n"
|
||||
"Takes a slice of a byte sequence from start to end. The range is half open, "
|
||||
"[start, end). Indexes can also be negative, indicating indexing from the end of the "
|
||||
"end of the array. By default, start is 0 and end is the length of the buffer. "
|
||||
"Returns a new buffer.")
|
||||
},
|
||||
{
|
||||
"buffer/bit-set", cfun_buffer_bitset,
|
||||
JDOC("(buffer/bit-set buffer index)\n\n"
|
||||
"Sets the bit at the given bit-index. Returns the buffer.")
|
||||
},
|
||||
{
|
||||
"buffer/bit-clear", cfun_buffer_bitclear,
|
||||
JDOC("(buffer/bit-clear buffer index)\n\n"
|
||||
"Clears the bit at the given bit-index. Returns the buffer.")
|
||||
},
|
||||
{
|
||||
"buffer/bit", cfun_buffer_bitget,
|
||||
JDOC("(buffer/bit buffer index)\n\n"
|
||||
"Gets the bit at the given bit-index. Returns true if the bit is set, false if not.")
|
||||
},
|
||||
{
|
||||
"buffer/bit-toggle", cfun_buffer_bittoggle,
|
||||
JDOC("(buffer/bit-toggle buffer index)\n\n"
|
||||
"Toggles the bit at the given bit index in buffer. Returns the buffer.")
|
||||
},
|
||||
{
|
||||
"buffer/blit", cfun_buffer_blit,
|
||||
JDOC("(buffer/blit dest src & opt dest-start src-start src-end)\n\n"
|
||||
"Insert the contents of src into dest. Can optionally take indices that "
|
||||
"indicate which part of src to copy into which part of dest. Indices can be "
|
||||
"negative to index from the end of src or dest. Returns dest.")
|
||||
},
|
||||
{
|
||||
"buffer/format", cfun_buffer_format,
|
||||
JDOC("(buffer/format buffer format & args)\n\n"
|
||||
"Snprintf like functionality for printing values into a buffer. Returns "
|
||||
" the modified buffer.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
int janet_lib_buffer(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
return 0;
|
||||
void janet_lib_buffer(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, buffer_cfuns);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,8 +20,11 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Look up table for instructions */
|
||||
enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
||||
@@ -30,20 +33,12 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
||||
JINT_ST, /* JOP_TYPECHECK, */
|
||||
JINT_S, /* JOP_RETURN, */
|
||||
JINT_0, /* JOP_RETURN_NIL, */
|
||||
JINT_SSS, /* JOP_ADD_INTEGER, */
|
||||
JINT_SSI, /* JOP_ADD_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_ADD_REAL, */
|
||||
JINT_SSS, /* JOP_ADD, */
|
||||
JINT_SSS, /* JOP_SUBTRACT_INTEGER, */
|
||||
JINT_SSS, /* JOP_SUBTRACT_REAL, */
|
||||
JINT_SSS, /* JOP_SUBTRACT, */
|
||||
JINT_SSS, /* JOP_MULTIPLY_INTEGER, */
|
||||
JINT_SSI, /* JOP_MULTIPLY_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_MULTIPLY_REAL, */
|
||||
JINT_SSS, /* JOP_MULTIPLY, */
|
||||
JINT_SSS, /* JOP_DIVIDE_INTEGER, */
|
||||
JINT_SSI, /* JOP_DIVIDE_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_DIVIDE_REAL, */
|
||||
JINT_SSS, /* JOP_DIVIDE, */
|
||||
JINT_SSS, /* JOP_BAND, */
|
||||
JINT_SSS, /* JOP_BOR, */
|
||||
@@ -61,19 +56,11 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
||||
JINT_SL, /* JOP_JUMP_IF, */
|
||||
JINT_SL, /* JOP_JUMP_IF_NOT, */
|
||||
JINT_SSS, /* JOP_GREATER_THAN, */
|
||||
JINT_SSS, /* JOP_GREATER_THAN_INTEGER, */
|
||||
JINT_SSI, /* JOP_GREATER_THAN_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_GREATER_THAN_REAL, */
|
||||
JINT_SSS, /* JOP_GREATER_THAN_EQUAL_REAL, */
|
||||
JINT_SSS, /* JOP_LESS_THAN, */
|
||||
JINT_SSS, /* JOP_LESS_THAN_INTEGER, */
|
||||
JINT_SSI, /* JOP_LESS_THAN_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_LESS_THAN_REAL, */
|
||||
JINT_SSS, /* JOP_LESS_THAN_EQUAL_REAL, */
|
||||
JINT_SSS, /* JOP_EQUALS, */
|
||||
JINT_SSS, /* JOP_EQUALS_INTEGER, */
|
||||
JINT_SSI, /* JOP_EQUALS_IMMEDIATE, */
|
||||
JINT_SSS, /* JOP_EQUALS_REAL, */
|
||||
JINT_SSS, /* JOP_COMPARE, */
|
||||
JINT_S, /* JOP_LOAD_NIL, */
|
||||
JINT_S, /* JOP_LOAD_TRUE, */
|
||||
@@ -92,6 +79,7 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
||||
JINT_S, /* JOP_TAILCALL, */
|
||||
JINT_SSS, /* JOP_RESUME, */
|
||||
JINT_SSU, /* JOP_SIGNAL, */
|
||||
JINT_SSS, /* JOP_PROPAGATE */
|
||||
JINT_SSS, /* JOP_GET, */
|
||||
JINT_SSS, /* JOP_PUT, */
|
||||
JINT_SSU, /* JOP_GET_INDEX, */
|
||||
@@ -99,10 +87,11 @@ enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT] = {
|
||||
JINT_SS, /* JOP_LENGTH */
|
||||
JINT_S, /* JOP_MAKE_ARRAY */
|
||||
JINT_S, /* JOP_MAKE_BUFFER */
|
||||
JINT_S, /* JOP_MAKE_TUPLE */
|
||||
JINT_S, /* JOP_MAKE_STRING */
|
||||
JINT_S, /* JOP_MAKE_STRUCT */
|
||||
JINT_S, /* JOP_MAKE_TABLE */
|
||||
JINT_S, /* JOP_MAKE_STRING */
|
||||
JINT_S, /* JOP_MAKE_TUPLE */
|
||||
JINT_S, /* JOP_MAKE_BRACKET_TUPLE */
|
||||
JINT_SSS, /* JOP_NUMERIC_LESS_THAN */
|
||||
JINT_SSS, /* JOP_NUMERIC_LESS_THAN_EQUAL */
|
||||
JINT_SSS, /* JOP_NUMERIC_GREATER_THAN */
|
||||
@@ -125,79 +114,69 @@ int32_t janet_verify(JanetFuncDef *def) {
|
||||
for (i = 0; i < def->bytecode_length; i++) {
|
||||
uint32_t instr = def->bytecode[i];
|
||||
/* Check for invalid instructions */
|
||||
if ((instr & 0xFF) >= JOP_INSTRUCTION_COUNT) {
|
||||
if ((instr & 0x7F) >= JOP_INSTRUCTION_COUNT) {
|
||||
return 3;
|
||||
}
|
||||
enum JanetInstructionType type = janet_instructions[instr & 0xFF];
|
||||
enum JanetInstructionType type = janet_instructions[instr & 0x7F];
|
||||
switch (type) {
|
||||
case JINT_0:
|
||||
continue;
|
||||
case JINT_S:
|
||||
{
|
||||
if ((int32_t)(instr >> 8) >= sc) return 4;
|
||||
continue;
|
||||
}
|
||||
case JINT_S: {
|
||||
if ((int32_t)(instr >> 8) >= sc) return 4;
|
||||
continue;
|
||||
}
|
||||
case JINT_SI:
|
||||
case JINT_SU:
|
||||
case JINT_ST:
|
||||
{
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
continue;
|
||||
}
|
||||
case JINT_L:
|
||||
{
|
||||
int32_t jumpdest = i + (((int32_t)instr) >> 8);
|
||||
if (jumpdest < 0 || jumpdest >= def->bytecode_length) return 5;
|
||||
continue;
|
||||
}
|
||||
case JINT_SS:
|
||||
{
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc ||
|
||||
case JINT_ST: {
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
continue;
|
||||
}
|
||||
case JINT_L: {
|
||||
int32_t jumpdest = i + (((int32_t)instr) >> 8);
|
||||
if (jumpdest < 0 || jumpdest >= def->bytecode_length) return 5;
|
||||
continue;
|
||||
}
|
||||
case JINT_SS: {
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc ||
|
||||
(int32_t)(instr >> 16) >= sc) return 4;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case JINT_SSI:
|
||||
case JINT_SSU:
|
||||
{
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc ||
|
||||
case JINT_SSU: {
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc ||
|
||||
(int32_t)((instr >> 16) & 0xFF) >= sc) return 4;
|
||||
continue;
|
||||
}
|
||||
case JINT_SL:
|
||||
{
|
||||
int32_t jumpdest = i + (((int32_t)instr) >> 16);
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
if (jumpdest < 0 || jumpdest >= def->bytecode_length) return 5;
|
||||
continue;
|
||||
}
|
||||
case JINT_SSS:
|
||||
{
|
||||
if (((int32_t)(instr >> 8) & 0xFF) >= sc ||
|
||||
continue;
|
||||
}
|
||||
case JINT_SL: {
|
||||
int32_t jumpdest = i + (((int32_t)instr) >> 16);
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
if (jumpdest < 0 || jumpdest >= def->bytecode_length) return 5;
|
||||
continue;
|
||||
}
|
||||
case JINT_SSS: {
|
||||
if (((int32_t)(instr >> 8) & 0xFF) >= sc ||
|
||||
((int32_t)(instr >> 16) & 0xFF) >= sc ||
|
||||
((int32_t)(instr >> 24) & 0xFF) >= sc) return 4;
|
||||
continue;
|
||||
}
|
||||
case JINT_SD:
|
||||
{
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
if ((int32_t)(instr >> 16) >= def->defs_length) return 6;
|
||||
continue;
|
||||
}
|
||||
case JINT_SC:
|
||||
{
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
if ((int32_t)(instr >> 16) >= def->constants_length) return 7;
|
||||
continue;
|
||||
}
|
||||
case JINT_SES:
|
||||
{
|
||||
/* How can we check the last slot index? We need info parent funcdefs. Resort
|
||||
* to runtime checks for now. Maybe invalid upvalue references could be defaulted
|
||||
* to nil? (don't commit to this in the long term, though) */
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
if ((int32_t)((instr >> 16) & 0xFF) >= def->environments_length) return 8;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
case JINT_SD: {
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
if ((int32_t)(instr >> 16) >= def->defs_length) return 6;
|
||||
continue;
|
||||
}
|
||||
case JINT_SC: {
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
if ((int32_t)(instr >> 16) >= def->constants_length) return 7;
|
||||
continue;
|
||||
}
|
||||
case JINT_SES: {
|
||||
/* How can we check the last slot index? We need info parent funcdefs. Resort
|
||||
* to runtime checks for now. Maybe invalid upvalue references could be defaulted
|
||||
* to nil? (don't commit to this in the long term, though) */
|
||||
if ((int32_t)((instr >> 8) & 0xFF) >= sc) return 4;
|
||||
if ((int32_t)((instr >> 16) & 0xFF) >= def->environments_length) return 8;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +211,8 @@ JanetFuncDef *janet_funcdef_alloc() {
|
||||
def->flags = 0;
|
||||
def->slotcount = 0;
|
||||
def->arity = 0;
|
||||
def->min_arity = 0;
|
||||
def->max_arity = INT32_MAX;
|
||||
def->source = NULL;
|
||||
def->sourcemap = NULL;
|
||||
def->name = NULL;
|
||||
|
||||
291
src/core/capi.c
Normal file
291
src/core/capi.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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 <janet.h>
|
||||
#include "state.h"
|
||||
#include "fiber.h"
|
||||
#endif
|
||||
|
||||
void janet_panicv(Janet message) {
|
||||
if (janet_vm_return_reg != NULL) {
|
||||
*janet_vm_return_reg = message;
|
||||
longjmp(*janet_vm_jmp_buf, 1);
|
||||
} else {
|
||||
fputs((const char *)janet_formatc("janet top level panic - %v\n", message), stdout);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void janet_panicf(const char *format, ...) {
|
||||
va_list args;
|
||||
const uint8_t *ret;
|
||||
JanetBuffer buffer;
|
||||
int32_t len = 0;
|
||||
while (format[len]) len++;
|
||||
janet_buffer_init(&buffer, len);
|
||||
va_start(args, format);
|
||||
janet_formatb(&buffer, format, args);
|
||||
va_end(args);
|
||||
ret = janet_string(buffer.data, buffer.count);
|
||||
janet_buffer_deinit(&buffer);
|
||||
janet_panics(ret);
|
||||
}
|
||||
|
||||
void janet_printf(const char *format, ...) {
|
||||
va_list args;
|
||||
JanetBuffer buffer;
|
||||
int32_t len = 0;
|
||||
while (format[len]) len++;
|
||||
janet_buffer_init(&buffer, len);
|
||||
va_start(args, format);
|
||||
janet_formatb(&buffer, format, args);
|
||||
va_end(args);
|
||||
fwrite(buffer.data, buffer.count, 1, janet_dynfile("out", stdout));
|
||||
janet_buffer_deinit(&buffer);
|
||||
}
|
||||
|
||||
void janet_panic(const char *message) {
|
||||
janet_panicv(janet_cstringv(message));
|
||||
}
|
||||
|
||||
void janet_panics(const uint8_t *message) {
|
||||
janet_panicv(janet_wrap_string(message));
|
||||
}
|
||||
|
||||
void janet_panic_type(Janet x, int32_t n, int expected) {
|
||||
janet_panicf("bad slot #%d, expected %T, got %v", n, expected, x);
|
||||
}
|
||||
|
||||
void janet_panic_abstract(Janet x, int32_t n, const JanetAbstractType *at) {
|
||||
janet_panicf("bad slot #%d, expected %s, got %v", n, at->name, x);
|
||||
}
|
||||
|
||||
void janet_fixarity(int32_t arity, int32_t fix) {
|
||||
if (arity != fix)
|
||||
janet_panicf("arity mismatch, expected %d, got %d", fix, arity);
|
||||
}
|
||||
|
||||
void janet_arity(int32_t arity, int32_t min, int32_t max) {
|
||||
if (min >= 0 && arity < min)
|
||||
janet_panicf("arity mismatch, expected at least %d, got %d", min, arity);
|
||||
if (max >= 0 && arity > max)
|
||||
janet_panicf("arity mismatch, expected at most %d, got %d", max, arity);
|
||||
}
|
||||
|
||||
#define DEFINE_GETTER(name, NAME, type) \
|
||||
type janet_get##name(const Janet *argv, int32_t n) { \
|
||||
Janet x = argv[n]; \
|
||||
if (!janet_checktype(x, JANET_##NAME)) { \
|
||||
janet_panic_type(x, n, JANET_TFLAG_##NAME); \
|
||||
} \
|
||||
return janet_unwrap_##name(x); \
|
||||
}
|
||||
|
||||
Janet janet_getmethod(const uint8_t *method, const JanetMethod *methods) {
|
||||
while (methods->name) {
|
||||
if (!janet_cstrcmp(method, methods->name))
|
||||
return janet_wrap_cfunction(methods->cfun);
|
||||
methods++;
|
||||
}
|
||||
janet_panicf("unknown method %S invoked", method);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
DEFINE_GETTER(number, NUMBER, double)
|
||||
DEFINE_GETTER(array, ARRAY, JanetArray *)
|
||||
DEFINE_GETTER(tuple, TUPLE, const Janet *)
|
||||
DEFINE_GETTER(table, TABLE, JanetTable *)
|
||||
DEFINE_GETTER(struct, STRUCT, const JanetKV *)
|
||||
DEFINE_GETTER(string, STRING, const uint8_t *)
|
||||
DEFINE_GETTER(keyword, KEYWORD, const uint8_t *)
|
||||
DEFINE_GETTER(symbol, SYMBOL, const uint8_t *)
|
||||
DEFINE_GETTER(buffer, BUFFER, JanetBuffer *)
|
||||
DEFINE_GETTER(fiber, FIBER, JanetFiber *)
|
||||
DEFINE_GETTER(function, FUNCTION, JanetFunction *)
|
||||
DEFINE_GETTER(cfunction, CFUNCTION, JanetCFunction)
|
||||
DEFINE_GETTER(boolean, BOOLEAN, int)
|
||||
DEFINE_GETTER(pointer, POINTER, void *)
|
||||
|
||||
const char *janet_getcstring(const Janet *argv, int32_t n) {
|
||||
const uint8_t *jstr = janet_getstring(argv, n);
|
||||
const char *cstr = (const char *)jstr;
|
||||
if (strlen(cstr) != (size_t) janet_string_length(jstr)) {
|
||||
janet_panicf("string %v contains embedded 0s");
|
||||
}
|
||||
return cstr;
|
||||
}
|
||||
|
||||
int32_t janet_getinteger(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
if (!janet_checkint(x)) {
|
||||
janet_panicf("bad slot #%d, expected integer, got %v", n, x);
|
||||
}
|
||||
return janet_unwrap_integer(x);
|
||||
}
|
||||
|
||||
int64_t janet_getinteger64(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
if (!janet_checkint64(x)) {
|
||||
janet_panicf("bad slot #%d, expected 64 bit integer, got %v", n, x);
|
||||
}
|
||||
return (int64_t) janet_unwrap_number(x);
|
||||
}
|
||||
|
||||
size_t janet_getsize(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
if (!janet_checksize(x)) {
|
||||
janet_panicf("bad slot #%d, expected size, got %v", n, x);
|
||||
}
|
||||
return (size_t) janet_unwrap_number(x);
|
||||
}
|
||||
|
||||
int32_t janet_gethalfrange(const Janet *argv, int32_t n, int32_t length, const char *which) {
|
||||
int32_t raw = janet_getinteger(argv, n);
|
||||
if (raw < 0) raw += length + 1;
|
||||
if (raw < 0 || raw > length)
|
||||
janet_panicf("%s index %d out of range [0,%d]", which, raw, length);
|
||||
return raw;
|
||||
}
|
||||
|
||||
int32_t janet_getargindex(const Janet *argv, int32_t n, int32_t length, const char *which) {
|
||||
int32_t raw = janet_getinteger(argv, n);
|
||||
if (raw < 0) raw += length;
|
||||
if (raw < 0 || raw > length)
|
||||
janet_panicf("%s index %d out of range [0,%d)", which, raw, length);
|
||||
return raw;
|
||||
}
|
||||
|
||||
JanetView janet_getindexed(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
JanetView view;
|
||||
if (!janet_indexed_view(x, &view.items, &view.len)) {
|
||||
janet_panic_type(x, n, JANET_TFLAG_INDEXED);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
JanetByteView janet_getbytes(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
JanetByteView view;
|
||||
if (!janet_bytes_view(x, &view.bytes, &view.len)) {
|
||||
janet_panic_type(x, n, JANET_TFLAG_BYTES);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
JanetDictView janet_getdictionary(const Janet *argv, int32_t n) {
|
||||
Janet x = argv[n];
|
||||
JanetDictView view;
|
||||
if (!janet_dictionary_view(x, &view.kvs, &view.len, &view.cap)) {
|
||||
janet_panic_type(x, n, JANET_TFLAG_DICTIONARY);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
void *janet_getabstract(const Janet *argv, int32_t n, const JanetAbstractType *at) {
|
||||
Janet x = argv[n];
|
||||
if (!janet_checktype(x, JANET_ABSTRACT)) {
|
||||
janet_panic_abstract(x, n, at);
|
||||
}
|
||||
void *abstractx = janet_unwrap_abstract(x);
|
||||
if (janet_abstract_type(abstractx) != at) {
|
||||
janet_panic_abstract(x, n, at);
|
||||
}
|
||||
return abstractx;
|
||||
}
|
||||
|
||||
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_gethalfrange(argv, 1, length, "start");
|
||||
range.end = length;
|
||||
} else {
|
||||
range.start = janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = janet_gethalfrange(argv, 2, length, "end");
|
||||
if (range.end < range.start)
|
||||
range.end = range.start;
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
Janet janet_dyn(const char *name) {
|
||||
if (!janet_vm_fiber) return janet_wrap_nil();
|
||||
if (janet_vm_fiber->env) {
|
||||
return janet_table_get(janet_vm_fiber->env, janet_ckeywordv(name));
|
||||
} else {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
}
|
||||
|
||||
void janet_setdyn(const char *name, Janet value) {
|
||||
if (!janet_vm_fiber) return;
|
||||
if (!janet_vm_fiber->env) {
|
||||
janet_vm_fiber->env = janet_table(1);
|
||||
}
|
||||
janet_table_put(janet_vm_fiber->env, janet_ckeywordv(name), value);
|
||||
}
|
||||
|
||||
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);
|
||||
int32_t klen = janet_string_length(keyw);
|
||||
int32_t flen = (int32_t) strlen(flags);
|
||||
if (flen > 64) {
|
||||
flen = 64;
|
||||
}
|
||||
for (int32_t j = 0; j < klen; j++) {
|
||||
for (int32_t i = 0; i < flen; i++) {
|
||||
if (((uint8_t) flags[i]) == keyw[j]) {
|
||||
ret |= 1ULL << i;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
janet_panicf("unexpected flag %c, expected one of \"%s\"", (char) keyw[j], flags);
|
||||
found:
|
||||
;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Some definitions for function-like macros */
|
||||
|
||||
JANET_API JanetStructHead *(janet_struct_head)(const JanetKV *st) {
|
||||
return janet_struct_head(st);
|
||||
}
|
||||
|
||||
JANET_API JanetAbstractHead *(janet_abstract_head)(const void *abstract) {
|
||||
return janet_abstract_head(abstract);
|
||||
}
|
||||
|
||||
JANET_API JanetStringHead *(janet_string_head)(const uint8_t *s) {
|
||||
return janet_string_head(s);
|
||||
}
|
||||
|
||||
JANET_API JanetTupleHead *(janet_tuple_head)(const Janet *tuple) {
|
||||
return janet_tuple_head(tuple);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,10 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "compile.h"
|
||||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
#endif
|
||||
|
||||
static int fixarity0(JanetFopts opts, JanetSlot *args) {
|
||||
(void) opts;
|
||||
@@ -33,6 +35,10 @@ static int fixarity1(JanetFopts opts, JanetSlot *args) {
|
||||
(void) opts;
|
||||
return janet_v_count(args) == 1;
|
||||
}
|
||||
static int maxarity1(JanetFopts opts, JanetSlot *args) {
|
||||
(void) opts;
|
||||
return janet_v_count(args) <= 1;
|
||||
}
|
||||
static int minarity2(JanetFopts opts, JanetSlot *args) {
|
||||
(void) opts;
|
||||
return janet_v_count(args) >= 2;
|
||||
@@ -46,14 +52,14 @@ static int fixarity3(JanetFopts opts, JanetSlot *args) {
|
||||
return janet_v_count(args) == 3;
|
||||
}
|
||||
|
||||
/* Generic hanldling for $A = op $B */
|
||||
/* Generic handling for $A = op $B */
|
||||
static JanetSlot genericSS(JanetFopts opts, int op, JanetSlot s) {
|
||||
JanetSlot target = janetc_gettarget(opts);
|
||||
janetc_emit_ss(opts.compiler, op, target, s, 1);
|
||||
return target;
|
||||
}
|
||||
|
||||
/* Generic hanldling for $A = $B op I */
|
||||
/* Generic handling for $A = $B op I */
|
||||
static JanetSlot genericSSI(JanetFopts opts, int op, JanetSlot s, int32_t imm) {
|
||||
JanetSlot target = janetc_gettarget(opts);
|
||||
janetc_emit_ssi(opts.compiler, op, target, s, imm, 1);
|
||||
@@ -62,10 +68,10 @@ static JanetSlot genericSSI(JanetFopts opts, int op, JanetSlot s, int32_t imm) {
|
||||
|
||||
/* Emit a series of instructions instead of a function call to a math op */
|
||||
static JanetSlot opreduce(
|
||||
JanetFopts opts,
|
||||
JanetSlot *args,
|
||||
int op,
|
||||
Janet nullary) {
|
||||
JanetFopts opts,
|
||||
JanetSlot *args,
|
||||
int op,
|
||||
Janet nullary) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
int32_t i, len;
|
||||
len = janet_v_count(args);
|
||||
@@ -86,6 +92,9 @@ static JanetSlot opreduce(
|
||||
|
||||
/* Function optimizers */
|
||||
|
||||
static JanetSlot do_propagate(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_PROPAGATE, janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_error(JanetFopts opts, JanetSlot *args) {
|
||||
janetc_emit_s(opts.compiler, JOP_ERROR, args[0], 0);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
@@ -99,14 +108,25 @@ static JanetSlot do_get(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_GET, janet_wrap_nil());
|
||||
}
|
||||
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
|
||||
return args[0];
|
||||
if (opts.flags & JANET_FOPTS_DROP) {
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
} else {
|
||||
JanetSlot t = janetc_gettarget(opts);
|
||||
janetc_copy(opts.compiler, t, args[0]);
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, t, args[1], args[2], 0);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
static JanetSlot do_length(JanetFopts opts, JanetSlot *args) {
|
||||
return genericSS(opts, JOP_LENGTH, args[0]);
|
||||
}
|
||||
static JanetSlot do_yield(JanetFopts opts, JanetSlot *args) {
|
||||
return genericSSI(opts, JOP_SIGNAL, args[0], 3);
|
||||
if (janet_v_count(args) == 0) {
|
||||
return genericSSI(opts, JOP_SIGNAL, janetc_cslot(janet_wrap_nil()), 3);
|
||||
} else {
|
||||
return genericSSI(opts, JOP_SIGNAL, args[0], 3);
|
||||
}
|
||||
}
|
||||
static JanetSlot do_resume(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_RESUME, janet_wrap_nil());
|
||||
@@ -116,9 +136,9 @@ static JanetSlot do_apply(JanetFopts opts, JanetSlot *args) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
int32_t i;
|
||||
for (i = 1; i < janet_v_count(args) - 3; i += 3)
|
||||
janetc_emit_sss(c, JOP_PUSH_3, args[i], args[i+1], args[i+2], 0);
|
||||
janetc_emit_sss(c, JOP_PUSH_3, args[i], args[i + 1], args[i + 2], 0);
|
||||
if (i == janet_v_count(args) - 3)
|
||||
janetc_emit_ss(c, JOP_PUSH_2, args[i], args[i+1], 0);
|
||||
janetc_emit_ss(c, JOP_PUSH_2, args[i], args[i + 1], 0);
|
||||
else if (i == janet_v_count(args) - 2)
|
||||
janetc_emit_s(c, JOP_PUSH, args[i], 0);
|
||||
/* Push array phase */
|
||||
@@ -136,7 +156,7 @@ static JanetSlot do_apply(JanetFopts opts, JanetSlot *args) {
|
||||
return target;
|
||||
}
|
||||
|
||||
/* Varidadic operators specialization */
|
||||
/* Variadic operators specialization */
|
||||
|
||||
static JanetSlot do_add(JanetFopts opts, JanetSlot *args) {
|
||||
return opreduce(opts, args, JOP_ADD, janet_wrap_integer(0));
|
||||
@@ -174,10 +194,10 @@ static JanetSlot do_bnot(JanetFopts opts, JanetSlot *args) {
|
||||
|
||||
/* Specialization for comparators */
|
||||
static JanetSlot compreduce(
|
||||
JanetFopts opts,
|
||||
JanetSlot *args,
|
||||
int op,
|
||||
int invert) {
|
||||
JanetFopts opts,
|
||||
JanetSlot *args,
|
||||
int op,
|
||||
int invert) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
int32_t i, len;
|
||||
len = janet_v_count(args);
|
||||
@@ -185,8 +205,8 @@ static JanetSlot compreduce(
|
||||
JanetSlot t;
|
||||
if (len < 2) {
|
||||
return invert
|
||||
? janetc_cslot(janet_wrap_false())
|
||||
: janetc_cslot(janet_wrap_true());
|
||||
? janetc_cslot(janet_wrap_false())
|
||||
: janetc_cslot(janet_wrap_true());
|
||||
}
|
||||
t = janetc_gettarget(opts);
|
||||
for (i = 1; i < len; i++) {
|
||||
@@ -253,7 +273,7 @@ static const JanetFunOptimizer optimizers[] = {
|
||||
{fixarity0, do_debug},
|
||||
{fixarity1, do_error},
|
||||
{minarity2, do_apply},
|
||||
{fixarity1, do_yield},
|
||||
{maxarity1, do_yield},
|
||||
{fixarity2, do_resume},
|
||||
{fixarity2, do_get},
|
||||
{fixarity3, do_put},
|
||||
@@ -280,7 +300,8 @@ static const JanetFunOptimizer optimizers[] = {
|
||||
{NULL, do_gte},
|
||||
{NULL, do_lte},
|
||||
{NULL, do_eq},
|
||||
{NULL, do_neq}
|
||||
{NULL, do_neq},
|
||||
{fixarity2, do_propagate}
|
||||
};
|
||||
|
||||
const JanetFunOptimizer *janetc_funopt(uint32_t flags) {
|
||||
@@ -288,7 +309,7 @@ const JanetFunOptimizer *janetc_funopt(uint32_t flags) {
|
||||
if (tag == 0)
|
||||
return NULL;
|
||||
uint32_t index = tag - 1;
|
||||
if (index >= (sizeof(optimizers)/sizeof(optimizers[0])))
|
||||
if (index >= (sizeof(optimizers) / sizeof(optimizers[0])))
|
||||
return NULL;
|
||||
return optimizers + index;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,10 +20,14 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "compile.h"
|
||||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
JanetFopts janetc_fopts_default(JanetCompiler *c) {
|
||||
JanetFopts ret;
|
||||
@@ -94,7 +98,6 @@ void janetc_scope(JanetScope *s, JanetCompiler *c, int flags, const char *name)
|
||||
scope.syms = NULL;
|
||||
scope.envs = NULL;
|
||||
scope.defs = NULL;
|
||||
scope.selfconst = -1;
|
||||
scope.bytecode_start = janet_v_count(c->buffer);
|
||||
scope.flags = flags;
|
||||
scope.parent = c->scope;
|
||||
@@ -163,8 +166,8 @@ void janetc_popscope_keepslot(JanetCompiler *c, JanetSlot retslot) {
|
||||
|
||||
/* Allow searching for symbols. Return information about the symbol */
|
||||
JanetSlot janetc_resolve(
|
||||
JanetCompiler *c,
|
||||
const uint8_t *sym) {
|
||||
JanetCompiler *c,
|
||||
const uint8_t *sym) {
|
||||
|
||||
JanetSlot ret = janetc_cslot(janet_wrap_nil());
|
||||
JanetScope *scope = c->scope;
|
||||
@@ -203,8 +206,7 @@ JanetSlot janetc_resolve(
|
||||
case JANET_BINDING_DEF:
|
||||
case JANET_BINDING_MACRO: /* Macro should function like defs when not in calling pos */
|
||||
return janetc_cslot(check);
|
||||
case JANET_BINDING_VAR:
|
||||
{
|
||||
case JANET_BINDING_VAR: {
|
||||
JanetSlot ret = janetc_cslot(check);
|
||||
/* TODO save type info */
|
||||
ret.flags |= JANET_SLOT_REF | JANET_SLOT_NAMED | JANET_SLOT_MUTABLE | JANET_SLOTTYPE_ANY;
|
||||
@@ -215,7 +217,7 @@ JanetSlot janetc_resolve(
|
||||
}
|
||||
|
||||
/* Symbol was found */
|
||||
found:
|
||||
found:
|
||||
|
||||
/* Constants can be returned immediately (they are stateless) */
|
||||
if (ret.flags & (JANET_SLOT_CONSTANT | JANET_SLOT_REF))
|
||||
@@ -235,7 +237,7 @@ JanetSlot janetc_resolve(
|
||||
scope->flags |= JANET_SCOPE_ENV;
|
||||
scope = scope->child;
|
||||
|
||||
/* Propogate env up to current scope */
|
||||
/* Propagate env up to current scope */
|
||||
int32_t envindex = -1;
|
||||
while (scope) {
|
||||
if (scope->flags & JANET_SCOPE_FUNCTION) {
|
||||
@@ -280,8 +282,8 @@ JanetSlot janetc_return(JanetCompiler *c, JanetSlot s) {
|
||||
JanetSlot janetc_gettarget(JanetFopts opts) {
|
||||
JanetSlot slot;
|
||||
if ((opts.flags & JANET_FOPTS_HINT) &&
|
||||
(opts.hint.envindex < 0) &&
|
||||
(opts.hint.index >= 0 && opts.hint.index <= 0xFF)) {
|
||||
(opts.hint.envindex < 0) &&
|
||||
(opts.hint.index >= 0 && opts.hint.index <= 0xFF)) {
|
||||
slot = opts.hint;
|
||||
} else {
|
||||
slot.envindex = -1;
|
||||
@@ -308,9 +310,9 @@ JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
|
||||
JanetSlot *ret = NULL;
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
const JanetKV *kvs = NULL;
|
||||
int32_t cap, i, len;
|
||||
int32_t cap = 0, len = 0;
|
||||
janet_dictionary_view(ds, &kvs, &len, &cap);
|
||||
for (i = 0; i < cap; i++) {
|
||||
for (int32_t i = 0; i < cap; i++) {
|
||||
if (janet_checktype(kvs[i].key, JANET_NIL)) continue;
|
||||
janet_v_push(ret, janetc_value(subopts, kvs[i].key));
|
||||
janet_v_push(ret, janetc_value(subopts, kvs[i].value));
|
||||
@@ -331,17 +333,17 @@ void janetc_pushslots(JanetCompiler *c, JanetSlot *slots) {
|
||||
i++;
|
||||
} else if (slots[i + 1].flags & JANET_SLOT_SPLICED) {
|
||||
janetc_emit_s(c, JOP_PUSH, slots[i], 0);
|
||||
janetc_emit_s(c, JOP_PUSH_ARRAY, slots[i+1], 0);
|
||||
janetc_emit_s(c, JOP_PUSH_ARRAY, slots[i + 1], 0);
|
||||
i += 2;
|
||||
} else if (i + 2 == count) {
|
||||
janetc_emit_ss(c, JOP_PUSH_2, slots[i], slots[i+1], 0);
|
||||
janetc_emit_ss(c, JOP_PUSH_2, slots[i], slots[i + 1], 0);
|
||||
i += 2;
|
||||
} else if (slots[i + 2].flags & JANET_SLOT_SPLICED) {
|
||||
janetc_emit_ss(c, JOP_PUSH_2, slots[i], slots[i+1], 0);
|
||||
janetc_emit_s(c, JOP_PUSH_ARRAY, slots[i+2], 0);
|
||||
janetc_emit_ss(c, JOP_PUSH_2, slots[i], slots[i + 1], 0);
|
||||
janetc_emit_s(c, JOP_PUSH_ARRAY, slots[i + 2], 0);
|
||||
i += 3;
|
||||
} else {
|
||||
janetc_emit_sss(c, JOP_PUSH_3, slots[i], slots[i+1], slots[i+2], 0);
|
||||
janetc_emit_sss(c, JOP_PUSH_3, slots[i], slots[i + 1], slots[i + 2], 0);
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
@@ -402,7 +404,9 @@ static JanetSlot janetc_call(JanetFopts opts, JanetSlot *slots, JanetSlot fun) {
|
||||
}
|
||||
if (!specialized) {
|
||||
janetc_pushslots(c, slots);
|
||||
if (opts.flags & JANET_FOPTS_TAIL) {
|
||||
if ((opts.flags & JANET_FOPTS_TAIL) &&
|
||||
/* Prevent top level tail calls for better errors */
|
||||
!(c->scope->flags & JANET_SCOPE_TOP)) {
|
||||
janetc_emit_s(c, JOP_TAILCALL, fun, 0);
|
||||
retslot = janetc_cslot(janet_wrap_nil());
|
||||
retslot.flags = JANET_SLOT_RETURNED;
|
||||
@@ -429,15 +433,23 @@ static JanetSlot janetc_array(JanetFopts opts, Janet x) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
JanetArray *a = janet_unwrap_array(x);
|
||||
return janetc_maker(opts,
|
||||
janetc_toslots(c, a->data, a->count),
|
||||
JOP_MAKE_ARRAY);
|
||||
janetc_toslots(c, a->data, a->count),
|
||||
JOP_MAKE_ARRAY);
|
||||
}
|
||||
|
||||
static JanetSlot janetc_tuple(JanetFopts opts, Janet x) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
const Janet *t = janet_unwrap_tuple(x);
|
||||
return janetc_maker(opts,
|
||||
janetc_toslots(c, t, janet_tuple_length(t)),
|
||||
JOP_MAKE_TUPLE);
|
||||
}
|
||||
|
||||
static JanetSlot janetc_tablector(JanetFopts opts, Janet x, int op) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
return janetc_maker(opts,
|
||||
janetc_toslotskv(c, x),
|
||||
op);
|
||||
janetc_toslotskv(c, x),
|
||||
op);
|
||||
}
|
||||
|
||||
static JanetSlot janetc_bufferctor(JanetFopts opts, Janet x) {
|
||||
@@ -445,27 +457,30 @@ static JanetSlot janetc_bufferctor(JanetFopts opts, Janet x) {
|
||||
JanetBuffer *b = janet_unwrap_buffer(x);
|
||||
Janet onearg = janet_stringv(b->data, b->count);
|
||||
return janetc_maker(opts,
|
||||
janetc_toslots(c, &onearg, 1),
|
||||
JOP_MAKE_BUFFER);
|
||||
janetc_toslots(c, &onearg, 1),
|
||||
JOP_MAKE_BUFFER);
|
||||
}
|
||||
|
||||
/* Expand a macro one time. Also get the special form compiler if we
|
||||
* find that instead. */
|
||||
static int macroexpand1(
|
||||
JanetCompiler *c,
|
||||
Janet x,
|
||||
Janet *out,
|
||||
const JanetSpecial **spec) {
|
||||
JanetCompiler *c,
|
||||
Janet x,
|
||||
Janet *out,
|
||||
const JanetSpecial **spec) {
|
||||
if (!janet_checktype(x, JANET_TUPLE))
|
||||
return 0;
|
||||
const Janet *form = janet_unwrap_tuple(x);
|
||||
if (janet_tuple_length(form) == 0)
|
||||
return 0;
|
||||
/* Source map - only set when we get a tuple */
|
||||
if (janet_tuple_sm_line(form) > 0) {
|
||||
c->current_mapping.line = janet_tuple_sm_line(form);
|
||||
c->current_mapping.column = janet_tuple_sm_col(form);
|
||||
if (janet_tuple_sm_start(form) >= 0) {
|
||||
c->current_mapping.start = janet_tuple_sm_start(form);
|
||||
c->current_mapping.end = janet_tuple_sm_end(form);
|
||||
}
|
||||
/* Bracketed tuples are not specials or macros! */
|
||||
if (janet_tuple_flag(form) & JANET_TUPLE_FLAG_BRACKETCTOR)
|
||||
return 0;
|
||||
if (!janet_checktype(form[0], JANET_SYMBOL))
|
||||
return 0;
|
||||
const uint8_t *name = janet_unwrap_symbol(form[0]);
|
||||
@@ -480,17 +495,16 @@ static int macroexpand1(
|
||||
!janet_checktype(macroval, JANET_FUNCTION))
|
||||
return 0;
|
||||
|
||||
|
||||
/* Evaluate macro */
|
||||
JanetFiber *fiberp;
|
||||
JanetFiber *fiberp = NULL;
|
||||
JanetFunction *macro = janet_unwrap_function(macroval);
|
||||
int lock = janet_gclock();
|
||||
JanetSignal status = janet_call(
|
||||
macro,
|
||||
janet_tuple_length(form) - 1,
|
||||
form + 1,
|
||||
&x,
|
||||
&fiberp);
|
||||
JanetSignal status = janet_pcall(
|
||||
macro,
|
||||
janet_tuple_length(form) - 1,
|
||||
form + 1,
|
||||
&x,
|
||||
&fiberp);
|
||||
janet_gcunlock(lock);
|
||||
if (status != JANET_SIGNAL_OK) {
|
||||
const uint8_t *es = janet_formatc("(macro) %V", x);
|
||||
@@ -536,24 +550,25 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
|
||||
ret = spec->compile(opts, janet_tuple_length(tup) - 1, tup + 1);
|
||||
} else {
|
||||
switch (janet_type(x)) {
|
||||
case JANET_TUPLE:
|
||||
{
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
const Janet *tup = janet_unwrap_tuple(x);
|
||||
/* Empty tuple is tuple literal */
|
||||
if (janet_tuple_length(tup) == 0) {
|
||||
ret = janetc_cslot(x);
|
||||
} else {
|
||||
JanetSlot head = janetc_value(subopts, tup[0]);
|
||||
subopts.flags = JANET_FUNCTION | JANET_CFUNCTION;
|
||||
ret = janetc_call(opts, janetc_toslots(c, tup + 1, janet_tuple_length(tup) - 1), head);
|
||||
janetc_freeslot(c, head);
|
||||
}
|
||||
ret.flags &= ~JANET_SLOT_SPLICED;
|
||||
case JANET_TUPLE: {
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
const Janet *tup = janet_unwrap_tuple(x);
|
||||
/* Empty tuple is tuple literal */
|
||||
if (janet_tuple_length(tup) == 0) {
|
||||
ret = janetc_cslot(x);
|
||||
} else if (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR) { /* [] tuples are not function call */
|
||||
ret = janetc_tuple(opts, x);
|
||||
} else {
|
||||
JanetSlot head = janetc_value(subopts, tup[0]);
|
||||
subopts.flags = JANET_FUNCTION | JANET_CFUNCTION;
|
||||
ret = janetc_call(opts, janetc_toslots(c, tup + 1, janet_tuple_length(tup) - 1), head);
|
||||
janetc_freeslot(c, head);
|
||||
}
|
||||
break;
|
||||
ret.flags &= ~JANET_SLOT_SPLICED;
|
||||
}
|
||||
break;
|
||||
case JANET_SYMBOL:
|
||||
ret = janetc_sym_rvalue(opts, janet_unwrap_symbol(x));
|
||||
ret = janetc_resolve(c, janet_unwrap_symbol(x));
|
||||
break;
|
||||
case JANET_ARRAY:
|
||||
ret = janetc_array(opts, x);
|
||||
@@ -575,14 +590,14 @@ JanetSlot janetc_value(JanetFopts opts, Janet x) {
|
||||
|
||||
if (c->result.status == JANET_COMPILE_ERROR)
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
c->current_mapping = last_mapping;
|
||||
if (opts.flags & JANET_FOPTS_TAIL)
|
||||
ret = janetc_return(opts.compiler, ret);
|
||||
ret = janetc_return(c, ret);
|
||||
if (opts.flags & JANET_FOPTS_HINT) {
|
||||
janetc_copy(opts.compiler, opts.hint, ret);
|
||||
janetc_copy(c, opts.hint, ret);
|
||||
ret = opts.hint;
|
||||
}
|
||||
opts.compiler->recursion_guard++;
|
||||
c->current_mapping = last_mapping;
|
||||
c->recursion_guard++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -614,7 +629,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
|
||||
}
|
||||
memcpy(def->bytecode, c->buffer + scope->bytecode_start, s);
|
||||
janet_v__cnt(c->buffer) = scope->bytecode_start;
|
||||
if (NULL != c->mapbuffer) {
|
||||
if (NULL != c->mapbuffer && c->source) {
|
||||
size_t s = sizeof(JanetSourceMapping) * def->bytecode_length;
|
||||
def->sourcemap = malloc(s);
|
||||
if (NULL == def->sourcemap) {
|
||||
@@ -629,6 +644,7 @@ JanetFuncDef *janetc_pop_funcdef(JanetCompiler *c) {
|
||||
def->source = c->source;
|
||||
|
||||
def->arity = 0;
|
||||
def->min_arity = 0;
|
||||
def->flags = 0;
|
||||
if (scope->flags & JANET_SCOPE_ENV) {
|
||||
def->flags |= JANET_FUNCDEF_FLAG_NEEDSENV;
|
||||
@@ -648,15 +664,15 @@ static void janetc_init(JanetCompiler *c, JanetTable *env, const uint8_t *where)
|
||||
c->recursion_guard = JANET_RECURSION_GUARD;
|
||||
c->env = env;
|
||||
c->source = where;
|
||||
c->current_mapping.line = 0;
|
||||
c->current_mapping.column = 0;
|
||||
c->current_mapping.start = -1;
|
||||
c->current_mapping.end = -1;
|
||||
/* Init result */
|
||||
c->result.error = NULL;
|
||||
c->result.status = JANET_COMPILE_OK;
|
||||
c->result.funcdef = NULL;
|
||||
c->result.macrofiber = NULL;
|
||||
c->result.error_mapping.line = 0;
|
||||
c->result.error_mapping.column = 0;
|
||||
c->result.error_mapping.start = -1;
|
||||
c->result.error_mapping.end = -1;
|
||||
}
|
||||
|
||||
/* Deinitialize a compiler struct */
|
||||
@@ -700,45 +716,44 @@ JanetCompileResult janet_compile(Janet source, JanetTable *env, const uint8_t *w
|
||||
}
|
||||
|
||||
/* C Function for compiling */
|
||||
static int cfun(JanetArgs args) {
|
||||
JanetCompileResult res;
|
||||
JanetTable *t;
|
||||
JanetTable *env;
|
||||
JANET_MINARITY(args, 2);
|
||||
JANET_MAXARITY(args, 3);
|
||||
JANET_ARG_TABLE(env, args, 1);
|
||||
const uint8_t *source = NULL;
|
||||
if (args.n == 3) {
|
||||
JANET_ARG_STRING(source, args, 2);
|
||||
static Janet cfun(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetTable *env = argc > 1 ? janet_gettable(argv, 1) : janet_vm_fiber->env;
|
||||
if (NULL == env) {
|
||||
env = janet_table(0);
|
||||
janet_vm_fiber->env = env;
|
||||
}
|
||||
res = janet_compile(args.v[0], env, source);
|
||||
const uint8_t *source = NULL;
|
||||
if (argc == 3) {
|
||||
source = janet_getstring(argv, 2);
|
||||
}
|
||||
JanetCompileResult res = janet_compile(argv[0], env, source);
|
||||
if (res.status == JANET_COMPILE_OK) {
|
||||
JANET_RETURN_FUNCTION(args, janet_thunk(res.funcdef));
|
||||
return janet_wrap_function(janet_thunk(res.funcdef));
|
||||
} else {
|
||||
t = janet_table(4);
|
||||
janet_table_put(t, janet_csymbolv(":error"), janet_wrap_string(res.error));
|
||||
janet_table_put(t, janet_csymbolv(":line"), janet_wrap_integer(res.error_mapping.line));
|
||||
janet_table_put(t, janet_csymbolv(":column"), janet_wrap_integer(res.error_mapping.column));
|
||||
JanetTable *t = janet_table(4);
|
||||
janet_table_put(t, janet_ckeywordv("error"), janet_wrap_string(res.error));
|
||||
janet_table_put(t, janet_ckeywordv("start"), janet_wrap_integer(res.error_mapping.start));
|
||||
janet_table_put(t, janet_ckeywordv("end"), janet_wrap_integer(res.error_mapping.end));
|
||||
if (res.macrofiber) {
|
||||
janet_table_put(t, janet_csymbolv(":fiber"), janet_wrap_fiber(res.macrofiber));
|
||||
janet_table_put(t, janet_ckeywordv("fiber"), janet_wrap_fiber(res.macrofiber));
|
||||
}
|
||||
JANET_RETURN_TABLE(args, t);
|
||||
return janet_wrap_table(t);
|
||||
}
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"compile", cfun,
|
||||
"(compile ast env [, source])\n\n"
|
||||
"Compiles an Abstract Sytnax Tree (ast) into a janet function. "
|
||||
"Pair the compile function with parsing functionality to implement "
|
||||
"eval. Returns a janet function and does not modify ast. Throws an "
|
||||
"error if the ast cannot be compiled."
|
||||
static const JanetReg compile_cfuns[] = {
|
||||
{
|
||||
"compile", cfun,
|
||||
JDOC("(compile ast &opt env source)\n\n"
|
||||
"Compiles an Abstract Syntax Tree (ast) into a janet function. "
|
||||
"Pair the compile function with parsing functionality to implement "
|
||||
"eval. Returns a janet function and does not modify ast. Throws an "
|
||||
"error if the ast cannot be compiled.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
int janet_lib_compile(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
return 0;
|
||||
void janet_lib_compile(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, compile_cfuns);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -23,8 +23,10 @@
|
||||
#ifndef JANET_COMPILE_H
|
||||
#define JANET_COMPILE_H
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "regalloc.h"
|
||||
#endif
|
||||
|
||||
/* Tags for some functions for the prepared inliner */
|
||||
#define JANET_FUN_DEBUG 1
|
||||
@@ -58,6 +60,7 @@
|
||||
#define JANET_FUN_LTE 29
|
||||
#define JANET_FUN_EQ 30
|
||||
#define JANET_FUN_NEQ 31
|
||||
#define JANET_FUN_PROP 32
|
||||
|
||||
/* Compiler typedefs */
|
||||
typedef struct JanetCompiler JanetCompiler;
|
||||
@@ -94,6 +97,7 @@ struct JanetSlot {
|
||||
#define JANET_SCOPE_TOP 4
|
||||
#define JANET_SCOPE_UNUSED 8
|
||||
#define JANET_SCOPE_CLOSURE 16
|
||||
#define JANET_SCOPE_WHILE 32
|
||||
|
||||
/* A symbol and slot pair */
|
||||
typedef struct SymPair {
|
||||
@@ -129,9 +133,6 @@ struct JanetScope {
|
||||
* that corresponds to the direct parent's stack will always have value 0. */
|
||||
int32_t *envs;
|
||||
|
||||
/* Where to add reference to self in constants */
|
||||
int32_t selfconst;
|
||||
|
||||
int32_t bytecode_start;
|
||||
int flags;
|
||||
};
|
||||
@@ -178,13 +179,13 @@ JanetFopts janetc_fopts_default(JanetCompiler *c);
|
||||
/* For optimizing builtin normal functions. */
|
||||
struct JanetFunOptimizer {
|
||||
int (*can_optimize)(JanetFopts opts, JanetSlot *args);
|
||||
JanetSlot (*optimize)(JanetFopts opts, JanetSlot *args);
|
||||
JanetSlot(*optimize)(JanetFopts opts, JanetSlot *args);
|
||||
};
|
||||
|
||||
/* A grouping of a named special and the corresponding compiler fragment */
|
||||
struct JanetSpecial {
|
||||
const char *name;
|
||||
JanetSlot (*compile)(JanetFopts opts, int32_t argn, const Janet *argv);
|
||||
JanetSlot(*compile)(JanetFopts opts, int32_t argn, const Janet *argv);
|
||||
};
|
||||
|
||||
/****************************************************/
|
||||
@@ -240,10 +241,4 @@ JanetSlot janetc_cslot(Janet x);
|
||||
/* Search for a symbol */
|
||||
JanetSlot janetc_resolve(JanetCompiler *c, const uint8_t *sym);
|
||||
|
||||
/* Compile a symbol (or mutltisym) when used as an rvalue. */
|
||||
JanetSlot janetc_sym_rvalue(JanetFopts opts, const uint8_t *sym);
|
||||
|
||||
/* Compile an assignment to a symbol (or multisym) */
|
||||
JanetSlot janetc_sym_lvalue(JanetFopts opts, const uint8_t *sym, Janet value);
|
||||
|
||||
#endif
|
||||
|
||||
1527
src/core/core.janet
1527
src/core/core.janet
File diff suppressed because it is too large
Load Diff
1251
src/core/corelib.c
1251
src/core/corelib.c
File diff suppressed because it is too large
Load Diff
381
src/core/debug.c
Normal file
381
src/core/debug.c
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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 <janet.h>
|
||||
#include "gc.h"
|
||||
#include "state.h"
|
||||
#include "util.h"
|
||||
#include "vector.h"
|
||||
#endif
|
||||
|
||||
/* Implements functionality to build a debugger from within janet.
|
||||
* The repl should also be able to serve as pretty featured debugger
|
||||
* out of the box. */
|
||||
|
||||
/* Add a break point to a function */
|
||||
void janet_debug_break(JanetFuncDef *def, int32_t pc) {
|
||||
if (pc >= def->bytecode_length || pc < 0)
|
||||
janet_panic("invalid bytecode offset");
|
||||
def->bytecode[pc] |= 0x80;
|
||||
}
|
||||
|
||||
/* Remove a break point from a function */
|
||||
void janet_debug_unbreak(JanetFuncDef *def, int32_t pc) {
|
||||
if (pc >= def->bytecode_length || pc < 0)
|
||||
janet_panic("invalid bytecode offset");
|
||||
def->bytecode[pc] &= ~((uint32_t)0x80);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a location for a breakpoint given a source file an
|
||||
* location.
|
||||
*/
|
||||
void janet_debug_find(
|
||||
JanetFuncDef **def_out, int32_t *pc_out,
|
||||
const uint8_t *source, int32_t offset) {
|
||||
/* Scan the heap for right func def */
|
||||
JanetGCObject *current = janet_vm_blocks;
|
||||
/* Keep track of the best source mapping we have seen so far */
|
||||
int32_t besti = -1;
|
||||
int32_t best_range = INT32_MAX;
|
||||
JanetFuncDef *best_def = NULL;
|
||||
while (NULL != current) {
|
||||
if ((current->flags & JANET_MEM_TYPEBITS) == JANET_MEMORY_FUNCDEF) {
|
||||
JanetFuncDef *def = (JanetFuncDef *)(current + 1);
|
||||
if (def->sourcemap &&
|
||||
def->source &&
|
||||
!janet_string_compare(source, def->source)) {
|
||||
/* Correct source file, check mappings. The chosen
|
||||
* pc index is the first match with the smallest range. */
|
||||
int32_t i;
|
||||
for (i = 0; i < def->bytecode_length; i++) {
|
||||
int32_t start = def->sourcemap[i].start;
|
||||
int32_t end = def->sourcemap[i].end;
|
||||
if (end - start < best_range &&
|
||||
start <= offset &&
|
||||
end >= offset) {
|
||||
best_range = end - start;
|
||||
besti = i;
|
||||
best_def = def;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
if (best_def) {
|
||||
*def_out = best_def;
|
||||
*pc_out = besti;
|
||||
} else {
|
||||
janet_panic("could not find breakpoint");
|
||||
}
|
||||
}
|
||||
|
||||
/* Error reporting. This can be emulated from within Janet, but for
|
||||
* consitency with the top level code it is defined once. */
|
||||
void janet_stacktrace(JanetFiber *fiber, Janet err) {
|
||||
int32_t fi;
|
||||
FILE *out = janet_dynfile("err", stderr);
|
||||
const char *errstr = (const char *)janet_to_string(err);
|
||||
JanetFiber **fibers = NULL;
|
||||
int wrote_error = 0;
|
||||
|
||||
while (fiber) {
|
||||
janet_v_push(fibers, fiber);
|
||||
fiber = fiber->child;
|
||||
}
|
||||
|
||||
for (fi = janet_v_count(fibers) - 1; fi >= 0; fi--) {
|
||||
fiber = fibers[fi];
|
||||
int32_t i = fiber->frame;
|
||||
while (i > 0) {
|
||||
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
JanetFuncDef *def = NULL;
|
||||
i = frame->prevframe;
|
||||
|
||||
/* Print prelude to stack frame */
|
||||
if (!wrote_error) {
|
||||
JanetFiberStatus status = janet_fiber_status(fiber);
|
||||
const char *prefix = status == JANET_STATUS_ERROR ? "" : "status ";
|
||||
fprintf(out, "%s%s: %s\n",
|
||||
prefix,
|
||||
janet_status_names[status],
|
||||
errstr);
|
||||
wrote_error = 1;
|
||||
}
|
||||
|
||||
fprintf(out, " in");
|
||||
|
||||
if (frame->func) {
|
||||
def = frame->func->def;
|
||||
fprintf(out, " %s", def->name ? (const char *)def->name : "<anonymous>");
|
||||
if (def->source) {
|
||||
fprintf(out, " [%s]", (const char *)def->source);
|
||||
}
|
||||
} else {
|
||||
JanetCFunction cfun = (JanetCFunction)(frame->pc);
|
||||
if (cfun) {
|
||||
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
|
||||
if (!janet_checktype(name, JANET_NIL))
|
||||
fprintf(out, " %s", (const char *)janet_to_string(name));
|
||||
else
|
||||
fprintf(out, " <cfunction>");
|
||||
}
|
||||
}
|
||||
if (frame->flags & JANET_STACKFRAME_TAILCALL)
|
||||
fprintf(out, " (tailcall)");
|
||||
if (frame->func && frame->pc) {
|
||||
int32_t off = (int32_t)(frame->pc - def->bytecode);
|
||||
if (def->sourcemap) {
|
||||
JanetSourceMapping mapping = def->sourcemap[off];
|
||||
fprintf(out, " at (%d:%d)", mapping.start, mapping.end);
|
||||
} else {
|
||||
fprintf(out, " pc=%d", off);
|
||||
}
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
janet_v_free(fibers);
|
||||
}
|
||||
|
||||
/*
|
||||
* CFuns
|
||||
*/
|
||||
|
||||
/* Helper to find funcdef and bytecode offset to insert or remove breakpoints.
|
||||
* Takes a source file name and byte offset. */
|
||||
static void helper_find(int32_t argc, Janet *argv, JanetFuncDef **def, int32_t *bytecode_offset) {
|
||||
janet_fixarity(argc, 2);
|
||||
const uint8_t *source = janet_getstring(argv, 0);
|
||||
int32_t source_offset = janet_getinteger(argv, 1);
|
||||
janet_debug_find(def, bytecode_offset, source, source_offset);
|
||||
}
|
||||
|
||||
/* Helper to find funcdef and bytecode offset to insert or remove breakpoints.
|
||||
* Takes a function and byte offset*/
|
||||
static void helper_find_fun(int32_t argc, Janet *argv, JanetFuncDef **def, int32_t *bytecode_offset) {
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetFunction *func = janet_getfunction(argv, 0);
|
||||
int32_t offset = (argc == 2) ? janet_getinteger(argv, 1) : 0;
|
||||
*def = func->def;
|
||||
*bytecode_offset = offset;
|
||||
}
|
||||
|
||||
static Janet cfun_debug_break(int32_t argc, Janet *argv) {
|
||||
JanetFuncDef *def;
|
||||
int32_t offset;
|
||||
helper_find(argc, argv, &def, &offset);
|
||||
janet_debug_break(def, offset);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_debug_unbreak(int32_t argc, Janet *argv) {
|
||||
JanetFuncDef *def;
|
||||
int32_t offset = 0;
|
||||
helper_find(argc, argv, &def, &offset);
|
||||
janet_debug_unbreak(def, offset);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_debug_fbreak(int32_t argc, Janet *argv) {
|
||||
JanetFuncDef *def;
|
||||
int32_t offset = 0;
|
||||
helper_find_fun(argc, argv, &def, &offset);
|
||||
janet_debug_break(def, offset);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_debug_unfbreak(int32_t argc, Janet *argv) {
|
||||
JanetFuncDef *def;
|
||||
int32_t offset;
|
||||
helper_find_fun(argc, argv, &def, &offset);
|
||||
janet_debug_unbreak(def, offset);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_debug_lineage(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
JanetArray *array = janet_array(0);
|
||||
while (fiber) {
|
||||
janet_array_push(array, janet_wrap_fiber(fiber));
|
||||
fiber = fiber->child;
|
||||
}
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
/* Extract info from one stack frame */
|
||||
static Janet doframe(JanetStackFrame *frame) {
|
||||
int32_t off;
|
||||
JanetTable *t = janet_table(3);
|
||||
JanetFuncDef *def = NULL;
|
||||
if (frame->func) {
|
||||
janet_table_put(t, janet_ckeywordv("function"), janet_wrap_function(frame->func));
|
||||
def = frame->func->def;
|
||||
if (def->name) {
|
||||
janet_table_put(t, janet_ckeywordv("name"), janet_wrap_string(def->name));
|
||||
}
|
||||
} else {
|
||||
JanetCFunction cfun = (JanetCFunction)(frame->pc);
|
||||
if (cfun) {
|
||||
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
|
||||
if (!janet_checktype(name, JANET_NIL)) {
|
||||
janet_table_put(t, janet_ckeywordv("name"), name);
|
||||
}
|
||||
}
|
||||
janet_table_put(t, janet_ckeywordv("c"), janet_wrap_true());
|
||||
}
|
||||
if (frame->flags & JANET_STACKFRAME_TAILCALL) {
|
||||
janet_table_put(t, janet_ckeywordv("tail"), janet_wrap_true());
|
||||
}
|
||||
if (frame->func && frame->pc) {
|
||||
Janet *stack = (Janet *)frame + JANET_FRAME_SIZE;
|
||||
JanetArray *slots;
|
||||
off = (int32_t)(frame->pc - def->bytecode);
|
||||
janet_table_put(t, janet_ckeywordv("pc"), janet_wrap_integer(off));
|
||||
if (def->sourcemap) {
|
||||
JanetSourceMapping mapping = def->sourcemap[off];
|
||||
janet_table_put(t, janet_ckeywordv("source-start"), janet_wrap_integer(mapping.start));
|
||||
janet_table_put(t, janet_ckeywordv("source-end"), janet_wrap_integer(mapping.end));
|
||||
}
|
||||
if (def->source) {
|
||||
janet_table_put(t, janet_ckeywordv("source"), janet_wrap_string(def->source));
|
||||
}
|
||||
/* Add stack arguments */
|
||||
slots = janet_array(def->slotcount);
|
||||
memcpy(slots->data, stack, sizeof(Janet) * def->slotcount);
|
||||
slots->count = def->slotcount;
|
||||
janet_table_put(t, janet_ckeywordv("slots"), janet_wrap_array(slots));
|
||||
}
|
||||
return janet_wrap_table(t);
|
||||
}
|
||||
|
||||
static Janet cfun_debug_stack(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
JanetArray *array = janet_array(0);
|
||||
{
|
||||
int32_t i = fiber->frame;
|
||||
JanetStackFrame *frame;
|
||||
while (i > 0) {
|
||||
frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
janet_array_push(array, doframe(frame));
|
||||
i = frame->prevframe;
|
||||
}
|
||||
}
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static Janet cfun_debug_stacktrace(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
janet_stacktrace(fiber, argv[1]);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_debug_argstack(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
JanetArray *array = janet_array(fiber->stacktop - fiber->stackstart);
|
||||
memcpy(array->data, fiber->data + fiber->stackstart, array->capacity * sizeof(Janet));
|
||||
array->count = array->capacity;
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static const JanetReg debug_cfuns[] = {
|
||||
{
|
||||
"debug/break", cfun_debug_break,
|
||||
JDOC("(debug/break source byte-offset)\n\n"
|
||||
"Sets a breakpoint with source a key at a given byte offset. An offset "
|
||||
"of 0 is the first byte in a file. Will throw an error if the breakpoint location "
|
||||
"cannot be found. For example\n\n"
|
||||
"\t(debug/break \"core.janet\" 1000)\n\n"
|
||||
"wil set a breakpoint at the 1000th byte of the file core.janet.")
|
||||
},
|
||||
{
|
||||
"debug/unbreak", cfun_debug_unbreak,
|
||||
JDOC("(debug/unbreak source byte-offset)\n\n"
|
||||
"Remove a breakpoint with a source key at a given byte offset. An offset "
|
||||
"of 0 is the first byte in a file. Will throw an error if the breakpoint "
|
||||
"cannot be found.")
|
||||
},
|
||||
{
|
||||
"debug/fbreak", cfun_debug_fbreak,
|
||||
JDOC("(debug/fbreak fun &opt pc)\n\n"
|
||||
"Set a breakpoint in a given function. pc is an optional offset, which "
|
||||
"is in bytecode instructions. fun is a function value. Will throw an error "
|
||||
"if the offset is too large or negative.")
|
||||
},
|
||||
{
|
||||
"debug/unfbreak", cfun_debug_unfbreak,
|
||||
JDOC("(debug/unfbreak fun &opt pc)\n\n"
|
||||
"Unset a breakpoint set with debug/fbreak.")
|
||||
},
|
||||
{
|
||||
"debug/arg-stack", cfun_debug_argstack,
|
||||
JDOC("(debug/arg-stack fiber)\n\n"
|
||||
"Gets all values currently on the fiber's argument stack. Normally, "
|
||||
"this should be empty unless the fiber signals while pushing arguments "
|
||||
"to make a function call. Returns a new array.")
|
||||
},
|
||||
{
|
||||
"debug/stack", cfun_debug_stack,
|
||||
JDOC("(debug/stack fib)\n\n"
|
||||
"Gets information about the stack as an array of tables. Each table "
|
||||
"in the array contains information about a stack frame. The top most, current "
|
||||
"stack frame is the first table in the array, and the bottom most stack frame "
|
||||
"is the last value. Each stack frame contains some of the following attributes:\n\n"
|
||||
"\t:c - true if the stack frame is a c function invocation\n"
|
||||
"\t:column - the current source column of the stack frame\n"
|
||||
"\t:function - the function that the stack frame represents\n"
|
||||
"\t:line - the current source line of the stack frame\n"
|
||||
"\t:name - the human friendly name of the function\n"
|
||||
"\t:pc - integer indicating the location of the program counter\n"
|
||||
"\t:source - string with the file path or other identifier for the source code\n"
|
||||
"\t:slots - array of all values in each slot\n"
|
||||
"\t:tail - boolean indicating a tail call")
|
||||
},
|
||||
{
|
||||
"debug/stacktrace", cfun_debug_stacktrace,
|
||||
JDOC("(debug/stacktrace fiber err)\n\n"
|
||||
"Prints a nice looking stacktrace for a fiber. The error message "
|
||||
"err must be passed to the function as fiber's do not keep track of "
|
||||
"the last error they have thrown. Returns the fiber.")
|
||||
},
|
||||
{
|
||||
"debug/lineage", cfun_debug_lineage,
|
||||
JDOC("(debug/lineage fib)\n\n"
|
||||
"Returns an array of all child fibers from a root fiber. This function "
|
||||
"is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
|
||||
"the fiber handling the error can see which fiber raised the signal. This function should "
|
||||
"be used mostly for debugging purposes.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_debug(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, debug_cfuns);
|
||||
}
|
||||
113
src/core/emit.c
113
src/core/emit.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,10 +20,12 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
#include "regalloc.h"
|
||||
#endif
|
||||
|
||||
/* Get a register */
|
||||
int32_t janetc_allocfar(JanetCompiler *c) {
|
||||
@@ -61,7 +63,7 @@ static int32_t janetc_const(JanetCompiler *c, Janet x) {
|
||||
if (janet_equals(x, scope->consts[i]))
|
||||
return i;
|
||||
}
|
||||
/* Ensure not too many constsants. */
|
||||
/* Ensure not too many constants. */
|
||||
if (len >= 0xFFFF) {
|
||||
janetc_cerror(c, "too many constants");
|
||||
return 0;
|
||||
@@ -76,32 +78,31 @@ static void janetc_loadconst(JanetCompiler *c, Janet k, int32_t reg) {
|
||||
case JANET_NIL:
|
||||
janetc_emit(c, (reg << 8) | JOP_LOAD_NIL);
|
||||
break;
|
||||
case JANET_TRUE:
|
||||
janetc_emit(c, (reg << 8) | JOP_LOAD_TRUE);
|
||||
case JANET_BOOLEAN:
|
||||
janetc_emit(c, (reg << 8) |
|
||||
(janet_unwrap_boolean(k) ? JOP_LOAD_TRUE : JOP_LOAD_FALSE));
|
||||
break;
|
||||
case JANET_FALSE:
|
||||
janetc_emit(c, (reg << 8) | JOP_LOAD_FALSE);
|
||||
break;
|
||||
case JANET_INTEGER:
|
||||
{
|
||||
int32_t i = janet_unwrap_integer(k);
|
||||
if (i <= INT16_MAX && i >= INT16_MIN) {
|
||||
janetc_emit(c,
|
||||
(i << 16) |
|
||||
(reg << 8) |
|
||||
JOP_LOAD_INTEGER);
|
||||
break;
|
||||
}
|
||||
case JANET_NUMBER: {
|
||||
double dval = janet_unwrap_number(k);
|
||||
if (dval < INT16_MIN || dval > INT16_MAX)
|
||||
goto do_constant;
|
||||
}
|
||||
int32_t i = (int32_t) dval;
|
||||
if (dval != i)
|
||||
goto do_constant;
|
||||
uint32_t iu = (uint32_t)i;
|
||||
janetc_emit(c,
|
||||
(iu << 16) |
|
||||
(reg << 8) |
|
||||
JOP_LOAD_INTEGER);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
do_constant:
|
||||
{
|
||||
do_constant: {
|
||||
int32_t cindex = janetc_const(c, k);
|
||||
janetc_emit(c,
|
||||
(cindex << 16) |
|
||||
(reg << 8) |
|
||||
JOP_LOAD_CONSTANT);
|
||||
(cindex << 16) |
|
||||
(reg << 8) |
|
||||
JOP_LOAD_CONSTANT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -109,53 +110,53 @@ static void janetc_loadconst(JanetCompiler *c, Janet k, int32_t reg) {
|
||||
|
||||
/* Move a slot to a near register */
|
||||
static void janetc_movenear(JanetCompiler *c,
|
||||
int32_t dest,
|
||||
JanetSlot src) {
|
||||
int32_t dest,
|
||||
JanetSlot src) {
|
||||
if (src.flags & (JANET_SLOT_CONSTANT | JANET_SLOT_REF)) {
|
||||
janetc_loadconst(c, src.constant, dest);
|
||||
/* If we also are a reference, deref the one element array */
|
||||
if (src.flags & JANET_SLOT_REF) {
|
||||
janetc_emit(c,
|
||||
(dest << 16) |
|
||||
(dest << 8) |
|
||||
JOP_GET_INDEX);
|
||||
(dest << 16) |
|
||||
(dest << 8) |
|
||||
JOP_GET_INDEX);
|
||||
}
|
||||
} else if (src.envindex >= 0) {
|
||||
janetc_emit(c,
|
||||
((uint32_t)(src.index) << 24) |
|
||||
((uint32_t)(src.envindex) << 16) |
|
||||
((uint32_t)(dest) << 8) |
|
||||
JOP_LOAD_UPVALUE);
|
||||
((uint32_t)(src.index) << 24) |
|
||||
((uint32_t)(src.envindex) << 16) |
|
||||
((uint32_t)(dest) << 8) |
|
||||
JOP_LOAD_UPVALUE);
|
||||
} else if (src.index > 0xFF || src.index != dest) {
|
||||
janetc_emit(c,
|
||||
((uint32_t)(src.index) << 16) |
|
||||
((uint32_t)(dest) << 8) |
|
||||
((uint32_t)(src.index) << 16) |
|
||||
((uint32_t)(dest) << 8) |
|
||||
JOP_MOVE_NEAR);
|
||||
}
|
||||
}
|
||||
|
||||
/* Move a near register to a Slot. */
|
||||
static void janetc_moveback(JanetCompiler *c,
|
||||
JanetSlot dest,
|
||||
int32_t src) {
|
||||
JanetSlot dest,
|
||||
int32_t src) {
|
||||
if (dest.flags & JANET_SLOT_REF) {
|
||||
int32_t refreg = janetc_regalloc_temp(&c->scope->ra, JANETC_REGTEMP_5);
|
||||
janetc_loadconst(c, dest.constant, refreg);
|
||||
janetc_emit(c,
|
||||
(src << 16) |
|
||||
(refreg << 8) |
|
||||
JOP_PUT_INDEX);
|
||||
(src << 16) |
|
||||
(refreg << 8) |
|
||||
JOP_PUT_INDEX);
|
||||
janetc_regalloc_freetemp(&c->scope->ra, refreg, JANETC_REGTEMP_5);
|
||||
} else if (dest.envindex >= 0) {
|
||||
janetc_emit(c,
|
||||
((uint32_t)(dest.index) << 24) |
|
||||
((uint32_t)(dest.envindex) << 16) |
|
||||
((uint32_t)(src) << 8) |
|
||||
JOP_SET_UPVALUE);
|
||||
((uint32_t)(dest.index) << 24) |
|
||||
((uint32_t)(dest.envindex) << 16) |
|
||||
((uint32_t)(src) << 8) |
|
||||
JOP_SET_UPVALUE);
|
||||
} else if (dest.index != src) {
|
||||
janetc_emit(c,
|
||||
((uint32_t)(dest.index) << 16) |
|
||||
((uint32_t)(src) << 8) |
|
||||
((uint32_t)(dest.index) << 16) |
|
||||
((uint32_t)(src) << 8) |
|
||||
JOP_MOVE_FAR);
|
||||
}
|
||||
}
|
||||
@@ -219,9 +220,9 @@ static int janetc_sequal(JanetSlot lhs, JanetSlot rhs) {
|
||||
/* Move values from one slot to another. The destination must
|
||||
* be writeable (not a literal). */
|
||||
void janetc_copy(
|
||||
JanetCompiler *c,
|
||||
JanetSlot dest,
|
||||
JanetSlot src) {
|
||||
JanetCompiler *c,
|
||||
JanetSlot dest,
|
||||
JanetSlot src) {
|
||||
if (dest.flags & JANET_SLOT_CONSTANT) {
|
||||
janetc_cerror(c, "cannot write to constant");
|
||||
return;
|
||||
@@ -238,11 +239,11 @@ void janetc_copy(
|
||||
return;
|
||||
}
|
||||
/* Process: src -> near -> dest */
|
||||
int32_t near = janetc_allocnear(c, JANETC_REGTEMP_3);
|
||||
janetc_movenear(c, near, src);
|
||||
janetc_moveback(c, dest, near);
|
||||
int32_t nearreg = janetc_allocnear(c, JANETC_REGTEMP_3);
|
||||
janetc_movenear(c, nearreg, src);
|
||||
janetc_moveback(c, dest, nearreg);
|
||||
/* Cleanup */
|
||||
janetc_regalloc_freetemp(&c->scope->ra, near, JANETC_REGTEMP_3);
|
||||
janetc_regalloc_freetemp(&c->scope->ra, nearreg, JANETC_REGTEMP_3);
|
||||
|
||||
}
|
||||
/* Instruction templated emitters */
|
||||
@@ -250,7 +251,7 @@ void janetc_copy(
|
||||
static int32_t emit1s(JanetCompiler *c, uint8_t op, JanetSlot s, int32_t rest, int wr) {
|
||||
int32_t reg = janetc_regnear(c, s, JANETC_REGTEMP_0);
|
||||
int32_t label = janet_v_count(c->buffer);
|
||||
janetc_emit(c, op | (reg << 8) | (rest << 16));
|
||||
janetc_emit(c, op | (reg << 8) | ((uint32_t)rest << 16));
|
||||
if (wr)
|
||||
janetc_moveback(c, s, reg);
|
||||
janetc_free_regnear(c, s, reg, JANETC_REGTEMP_0);
|
||||
@@ -292,7 +293,7 @@ static int32_t emit2s(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2,
|
||||
int32_t reg1 = janetc_regnear(c, s1, JANETC_REGTEMP_0);
|
||||
int32_t reg2 = janetc_regnear(c, s2, JANETC_REGTEMP_1);
|
||||
int32_t label = janet_v_count(c->buffer);
|
||||
janetc_emit(c, op | (reg1 << 8) | (reg2 << 16) | (rest << 24));
|
||||
janetc_emit(c, op | (reg1 << 8) | (reg2 << 16) | ((uint32_t)rest << 24));
|
||||
janetc_free_regnear(c, s2, reg2, JANETC_REGTEMP_1);
|
||||
if (wr)
|
||||
janetc_moveback(c, s1, reg1);
|
||||
@@ -325,7 +326,7 @@ int32_t janetc_emit_sss(JanetCompiler *c, uint8_t op, JanetSlot s1, JanetSlot s2
|
||||
int32_t reg2 = janetc_regnear(c, s2, JANETC_REGTEMP_1);
|
||||
int32_t reg3 = janetc_regnear(c, s3, JANETC_REGTEMP_2);
|
||||
int32_t label = janet_v_count(c->buffer);
|
||||
janetc_emit(c, op | (reg1 << 8) | (reg2 << 16) | (reg3 << 24));
|
||||
janetc_emit(c, op | (reg1 << 8) | (reg2 << 16) | ((uint32_t)reg3 << 24));
|
||||
janetc_free_regnear(c, s2, reg2, JANETC_REGTEMP_1);
|
||||
janetc_free_regnear(c, s3, reg3, JANETC_REGTEMP_2);
|
||||
if (wr)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_EMIT_H
|
||||
#define JANET_EMIT_H
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include "compile.h"
|
||||
#endif
|
||||
|
||||
void janetc_emit(JanetCompiler *c, uint32_t instr);
|
||||
|
||||
|
||||
463
src/core/fiber.c
463
src/core/fiber.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,16 +20,30 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "fiber.h"
|
||||
#include "state.h"
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
static JanetFiber *make_fiber(int32_t capacity) {
|
||||
static void fiber_reset(JanetFiber *fiber) {
|
||||
fiber->maxstack = JANET_STACK_MAX;
|
||||
fiber->frame = 0;
|
||||
fiber->stackstart = JANET_FRAME_SIZE;
|
||||
fiber->stacktop = JANET_FRAME_SIZE;
|
||||
fiber->child = NULL;
|
||||
fiber->flags = JANET_FIBER_MASK_YIELD;
|
||||
fiber->env = NULL;
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_NEW);
|
||||
}
|
||||
|
||||
static JanetFiber *fiber_alloc(int32_t capacity) {
|
||||
Janet *data;
|
||||
JanetFiber *fiber = janet_gcalloc(JANET_MEMORY_FIBER, sizeof(JanetFiber));
|
||||
if (capacity < 16) {
|
||||
capacity = 16;
|
||||
if (capacity < 32) {
|
||||
capacity = 32;
|
||||
}
|
||||
fiber->capacity = capacity;
|
||||
data = malloc(sizeof(Janet) * capacity);
|
||||
@@ -37,39 +51,31 @@ static JanetFiber *make_fiber(int32_t capacity) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
fiber->data = data;
|
||||
fiber->maxstack = JANET_STACK_MAX;
|
||||
fiber->frame = 0;
|
||||
fiber->stackstart = JANET_FRAME_SIZE;
|
||||
fiber->stacktop = JANET_FRAME_SIZE;
|
||||
fiber->child = NULL;
|
||||
fiber->flags = JANET_FIBER_MASK_YIELD;
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_NEW);
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/* Initialize a new fiber */
|
||||
JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity) {
|
||||
JanetFiber *fiber = make_fiber(capacity);
|
||||
if (janet_fiber_funcframe(fiber, callee))
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_ERROR);
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/* Clear a fiber (reset it) with argn values on the stack. */
|
||||
JanetFiber *janet_fiber_n(JanetFunction *callee, int32_t capacity, const Janet *argv, int32_t argn) {
|
||||
/* Create a new fiber with argn values on the stack by reusing a fiber. */
|
||||
JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t argc, const Janet *argv) {
|
||||
int32_t newstacktop;
|
||||
JanetFiber *fiber = make_fiber(capacity);
|
||||
newstacktop = fiber->stacktop + argn;
|
||||
if (newstacktop >= fiber->capacity) {
|
||||
janet_fiber_setcapacity(fiber, 2 * newstacktop);
|
||||
fiber_reset(fiber);
|
||||
if (argc) {
|
||||
newstacktop = fiber->stacktop + argc;
|
||||
if (newstacktop >= fiber->capacity) {
|
||||
janet_fiber_setcapacity(fiber, 2 * newstacktop);
|
||||
}
|
||||
memcpy(fiber->data + fiber->stacktop, argv, argc * sizeof(Janet));
|
||||
fiber->stacktop = newstacktop;
|
||||
}
|
||||
memcpy(fiber->data + fiber->stacktop, argv, argn * sizeof(Janet));
|
||||
fiber->stacktop = newstacktop;
|
||||
if (janet_fiber_funcframe(fiber, callee))
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_ERROR);
|
||||
if (janet_fiber_funcframe(fiber, callee)) return NULL;
|
||||
janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
|
||||
return fiber;
|
||||
}
|
||||
|
||||
/* Create a new fiber with argn values on the stack. */
|
||||
JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity, int32_t argc, const Janet *argv) {
|
||||
return janet_fiber_reset(fiber_alloc(capacity), callee, argc, argv);
|
||||
}
|
||||
|
||||
/* Ensure that the fiber has enough extra capacity */
|
||||
void janet_fiber_setcapacity(JanetFiber *fiber, int32_t n) {
|
||||
Janet *newData = realloc(fiber->data, sizeof(Janet) * n);
|
||||
@@ -121,6 +127,16 @@ void janet_fiber_pushn(JanetFiber *fiber, const Janet *arr, int32_t n) {
|
||||
fiber->stacktop = newtop;
|
||||
}
|
||||
|
||||
/* Create a struct with n values. If n is odd, the last value is ignored. */
|
||||
static Janet make_struct_n(const Janet *args, int32_t n) {
|
||||
int32_t i = 0;
|
||||
JanetKV *st = janet_struct_begin(n & (~1));
|
||||
for (; i < n; i += 2) {
|
||||
janet_struct_put(st, args[i], args[i + 1]);
|
||||
}
|
||||
return janet_wrap_struct(janet_struct_end(st));
|
||||
}
|
||||
|
||||
/* Push a stack frame to a fiber */
|
||||
int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) {
|
||||
JanetStackFrame *newframe;
|
||||
@@ -132,6 +148,10 @@ int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) {
|
||||
int32_t nextstacktop = nextframe + func->def->slotcount + JANET_FRAME_SIZE;
|
||||
int32_t next_arity = fiber->stacktop - fiber->stackstart;
|
||||
|
||||
/* Check strict arity before messing with state */
|
||||
if (next_arity < func->def->min_arity) return 1;
|
||||
if (next_arity > func->def->max_arity) return 1;
|
||||
|
||||
if (fiber->capacity < nextstacktop) {
|
||||
janet_fiber_setcapacity(fiber, 2 * nextstacktop);
|
||||
}
|
||||
@@ -154,19 +174,19 @@ int janet_fiber_funcframe(JanetFiber *fiber, JanetFunction *func) {
|
||||
/* Check varargs */
|
||||
if (func->def->flags & JANET_FUNCDEF_FLAG_VARARG) {
|
||||
int32_t tuplehead = fiber->frame + func->def->arity;
|
||||
int st = func->def->flags & JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
if (tuplehead >= oldtop) {
|
||||
fiber->data[tuplehead] = janet_wrap_tuple(janet_tuple_n(NULL, 0));
|
||||
fiber->data[tuplehead] = st
|
||||
? make_struct_n(NULL, 0)
|
||||
: janet_wrap_tuple(janet_tuple_n(NULL, 0));
|
||||
} else {
|
||||
fiber->data[tuplehead] = janet_wrap_tuple(janet_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead));
|
||||
}
|
||||
}
|
||||
|
||||
/* Check strict arity AFTER getting fiber to valid state. */
|
||||
if (func->def->flags & JANET_FUNCDEF_FLAG_FIXARITY) {
|
||||
if (func->def->arity != next_arity) {
|
||||
return 1;
|
||||
fiber->data[tuplehead] = st
|
||||
? make_struct_n(
|
||||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead)
|
||||
: janet_wrap_tuple(janet_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
oldtop - tuplehead));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,6 +218,10 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
|
||||
int32_t next_arity = fiber->stacktop - fiber->stackstart;
|
||||
int32_t stacksize;
|
||||
|
||||
/* Check strict arity before messing with state */
|
||||
if (next_arity < func->def->min_arity) return 1;
|
||||
if (next_arity > func->def->max_arity) return 1;
|
||||
|
||||
if (fiber->capacity < nextstacktop) {
|
||||
janet_fiber_setcapacity(fiber, 2 * nextstacktop);
|
||||
}
|
||||
@@ -205,7 +229,7 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
|
||||
Janet *stack = fiber->data + fiber->frame;
|
||||
Janet *args = fiber->data + fiber->stackstart;
|
||||
|
||||
/* Detatch old function */
|
||||
/* Detach old function */
|
||||
if (NULL != janet_fiber_frame(fiber)->func)
|
||||
janet_env_detach(janet_fiber_frame(fiber)->env);
|
||||
janet_fiber_frame(fiber)->env = NULL;
|
||||
@@ -213,14 +237,21 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
|
||||
/* Check varargs */
|
||||
if (func->def->flags & JANET_FUNCDEF_FLAG_VARARG) {
|
||||
int32_t tuplehead = fiber->stackstart + func->def->arity;
|
||||
int st = func->def->flags & JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
if (tuplehead >= fiber->stacktop) {
|
||||
if (tuplehead >= fiber->capacity) janet_fiber_setcapacity(fiber, 2 * (tuplehead + 1));
|
||||
for (i = fiber->stacktop; i < tuplehead; ++i) fiber->data[i] = janet_wrap_nil();
|
||||
fiber->data[tuplehead] = janet_wrap_tuple(janet_tuple_n(NULL, 0));
|
||||
fiber->data[tuplehead] = st
|
||||
? make_struct_n(NULL, 0)
|
||||
: janet_wrap_tuple(janet_tuple_n(NULL, 0));
|
||||
} else {
|
||||
fiber->data[tuplehead] = janet_wrap_tuple(janet_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead));
|
||||
fiber->data[tuplehead] = st
|
||||
? make_struct_n(
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead)
|
||||
: janet_wrap_tuple(janet_tuple_n(
|
||||
fiber->data + tuplehead,
|
||||
fiber->stacktop - tuplehead));
|
||||
}
|
||||
stacksize = tuplehead - fiber->stackstart + 1;
|
||||
} else {
|
||||
@@ -241,13 +272,6 @@ int janet_fiber_funcframe_tail(JanetFiber *fiber, JanetFunction *func) {
|
||||
janet_fiber_frame(fiber)->pc = func->def->bytecode;
|
||||
janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_TAILCALL;
|
||||
|
||||
/* Check strict arity AFTER getting fiber to valid state. */
|
||||
if (func->def->flags & JANET_FUNCDEF_FLAG_FIXARITY) {
|
||||
if (func->def->arity != next_arity) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Good return */
|
||||
return 0;
|
||||
}
|
||||
@@ -292,34 +316,55 @@ void janet_fiber_popframe(JanetFiber *fiber) {
|
||||
fiber->frame = frame->prevframe;
|
||||
}
|
||||
|
||||
JanetFiberStatus janet_fiber_status(JanetFiber *f) {
|
||||
return ((f)->flags & JANET_FIBER_STATUS_MASK) >> JANET_FIBER_STATUS_OFFSET;
|
||||
}
|
||||
|
||||
JanetFiber *janet_current_fiber(void) {
|
||||
return janet_vm_fiber;
|
||||
}
|
||||
|
||||
/* CFuns */
|
||||
|
||||
static int cfun_new(JanetArgs args) {
|
||||
JanetFiber *fiber;
|
||||
JanetFunction *func;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_MAXARITY(args, 2);
|
||||
JANET_ARG_FUNCTION(func, args, 0);
|
||||
if (func->def->flags & JANET_FUNCDEF_FLAG_FIXARITY) {
|
||||
if (func->def->arity != 0) {
|
||||
JANET_THROW(args, "expected nullary function in fiber constructor");
|
||||
}
|
||||
static Janet cfun_fiber_getenv(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
return fiber->env ?
|
||||
janet_wrap_table(fiber->env) :
|
||||
janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_fiber_setenv(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
if (janet_checktype(argv[1], JANET_NIL)) {
|
||||
fiber->env = NULL;
|
||||
} else {
|
||||
fiber->env = janet_gettable(argv, 1);
|
||||
}
|
||||
fiber = janet_fiber(func, 64);
|
||||
if (args.n == 2) {
|
||||
const uint8_t *flags;
|
||||
int32_t len, i;
|
||||
JANET_ARG_BYTES(flags, len, args, 1);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static Janet cfun_fiber_new(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
JanetFunction *func = janet_getfunction(argv, 0);
|
||||
JanetFiber *fiber;
|
||||
if (func->def->min_arity != 0) {
|
||||
janet_panic("expected nullary function in fiber constructor");
|
||||
}
|
||||
fiber = janet_fiber(func, 64, 0, NULL);
|
||||
if (argc == 2) {
|
||||
int32_t i;
|
||||
JanetByteView view = janet_getbytes(argv, 1);
|
||||
fiber->flags = 0;
|
||||
janet_fiber_set_status(fiber, JANET_STATUS_NEW);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (flags[i] >= '0' && flags[i] <= '9') {
|
||||
fiber->flags |= JANET_FIBER_MASK_USERN(flags[i] - '0');
|
||||
for (i = 0; i < view.len; i++) {
|
||||
if (view.bytes[i] >= '0' && view.bytes[i] <= '9') {
|
||||
fiber->flags |= JANET_FIBER_MASK_USERN(view.bytes[i] - '0');
|
||||
} else {
|
||||
switch (flags[i]) {
|
||||
switch (view.bytes[i]) {
|
||||
default:
|
||||
JANET_THROW(args, "invalid flag, expected a, d, e, u, or y");
|
||||
case ':':
|
||||
janet_panicf("invalid flag %c, expected a, d, e, u, or y", view.bytes[i]);
|
||||
break;
|
||||
case 'a':
|
||||
fiber->flags |=
|
||||
@@ -340,209 +385,125 @@ static int cfun_new(JanetArgs args) {
|
||||
case 'y':
|
||||
fiber->flags |= JANET_FIBER_MASK_YIELD;
|
||||
break;
|
||||
case 'i':
|
||||
if (!janet_vm_fiber->env) {
|
||||
janet_vm_fiber->env = janet_table(0);
|
||||
}
|
||||
fiber->env = janet_vm_fiber->env;
|
||||
break;
|
||||
case 'p':
|
||||
if (!janet_vm_fiber->env) {
|
||||
janet_vm_fiber->env = janet_table(0);
|
||||
}
|
||||
fiber->env = janet_table(0);
|
||||
fiber->env->proto = janet_vm_fiber->env;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
JANET_RETURN_FIBER(args, fiber);
|
||||
return janet_wrap_fiber(fiber);
|
||||
}
|
||||
|
||||
static int cfun_status(JanetArgs args) {
|
||||
JanetFiber *fiber;
|
||||
const char *status = "";
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_FIBER(fiber, args, 0);
|
||||
uint32_t s = (fiber->flags & JANET_FIBER_STATUS_MASK) >>
|
||||
JANET_FIBER_STATUS_OFFSET;
|
||||
switch (s) {
|
||||
case JANET_STATUS_DEAD: status = ":dead"; break;
|
||||
case JANET_STATUS_ERROR: status = ":error"; break;
|
||||
case JANET_STATUS_DEBUG: status = ":debug"; break;
|
||||
case JANET_STATUS_PENDING: status = ":pending"; break;
|
||||
case JANET_STATUS_USER0: status = ":user0"; break;
|
||||
case JANET_STATUS_USER1: status = ":user1"; break;
|
||||
case JANET_STATUS_USER2: status = ":user2"; break;
|
||||
case JANET_STATUS_USER3: status = ":user3"; break;
|
||||
case JANET_STATUS_USER4: status = ":user4"; break;
|
||||
case JANET_STATUS_USER5: status = ":user5"; break;
|
||||
case JANET_STATUS_USER6: status = ":user6"; break;
|
||||
case JANET_STATUS_USER7: status = ":user7"; break;
|
||||
case JANET_STATUS_USER8: status = ":user8"; break;
|
||||
case JANET_STATUS_USER9: status = ":user9"; break;
|
||||
case JANET_STATUS_NEW: status = ":new"; break;
|
||||
default:
|
||||
case JANET_STATUS_ALIVE: status = ":alive"; break;
|
||||
}
|
||||
JANET_RETURN_CSYMBOL(args, status);
|
||||
static Janet cfun_fiber_status(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
uint32_t s = janet_fiber_status(fiber);
|
||||
return janet_ckeywordv(janet_status_names[s]);
|
||||
}
|
||||
|
||||
/* Extract info from one stack frame */
|
||||
static Janet doframe(JanetStackFrame *frame) {
|
||||
int32_t off;
|
||||
JanetTable *t = janet_table(3);
|
||||
JanetFuncDef *def = NULL;
|
||||
if (frame->func) {
|
||||
janet_table_put(t, janet_csymbolv(":function"), janet_wrap_function(frame->func));
|
||||
def = frame->func->def;
|
||||
if (def->name) {
|
||||
janet_table_put(t, janet_csymbolv(":name"), janet_wrap_string(def->name));
|
||||
}
|
||||
} else {
|
||||
JanetCFunction cfun = (JanetCFunction)(frame->pc);
|
||||
if (cfun) {
|
||||
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
|
||||
if (!janet_checktype(name, JANET_NIL)) {
|
||||
janet_table_put(t, janet_csymbolv(":name"), name);
|
||||
}
|
||||
}
|
||||
janet_table_put(t, janet_csymbolv(":c"), janet_wrap_true());
|
||||
}
|
||||
if (frame->flags & JANET_STACKFRAME_TAILCALL) {
|
||||
janet_table_put(t, janet_csymbolv(":tail"), janet_wrap_true());
|
||||
}
|
||||
if (frame->func && frame->pc) {
|
||||
off = (int32_t) (frame->pc - def->bytecode);
|
||||
janet_table_put(t, janet_csymbolv(":pc"), janet_wrap_integer(off));
|
||||
if (def->sourcemap) {
|
||||
JanetSourceMapping mapping = def->sourcemap[off];
|
||||
janet_table_put(t, janet_csymbolv(":line"), janet_wrap_integer(mapping.line));
|
||||
janet_table_put(t, janet_csymbolv(":column"), janet_wrap_integer(mapping.column));
|
||||
}
|
||||
if (def->source) {
|
||||
janet_table_put(t, janet_csymbolv(":source"), janet_wrap_string(def->source));
|
||||
}
|
||||
}
|
||||
return janet_wrap_table(t);
|
||||
static Janet cfun_fiber_current(int32_t argc, Janet *argv) {
|
||||
(void) argv;
|
||||
janet_fixarity(argc, 0);
|
||||
return janet_wrap_fiber(janet_vm_fiber);
|
||||
}
|
||||
|
||||
static int cfun_stack(JanetArgs args) {
|
||||
JanetFiber *fiber;
|
||||
JanetArray *array;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_FIBER(fiber, args, 0);
|
||||
array = janet_array(0);
|
||||
{
|
||||
int32_t i = fiber->frame;
|
||||
JanetStackFrame *frame;
|
||||
while (i > 0) {
|
||||
frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
janet_array_push(array, doframe(frame));
|
||||
i = frame->prevframe;
|
||||
}
|
||||
}
|
||||
JANET_RETURN_ARRAY(args, array);
|
||||
static Janet cfun_fiber_maxstack(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
return janet_wrap_integer(fiber->maxstack);
|
||||
}
|
||||
|
||||
static int cfun_current(JanetArgs args) {
|
||||
JANET_FIXARITY(args, 0);
|
||||
JANET_RETURN_FIBER(args, janet_vm_fiber);
|
||||
}
|
||||
|
||||
static int cfun_lineage(JanetArgs args) {
|
||||
JanetFiber *fiber;
|
||||
JanetArray *array;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_FIBER(fiber, args, 0);
|
||||
array = janet_array(0);
|
||||
while (fiber) {
|
||||
janet_array_push(array, janet_wrap_fiber(fiber));
|
||||
fiber = fiber->child;
|
||||
}
|
||||
JANET_RETURN_ARRAY(args, array);
|
||||
}
|
||||
|
||||
static int cfun_maxstack(JanetArgs args) {
|
||||
JanetFiber *fiber;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_FIBER(fiber, args, 0);
|
||||
JANET_RETURN_INTEGER(args, fiber->maxstack);
|
||||
}
|
||||
|
||||
static int cfun_setmaxstack(JanetArgs args) {
|
||||
JanetFiber *fiber;
|
||||
int32_t maxs;
|
||||
JANET_FIXARITY(args, 2);
|
||||
JANET_ARG_FIBER(fiber, args, 0);
|
||||
JANET_ARG_INTEGER(maxs, args, 1);
|
||||
static Janet cfun_fiber_setmaxstack(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
int32_t maxs = janet_getinteger(argv, 1);
|
||||
if (maxs < 0) {
|
||||
JANET_THROW(args, "expected positive integer");
|
||||
janet_panic("expected positive integer");
|
||||
}
|
||||
fiber->maxstack = maxs;
|
||||
JANET_RETURN_FIBER(args, fiber);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"fiber/new", cfun_new,
|
||||
"(fiber/new func [,sigmask])\n\n"
|
||||
"Create a new fiber with function body func. Can optionally "
|
||||
"take a set of signals to block from the current parent fiber "
|
||||
"when called. The mask is specified as symbol where each character "
|
||||
"is used to indicate a signal to block. The default sigmask is :y. "
|
||||
"For example, \n\n"
|
||||
"\t(fiber/new myfun :e123)\n\n"
|
||||
"blocks error signals and user signals 1, 2 and 3. The signals are "
|
||||
"as follows: \n\n"
|
||||
"\ta - block all signals\n"
|
||||
"\td - block debug signals\n"
|
||||
"\te - block error signals\n"
|
||||
"\tu - block user signals\n"
|
||||
"\ty - block yield signals\n"
|
||||
"\t0-9 - block a specific user signal"
|
||||
static const JanetReg fiber_cfuns[] = {
|
||||
{
|
||||
"fiber/new", cfun_fiber_new,
|
||||
JDOC("(fiber/new func &opt sigmask)\n\n"
|
||||
"Create a new fiber with function body func. Can optionally "
|
||||
"take a set of signals to block from the current parent fiber "
|
||||
"when called. The mask is specified as a keyword where each character "
|
||||
"is used to indicate a signal to block. The default sigmask is :y. "
|
||||
"For example, \n\n"
|
||||
"\t(fiber/new myfun :e123)\n\n"
|
||||
"blocks error signals and user signals 1, 2 and 3. The signals are "
|
||||
"as follows: \n\n"
|
||||
"\ta - block all signals\n"
|
||||
"\td - block debug signals\n"
|
||||
"\te - block error signals\n"
|
||||
"\tu - block user signals\n"
|
||||
"\ty - block yield signals\n"
|
||||
"\t0-9 - block a specific user signal\n\n"
|
||||
"The sigmask argument also can take environment flags. If any mutually "
|
||||
"exclusive flags are present, the last flag takes precedence.\n\n"
|
||||
"\ti - inherit the environment from the current fiber\n"
|
||||
"\tp - the environment table's prototype is the current environment table")
|
||||
},
|
||||
{"fiber/status", cfun_status,
|
||||
"(fiber/status fib)\n\n"
|
||||
"Get the status of a fiber. The status will be one of:\n\n"
|
||||
"\t:dead - the fiber has finished\n"
|
||||
"\t:error - the fiber has errored out\n"
|
||||
"\t:debug - the fiber is suspended in debug mode\n"
|
||||
"\t:pending - the fiber has been yielded\n"
|
||||
"\t:user(0-9) - the fiber is suspended by a user signal\n"
|
||||
"\t:alive - the fiber is currently running and cannot be resumed\n"
|
||||
"\t:new - the fiber has just been created and not yet run"
|
||||
{
|
||||
"fiber/status", cfun_fiber_status,
|
||||
JDOC("(fiber/status fib)\n\n"
|
||||
"Get the status of a fiber. The status will be one of:\n\n"
|
||||
"\t:dead - the fiber has finished\n"
|
||||
"\t:error - the fiber has errored out\n"
|
||||
"\t:debug - the fiber is suspended in debug mode\n"
|
||||
"\t:pending - the fiber has been yielded\n"
|
||||
"\t:user(0-9) - the fiber is suspended by a user signal\n"
|
||||
"\t:alive - the fiber is currently running and cannot be resumed\n"
|
||||
"\t:new - the fiber has just been created and not yet run")
|
||||
},
|
||||
{"fiber/stack", cfun_stack,
|
||||
"(fiber/stack fib)\n\n"
|
||||
"Gets information about the stack as an array of tables. Each table "
|
||||
"in the array contains information about a stack frame. The top most, current "
|
||||
"stack frame is the first table in the array, and the bottom most stack frame "
|
||||
"is the last value. Each stack frame contains some of the following attributes:\n\n"
|
||||
"\t:c - true if the stack frame is a c function invokation\n"
|
||||
"\t:column - the current source column of the stack frame\n"
|
||||
"\t:function - the function that the stack frame represents\n"
|
||||
"\t:line - the current source line of the stack frame\n"
|
||||
"\t:name - the human friendly name of the function\n"
|
||||
"\t:pc - integer indicating the location of the program counter\n"
|
||||
"\t:source - string with filename or other identifier for the source code\n"
|
||||
"\t:tail - boolean indicating a tail call"
|
||||
{
|
||||
"fiber/current", cfun_fiber_current,
|
||||
JDOC("(fiber/current)\n\n"
|
||||
"Returns the currently running fiber.")
|
||||
},
|
||||
{"fiber/current", cfun_current,
|
||||
"(fiber/current)\n\n"
|
||||
"Returns the currently running fiber."
|
||||
{
|
||||
"fiber/maxstack", cfun_fiber_maxstack,
|
||||
JDOC("(fiber/maxstack fib)\n\n"
|
||||
"Gets the maximum stack size in janet values allowed for a fiber. While memory for "
|
||||
"the fiber's stack is not allocated up front, the fiber will not allocated more "
|
||||
"than this amount and will throw a stack-overflow error if more memory is needed. ")
|
||||
},
|
||||
{"fiber/lineage", cfun_lineage,
|
||||
"(fiber/lineage fib)\n\n"
|
||||
"Returns an array of all child fibers from a root fiber. This function "
|
||||
"is useful when a fiber signals or errors to an ancestor fiber. Using this function, "
|
||||
"the fiber handling the error can see which fiber raised the signal. This function should "
|
||||
"be used mostly for debugging purposes."
|
||||
{
|
||||
"fiber/setmaxstack", cfun_fiber_setmaxstack,
|
||||
JDOC("(fiber/setmaxstack fib maxstack)\n\n"
|
||||
"Sets the maximum stack size in janet values for a fiber. By default, the "
|
||||
"maximum stack size is usually 8192.")
|
||||
},
|
||||
{"fiber/maxstack", cfun_maxstack,
|
||||
"(fiber/maxstack fib)\n\n"
|
||||
"Gets the maximum stack size in janet values allowed for a fiber. While memory for "
|
||||
"the fiber's stack is not allocated up front, the fiber will not allocated more "
|
||||
"than this amount and will throw a stackoverflow error if more memory is needed. "
|
||||
{
|
||||
"fiber/getenv", cfun_fiber_getenv,
|
||||
JDOC("(fiber/getenv fiber)\n\n"
|
||||
"Gets the environment for a fiber. Returns nil if no such table is "
|
||||
"set yet.")
|
||||
},
|
||||
{"fiber/setmaxstack", cfun_setmaxstack,
|
||||
"(fiber/setmaxstack fib maxstack)\n\n"
|
||||
"Sets the maximum stack size in janet values for a fiber. By default, the "
|
||||
"maximum stacksize is usually 8192."
|
||||
{
|
||||
"fiber/setenv", cfun_fiber_setenv,
|
||||
JDOC("(fiber/setenv fiber table)\n\n"
|
||||
"Sets the environment table for a fiber. Set to nil to remove the current "
|
||||
"environment.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Module entry point */
|
||||
int janet_lib_fiber(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
return 0;
|
||||
void janet_lib_fiber(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, fiber_cfuns);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_FIBER_H_defined
|
||||
#define JANET_FIBER_H_defined
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#endif
|
||||
|
||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
|
||||
|
||||
|
||||
247
src/core/gc.c
247
src/core/gc.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,10 +20,13 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "state.h"
|
||||
#include "symcache.h"
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* GC State */
|
||||
JANET_THREAD_LOCAL void *janet_vm_blocks;
|
||||
@@ -36,6 +39,11 @@ JANET_THREAD_LOCAL Janet *janet_vm_roots;
|
||||
JANET_THREAD_LOCAL uint32_t janet_vm_root_count;
|
||||
JANET_THREAD_LOCAL uint32_t janet_vm_root_capacity;
|
||||
|
||||
/* Scratch Memory */
|
||||
JANET_THREAD_LOCAL void **janet_scratch_mem;
|
||||
JANET_THREAD_LOCAL size_t janet_scratch_cap;
|
||||
JANET_THREAD_LOCAL size_t janet_scratch_len;
|
||||
|
||||
/* Helpers for marking the various gc types */
|
||||
static void janet_mark_funcenv(JanetFuncEnv *env);
|
||||
static void janet_mark_funcdef(JanetFuncDef *def);
|
||||
@@ -58,17 +66,37 @@ void janet_mark(Janet x) {
|
||||
if (depth) {
|
||||
depth--;
|
||||
switch (janet_type(x)) {
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL: janet_mark_string(janet_unwrap_string(x)); break;
|
||||
case JANET_FUNCTION: janet_mark_function(janet_unwrap_function(x)); break;
|
||||
case JANET_ARRAY: janet_mark_array(janet_unwrap_array(x)); break;
|
||||
case JANET_TABLE: janet_mark_table(janet_unwrap_table(x)); break;
|
||||
case JANET_STRUCT: janet_mark_struct(janet_unwrap_struct(x)); break;
|
||||
case JANET_TUPLE: janet_mark_tuple(janet_unwrap_tuple(x)); break;
|
||||
case JANET_BUFFER: janet_mark_buffer(janet_unwrap_buffer(x)); break;
|
||||
case JANET_FIBER: janet_mark_fiber(janet_unwrap_fiber(x)); break;
|
||||
case JANET_ABSTRACT: janet_mark_abstract(janet_unwrap_abstract(x)); break;
|
||||
case JANET_KEYWORD:
|
||||
case JANET_SYMBOL:
|
||||
janet_mark_string(janet_unwrap_string(x));
|
||||
break;
|
||||
case JANET_FUNCTION:
|
||||
janet_mark_function(janet_unwrap_function(x));
|
||||
break;
|
||||
case JANET_ARRAY:
|
||||
janet_mark_array(janet_unwrap_array(x));
|
||||
break;
|
||||
case JANET_TABLE:
|
||||
janet_mark_table(janet_unwrap_table(x));
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
janet_mark_struct(janet_unwrap_struct(x));
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
janet_mark_tuple(janet_unwrap_tuple(x));
|
||||
break;
|
||||
case JANET_BUFFER:
|
||||
janet_mark_buffer(janet_unwrap_buffer(x));
|
||||
break;
|
||||
case JANET_FIBER:
|
||||
janet_mark_fiber(janet_unwrap_fiber(x));
|
||||
break;
|
||||
case JANET_ABSTRACT:
|
||||
janet_mark_abstract(janet_unwrap_abstract(x));
|
||||
break;
|
||||
}
|
||||
depth++;
|
||||
} else {
|
||||
@@ -77,7 +105,7 @@ void janet_mark(Janet x) {
|
||||
}
|
||||
|
||||
static void janet_mark_string(const uint8_t *str) {
|
||||
janet_gc_mark(janet_string_raw(str));
|
||||
janet_gc_mark(janet_string_head(str));
|
||||
}
|
||||
|
||||
static void janet_mark_buffer(JanetBuffer *buffer) {
|
||||
@@ -85,11 +113,11 @@ static void janet_mark_buffer(JanetBuffer *buffer) {
|
||||
}
|
||||
|
||||
static void janet_mark_abstract(void *adata) {
|
||||
if (janet_gc_reachable(janet_abstract_header(adata)))
|
||||
if (janet_gc_reachable(janet_abstract_head(adata)))
|
||||
return;
|
||||
janet_gc_mark(janet_abstract_header(adata));
|
||||
if (janet_abstract_header(adata)->type->gcmark) {
|
||||
janet_abstract_header(adata)->type->gcmark(adata, janet_abstract_size(adata));
|
||||
janet_gc_mark(janet_abstract_head(adata));
|
||||
if (janet_abstract_head(adata)->type->gcmark) {
|
||||
janet_abstract_head(adata)->type->gcmark(adata, janet_abstract_size(adata));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +148,7 @@ static void janet_mark_array(JanetArray *array) {
|
||||
}
|
||||
|
||||
static void janet_mark_table(JanetTable *table) {
|
||||
recur: /* Manual tail recursion */
|
||||
recur: /* Manual tail recursion */
|
||||
if (janet_gc_reachable(table))
|
||||
return;
|
||||
janet_gc_mark(table);
|
||||
@@ -132,16 +160,16 @@ static void janet_mark_table(JanetTable *table) {
|
||||
}
|
||||
|
||||
static void janet_mark_struct(const JanetKV *st) {
|
||||
if (janet_gc_reachable(janet_struct_raw(st)))
|
||||
if (janet_gc_reachable(janet_struct_head(st)))
|
||||
return;
|
||||
janet_gc_mark(janet_struct_raw(st));
|
||||
janet_gc_mark(janet_struct_head(st));
|
||||
janet_mark_kvs(st, janet_struct_capacity(st));
|
||||
}
|
||||
|
||||
static void janet_mark_tuple(const Janet *tuple) {
|
||||
if (janet_gc_reachable(janet_tuple_raw(tuple)))
|
||||
if (janet_gc_reachable(janet_tuple_head(tuple)))
|
||||
return;
|
||||
janet_gc_mark(janet_tuple_raw(tuple));
|
||||
janet_gc_mark(janet_tuple_head(tuple));
|
||||
janet_mark_many(tuple, janet_tuple_length(tuple));
|
||||
}
|
||||
|
||||
@@ -195,6 +223,11 @@ recur:
|
||||
if (janet_gc_reachable(fiber))
|
||||
return;
|
||||
janet_gc_mark(fiber);
|
||||
|
||||
/* Mark values on the argument stack */
|
||||
janet_mark_many(fiber->data + fiber->stackstart,
|
||||
fiber->stacktop - fiber->stackstart);
|
||||
|
||||
i = fiber->frame;
|
||||
j = fiber->stackstart - JANET_FRAME_SIZE;
|
||||
while (i > 0) {
|
||||
@@ -209,6 +242,9 @@ recur:
|
||||
i = frame->prevframe;
|
||||
}
|
||||
|
||||
if (fiber->env)
|
||||
janet_mark_table(fiber->env);
|
||||
|
||||
/* Explicit tail recursion */
|
||||
if (fiber->child) {
|
||||
fiber = fiber->child;
|
||||
@@ -217,21 +253,19 @@ recur:
|
||||
}
|
||||
|
||||
/* Deinitialize a block of memory */
|
||||
static void janet_deinit_block(JanetGCMemoryHeader *block) {
|
||||
void *mem = ((char *)(block + 1));
|
||||
JanetAbstractHeader *h = (JanetAbstractHeader *)mem;
|
||||
switch (block->flags & JANET_MEM_TYPEBITS) {
|
||||
static void janet_deinit_block(JanetGCObject *mem) {
|
||||
switch (mem->flags & JANET_MEM_TYPEBITS) {
|
||||
default:
|
||||
case JANET_MEMORY_FUNCTION:
|
||||
break; /* Do nothing for non gc types */
|
||||
case JANET_MEMORY_SYMBOL:
|
||||
janet_symbol_deinit((const uint8_t *)mem + 2 * sizeof(int32_t));
|
||||
janet_symbol_deinit(((JanetStringHead *) mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_ARRAY:
|
||||
janet_array_deinit((JanetArray*) mem);
|
||||
free(((JanetArray *) mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_TABLE:
|
||||
janet_table_deinit((JanetTable*) mem);
|
||||
free(((JanetTable *) mem)->data);
|
||||
break;
|
||||
case JANET_MEMORY_FIBER:
|
||||
free(((JanetFiber *)mem)->data);
|
||||
@@ -239,38 +273,38 @@ static void janet_deinit_block(JanetGCMemoryHeader *block) {
|
||||
case JANET_MEMORY_BUFFER:
|
||||
janet_buffer_deinit((JanetBuffer *) mem);
|
||||
break;
|
||||
case JANET_MEMORY_ABSTRACT:
|
||||
if (h->type->gc) {
|
||||
janet_assert(!h->type->gc((void *)(h + 1), h->size), "finalizer failed");
|
||||
case JANET_MEMORY_ABSTRACT: {
|
||||
JanetAbstractHead *head = (JanetAbstractHead *)mem;
|
||||
if (head->type->gc) {
|
||||
janet_assert(!head->type->gc(head->data, head->size), "finalizer failed");
|
||||
}
|
||||
break;
|
||||
case JANET_MEMORY_FUNCENV:
|
||||
{
|
||||
JanetFuncEnv *env = (JanetFuncEnv *)mem;
|
||||
if (0 == env->offset)
|
||||
free(env->as.values);
|
||||
}
|
||||
break;
|
||||
case JANET_MEMORY_FUNCDEF:
|
||||
{
|
||||
JanetFuncDef *def = (JanetFuncDef *)mem;
|
||||
/* TODO - get this all with one alloc and one free */
|
||||
free(def->defs);
|
||||
free(def->environments);
|
||||
free(def->constants);
|
||||
free(def->bytecode);
|
||||
free(def->sourcemap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case JANET_MEMORY_FUNCENV: {
|
||||
JanetFuncEnv *env = (JanetFuncEnv *)mem;
|
||||
if (0 == env->offset)
|
||||
free(env->as.values);
|
||||
}
|
||||
break;
|
||||
case JANET_MEMORY_FUNCDEF: {
|
||||
JanetFuncDef *def = (JanetFuncDef *)mem;
|
||||
/* TODO - get this all with one alloc and one free */
|
||||
free(def->defs);
|
||||
free(def->environments);
|
||||
free(def->constants);
|
||||
free(def->bytecode);
|
||||
free(def->sourcemap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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() {
|
||||
JanetGCMemoryHeader *previous = NULL;
|
||||
JanetGCMemoryHeader *current = janet_vm_blocks;
|
||||
JanetGCMemoryHeader *next;
|
||||
JanetGCObject *previous = NULL;
|
||||
JanetGCObject *current = janet_vm_blocks;
|
||||
JanetGCObject *next;
|
||||
while (NULL != current) {
|
||||
next = current->next;
|
||||
if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) {
|
||||
@@ -291,29 +325,33 @@ void janet_sweep() {
|
||||
|
||||
/* Allocate some memory that is tracked for garbage collection */
|
||||
void *janet_gcalloc(enum JanetMemoryType type, size_t size) {
|
||||
JanetGCMemoryHeader *mdata;
|
||||
size_t total = size + sizeof(JanetGCMemoryHeader);
|
||||
JanetGCObject *mem;
|
||||
|
||||
/* Make sure everything is inited */
|
||||
janet_assert(NULL != janet_vm_cache, "please initialize janet before use");
|
||||
void *mem = malloc(total);
|
||||
mem = malloc(size);
|
||||
|
||||
/* Check for bad malloc */
|
||||
if (NULL == mem) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mdata = (JanetGCMemoryHeader *)mem;
|
||||
|
||||
/* Configure block */
|
||||
mdata->flags = type;
|
||||
mem->flags = type;
|
||||
|
||||
/* Prepend block to heap list */
|
||||
janet_vm_next_collection += (int32_t) size;
|
||||
mdata->next = janet_vm_blocks;
|
||||
janet_vm_blocks = mdata;
|
||||
mem->next = janet_vm_blocks;
|
||||
janet_vm_blocks = mem;
|
||||
|
||||
return (char *) mem + sizeof(JanetGCMemoryHeader);
|
||||
return (void *)mem;
|
||||
}
|
||||
|
||||
/* Free all allocated scratch memory */
|
||||
static void janet_free_all_scratch(void) {
|
||||
for (size_t i = 0; i < janet_scratch_len; i++)
|
||||
free(janet_scratch_mem[i]);
|
||||
janet_scratch_len = 0;
|
||||
}
|
||||
|
||||
/* Run garbage collection */
|
||||
@@ -330,6 +368,7 @@ void janet_collect(void) {
|
||||
}
|
||||
janet_sweep();
|
||||
janet_vm_next_collection = 0;
|
||||
janet_free_all_scratch();
|
||||
}
|
||||
|
||||
/* Add a root value to the GC. This prevents the GC from removing a value
|
||||
@@ -354,14 +393,11 @@ static int janet_gc_idequals(Janet lhs, Janet rhs) {
|
||||
if (janet_type(lhs) != janet_type(rhs))
|
||||
return 0;
|
||||
switch (janet_type(lhs)) {
|
||||
case JANET_TRUE:
|
||||
case JANET_FALSE:
|
||||
case JANET_BOOLEAN:
|
||||
case JANET_NIL:
|
||||
case JANET_NUMBER:
|
||||
/* These values don't really matter to the gc so returning 1 all the time is fine. */
|
||||
return 1;
|
||||
case JANET_INTEGER:
|
||||
return janet_unwrap_integer(lhs) == janet_unwrap_integer(rhs);
|
||||
case JANET_REAL:
|
||||
return janet_unwrap_real(lhs) == janet_unwrap_real(rhs);
|
||||
default:
|
||||
return janet_unwrap_pointer(lhs) == janet_unwrap_pointer(rhs);
|
||||
}
|
||||
@@ -371,9 +407,8 @@ static int janet_gc_idequals(Janet lhs, Janet rhs) {
|
||||
* a value and all its children. */
|
||||
int janet_gcunroot(Janet root) {
|
||||
Janet *vtop = janet_vm_roots + janet_vm_root_count;
|
||||
Janet *v = janet_vm_roots;
|
||||
/* Search from top to bottom as access is most likely LIFO */
|
||||
for (v = janet_vm_roots; v < vtop; v++) {
|
||||
for (Janet *v = janet_vm_roots; v < vtop; v++) {
|
||||
if (janet_gc_idequals(root, *v)) {
|
||||
*v = janet_vm_roots[--janet_vm_root_count];
|
||||
return 1;
|
||||
@@ -385,10 +420,9 @@ int janet_gcunroot(Janet root) {
|
||||
/* Remove a root value from the GC. This sets the effective reference count to 0. */
|
||||
int janet_gcunrootall(Janet root) {
|
||||
Janet *vtop = janet_vm_roots + janet_vm_root_count;
|
||||
Janet *v = janet_vm_roots;
|
||||
int ret = 0;
|
||||
/* Search from top to bottom as access is most likely LIFO */
|
||||
for (v = janet_vm_roots; v < vtop; v++) {
|
||||
for (Janet *v = janet_vm_roots; v < vtop; v++) {
|
||||
if (janet_gc_idequals(root, *v)) {
|
||||
*v = janet_vm_roots[--janet_vm_root_count];
|
||||
vtop--;
|
||||
@@ -400,16 +434,75 @@ int janet_gcunrootall(Janet root) {
|
||||
|
||||
/* Free all allocated memory */
|
||||
void janet_clear_memory(void) {
|
||||
JanetGCMemoryHeader *current = janet_vm_blocks;
|
||||
JanetGCObject *current = janet_vm_blocks;
|
||||
while (NULL != current) {
|
||||
janet_deinit_block(current);
|
||||
JanetGCMemoryHeader *next = current->next;
|
||||
JanetGCObject *next = current->next;
|
||||
free(current);
|
||||
current = next;
|
||||
}
|
||||
janet_vm_blocks = NULL;
|
||||
janet_free_all_scratch();
|
||||
free(janet_scratch_mem);
|
||||
}
|
||||
|
||||
/* Primitives for suspending GC. */
|
||||
int janet_gclock(void) { return janet_vm_gc_suspend++; }
|
||||
void janet_gcunlock(int handle) { janet_vm_gc_suspend = handle; }
|
||||
int janet_gclock(void) {
|
||||
return janet_vm_gc_suspend++;
|
||||
}
|
||||
void janet_gcunlock(int handle) {
|
||||
janet_vm_gc_suspend = handle;
|
||||
}
|
||||
|
||||
/* Scratch memory API */
|
||||
|
||||
void *janet_smalloc(size_t size) {
|
||||
void *mem = malloc(size);
|
||||
if (NULL == mem) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
if (janet_scratch_len == janet_scratch_cap) {
|
||||
size_t newcap = 2 * janet_scratch_cap + 2;
|
||||
void **newmem = (void **) realloc(janet_scratch_mem, newcap * sizeof(void *));
|
||||
if (NULL == newmem) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
janet_scratch_cap = newcap;
|
||||
janet_scratch_mem = newmem;
|
||||
}
|
||||
janet_scratch_mem[janet_scratch_len++] = mem;
|
||||
return mem;
|
||||
}
|
||||
|
||||
void *janet_srealloc(void *mem, size_t size) {
|
||||
if (NULL == mem) return janet_smalloc(size);
|
||||
if (janet_scratch_len) {
|
||||
for (size_t i = janet_scratch_len - 1; ; i--) {
|
||||
if (janet_scratch_mem[i] == mem) {
|
||||
void *newmem = realloc(mem, size);
|
||||
if (NULL == newmem) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
janet_scratch_mem[i] = newmem;
|
||||
return newmem;
|
||||
}
|
||||
if (i == 0) break;
|
||||
}
|
||||
}
|
||||
janet_exit("invalid janet_srealloc");
|
||||
}
|
||||
|
||||
void janet_sfree(void *mem) {
|
||||
if (NULL == mem) return;
|
||||
if (janet_scratch_len) {
|
||||
for (size_t i = janet_scratch_len - 1; ; i--) {
|
||||
if (janet_scratch_mem[i] == mem) {
|
||||
janet_scratch_mem[i] = janet_scratch_mem[--janet_scratch_len];
|
||||
free(mem);
|
||||
return;
|
||||
}
|
||||
if (i == 0) break;
|
||||
}
|
||||
}
|
||||
janet_exit("invalid janet_sfree");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 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,10 +23,12 @@
|
||||
#ifndef JANET_GC_H
|
||||
#define JANET_GC_H
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#endif
|
||||
|
||||
/* The metadata header associated with an allocated block of memory */
|
||||
#define janet_gc_header(mem) ((JanetGCMemoryHeader *)(mem) - 1)
|
||||
#define janet_gc_header(mem) ((JanetGCObject *)(mem))
|
||||
|
||||
#define JANET_MEM_TYPEBITS 0xFF
|
||||
#define JANET_MEM_REACHABLE 0x100
|
||||
@@ -36,16 +38,8 @@
|
||||
#define janet_gc_type(m) (janet_gc_header(m)->flags & 0xFF)
|
||||
|
||||
#define janet_gc_mark(m) (janet_gc_header(m)->flags |= JANET_MEM_REACHABLE)
|
||||
#define janet_gc_unmark(m) (janet_gc_header(m)->flags &= ~JANET_MEM_COLOR)
|
||||
#define janet_gc_reachable(m) (janet_gc_header(m)->flags & JANET_MEM_REACHABLE)
|
||||
|
||||
/* Memory header struct. Node of a linked list of memory blocks. */
|
||||
typedef struct JanetGCMemoryHeader JanetGCMemoryHeader;
|
||||
struct JanetGCMemoryHeader {
|
||||
JanetGCMemoryHeader *next;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/* Memory types for the GC. Different from JanetType to include funcenv and funcdef. */
|
||||
enum JanetMemoryType {
|
||||
JANET_MEMORY_NONE,
|
||||
|
||||
386
src/core/inttypes.c
Normal file
386
src/core/inttypes.c
Normal file
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Calvin Rose & contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Conditional compilation */
|
||||
#ifdef JANET_INT_TYPES
|
||||
|
||||
#define MAX_INT_IN_DBL 9007199254740992ULL /* 2^53 */
|
||||
|
||||
static Janet it_s64_get(void *p, Janet key);
|
||||
static Janet it_u64_get(void *p, Janet key);
|
||||
|
||||
static void int64_marshal(void *p, JanetMarshalContext *ctx) {
|
||||
janet_marshal_int64(ctx, *((int64_t *)p));
|
||||
}
|
||||
|
||||
static void int64_unmarshal(void *p, JanetMarshalContext *ctx) {
|
||||
*((int64_t *)p) = janet_unmarshal_int64(ctx);
|
||||
}
|
||||
|
||||
static void it_s64_tostring(void *p, JanetBuffer *buffer) {
|
||||
char str[32];
|
||||
sprintf(str, "<core/s64 %" PRId64 ">", *((int64_t *)p));
|
||||
janet_buffer_push_cstring(buffer, str);
|
||||
}
|
||||
|
||||
static void it_u64_tostring(void *p, JanetBuffer *buffer) {
|
||||
char str[32];
|
||||
sprintf(str, "<core/u64 %" PRIu64 ">", *((uint64_t *)p));
|
||||
janet_buffer_push_cstring(buffer, str);
|
||||
}
|
||||
|
||||
static const JanetAbstractType it_s64_type = {
|
||||
"core/s64",
|
||||
NULL,
|
||||
NULL,
|
||||
it_s64_get,
|
||||
NULL,
|
||||
int64_marshal,
|
||||
int64_unmarshal,
|
||||
it_s64_tostring
|
||||
};
|
||||
|
||||
static const JanetAbstractType it_u64_type = {
|
||||
"core/u64",
|
||||
NULL,
|
||||
NULL,
|
||||
it_u64_get,
|
||||
NULL,
|
||||
int64_marshal,
|
||||
int64_unmarshal,
|
||||
it_u64_tostring
|
||||
};
|
||||
|
||||
int64_t janet_unwrap_s64(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
break;
|
||||
case JANET_NUMBER : {
|
||||
double dbl = janet_unwrap_number(x);
|
||||
if (fabs(dbl) <= MAX_INT_IN_DBL)
|
||||
return (int64_t)dbl;
|
||||
break;
|
||||
}
|
||||
case JANET_STRING: {
|
||||
int64_t value;
|
||||
const uint8_t *str = janet_unwrap_string(x);
|
||||
if (janet_scan_int64(str, janet_string_length(str), &value))
|
||||
return value;
|
||||
break;
|
||||
}
|
||||
case JANET_ABSTRACT: {
|
||||
void *abst = janet_unwrap_abstract(x);
|
||||
if (janet_abstract_type(abst) == &it_s64_type ||
|
||||
(janet_abstract_type(abst) == &it_u64_type))
|
||||
return *(int64_t *)abst;
|
||||
break;
|
||||
}
|
||||
}
|
||||
janet_panic("bad s64 initializer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t janet_unwrap_u64(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
break;
|
||||
case JANET_NUMBER : {
|
||||
double dbl = janet_unwrap_number(x);
|
||||
if ((dbl >= 0) && (dbl <= MAX_INT_IN_DBL))
|
||||
return (uint64_t)dbl;
|
||||
break;
|
||||
}
|
||||
case JANET_STRING: {
|
||||
uint64_t value;
|
||||
const uint8_t *str = janet_unwrap_string(x);
|
||||
if (janet_scan_uint64(str, janet_string_length(str), &value))
|
||||
return value;
|
||||
break;
|
||||
}
|
||||
case JANET_ABSTRACT: {
|
||||
void *abst = janet_unwrap_abstract(x);
|
||||
if (janet_abstract_type(abst) == &it_s64_type ||
|
||||
(janet_abstract_type(abst) == &it_u64_type))
|
||||
return *(uint64_t *)abst;
|
||||
break;
|
||||
}
|
||||
}
|
||||
janet_panic("bad u64 initializer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
JanetIntType janet_is_int(Janet x) {
|
||||
if (!janet_checktype(x, JANET_ABSTRACT)) return JANET_INT_NONE;
|
||||
const JanetAbstractType *at = janet_abstract_type(janet_unwrap_abstract(x));
|
||||
return (at == &it_s64_type) ? JANET_INT_S64 :
|
||||
((at == &it_u64_type) ? JANET_INT_U64 :
|
||||
JANET_INT_NONE);
|
||||
}
|
||||
|
||||
Janet janet_wrap_s64(int64_t x) {
|
||||
int64_t *box = janet_abstract(&it_s64_type, sizeof(int64_t));
|
||||
*box = (int64_t)x;
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
Janet janet_wrap_u64(uint64_t x) {
|
||||
uint64_t *box = janet_abstract(&it_u64_type, sizeof(uint64_t));
|
||||
*box = (uint64_t)x;
|
||||
return janet_wrap_abstract(box);
|
||||
}
|
||||
|
||||
static Janet cfun_it_s64_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_s64(janet_unwrap_s64(argv[0]));
|
||||
}
|
||||
|
||||
static Janet cfun_it_u64_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_u64(janet_unwrap_u64(argv[0]));
|
||||
}
|
||||
|
||||
#define OPMETHOD(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_abstract(&it_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[0]); \
|
||||
for (int i = 1; i < argc; i++) \
|
||||
*box oper##= janet_unwrap_##type(argv[i]); \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
\
|
||||
static Janet cfun_it_##type##_##name##_mut(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_getabstract(argv,0,&it_##type##_type); \
|
||||
for (int i = 1; i < argc; i++) \
|
||||
*box oper##= janet_unwrap_##type(argv[i]); \
|
||||
return janet_wrap_abstract(box); \
|
||||
}
|
||||
|
||||
#define DIVMETHOD(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_abstract(&it_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[0]); \
|
||||
for (int i = 1; i < argc; i++) { \
|
||||
T value = janet_unwrap_##type(argv[i]); \
|
||||
if (value == 0) janet_panic("division by zero"); \
|
||||
*box oper##= value; \
|
||||
} \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
\
|
||||
static Janet cfun_it_##type##_##name##_mut(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_getabstract(argv,0,&it_##type##_type); \
|
||||
for (int i = 1; i < argc; i++) { \
|
||||
T value = janet_unwrap_##type(argv[i]); \
|
||||
if (value == 0) janet_panic("division by zero"); \
|
||||
*box oper##= value; \
|
||||
} \
|
||||
return janet_wrap_abstract(box); \
|
||||
}
|
||||
|
||||
#define DIVMETHOD_SIGNED(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_abstract(&it_##type##_type, sizeof(T)); \
|
||||
*box = janet_unwrap_##type(argv[0]); \
|
||||
for (int i = 1; i < argc; i++) { \
|
||||
T value = janet_unwrap_##type(argv[i]); \
|
||||
if (value == 0) janet_panic("division by zero"); \
|
||||
if ((value == -1) && (*box == INT64_MIN)) janet_panic("INT64_MIN divided by -1"); \
|
||||
*box oper##= value; \
|
||||
} \
|
||||
return janet_wrap_abstract(box); \
|
||||
} \
|
||||
\
|
||||
static Janet cfun_it_##type##_##name##_mut(int32_t argc, Janet *argv) { \
|
||||
janet_arity(argc, 2, -1); \
|
||||
T *box = janet_getabstract(argv,0,&it_##type##_type); \
|
||||
for (int i = 1; i < argc; i++) { \
|
||||
T value = janet_unwrap_##type(argv[i]); \
|
||||
if (value == 0) janet_panic("division by zero"); \
|
||||
if ((value == -1) && (*box == INT64_MIN)) janet_panic("INT64_MIN divided by -1"); \
|
||||
*box oper##= value; \
|
||||
} \
|
||||
return janet_wrap_abstract(box); \
|
||||
}
|
||||
|
||||
#define COMPMETHOD(T, type, name, oper) \
|
||||
static Janet cfun_it_##type##_##name(int32_t argc, Janet *argv) { \
|
||||
janet_fixarity(argc, 2); \
|
||||
T v1 = janet_unwrap_##type(argv[0]); \
|
||||
T v2 = janet_unwrap_##type(argv[1]); \
|
||||
return janet_wrap_boolean(v1 oper v2); \
|
||||
}
|
||||
|
||||
OPMETHOD(int64_t, s64, add, +)
|
||||
OPMETHOD(int64_t, s64, sub, -)
|
||||
OPMETHOD(int64_t, s64, mul, *)
|
||||
DIVMETHOD_SIGNED(int64_t, s64, div, /)
|
||||
DIVMETHOD_SIGNED(int64_t, s64, mod, %)
|
||||
OPMETHOD(int64_t, s64, and, &)
|
||||
OPMETHOD(int64_t, s64, or, |)
|
||||
OPMETHOD(int64_t, s64, xor, ^)
|
||||
OPMETHOD(int64_t, s64, lshift, <<)
|
||||
OPMETHOD(int64_t, s64, rshift, >>)
|
||||
COMPMETHOD(int64_t, s64, lt, <)
|
||||
COMPMETHOD(int64_t, s64, gt, >)
|
||||
COMPMETHOD(int64_t, s64, le, <=)
|
||||
COMPMETHOD(int64_t, s64, ge, >=)
|
||||
COMPMETHOD(int64_t, s64, eq, ==)
|
||||
COMPMETHOD(int64_t, s64, ne, !=)
|
||||
|
||||
OPMETHOD(uint64_t, u64, add, +)
|
||||
OPMETHOD(uint64_t, u64, sub, -)
|
||||
OPMETHOD(uint64_t, u64, mul, *)
|
||||
DIVMETHOD(uint64_t, u64, div, /)
|
||||
DIVMETHOD(uint64_t, u64, mod, %)
|
||||
OPMETHOD(uint64_t, u64, and, &)
|
||||
OPMETHOD(uint64_t, u64, or, |)
|
||||
OPMETHOD(uint64_t, u64, xor, ^)
|
||||
OPMETHOD(uint64_t, u64, lshift, <<)
|
||||
OPMETHOD(uint64_t, u64, rshift, >>)
|
||||
COMPMETHOD(uint64_t, u64, lt, <)
|
||||
COMPMETHOD(uint64_t, u64, gt, >)
|
||||
COMPMETHOD(uint64_t, u64, le, <=)
|
||||
COMPMETHOD(uint64_t, u64, ge, >=)
|
||||
COMPMETHOD(uint64_t, u64, eq, ==)
|
||||
COMPMETHOD(uint64_t, u64, ne, !=)
|
||||
|
||||
#undef OPMETHOD
|
||||
#undef DIVMETHOD
|
||||
#undef DIVMETHOD_SIGNED
|
||||
#undef COMPMETHOD
|
||||
|
||||
static JanetMethod it_s64_methods[] = {
|
||||
{"+", cfun_it_s64_add},
|
||||
{"-", cfun_it_s64_sub},
|
||||
{"*", cfun_it_s64_mul},
|
||||
{"/", cfun_it_s64_div},
|
||||
{"%", cfun_it_s64_mod},
|
||||
{"<", cfun_it_s64_lt},
|
||||
{">", cfun_it_s64_gt},
|
||||
{"<=", cfun_it_s64_le},
|
||||
{">=", cfun_it_s64_ge},
|
||||
{"==", cfun_it_s64_eq},
|
||||
{"!=", cfun_it_s64_ne},
|
||||
{"&", cfun_it_s64_and},
|
||||
{"|", cfun_it_s64_or},
|
||||
{"^", cfun_it_s64_xor},
|
||||
{"<<", cfun_it_s64_lshift},
|
||||
{">>", cfun_it_s64_rshift},
|
||||
|
||||
{"+!", cfun_it_s64_add_mut},
|
||||
{"-!", cfun_it_s64_sub_mut},
|
||||
{"*!", cfun_it_s64_mul_mut},
|
||||
{"/!", cfun_it_s64_div_mut},
|
||||
{"%!", cfun_it_s64_mod_mut},
|
||||
{"&!", cfun_it_s64_and_mut},
|
||||
{"|!", cfun_it_s64_or_mut},
|
||||
{"^!", cfun_it_s64_xor_mut},
|
||||
{"<<!", cfun_it_s64_lshift_mut},
|
||||
{">>!", cfun_it_s64_rshift_mut},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static JanetMethod it_u64_methods[] = {
|
||||
{"+", cfun_it_u64_add},
|
||||
{"-", cfun_it_u64_sub},
|
||||
{"*", cfun_it_u64_mul},
|
||||
{"/", cfun_it_u64_div},
|
||||
{"%", cfun_it_u64_mod},
|
||||
{"<", cfun_it_u64_lt},
|
||||
{">", cfun_it_u64_gt},
|
||||
{"<=", cfun_it_u64_le},
|
||||
{">=", cfun_it_u64_ge},
|
||||
{"==", cfun_it_u64_eq},
|
||||
{"!=", cfun_it_u64_ne},
|
||||
{"&", cfun_it_u64_and},
|
||||
{"|", cfun_it_u64_or},
|
||||
{"^", cfun_it_u64_xor},
|
||||
{"<<", cfun_it_u64_lshift},
|
||||
{">>", cfun_it_u64_rshift},
|
||||
|
||||
{"+!", cfun_it_u64_add_mut},
|
||||
{"-!", cfun_it_u64_sub_mut},
|
||||
{"*!", cfun_it_u64_mul_mut},
|
||||
{"/!", cfun_it_u64_div_mut},
|
||||
{"%!", cfun_it_u64_mod_mut},
|
||||
{"&!", cfun_it_u64_and_mut},
|
||||
{"|!", cfun_it_u64_or_mut},
|
||||
{"^!", cfun_it_u64_xor_mut},
|
||||
{"<<!", cfun_it_u64_lshift_mut},
|
||||
{">>!", cfun_it_u64_rshift_mut},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static Janet it_s64_get(void *p, Janet key) {
|
||||
(void) p;
|
||||
if (!janet_checktype(key, JANET_KEYWORD))
|
||||
janet_panicf("expected keyword, got %v", key);
|
||||
return janet_getmethod(janet_unwrap_keyword(key), it_s64_methods);
|
||||
}
|
||||
|
||||
static Janet it_u64_get(void *p, Janet key) {
|
||||
(void) p;
|
||||
if (!janet_checktype(key, JANET_KEYWORD))
|
||||
janet_panicf("expected keyword, got %v", key);
|
||||
return janet_getmethod(janet_unwrap_keyword(key), it_u64_methods);
|
||||
}
|
||||
|
||||
static const JanetReg it_cfuns[] = {
|
||||
{
|
||||
"int/s64", cfun_it_s64_new,
|
||||
JDOC("(int/s64 value)\n\n"
|
||||
"Create a boxed signed 64 bit integer from a string value.")
|
||||
},
|
||||
{
|
||||
"int/u64", cfun_it_u64_new,
|
||||
JDOC("(int/u64 value)\n\n"
|
||||
"Create a boxed unsigned 64 bit integer from a string value.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_inttypes(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, it_cfuns);
|
||||
janet_register_abstract_type(&it_s64_type);
|
||||
janet_register_abstract_type(&it_u64_type);
|
||||
}
|
||||
|
||||
#endif
|
||||
590
src/core/io.c
590
src/core/io.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 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,9 +25,17 @@
|
||||
#define _BSD_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <janet/janet.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#ifndef JANET_WINDOWS
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
#define IO_WRITE 1
|
||||
#define IO_READ 2
|
||||
#define IO_APPEND 4
|
||||
@@ -44,22 +52,31 @@ struct IOFile {
|
||||
int flags;
|
||||
};
|
||||
|
||||
static int janet_io_gc(void *p, size_t len);
|
||||
static int cfun_io_gc(void *p, size_t len);
|
||||
static Janet io_file_get(void *p, Janet);
|
||||
|
||||
JanetAbstractType janet_io_filetype = {
|
||||
":core.file",
|
||||
janet_io_gc,
|
||||
JanetAbstractType cfun_io_filetype = {
|
||||
"core/file",
|
||||
cfun_io_gc,
|
||||
NULL,
|
||||
io_file_get,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Check argupments to fopen */
|
||||
static int checkflags(const uint8_t *str, int32_t len) {
|
||||
/* Check arguments to fopen */
|
||||
static int checkflags(const uint8_t *str) {
|
||||
int flags = 0;
|
||||
int32_t i;
|
||||
if (!len || len > 3) return -1;
|
||||
int32_t len = janet_string_length(str);
|
||||
if (!len || len > 3)
|
||||
janet_panic("file mode must have a length between 1 and 3");
|
||||
switch (*str) {
|
||||
default:
|
||||
return -1;
|
||||
janet_panicf("invalid flag %c, expected w, a, or r", *str);
|
||||
break;
|
||||
case 'w':
|
||||
flags |= IO_WRITE;
|
||||
break;
|
||||
@@ -73,7 +90,8 @@ static int checkflags(const uint8_t *str, int32_t len) {
|
||||
for (i = 1; i < len; i++) {
|
||||
switch (str[i]) {
|
||||
default:
|
||||
return -1;
|
||||
janet_panicf("invalid flag %c, expected + or b", str[i]);
|
||||
break;
|
||||
case '+':
|
||||
if (flags & IO_UPDATE) return -1;
|
||||
flags |= IO_UPDATE;
|
||||
@@ -87,223 +105,201 @@ static int checkflags(const uint8_t *str, int32_t len) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
/* Check file argument */
|
||||
static IOFile *checkfile(JanetArgs args, int32_t n) {
|
||||
IOFile *iof;
|
||||
if (n >= args.n) {
|
||||
*args.ret = janet_cstringv("expected core.file");
|
||||
return NULL;
|
||||
}
|
||||
if (!janet_checktype(args.v[n], JANET_ABSTRACT)) {
|
||||
*args.ret = janet_cstringv("expected core.file");
|
||||
return NULL;
|
||||
}
|
||||
iof = (IOFile *) janet_unwrap_abstract(args.v[n]);
|
||||
if (janet_abstract_type(iof) != &janet_io_filetype) {
|
||||
*args.ret = janet_cstringv("expected core.file");
|
||||
return NULL;
|
||||
}
|
||||
return iof;
|
||||
}
|
||||
|
||||
/* Check buffer argument */
|
||||
static JanetBuffer *checkbuffer(JanetArgs args, int32_t n, int optional) {
|
||||
if (optional && n == args.n) {
|
||||
return janet_buffer(0);
|
||||
}
|
||||
if (n >= args.n) {
|
||||
*args.ret = janet_cstringv("expected buffer");
|
||||
return NULL;
|
||||
}
|
||||
if (!janet_checktype(args.v[n], JANET_BUFFER)) {
|
||||
*args.ret = janet_cstringv("expected buffer");
|
||||
return NULL;
|
||||
}
|
||||
return janet_unwrap_abstract(args.v[n]);
|
||||
}
|
||||
|
||||
static Janet makef(FILE *f, int flags) {
|
||||
IOFile *iof = (IOFile *) janet_abstract(&janet_io_filetype, sizeof(IOFile));
|
||||
IOFile *iof = (IOFile *) janet_abstract(&cfun_io_filetype, sizeof(IOFile));
|
||||
iof->file = f;
|
||||
iof->flags = flags;
|
||||
return janet_wrap_abstract(iof);
|
||||
}
|
||||
|
||||
/* Open a process */
|
||||
static int janet_io_popen(JanetArgs args) {
|
||||
const uint8_t *fname, *fmode;
|
||||
int32_t modelen;
|
||||
FILE *f;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
janet_panic("not implemented on this platform");
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
#else
|
||||
static Janet cfun_io_popen(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
const uint8_t *fname = janet_getstring(argv, 0);
|
||||
const uint8_t *fmode = NULL;
|
||||
int flags;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_MAXARITY(args, 2);
|
||||
JANET_ARG_STRING(fname, args, 0);
|
||||
if (args.n == 2) {
|
||||
if (!janet_checktype(args.v[1], JANET_STRING) &&
|
||||
!janet_checktype(args.v[1], JANET_SYMBOL))
|
||||
JANET_THROW(args, "expected string mode");
|
||||
fmode = janet_unwrap_string(args.v[1]);
|
||||
modelen = janet_string_length(fmode);
|
||||
if (argc == 2) {
|
||||
fmode = janet_getkeyword(argv, 1);
|
||||
if (janet_string_length(fmode) != 1 ||
|
||||
!(fmode[0] == 'r' || fmode[0] == 'w')) {
|
||||
janet_panicf("invalid file mode :%S, expected :r or :w", fmode);
|
||||
}
|
||||
flags = IO_PIPED | (fmode[0] == 'r' ? IO_READ : IO_WRITE);
|
||||
} else {
|
||||
fmode = (const uint8_t *)"r";
|
||||
modelen = 1;
|
||||
flags = IO_PIPED | IO_READ;
|
||||
}
|
||||
if (fmode[0] == ':') {
|
||||
fmode++;
|
||||
modelen--;
|
||||
}
|
||||
if (modelen != 1 || !(fmode[0] == 'r' || fmode[0] == 'w')) {
|
||||
JANET_THROW(args, "invalid file mode");
|
||||
}
|
||||
flags = (fmode[0] == 'r') ? IO_PIPED | IO_READ : IO_PIPED | IO_WRITE;
|
||||
#ifdef JANET_WINDOWS
|
||||
#define popen _popen
|
||||
#endif
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#define popen(A, B) (errno = 0, NULL)
|
||||
#endif
|
||||
f = popen((const char *)fname, (const char *)fmode);
|
||||
FILE *f = popen((const char *)fname, (const char *)fmode);
|
||||
if (!f) {
|
||||
if (errno == EMFILE) {
|
||||
JANET_THROW(args, "too many streams are open");
|
||||
}
|
||||
JANET_THROW(args, "could not open file");
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
JANET_RETURN(args, makef(f, flags));
|
||||
return makef(f, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Open a a file and return a userdata wrapper around the C file API. */
|
||||
static int janet_io_fopen(JanetArgs args) {
|
||||
const uint8_t *fname, *fmode;
|
||||
int32_t modelen;
|
||||
FILE *f;
|
||||
static Janet cfun_io_fopen(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
const uint8_t *fname = janet_getstring(argv, 0);
|
||||
const uint8_t *fmode;
|
||||
int flags;
|
||||
JANET_MINARITY(args, 1);
|
||||
JANET_MAXARITY(args, 2);
|
||||
JANET_ARG_STRING(fname, args, 0);
|
||||
if (args.n == 2) {
|
||||
if (!janet_checktype(args.v[1], JANET_STRING) &&
|
||||
!janet_checktype(args.v[1], JANET_SYMBOL))
|
||||
JANET_THROW(args, "expected string mode");
|
||||
fmode = janet_unwrap_string(args.v[1]);
|
||||
modelen = janet_string_length(fmode);
|
||||
if (argc == 2) {
|
||||
fmode = janet_getkeyword(argv, 1);
|
||||
flags = checkflags(fmode);
|
||||
} else {
|
||||
fmode = (const uint8_t *)"r";
|
||||
modelen = 1;
|
||||
flags = IO_READ;
|
||||
}
|
||||
if (fmode[0] == ':') {
|
||||
fmode++;
|
||||
modelen--;
|
||||
}
|
||||
if ((flags = checkflags(fmode, modelen)) < 0) {
|
||||
JANET_THROW(args, "invalid file mode");
|
||||
}
|
||||
f = fopen((const char *)fname, (const char *)fmode);
|
||||
JANET_RETURN(args, f ? makef(f, flags) : janet_wrap_nil());
|
||||
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
||||
return f ? makef(f, flags) : janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Read up to n bytes into buffer. Return error string if error. */
|
||||
static const char *read_chunk(IOFile *iof, JanetBuffer *buffer, int32_t nBytesMax) {
|
||||
static Janet cfun_io_fdopen(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 2);
|
||||
const int fd = janet_getinteger(argv, 0);
|
||||
const uint8_t *fmode;
|
||||
int flags;
|
||||
if (argc == 2) {
|
||||
fmode = janet_getkeyword(argv, 1);
|
||||
flags = checkflags(fmode);
|
||||
} else {
|
||||
fmode = (const uint8_t *)"r";
|
||||
flags = IO_READ;
|
||||
}
|
||||
#ifdef JANET_WINDOWS
|
||||
#define fdopen _fdopen
|
||||
#endif
|
||||
FILE *f = fdopen(fd, (const char *)fmode);
|
||||
return f ? makef(f, flags) : janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_io_fileno(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
#ifdef JANET_WINDOWS
|
||||
#define fileno _fileno
|
||||
#endif
|
||||
return janet_wrap_integer(fileno(iof->file));
|
||||
}
|
||||
|
||||
/* Read up to n bytes into buffer. */
|
||||
static void read_chunk(IOFile *iof, JanetBuffer *buffer, int32_t nBytesMax) {
|
||||
if (!(iof->flags & (IO_READ | IO_UPDATE)))
|
||||
return "file is not readable";
|
||||
/* Ensure buffer size */
|
||||
if (janet_buffer_extra(buffer, nBytesMax))
|
||||
return "buffer overflow";
|
||||
janet_panic("file is not readable");
|
||||
janet_buffer_extra(buffer, nBytesMax);
|
||||
size_t ntoread = nBytesMax;
|
||||
size_t nread = fread((char *)(buffer->data + buffer->count), 1, ntoread, iof->file);
|
||||
if (nread != ntoread && ferror(iof->file))
|
||||
return "could not read file";
|
||||
janet_panic("could not read file");
|
||||
buffer->count += (int32_t) nread;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Read a certain number of bytes into memory */
|
||||
static int janet_io_fread(JanetArgs args) {
|
||||
JanetBuffer *b;
|
||||
IOFile *iof = checkfile(args, 0);
|
||||
if (!iof) return 1;
|
||||
if (iof->flags & IO_CLOSED)
|
||||
JANET_THROW(args, "file is closed");
|
||||
b = checkbuffer(args, 2, 1);
|
||||
if (!b) return 1;
|
||||
if (janet_checktype(args.v[1], JANET_SYMBOL)) {
|
||||
const uint8_t *sym = janet_unwrap_symbol(args.v[1]);
|
||||
if (!janet_cstrcmp(sym, ":all")) {
|
||||
static Janet cfun_io_fread(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 3);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED) janet_panic("file is closed");
|
||||
JanetBuffer *buffer;
|
||||
if (argc == 2) {
|
||||
buffer = janet_buffer(0);
|
||||
} else {
|
||||
buffer = janet_getbuffer(argv, 2);
|
||||
}
|
||||
int32_t bufstart = buffer->count;
|
||||
if (janet_checktype(argv[1], JANET_KEYWORD)) {
|
||||
const uint8_t *sym = janet_unwrap_keyword(argv[1]);
|
||||
if (!janet_cstrcmp(sym, "all")) {
|
||||
/* Read whole file */
|
||||
int status = fseek(iof->file, 0, SEEK_SET);
|
||||
if (status) {
|
||||
/* backwards fseek did not work (stream like popen) */
|
||||
int32_t sizeBefore;
|
||||
do {
|
||||
sizeBefore = b->count;
|
||||
const char *maybeErr = read_chunk(iof, b, 1024);
|
||||
if (maybeErr) JANET_THROW(args, maybeErr);
|
||||
} while (sizeBefore < b->count);
|
||||
sizeBefore = buffer->count;
|
||||
read_chunk(iof, buffer, 1024);
|
||||
} while (sizeBefore < buffer->count);
|
||||
} else {
|
||||
fseek(iof->file, 0, SEEK_END);
|
||||
long fsize = ftell(iof->file);
|
||||
if (fsize < 0) {
|
||||
janet_panicf("could not get file size of %v", argv[0]);
|
||||
}
|
||||
if (fsize > (INT32_MAX)) {
|
||||
janet_panic("file to large to read into buffer");
|
||||
}
|
||||
fseek(iof->file, 0, SEEK_SET);
|
||||
if (fsize > INT32_MAX) JANET_THROW(args, "buffer overflow");
|
||||
const char *maybeErr = read_chunk(iof, b, (int32_t) fsize);;
|
||||
if (maybeErr) JANET_THROW(args, maybeErr);
|
||||
read_chunk(iof, buffer, (int32_t) fsize);
|
||||
}
|
||||
} else if (!janet_cstrcmp(sym, ":line")) {
|
||||
/* Never return nil for :all */
|
||||
return janet_wrap_buffer(buffer);
|
||||
} else if (!janet_cstrcmp(sym, "line")) {
|
||||
for (;;) {
|
||||
int x = fgetc(iof->file);
|
||||
if (x != EOF && janet_buffer_push_u8(b, (uint8_t)x))
|
||||
JANET_THROW(args, "buffer overflow");
|
||||
if (x != EOF) janet_buffer_push_u8(buffer, (uint8_t)x);
|
||||
if (x == EOF || x == '\n') break;
|
||||
}
|
||||
} else {
|
||||
JANET_THROW(args, "expected one of :all, :line");
|
||||
janet_panicf("expected one of :all, :line, got %v", argv[1]);
|
||||
}
|
||||
} else if (!janet_checktype(args.v[1], JANET_INTEGER)) {
|
||||
JANET_THROW(args, "expected positive integer");
|
||||
} else {
|
||||
int32_t len = janet_unwrap_integer(args.v[1]);
|
||||
if (len < 0) JANET_THROW(args, "expected positive integer");
|
||||
const char *maybeErr = read_chunk(iof, b, len);
|
||||
if (maybeErr) JANET_THROW(args, maybeErr);
|
||||
int32_t len = janet_getinteger(argv, 1);
|
||||
if (len < 0) janet_panic("expected positive integer");
|
||||
read_chunk(iof, buffer, len);
|
||||
}
|
||||
JANET_RETURN(args, janet_wrap_buffer(b));
|
||||
if (bufstart == buffer->count) return janet_wrap_nil();
|
||||
return janet_wrap_buffer(buffer);
|
||||
}
|
||||
|
||||
/* Write bytes to a file */
|
||||
static int janet_io_fwrite(JanetArgs args) {
|
||||
int32_t len, i;
|
||||
const uint8_t *str;
|
||||
IOFile *iof = checkfile(args, 0);
|
||||
if (!iof) return 1;
|
||||
static Janet cfun_io_fwrite(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, -1);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
JANET_THROW(args, "file is closed");
|
||||
janet_panic("file is closed");
|
||||
if (!(iof->flags & (IO_WRITE | IO_APPEND | IO_UPDATE)))
|
||||
JANET_THROW(args, "file is not writeable");
|
||||
for (i = 1; i < args.n; i++) {
|
||||
JANET_CHECKMANY(args, i, JANET_TFLAG_BYTES);
|
||||
}
|
||||
for (i = 1; i < args.n; i++) {
|
||||
JANET_ARG_BYTES(str, len, args, i);
|
||||
if (len) {
|
||||
if (!fwrite(str, len, 1, iof->file)) JANET_THROW(args, "error writing to file");
|
||||
janet_panic("file is not writeable");
|
||||
int32_t i;
|
||||
/* Verify all arguments before writing to file */
|
||||
for (i = 1; i < argc; i++)
|
||||
janet_getbytes(argv, i);
|
||||
for (i = 1; i < argc; i++) {
|
||||
JanetByteView view = janet_getbytes(argv, i);
|
||||
if (view.len) {
|
||||
if (!fwrite(view.bytes, view.len, 1, iof->file)) {
|
||||
janet_panic("error writing to file");
|
||||
}
|
||||
}
|
||||
}
|
||||
JANET_RETURN(args, janet_wrap_abstract(iof));
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
/* Flush the bytes in the file */
|
||||
static int janet_io_fflush(JanetArgs args) {
|
||||
IOFile *iof = checkfile(args, 0);
|
||||
if (!iof) return 1;
|
||||
static Janet cfun_io_fflush(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
JANET_THROW(args, "file is closed");
|
||||
janet_panic("file is closed");
|
||||
if (!(iof->flags & (IO_WRITE | IO_APPEND | IO_UPDATE)))
|
||||
JANET_THROW(args, "file is not flushable");
|
||||
if (fflush(iof->file)) JANET_THROW(args, "could not flush file");
|
||||
JANET_RETURN(args, janet_wrap_abstract(iof));
|
||||
janet_panic("file is not writeable");
|
||||
if (fflush(iof->file))
|
||||
janet_panic("could not flush file");
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
/* Cleanup a file */
|
||||
static int janet_io_gc(void *p, size_t len) {
|
||||
static int cfun_io_gc(void *p, size_t len) {
|
||||
(void) len;
|
||||
IOFile *iof = (IOFile *)p;
|
||||
if (!(iof->flags & (IO_NOT_CLOSEABLE | IO_CLOSED))) {
|
||||
@@ -313,139 +309,213 @@ static int janet_io_gc(void *p, size_t len) {
|
||||
}
|
||||
|
||||
/* Close a file */
|
||||
static int janet_io_fclose(JanetArgs args) {
|
||||
IOFile *iof = checkfile(args, 0);
|
||||
if (!iof) return 1;
|
||||
if (iof->flags & (IO_CLOSED))
|
||||
JANET_THROW(args, "file already closed");
|
||||
static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
if (iof->flags & (IO_NOT_CLOSEABLE))
|
||||
JANET_THROW(args, "file not closable");
|
||||
janet_panic("file not closable");
|
||||
if (iof->flags & IO_PIPED) {
|
||||
#ifdef JANET_WINDOWS
|
||||
#define pclose _pclose
|
||||
#define WEXITSTATUS(x) x
|
||||
#endif
|
||||
if (pclose(iof->file)) JANET_THROW(args, "could not close file");
|
||||
int status = pclose(iof->file);
|
||||
iof->flags |= IO_CLOSED;
|
||||
if (status == -1) janet_panic("could not close file");
|
||||
return janet_wrap_integer(WEXITSTATUS(status));
|
||||
} else {
|
||||
if (fclose(iof->file)) JANET_THROW(args, "could not close file");
|
||||
if (fclose(iof->file)) janet_panic("could not close file");
|
||||
iof->flags |= IO_CLOSED;
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
iof->flags |= IO_CLOSED;
|
||||
JANET_RETURN(args, janet_wrap_abstract(iof));
|
||||
}
|
||||
|
||||
/* Seek a file */
|
||||
static int janet_io_fseek(JanetArgs args) {
|
||||
static Janet cfun_io_fseek(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 3);
|
||||
IOFile *iof = janet_getabstract(argv, 0, &cfun_io_filetype);
|
||||
if (iof->flags & IO_CLOSED)
|
||||
janet_panic("file is closed");
|
||||
long int offset = 0;
|
||||
int whence = SEEK_CUR;
|
||||
IOFile *iof = checkfile(args, 0);
|
||||
if (!iof) return 1;
|
||||
if (iof->flags & IO_CLOSED)
|
||||
JANET_THROW(args, "file is closed");
|
||||
if (args.n >= 2) {
|
||||
const uint8_t *whence_sym;
|
||||
if (!janet_checktype(args.v[1], JANET_SYMBOL))
|
||||
JANET_THROW(args, "expected symbol");
|
||||
whence_sym = janet_unwrap_symbol(args.v[1]);
|
||||
if (!janet_cstrcmp(whence_sym, ":cur")) {
|
||||
if (argc >= 2) {
|
||||
const uint8_t *whence_sym = janet_getkeyword(argv, 1);
|
||||
if (!janet_cstrcmp(whence_sym, "cur")) {
|
||||
whence = SEEK_CUR;
|
||||
} else if (!janet_cstrcmp(whence_sym, ":set")) {
|
||||
} else if (!janet_cstrcmp(whence_sym, "set")) {
|
||||
whence = SEEK_SET;
|
||||
} else if (!janet_cstrcmp(whence_sym, ":end")) {
|
||||
} else if (!janet_cstrcmp(whence_sym, "end")) {
|
||||
whence = SEEK_END;
|
||||
} else {
|
||||
JANET_THROW(args, "expected one of :cur, :set, :end");
|
||||
janet_panicf("expected one of :cur, :set, :end, got %v", argv[1]);
|
||||
}
|
||||
if (args.n >= 3) {
|
||||
double doffset;
|
||||
JANET_ARG_NUMBER(doffset, args, 2);
|
||||
offset = (long int)doffset;
|
||||
if (argc == 3) {
|
||||
offset = (long) janet_getinteger64(argv, 2);
|
||||
}
|
||||
}
|
||||
if (fseek(iof->file, offset, whence))
|
||||
JANET_THROW(args, "error seeking file");
|
||||
JANET_RETURN(args, args.v[0]);
|
||||
if (fseek(iof->file, offset, whence)) janet_panic("error seeking file");
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"file/open", janet_io_fopen,
|
||||
"(file/open path [,mode])\n\n"
|
||||
"Open a file. path is files absolute or relative path, and "
|
||||
"mode is a set of flags indicating the mode to open the file in. "
|
||||
"mode is a keyword where each character represents a flag. If the file "
|
||||
"cannot be opened, returns nil, otherwise returns the new file handle. "
|
||||
"Mode flags:\n\n"
|
||||
"\tr - allow reading from the file\n"
|
||||
"\tw - allow witing to the file\n"
|
||||
"\ta - append to the file\n"
|
||||
"\tb - open the file in binary mode (rather than text mode)\n"
|
||||
"\t+ - append to the file instead of overwriting it"
|
||||
static JanetMethod io_file_methods[] = {
|
||||
{"close", cfun_io_fclose},
|
||||
{"read", cfun_io_fread},
|
||||
{"write", cfun_io_fwrite},
|
||||
{"flush", cfun_io_fflush},
|
||||
{"seek", cfun_io_fseek},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static Janet io_file_get(void *p, Janet key) {
|
||||
(void) p;
|
||||
if (!janet_checktype(key, JANET_KEYWORD))
|
||||
janet_panicf("expected keyword, got %v", key);
|
||||
return janet_getmethod(janet_unwrap_keyword(key), io_file_methods);
|
||||
}
|
||||
|
||||
FILE *janet_dynfile(const char *name, FILE *def) {
|
||||
Janet x = janet_dyn(name);
|
||||
if (!janet_checktype(x, JANET_ABSTRACT)) return def;
|
||||
void *abstract = janet_unwrap_abstract(x);
|
||||
if (janet_abstract_type(abstract) != &cfun_io_filetype) return def;
|
||||
IOFile *iofile = abstract;
|
||||
return iofile->file;
|
||||
}
|
||||
|
||||
static Janet cfun_io_print(int32_t argc, Janet *argv) {
|
||||
FILE *f = janet_dynfile("out", stdout);
|
||||
for (int32_t i = 0; i < argc; ++i) {
|
||||
int32_t j, len;
|
||||
const uint8_t *vstr = janet_to_string(argv[i]);
|
||||
len = janet_string_length(vstr);
|
||||
for (j = 0; j < len; ++j) {
|
||||
putc(vstr[j], f);
|
||||
}
|
||||
}
|
||||
putc('\n', f);
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static const JanetReg io_cfuns[] = {
|
||||
{
|
||||
"print", cfun_io_print,
|
||||
JDOC("(print & xs)\n\n"
|
||||
"Print values to the console (standard out). Value are converted "
|
||||
"to strings if they are not already. After printing all values, a "
|
||||
"newline character is printed. Returns nil.")
|
||||
},
|
||||
{"file/close", janet_io_fclose,
|
||||
"(file/close f)\n\n"
|
||||
"Close a file and release all related resources. When you are "
|
||||
"done reading a file, close it to prevent a resource leak and let "
|
||||
"other processes read the file."
|
||||
{
|
||||
"file/open", cfun_io_fopen,
|
||||
JDOC("(file/open path &opt mode)\n\n"
|
||||
"Open a file. path is an absolute or relative path, and "
|
||||
"mode is a set of flags indicating the mode to open the file in. "
|
||||
"mode is a keyword where each character represents a flag. If the file "
|
||||
"cannot be opened, returns nil, otherwise returns the new file handle. "
|
||||
"Mode flags:\n\n"
|
||||
"\tr - allow reading from the file\n"
|
||||
"\tw - allow writing to the file\n"
|
||||
"\ta - append to the file\n"
|
||||
"\tb - open the file in binary mode (rather than text mode)\n"
|
||||
"\t+ - append to the file instead of overwriting it")
|
||||
},
|
||||
{"file/read", janet_io_fread,
|
||||
"(file/read f what [,buf])\n\n"
|
||||
"Read a number of bytes from a file into a buffer. A buffer can "
|
||||
"be provided as an optional fourth argument. otherwise a new buffer "
|
||||
"is created. 'what' can either be an integer or a keyword. Returns the "
|
||||
"buffer with file contents. "
|
||||
"Values for 'what':\n\n"
|
||||
"\t:all - read the whole file\n"
|
||||
"\t:line - read up to and including the next newline character\n"
|
||||
"\tn (integer) - read up to n bytes from the file"
|
||||
{
|
||||
"file/fdopen", cfun_io_fdopen,
|
||||
JDOC("(file/fdopen fd &opt mode)\n\n"
|
||||
"Create a file from an fd. fd is a platform specific file descriptor, and "
|
||||
"mode is a set of flags indicating the mode to open the file in. "
|
||||
"mode is a keyword where each character represents a flag. If the file "
|
||||
"cannot be opened, returns nil, otherwise returns the new file handle. "
|
||||
"Mode flags:\n\n"
|
||||
"\tr - allow reading from the file\n"
|
||||
"\tw - allow writing to the file\n"
|
||||
"\ta - append to the file\n"
|
||||
"\tb - open the file in binary mode (rather than text mode)\n"
|
||||
"\t+ - append to the file instead of overwriting it")
|
||||
},
|
||||
{"file/write", janet_io_fwrite,
|
||||
"(file/write f bytes)\n\n"
|
||||
"Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
|
||||
"file"
|
||||
{
|
||||
"file/fileno", cfun_io_fileno,
|
||||
JDOC("(file/fileno f)\n\n"
|
||||
"Return the underlying file descriptor for the file as a number."
|
||||
"The meaning of this number is platform specific.")
|
||||
},
|
||||
{"file/flush", janet_io_fflush,
|
||||
"(file/flush f)\n\n"
|
||||
"Flush any buffered bytes to the filesystem. In most files, writes are "
|
||||
"buffered for efficiency reasons. Returns the file handle."
|
||||
{
|
||||
"file/close", cfun_io_fclose,
|
||||
JDOC("(file/close f)\n\n"
|
||||
"Close a file and release all related resources. When you are "
|
||||
"done reading a file, close it to prevent a resource leak and let "
|
||||
"other processes read the file.")
|
||||
},
|
||||
{"file/seek", janet_io_fseek,
|
||||
"(file/seek f [,whence [,n]])\n\n"
|
||||
"Jump to a relative location in the file. 'whence' must be one of\n\n"
|
||||
"\t:cur - jump relative to the current file location\n"
|
||||
"\t:set - jump relative to the beginning of the file\n"
|
||||
"\t:end - jump relative to the end of the file\n\n"
|
||||
"By default, 'whence' is :cur. Optionally a value n may be passed "
|
||||
"for the relative number of bytes to seek in the file. n may be a real "
|
||||
"number to handle large files of more the 4GB. Returns the file handle."
|
||||
{
|
||||
"file/read", cfun_io_fread,
|
||||
JDOC("(file/read f what &opt buf)\n\n"
|
||||
"Read a number of bytes from a file into a buffer. A buffer can "
|
||||
"be provided as an optional fourth argument, otherwise a new buffer "
|
||||
"is created. 'what' can either be an integer or a keyword. Returns the "
|
||||
"buffer with file contents. "
|
||||
"Values for 'what':\n\n"
|
||||
"\t:all - read the whole file\n"
|
||||
"\t:line - read up to and including the next newline character\n"
|
||||
"\tn (integer) - read up to n bytes from the file")
|
||||
},
|
||||
{"file/popen", janet_io_popen,
|
||||
"(file/popen path [,mode])\n\n"
|
||||
"Open a file that is backed by a process. The file must be opened in either "
|
||||
"the :r (read) or the :w (write) mode. In :r mode, the stdout of the "
|
||||
"process can be read from the file. In :w mode, the stdin of the process "
|
||||
"can be written to. Returns the new file."
|
||||
{
|
||||
"file/write", cfun_io_fwrite,
|
||||
JDOC("(file/write f bytes)\n\n"
|
||||
"Writes to a file. 'bytes' must be string, buffer, or symbol. Returns the "
|
||||
"file.")
|
||||
},
|
||||
{
|
||||
"file/flush", cfun_io_fflush,
|
||||
JDOC("(file/flush f)\n\n"
|
||||
"Flush any buffered bytes to the file system. In most files, writes are "
|
||||
"buffered for efficiency reasons. Returns the file handle.")
|
||||
},
|
||||
{
|
||||
"file/seek", cfun_io_fseek,
|
||||
JDOC("(file/seek f &opt whence n)\n\n"
|
||||
"Jump to a relative location in the file. 'whence' must be one of\n\n"
|
||||
"\t:cur - jump relative to the current file location\n"
|
||||
"\t:set - jump relative to the beginning of the file\n"
|
||||
"\t:end - jump relative to the end of the file\n\n"
|
||||
"By default, 'whence' is :cur. Optionally a value n may be passed "
|
||||
"for the relative number of bytes to seek in the file. n may be a real "
|
||||
"number to handle large files of more the 4GB. Returns the file handle.")
|
||||
},
|
||||
{
|
||||
"file/popen", cfun_io_popen,
|
||||
JDOC("(file/popen path &opt mode)\n\n"
|
||||
"Open a file that is backed by a process. The file must be opened in either "
|
||||
"the :r (read) or the :w (write) mode. In :r mode, the stdout of the "
|
||||
"process can be read from the file. In :w mode, the stdin of the process "
|
||||
"can be written to. Returns the new file.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* C API */
|
||||
|
||||
FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) {
|
||||
IOFile *iof = janet_getabstract(argv, n, &cfun_io_filetype);
|
||||
if (NULL != flags) *flags = iof->flags;
|
||||
return iof->file;
|
||||
}
|
||||
|
||||
/* Module entry point */
|
||||
int janet_lib_io(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
void janet_lib_io(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, io_cfuns);
|
||||
|
||||
/* stdout */
|
||||
janet_def(env, "stdout",
|
||||
makef(stdout, IO_APPEND | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
"The standard output file.");
|
||||
|
||||
|
||||
janet_core_def(env, "stdout",
|
||||
makef(stdout, IO_APPEND | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
JDOC("The standard output file."));
|
||||
/* stderr */
|
||||
janet_def(env, "stderr",
|
||||
makef(stderr, IO_APPEND | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
"The standard error file.");
|
||||
|
||||
janet_core_def(env, "stderr",
|
||||
makef(stderr, IO_APPEND | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
JDOC("The standard error file."));
|
||||
/* stdin */
|
||||
janet_def(env, "stdin",
|
||||
makef(stdin, IO_READ | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
"The standard input file.");
|
||||
janet_core_def(env, "stdin",
|
||||
makef(stdin, IO_READ | IO_NOT_CLOSEABLE | IO_SERIALIZABLE),
|
||||
JDOC("The standard input file."));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
1179
src/core/marsh.c
1179
src/core/marsh.c
File diff suppressed because it is too large
Load Diff
270
src/core/math.c
270
src/core/math.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,79 +20,41 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Get a random number */
|
||||
int janet_rand(JanetArgs args) {
|
||||
JANET_FIXARITY(args, 0);
|
||||
static Janet janet_rand(int32_t argc, Janet *argv) {
|
||||
(void) argv;
|
||||
janet_fixarity(argc, 0);
|
||||
double r = (rand() % RAND_MAX) / ((double) RAND_MAX);
|
||||
JANET_RETURN_REAL(args, r);
|
||||
return janet_wrap_number(r);
|
||||
}
|
||||
|
||||
/* Seed the random number generator */
|
||||
int janet_srand(JanetArgs args) {
|
||||
int32_t x = 0;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_INTEGER(x, args, 0);
|
||||
static Janet janet_srand(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t x = janet_getinteger(argv, 0);
|
||||
srand((unsigned) x);
|
||||
return 0;
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Convert a number to an integer */
|
||||
int janet_int(JanetArgs args) {
|
||||
JANET_FIXARITY(args, 1);
|
||||
switch (janet_type(args.v[0])) {
|
||||
default:
|
||||
JANET_THROW(args, "could not convert to integer");
|
||||
case JANET_REAL:
|
||||
*args.ret = janet_wrap_integer((int32_t) janet_unwrap_real(args.v[0]));
|
||||
break;
|
||||
case JANET_INTEGER:
|
||||
*args.ret = args.v[0];
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Convert a number to a real number */
|
||||
int janet_real(JanetArgs args) {
|
||||
JANET_FIXARITY(args, 1);
|
||||
switch (janet_type(args.v[0])) {
|
||||
default:
|
||||
JANET_THROW(args, "could not convert to real");
|
||||
case JANET_REAL:
|
||||
*args.ret = args.v[0];
|
||||
break;
|
||||
case JANET_INTEGER:
|
||||
*args.ret = janet_wrap_real((double) janet_unwrap_integer(args.v[0]));
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int janet_remainder(JanetArgs args) {
|
||||
JANET_FIXARITY(args, 2);
|
||||
if (janet_checktype(args.v[0], JANET_INTEGER) &&
|
||||
janet_checktype(args.v[1], JANET_INTEGER)) {
|
||||
int32_t x, y;
|
||||
x = janet_unwrap_integer(args.v[0]);
|
||||
y = janet_unwrap_integer(args.v[1]);
|
||||
JANET_RETURN_INTEGER(args, x % y);
|
||||
} else {
|
||||
double x, y;
|
||||
JANET_ARG_NUMBER(x, args, 0);
|
||||
JANET_ARG_NUMBER(y, args, 1);
|
||||
JANET_RETURN_REAL(args, fmod(x, y));
|
||||
}
|
||||
static Janet janet_remainder(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
double x = janet_getnumber(argv, 0);
|
||||
double y = janet_getnumber(argv, 1);
|
||||
return janet_wrap_number(fmod(x, y));
|
||||
}
|
||||
|
||||
#define JANET_DEFINE_MATHOP(name, fop)\
|
||||
int janet_##name(JanetArgs args) {\
|
||||
double x;\
|
||||
JANET_FIXARITY(args, 1);\
|
||||
JANET_ARG_NUMBER(x, args, 0);\
|
||||
JANET_RETURN_REAL(args, fop(x));\
|
||||
static Janet janet_##name(int32_t argc, Janet *argv) {\
|
||||
janet_fixarity(argc, 1); \
|
||||
double x = janet_getnumber(argv, 0); \
|
||||
return janet_wrap_number(fop(x)); \
|
||||
}
|
||||
|
||||
JANET_DEFINE_MATHOP(acos, acos)
|
||||
@@ -113,110 +75,144 @@ JANET_DEFINE_MATHOP(fabs, fabs)
|
||||
JANET_DEFINE_MATHOP(floor, floor)
|
||||
|
||||
#define JANET_DEFINE_MATH2OP(name, fop)\
|
||||
int janet_##name(JanetArgs args) {\
|
||||
double lhs, rhs;\
|
||||
JANET_FIXARITY(args, 2);\
|
||||
JANET_ARG_NUMBER(lhs, args, 0);\
|
||||
JANET_ARG_NUMBER(rhs, args, 1);\
|
||||
JANET_RETURN_REAL(args, fop(lhs, rhs));\
|
||||
static Janet janet_##name(int32_t argc, Janet *argv) {\
|
||||
janet_fixarity(argc, 2); \
|
||||
double lhs = janet_getnumber(argv, 0); \
|
||||
double rhs = janet_getnumber(argv, 1); \
|
||||
return janet_wrap_number(fop(lhs, rhs)); \
|
||||
}\
|
||||
|
||||
JANET_DEFINE_MATH2OP(atan2, atan2)
|
||||
JANET_DEFINE_MATH2OP(pow, pow)
|
||||
|
||||
static int janet_not(JanetArgs args) {
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_RETURN_BOOLEAN(args, !janet_truthy(args.v[0]));
|
||||
static Janet janet_not(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
return janet_wrap_boolean(!janet_truthy(argv[0]));
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"%", janet_remainder,
|
||||
"(% dividend divisor)\n\n"
|
||||
"Returns the remainder of dividend / divisor."
|
||||
static const JanetReg math_cfuns[] = {
|
||||
{
|
||||
"%", janet_remainder,
|
||||
JDOC("(% dividend divisor)\n\n"
|
||||
"Returns the remainder of dividend / divisor.")
|
||||
},
|
||||
{"not", janet_not,
|
||||
"(not x)\n\nReturns the boolen inverse of x."
|
||||
{
|
||||
"not", janet_not,
|
||||
JDOC("(not x)\n\nReturns the boolean inverse of x.")
|
||||
},
|
||||
{"int", janet_int,
|
||||
"(int x)\n\nCast a number x to an integer."
|
||||
{
|
||||
"math/random", janet_rand,
|
||||
JDOC("(math/random)\n\n"
|
||||
"Returns a uniformly distributed random number between 0 and 1.")
|
||||
},
|
||||
{"real", janet_real,
|
||||
"(real x)\n\nCast a number x to a real number."
|
||||
{
|
||||
"math/seedrandom", janet_srand,
|
||||
JDOC("(math/seedrandom seed)\n\n"
|
||||
"Set the seed for the random number generator. 'seed' should be an "
|
||||
"an integer.")
|
||||
},
|
||||
{"math/random", janet_rand,
|
||||
"(math/random)\n\n"
|
||||
"Returns a uniformly distrbuted random real number between 0 and 1."
|
||||
{
|
||||
"math/cos", janet_cos,
|
||||
JDOC("(math/cos x)\n\n"
|
||||
"Returns the cosine of x.")
|
||||
},
|
||||
{"math/seedrandom", janet_srand,
|
||||
"(math/seedrandom seed)\n\n"
|
||||
"Set the seed for the random number generator. 'seed' should be an "
|
||||
"an integer."
|
||||
{
|
||||
"math/sin", janet_sin,
|
||||
JDOC("(math/sin x)\n\n"
|
||||
"Returns the sine of x.")
|
||||
},
|
||||
{"math/cos", janet_cos,
|
||||
"(math/cos x)\n\n"
|
||||
"Returns the cosine of x."
|
||||
{
|
||||
"math/tan", janet_tan,
|
||||
JDOC("(math/tan x)\n\n"
|
||||
"Returns the tangent of x.")
|
||||
},
|
||||
{"math/sin", janet_sin,
|
||||
"(math/sin x)\n\n"
|
||||
"Returns the sine of x."
|
||||
{
|
||||
"math/acos", janet_acos,
|
||||
JDOC("(math/acos x)\n\n"
|
||||
"Returns the arccosine of x.")
|
||||
},
|
||||
{"math/tan", janet_tan,
|
||||
"(math/tan x)\n\n"
|
||||
"Returns the tangent of x."
|
||||
{
|
||||
"math/asin", janet_asin,
|
||||
JDOC("(math/asin x)\n\n"
|
||||
"Returns the arcsine of x.")
|
||||
},
|
||||
{"math/acos", janet_acos,
|
||||
"(math/acos x)\n\n"
|
||||
"Returns the arccosine of x."
|
||||
{
|
||||
"math/atan", janet_atan,
|
||||
JDOC("(math/atan x)\n\n"
|
||||
"Returns the arctangent of x.")
|
||||
},
|
||||
{"math/asin", janet_asin,
|
||||
"(math/asin x)\n\n"
|
||||
"Returns the arcsine of x."
|
||||
{
|
||||
"math/exp", janet_exp,
|
||||
JDOC("(math/exp x)\n\n"
|
||||
"Returns e to the power of x.")
|
||||
},
|
||||
{"math/atan", janet_atan,
|
||||
"(math/atan x)\n\n"
|
||||
"Returns the arctangent of x."
|
||||
{
|
||||
"math/log", janet_log,
|
||||
JDOC("(math/log x)\n\n"
|
||||
"Returns log base 2 of x.")
|
||||
},
|
||||
{"math/exp", janet_exp,
|
||||
"(math/exp x)\n\n"
|
||||
"Returns e to the power of x."
|
||||
{
|
||||
"math/log10", janet_log10,
|
||||
JDOC("(math/log10 x)\n\n"
|
||||
"Returns log base 10 of x.")
|
||||
},
|
||||
{"math/log", janet_log,
|
||||
"(math/log x)\n\n"
|
||||
"Returns log base 2 of x."
|
||||
{
|
||||
"math/sqrt", janet_sqrt,
|
||||
JDOC("(math/sqrt x)\n\n"
|
||||
"Returns the square root of x.")
|
||||
},
|
||||
{"math/log10", janet_log10,
|
||||
"(math/log10 x)\n\n"
|
||||
"Returns log base 10 of x."
|
||||
{
|
||||
"math/floor", janet_floor,
|
||||
JDOC("(math/floor x)\n\n"
|
||||
"Returns the largest integer value number that is not greater than x.")
|
||||
},
|
||||
{"math/sqrt", janet_sqrt,
|
||||
"(math/sqrt x)\n\n"
|
||||
"Returns the square root of x."
|
||||
{
|
||||
"math/ceil", janet_ceil,
|
||||
JDOC("(math/ceil x)\n\n"
|
||||
"Returns the smallest integer value number that is not less than x.")
|
||||
},
|
||||
{"math/floor", janet_floor,
|
||||
"(math/floor x)\n\n"
|
||||
"Returns the largest integer value real number that is not greater than x."
|
||||
{
|
||||
"math/pow", janet_pow,
|
||||
JDOC("(math/pow a x)\n\n"
|
||||
"Return a to the power of x.")
|
||||
},
|
||||
{"math/ceil", janet_ceil,
|
||||
"(math/ceil x)\n\n"
|
||||
"Returns the smallest integer value real number that is not less than x."
|
||||
{
|
||||
"math/abs", janet_fabs,
|
||||
JDOC("(math/abs x)\n\n"
|
||||
"Return the absolute value of x.")
|
||||
},
|
||||
{"math/pow", janet_pow,
|
||||
"(math/pow a x)\n\n"
|
||||
"Return a to the power of x."
|
||||
{
|
||||
"math/sinh", janet_sinh,
|
||||
JDOC("(math/sinh x)\n\n"
|
||||
"Return the hyperbolic sine of x.")
|
||||
},
|
||||
{
|
||||
"math/cosh", janet_cosh,
|
||||
JDOC("(math/cosh x)\n\n"
|
||||
"Return the hyperbolic cosine of x.")
|
||||
},
|
||||
{
|
||||
"math/tanh", janet_tanh,
|
||||
JDOC("(math/tanh x)\n\n"
|
||||
"Return the hyperbolic tangent of x.")
|
||||
},
|
||||
{
|
||||
"math/atan2", janet_atan2,
|
||||
JDOC("(math/atan2 y x)\n\n"
|
||||
"Return the arctangent of y/x. Works even when x is 0.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Module entry point */
|
||||
int janet_lib_math(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
|
||||
janet_def(env, "math/pi", janet_wrap_real(3.1415926535897931),
|
||||
"The value pi.");
|
||||
janet_def(env, "math/e", janet_wrap_real(2.7182818284590451),
|
||||
"The base of the natural log.");
|
||||
janet_def(env, "math/inf", janet_wrap_real(INFINITY),
|
||||
"The real number representing positive infinity");
|
||||
return 0;
|
||||
void janet_lib_math(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, math_cfuns);
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
janet_def(env, "math/pi", janet_wrap_number(3.1415926535897931),
|
||||
JDOC("The value pi."));
|
||||
janet_def(env, "math/e", janet_wrap_number(2.7182818284590451),
|
||||
JDOC("The base of the natural log."));
|
||||
janet_def(env, "math/inf", janet_wrap_number(INFINITY),
|
||||
JDOC("The number representing positive infinity"));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include "compile.h"
|
||||
#include "emit.h"
|
||||
#include "vector.h"
|
||||
|
||||
/* Parse a part of a symbol that can be used for building up code. */
|
||||
static JanetSlot multisym_parse_part(JanetCompiler *c, const uint8_t *sympart, int32_t len) {
|
||||
if (sympart[0] == ':') {
|
||||
return janetc_cslot(janet_symbolv(sympart, len));
|
||||
} else {
|
||||
int err = 0;
|
||||
int32_t num = janet_scan_integer(sympart + 1, len - 1, &err);
|
||||
if (err) {
|
||||
return janetc_resolve(c, janet_symbol(sympart + 1, len - 1));
|
||||
} else {
|
||||
return janetc_cslot(janet_wrap_integer(num));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static JanetSlot multisym_do_parts(JanetFopts opts, int put, const uint8_t *sym, Janet rvalue) {
|
||||
JanetSlot slot;
|
||||
JanetFopts subopts = janetc_fopts_default(opts.compiler);
|
||||
int i, j;
|
||||
for (i = 1, j = 0; sym[i]; i++) {
|
||||
if (sym[i] == ':' || sym[i] == '.') {
|
||||
if (j) {
|
||||
JanetSlot target = janetc_gettarget(subopts);
|
||||
JanetSlot value = multisym_parse_part(opts.compiler, sym + j, i - j);
|
||||
janetc_emit_sss(opts.compiler, JOP_GET, target, slot, value, 1);
|
||||
slot = target;
|
||||
} else {
|
||||
const uint8_t *nextsym = janet_symbol(sym + j, i - j);
|
||||
slot = janetc_resolve(opts.compiler, nextsym);
|
||||
}
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (j) {
|
||||
/* multisym (outermost get or put) */
|
||||
JanetSlot target = janetc_gettarget(opts);
|
||||
JanetSlot key = multisym_parse_part(opts.compiler, sym + j, i - j);
|
||||
if (put) {
|
||||
subopts.flags = JANET_FOPTS_HINT;
|
||||
subopts.hint = target;
|
||||
JanetSlot r_slot = janetc_value(subopts, rvalue);
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, slot, key, r_slot, 0);
|
||||
janetc_copy(opts.compiler, target, r_slot);
|
||||
} else {
|
||||
janetc_emit_sss(opts.compiler, JOP_GET, target, slot, key, 1);
|
||||
}
|
||||
return target;
|
||||
} else {
|
||||
/* normal symbol */
|
||||
if (put) {
|
||||
JanetSlot ret, dest;
|
||||
dest = janetc_resolve(opts.compiler, sym);
|
||||
if (!(dest.flags & JANET_SLOT_MUTABLE)) {
|
||||
janetc_cerror(opts.compiler, "cannot set constant");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
subopts.flags = JANET_FOPTS_HINT;
|
||||
subopts.hint = dest;
|
||||
ret = janetc_value(subopts, rvalue);
|
||||
janetc_copy(opts.compiler, dest, ret);
|
||||
return ret;
|
||||
}
|
||||
return janetc_resolve(opts.compiler, sym);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if a symbol is a multisym, and if so, transform
|
||||
* it and emit the code for treating it as a bunch of nested
|
||||
* gets. */
|
||||
JanetSlot janetc_sym_rvalue(JanetFopts opts, const uint8_t *sym) {
|
||||
if (janet_string_length(sym) && sym[0] != ':') {
|
||||
return multisym_do_parts(opts, 0, sym, janet_wrap_nil());
|
||||
} else {
|
||||
/* keyword */
|
||||
return janetc_cslot(janet_wrap_symbol(sym));
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if a symbol is a multisym, and if so, transform
|
||||
* it into the correct 'put' expression. */
|
||||
JanetSlot janetc_sym_lvalue(JanetFopts opts, const uint8_t *sym, Janet value) {
|
||||
return multisym_do_parts(opts, 1, sym, value);
|
||||
}
|
||||
1005
src/core/os.c
1005
src/core/os.c
File diff suppressed because it is too large
Load Diff
912
src/core/parse.c
912
src/core/parse.c
File diff suppressed because it is too large
Load Diff
1243
src/core/peg.c
Normal file
1243
src/core/peg.c
Normal file
File diff suppressed because it is too large
Load Diff
748
src/core/pp.c
Normal file
748
src/core/pp.c
Normal file
@@ -0,0 +1,748 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#endif
|
||||
|
||||
/* Implements a pretty printer for Janet. The pretty printer
|
||||
* is farily simple and not that flexible, but fast. */
|
||||
|
||||
/* Temporary buffer size */
|
||||
#define BUFSIZE 64
|
||||
|
||||
static void number_to_string_b(JanetBuffer *buffer, double x) {
|
||||
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
|
||||
int count = snprintf((char *) buffer->data + buffer->count, BUFSIZE, "%g", x);
|
||||
buffer->count += count;
|
||||
}
|
||||
|
||||
/* expects non positive x */
|
||||
static int count_dig10(int32_t x) {
|
||||
int result = 1;
|
||||
for (;;) {
|
||||
if (x > -10) return result;
|
||||
if (x > -100) return result + 1;
|
||||
if (x > -1000) return result + 2;
|
||||
if (x > -10000) return result + 3;
|
||||
x /= 10000;
|
||||
result += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static void integer_to_string_b(JanetBuffer *buffer, int32_t x) {
|
||||
janet_buffer_extra(buffer, BUFSIZE);
|
||||
uint8_t *buf = buffer->data + buffer->count;
|
||||
int32_t neg = 0;
|
||||
int32_t len = 0;
|
||||
if (x == 0) {
|
||||
buf[0] = '0';
|
||||
buffer->count++;
|
||||
return;
|
||||
}
|
||||
if (x > 0) {
|
||||
x = -x;
|
||||
} else {
|
||||
neg = 1;
|
||||
*buf++ = '-';
|
||||
}
|
||||
len = count_dig10(x);
|
||||
buf += len;
|
||||
while (x) {
|
||||
uint8_t digit = (uint8_t) - (x % 10);
|
||||
*(--buf) = '0' + digit;
|
||||
x /= 10;
|
||||
}
|
||||
buffer->count += len + neg;
|
||||
}
|
||||
|
||||
#define HEX(i) (((uint8_t *) janet_base64)[(i)])
|
||||
|
||||
/* Returns a string description for a pointer. Truncates
|
||||
* title to 32 characters */
|
||||
static void string_description_b(JanetBuffer *buffer, const char *title, void *pointer) {
|
||||
janet_buffer_ensure(buffer, buffer->count + BUFSIZE, 2);
|
||||
uint8_t *c = buffer->data + buffer->count;
|
||||
int32_t i;
|
||||
union {
|
||||
uint8_t bytes[sizeof(void *)];
|
||||
void *p;
|
||||
} pbuf;
|
||||
|
||||
pbuf.p = pointer;
|
||||
*c++ = '<';
|
||||
/* Maximum of 32 bytes for abstract type name */
|
||||
for (i = 0; title[i] && i < 32; ++i)
|
||||
*c++ = ((uint8_t *)title) [i];
|
||||
*c++ = ' ';
|
||||
*c++ = '0';
|
||||
*c++ = 'x';
|
||||
#if defined(JANET_64)
|
||||
#define POINTSIZE 6
|
||||
#else
|
||||
#define POINTSIZE (sizeof(void *))
|
||||
#endif
|
||||
for (i = POINTSIZE; i > 0; --i) {
|
||||
uint8_t byte = pbuf.bytes[i - 1];
|
||||
*c++ = HEX(byte >> 4);
|
||||
*c++ = HEX(byte & 0xF);
|
||||
}
|
||||
*c++ = '>';
|
||||
buffer->count = (int32_t)(c - buffer->data);
|
||||
#undef POINTSIZE
|
||||
}
|
||||
|
||||
#undef HEX
|
||||
#undef BUFSIZE
|
||||
|
||||
static void janet_escape_string_impl(JanetBuffer *buffer, const uint8_t *str, int32_t len) {
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
for (int32_t i = 0; i < len; ++i) {
|
||||
uint8_t c = str[i];
|
||||
switch (c) {
|
||||
case '"':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\"", 2);
|
||||
break;
|
||||
case '\n':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\n", 2);
|
||||
break;
|
||||
case '\r':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\r", 2);
|
||||
break;
|
||||
case '\0':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\0", 2);
|
||||
break;
|
||||
case '\f':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\f", 2);
|
||||
break;
|
||||
case '\v':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\v", 2);
|
||||
break;
|
||||
case 27:
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\e", 2);
|
||||
break;
|
||||
case '\\':
|
||||
janet_buffer_push_bytes(buffer, (const uint8_t *)"\\\\", 2);
|
||||
break;
|
||||
default:
|
||||
if (c < 32 || c > 127) {
|
||||
uint8_t buf[4];
|
||||
buf[0] = '\\';
|
||||
buf[1] = 'x';
|
||||
buf[2] = janet_base64[(c >> 4) & 0xF];
|
||||
buf[3] = janet_base64[c & 0xF];
|
||||
janet_buffer_push_bytes(buffer, buf, 4);
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
janet_buffer_push_u8(buffer, '"');
|
||||
}
|
||||
|
||||
static void janet_escape_string_b(JanetBuffer *buffer, const uint8_t *str) {
|
||||
janet_escape_string_impl(buffer, str, janet_string_length(str));
|
||||
}
|
||||
|
||||
static void janet_escape_buffer_b(JanetBuffer *buffer, JanetBuffer *bx) {
|
||||
janet_buffer_push_u8(buffer, '@');
|
||||
janet_escape_string_impl(buffer, bx->data, bx->count);
|
||||
}
|
||||
|
||||
void janet_description_b(JanetBuffer *buffer, Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
janet_buffer_push_cstring(buffer, "nil");
|
||||
return;
|
||||
case JANET_BOOLEAN:
|
||||
janet_buffer_push_cstring(buffer,
|
||||
janet_unwrap_boolean(x) ? "true" : "false");
|
||||
return;
|
||||
case JANET_NUMBER:
|
||||
number_to_string_b(buffer, janet_unwrap_number(x));
|
||||
return;
|
||||
case JANET_KEYWORD:
|
||||
janet_buffer_push_u8(buffer, ':');
|
||||
/* fallthrough */
|
||||
case JANET_SYMBOL:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_string(x),
|
||||
janet_string_length(janet_unwrap_string(x)));
|
||||
return;
|
||||
case JANET_STRING:
|
||||
janet_escape_string_b(buffer, janet_unwrap_string(x));
|
||||
return;
|
||||
case JANET_BUFFER: {
|
||||
JanetBuffer *b = janet_unwrap_buffer(x);
|
||||
if (b == buffer) {
|
||||
/* Ensures buffer won't resize while escaping */
|
||||
janet_buffer_ensure(b, 5 * b->count + 3, 1);
|
||||
}
|
||||
janet_escape_buffer_b(buffer, b);
|
||||
return;
|
||||
}
|
||||
case JANET_ABSTRACT: {
|
||||
void *p = janet_unwrap_abstract(x);
|
||||
const JanetAbstractType *at = janet_abstract_type(p);
|
||||
if (at->tostring) {
|
||||
at->tostring(p, buffer);
|
||||
} else {
|
||||
const char *n = at->name;
|
||||
string_description_b(buffer, n, janet_unwrap_abstract(x));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case JANET_CFUNCTION: {
|
||||
Janet check = janet_table_get(janet_vm_registry, x);
|
||||
if (janet_checktype(check, JANET_SYMBOL)) {
|
||||
janet_buffer_push_cstring(buffer, "<cfunction ");
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_symbol(check),
|
||||
janet_string_length(janet_unwrap_symbol(check)));
|
||||
janet_buffer_push_u8(buffer, '>');
|
||||
break;
|
||||
}
|
||||
goto fallthrough;
|
||||
}
|
||||
case JANET_FUNCTION: {
|
||||
JanetFunction *fun = janet_unwrap_function(x);
|
||||
JanetFuncDef *def = fun->def;
|
||||
if (def->name) {
|
||||
const uint8_t *n = def->name;
|
||||
janet_buffer_push_cstring(buffer, "<function ");
|
||||
janet_buffer_push_bytes(buffer, n, janet_string_length(n));
|
||||
janet_buffer_push_u8(buffer, '>');
|
||||
break;
|
||||
}
|
||||
goto fallthrough;
|
||||
}
|
||||
fallthrough:
|
||||
default:
|
||||
string_description_b(buffer, janet_type_names[janet_type(x)], janet_unwrap_pointer(x));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void janet_to_string_b(JanetBuffer *buffer, Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
janet_description_b(buffer, x);
|
||||
break;
|
||||
case JANET_BUFFER:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_buffer(x)->data,
|
||||
janet_unwrap_buffer(x)->count);
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
janet_buffer_push_bytes(buffer,
|
||||
janet_unwrap_string(x),
|
||||
janet_string_length(janet_unwrap_string(x)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t *janet_description(Janet x) {
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 10);
|
||||
janet_description_b(&b, x);
|
||||
const uint8_t *ret = janet_string(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Convert any value to a janet string. Similar to description, but
|
||||
* strings, symbols, and buffers will return their content. */
|
||||
const uint8_t *janet_to_string(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default: {
|
||||
JanetBuffer b;
|
||||
janet_buffer_init(&b, 10);
|
||||
janet_to_string_b(&b, x);
|
||||
const uint8_t *ret = janet_string(b.data, b.count);
|
||||
janet_buffer_deinit(&b);
|
||||
return ret;
|
||||
}
|
||||
case JANET_BUFFER:
|
||||
return janet_string(janet_unwrap_buffer(x)->data, janet_unwrap_buffer(x)->count);
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
return janet_unwrap_string(x);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hold state for pretty printer. */
|
||||
struct pretty {
|
||||
JanetBuffer *buffer;
|
||||
int depth;
|
||||
int indent;
|
||||
int flags;
|
||||
int32_t bufstartlen;
|
||||
JanetTable seen;
|
||||
};
|
||||
|
||||
static void print_newline(struct pretty *S, int just_a_space) {
|
||||
int i;
|
||||
if (just_a_space) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
return;
|
||||
}
|
||||
janet_buffer_push_u8(S->buffer, '\n');
|
||||
for (i = 0; i < S->indent; i++) {
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* Color coding for types */
|
||||
static const char janet_cycle_color[] = "\x1B[36m";
|
||||
static const char *janet_pretty_colors[] = {
|
||||
"\x1B[32m",
|
||||
"\x1B[36m",
|
||||
"\x1B[36m",
|
||||
"\x1B[36m",
|
||||
"\x1B[35m",
|
||||
"\x1B[34m",
|
||||
"\x1B[33m",
|
||||
"\x1B[36m",
|
||||
"\x1B[36m",
|
||||
"\x1B[36m",
|
||||
"\x1B[36m"
|
||||
"\x1B[35m",
|
||||
"\x1B[36m",
|
||||
"\x1B[36m",
|
||||
"\x1B[36m",
|
||||
"\x1B[36m"
|
||||
};
|
||||
|
||||
#define JANET_PRETTY_DICT_ONELINE 4
|
||||
#define JANET_PRETTY_IND_ONELINE 10
|
||||
|
||||
/* Helper for pretty printing */
|
||||
static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
|
||||
/* Add to seen */
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
case JANET_NUMBER:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_BOOLEAN:
|
||||
break;
|
||||
default: {
|
||||
Janet seenid = janet_table_get(&S->seen, x);
|
||||
if (janet_checktype(seenid, JANET_NUMBER)) {
|
||||
if (S->flags & JANET_PRETTY_COLOR) {
|
||||
janet_buffer_push_cstring(S->buffer, janet_cycle_color);
|
||||
}
|
||||
janet_buffer_push_cstring(S->buffer, "<cycle ");
|
||||
integer_to_string_b(S->buffer, janet_unwrap_integer(seenid));
|
||||
janet_buffer_push_u8(S->buffer, '>');
|
||||
if (S->flags & JANET_PRETTY_COLOR) {
|
||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
janet_table_put(&S->seen, x, janet_wrap_integer(S->seen.count));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (janet_type(x)) {
|
||||
default: {
|
||||
const char *color = janet_pretty_colors[janet_type(x)];
|
||||
if (color && (S->flags & JANET_PRETTY_COLOR)) {
|
||||
janet_buffer_push_cstring(S->buffer, color);
|
||||
}
|
||||
if (janet_checktype(x, JANET_BUFFER) && janet_unwrap_buffer(x) == S->buffer) {
|
||||
janet_buffer_ensure(S->buffer, S->buffer->count + S->bufstartlen * 4 + 3, 1);
|
||||
janet_buffer_push_u8(S->buffer, '@');
|
||||
janet_escape_string_impl(S->buffer, S->buffer->data, S->bufstartlen);
|
||||
} else {
|
||||
janet_description_b(S->buffer, x);
|
||||
}
|
||||
if (color && (S->flags & JANET_PRETTY_COLOR)) {
|
||||
janet_buffer_push_cstring(S->buffer, "\x1B[0m");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_ARRAY:
|
||||
case JANET_TUPLE: {
|
||||
int32_t i = 0, len = 0;
|
||||
const Janet *arr = NULL;
|
||||
int isarray = janet_checktype(x, JANET_ARRAY);
|
||||
janet_indexed_view(x, &arr, &len);
|
||||
int hasbrackets = !isarray && (janet_tuple_flag(arr) & JANET_TUPLE_FLAG_BRACKETCTOR);
|
||||
const char *startstr = isarray ? "@[" : hasbrackets ? "[" : "(";
|
||||
const char endchar = isarray ? ']' : hasbrackets ? ']' : ')';
|
||||
janet_buffer_push_cstring(S->buffer, startstr);
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
} else {
|
||||
if (!isarray && len >= JANET_PRETTY_IND_ONELINE)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= JANET_PRETTY_IND_ONELINE) print_newline(S, 0);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i) print_newline(S, len < JANET_PRETTY_IND_ONELINE);
|
||||
janet_pretty_one(S, arr[i], 0);
|
||||
}
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, endchar);
|
||||
break;
|
||||
}
|
||||
case JANET_STRUCT:
|
||||
case JANET_TABLE: {
|
||||
int istable = janet_checktype(x, JANET_TABLE);
|
||||
janet_buffer_push_cstring(S->buffer, istable ? "@" : "{");
|
||||
|
||||
/* For object-like tables, print class name */
|
||||
if (istable) {
|
||||
JanetTable *t = janet_unwrap_table(x);
|
||||
JanetTable *proto = t->proto;
|
||||
if (NULL != proto) {
|
||||
Janet name = janet_table_get(proto, janet_csymbolv(":name"));
|
||||
if (janet_checktype(name, JANET_SYMBOL)) {
|
||||
const uint8_t *sym = janet_unwrap_symbol(name);
|
||||
janet_buffer_push_bytes(S->buffer, sym, janet_string_length(sym));
|
||||
}
|
||||
}
|
||||
janet_buffer_push_cstring(S->buffer, "{");
|
||||
}
|
||||
|
||||
S->depth--;
|
||||
S->indent += 2;
|
||||
if (S->depth == 0) {
|
||||
janet_buffer_push_cstring(S->buffer, "...");
|
||||
} else {
|
||||
int32_t i = 0, len = 0, cap = 0;
|
||||
int first_kv_pair = 1;
|
||||
const JanetKV *kvs = NULL;
|
||||
janet_dictionary_view(x, &kvs, &len, &cap);
|
||||
if (!istable && len >= JANET_PRETTY_DICT_ONELINE)
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
if (is_dict_value && len >= JANET_PRETTY_DICT_ONELINE) print_newline(S, 0);
|
||||
for (i = 0; i < cap; i++) {
|
||||
if (!janet_checktype(kvs[i].key, JANET_NIL)) {
|
||||
if (first_kv_pair) {
|
||||
first_kv_pair = 0;
|
||||
} else {
|
||||
print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
|
||||
}
|
||||
janet_pretty_one(S, kvs[i].key, 0);
|
||||
janet_buffer_push_u8(S->buffer, ' ');
|
||||
janet_pretty_one(S, kvs[i].value, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
S->indent -= 2;
|
||||
S->depth++;
|
||||
janet_buffer_push_u8(S->buffer, '}');
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Remove from seen */
|
||||
janet_table_remove(&S->seen, x);
|
||||
return;
|
||||
}
|
||||
|
||||
static JanetBuffer *janet_pretty_(JanetBuffer *buffer, int depth, int flags, Janet x, int32_t startlen) {
|
||||
struct pretty S;
|
||||
if (NULL == buffer) {
|
||||
buffer = janet_buffer(0);
|
||||
}
|
||||
S.buffer = buffer;
|
||||
S.depth = depth;
|
||||
S.indent = 0;
|
||||
S.flags = flags;
|
||||
S.bufstartlen = startlen;
|
||||
janet_table_init(&S.seen, 10);
|
||||
janet_pretty_one(&S, x, 0);
|
||||
janet_table_deinit(&S.seen);
|
||||
return S.buffer;
|
||||
}
|
||||
|
||||
/* Helper for printing a janet value in a pretty form. Not meant to be used
|
||||
* for serialization or anything like that. */
|
||||
JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, Janet x) {
|
||||
return janet_pretty_(buffer, depth, flags, x, buffer ? buffer->count : 0);
|
||||
}
|
||||
|
||||
static const char *typestr(Janet x) {
|
||||
JanetType t = janet_type(x);
|
||||
return (t == JANET_ABSTRACT)
|
||||
? janet_abstract_type(janet_unwrap_abstract(x))->name
|
||||
: janet_type_names[t];
|
||||
}
|
||||
|
||||
static void pushtypes(JanetBuffer *buffer, int types) {
|
||||
int first = 1;
|
||||
int i = 0;
|
||||
while (types) {
|
||||
if (1 & types) {
|
||||
if (first) {
|
||||
first = 0;
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, '|');
|
||||
}
|
||||
janet_buffer_push_cstring(buffer, janet_type_names[i]);
|
||||
}
|
||||
i++;
|
||||
types >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void janet_formatb(JanetBuffer *bufp, const char *format, va_list args) {
|
||||
for (const char *c = format; *c; c++) {
|
||||
switch (*c) {
|
||||
default:
|
||||
janet_buffer_push_u8(bufp, *c);
|
||||
break;
|
||||
case '%': {
|
||||
if (c[1] == '\0')
|
||||
break;
|
||||
switch (*++c) {
|
||||
default:
|
||||
janet_buffer_push_u8(bufp, *c);
|
||||
break;
|
||||
case 'f':
|
||||
number_to_string_b(bufp, va_arg(args, double));
|
||||
break;
|
||||
case 'd':
|
||||
integer_to_string_b(bufp, va_arg(args, long));
|
||||
break;
|
||||
case 'S': {
|
||||
const uint8_t *str = va_arg(args, const uint8_t *);
|
||||
janet_buffer_push_bytes(bufp, str, janet_string_length(str));
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
janet_buffer_push_cstring(bufp, va_arg(args, const char *));
|
||||
break;
|
||||
case 'c':
|
||||
janet_buffer_push_u8(bufp, (uint8_t) va_arg(args, long));
|
||||
break;
|
||||
case 'q': {
|
||||
const uint8_t *str = va_arg(args, const uint8_t *);
|
||||
janet_escape_string_b(bufp, str);
|
||||
break;
|
||||
}
|
||||
case 't': {
|
||||
janet_buffer_push_cstring(bufp, typestr(va_arg(args, Janet)));
|
||||
break;
|
||||
}
|
||||
case 'T': {
|
||||
int types = va_arg(args, long);
|
||||
pushtypes(bufp, types);
|
||||
break;
|
||||
}
|
||||
case 'V': {
|
||||
janet_to_string_b(bufp, va_arg(args, Janet));
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
janet_description_b(bufp, va_arg(args, Janet));
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
janet_pretty(bufp, 4, 0, va_arg(args, Janet));
|
||||
break;
|
||||
}
|
||||
case 'P': {
|
||||
janet_pretty(bufp, 4, JANET_PRETTY_COLOR, va_arg(args, Janet));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for formatting strings. Useful for generating error messages and the like.
|
||||
* Similar to printf, but specialized for operating with janet. */
|
||||
const uint8_t *janet_formatc(const char *format, ...) {
|
||||
va_list args;
|
||||
const uint8_t *ret;
|
||||
JanetBuffer buffer;
|
||||
int32_t len = 0;
|
||||
|
||||
/* Calculate length, init buffer and args */
|
||||
while (format[len]) len++;
|
||||
janet_buffer_init(&buffer, len);
|
||||
va_start(args, format);
|
||||
|
||||
/* Run format */
|
||||
janet_formatb(&buffer, format, args);
|
||||
|
||||
/* Iterate length */
|
||||
va_end(args);
|
||||
|
||||
ret = janet_string(buffer.data, buffer.count);
|
||||
janet_buffer_deinit(&buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* code adapted from lua/lstrlib.c http://lua.org
|
||||
*/
|
||||
|
||||
#define MAX_ITEM 256
|
||||
#define FMT_FLAGS "-+ #0"
|
||||
#define MAX_FORMAT 32
|
||||
|
||||
static const char *scanformat(
|
||||
const char *strfrmt,
|
||||
char *form,
|
||||
char width[3],
|
||||
char precision[3]) {
|
||||
const char *p = strfrmt;
|
||||
memset(width, '\0', 3);
|
||||
memset(precision, '\0', 3);
|
||||
while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL)
|
||||
p++; /* skip flags */
|
||||
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS) / sizeof(char))
|
||||
janet_panic("invalid format (repeated flags)");
|
||||
if (isdigit((int)(*p)))
|
||||
width[0] = *p++; /* skip width */
|
||||
if (isdigit((int)(*p)))
|
||||
width[1] = *p++; /* (2 digits at most) */
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
if (isdigit((int)(*p)))
|
||||
precision[0] = *p++; /* skip precision */
|
||||
if (isdigit((int)(*p)))
|
||||
precision[1] = *p++; /* (2 digits at most) */
|
||||
}
|
||||
if (isdigit((int)(*p)))
|
||||
janet_panic("invalid format (width or precision too long)");
|
||||
*(form++) = '%';
|
||||
memcpy(form, strfrmt, ((p - strfrmt) + 1) * sizeof(char));
|
||||
form += (p - strfrmt) + 1;
|
||||
*form = '\0';
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Shared implementation between string/format and
|
||||
* buffer/format */
|
||||
void janet_buffer_format(
|
||||
JanetBuffer *b,
|
||||
const char *strfrmt,
|
||||
int32_t argstart,
|
||||
int32_t argc,
|
||||
Janet *argv) {
|
||||
size_t sfl = strlen(strfrmt);
|
||||
const char *strfrmt_end = strfrmt + sfl;
|
||||
int32_t arg = argstart;
|
||||
int32_t startlen = b->count;
|
||||
while (strfrmt < strfrmt_end) {
|
||||
if (*strfrmt != '%')
|
||||
janet_buffer_push_u8(b, (uint8_t) * strfrmt++);
|
||||
else if (*++strfrmt == '%')
|
||||
janet_buffer_push_u8(b, (uint8_t) * strfrmt++); /* %% */
|
||||
else { /* format item */
|
||||
char form[MAX_FORMAT], item[MAX_ITEM];
|
||||
char width[3], precision[3];
|
||||
int nb = 0; /* number of bytes in added item */
|
||||
if (++arg >= argc)
|
||||
janet_panic("not enough values for format");
|
||||
strfrmt = scanformat(strfrmt, form, width, precision);
|
||||
switch (*strfrmt++) {
|
||||
case 'c': {
|
||||
nb = snprintf(item, MAX_ITEM, form, (int)
|
||||
janet_getinteger(argv, arg));
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'o':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X': {
|
||||
int32_t n = janet_getinteger(argv, arg);
|
||||
nb = snprintf(item, MAX_ITEM, form, n);
|
||||
break;
|
||||
}
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'g':
|
||||
case 'G': {
|
||||
double d = janet_getnumber(argv, arg);
|
||||
nb = snprintf(item, MAX_ITEM, form, d);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
const uint8_t *s = janet_getstring(argv, arg);
|
||||
int32_t l = janet_string_length(s);
|
||||
if (form[2] == '\0')
|
||||
janet_buffer_push_bytes(b, s, l);
|
||||
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);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'V': {
|
||||
janet_to_string_b(b, argv[arg]);
|
||||
break;
|
||||
}
|
||||
case 'v': {
|
||||
janet_description_b(b, argv[arg]);
|
||||
break;
|
||||
}
|
||||
case 'P':
|
||||
case 'p': { /* janet pretty , precision = depth */
|
||||
int depth = atoi(precision);
|
||||
if (depth < 1)
|
||||
depth = 4;
|
||||
janet_pretty_(b, depth, (strfrmt[-1] == 'P') ? JANET_PRETTY_COLOR : 0, argv[arg], startlen);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
/* also treat cases 'nLlh' */
|
||||
janet_panicf("invalid conversion '%s' to 'format'",
|
||||
form);
|
||||
}
|
||||
}
|
||||
if (nb >= MAX_ITEM)
|
||||
janet_panicf("format buffer overflow", form);
|
||||
if (nb > 0)
|
||||
janet_buffer_push_bytes(b, (uint8_t *) item, nb);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,8 +20,11 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "regalloc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
void janetc_regalloc_init(JanetcRegisterAllocator *ra) {
|
||||
ra->chunks = NULL;
|
||||
@@ -57,19 +60,23 @@ static int32_t count_trailing_ones(uint32_t x) {
|
||||
/* Get N bits */
|
||||
#define nbits(N) (ithbit(N) - 1)
|
||||
|
||||
/* Copy a regsiter allocator */
|
||||
/* Copy a register allocator */
|
||||
void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src) {
|
||||
size_t size;
|
||||
dest->count = src->count;
|
||||
dest->capacity = src->capacity;
|
||||
dest->max = src->max;
|
||||
size = sizeof(uint32_t) * dest->capacity;
|
||||
dest->chunks = malloc(size);
|
||||
dest->regtemps = 0;
|
||||
if (!dest->chunks) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
if (size) {
|
||||
dest->chunks = malloc(size);
|
||||
if (!dest->chunks) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(dest->chunks, src->chunks, size);
|
||||
} else {
|
||||
dest->chunks = NULL;
|
||||
}
|
||||
memcpy(dest->chunks, src->chunks, size);
|
||||
}
|
||||
|
||||
/* Allocate one more chunk in chunks */
|
||||
@@ -153,78 +160,3 @@ void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRe
|
||||
if (reg < 0xF0)
|
||||
janetc_regalloc_free(ra, reg);
|
||||
}
|
||||
|
||||
/* Disable multi-slot allocation for now. */
|
||||
|
||||
/*
|
||||
static int32_t checkrange(JanetcRegisterAllocator *ra, int32_t start, int32_t end) {
|
||||
int32_t startchunk = start / 32;
|
||||
int32_t endchunk = end / 32;
|
||||
for (int32_t chunk = startchunk; chunk <= endchunk; chunk++) {
|
||||
while (ra->count <= chunk) pushchunk(ra);
|
||||
uint32_t mask = 0xFFFFFFFF;
|
||||
if (chunk == startchunk)
|
||||
mask &= ~nbits(start & 0x1F);
|
||||
if (chunk == endchunk)
|
||||
mask &= nbits(end & 0x1F);
|
||||
uint32_t block = ra->chunks[chunk];
|
||||
uint32_t masking = mask & block;
|
||||
if (masking) {
|
||||
int32_t nextbit = (block == 0xFFFFFFFF)
|
||||
? 32
|
||||
: count_trailing_zeros(masking) + 1;
|
||||
return chunk * 32 + nextbit;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void markrange(JanetcRegisterAllocator *ra, int32_t start, int32_t end) {
|
||||
int32_t startchunk = start / 32;
|
||||
int32_t endchunk = end / 32;
|
||||
for (int32_t chunk = startchunk; chunk <= endchunk; chunk++) {
|
||||
uint32_t mask = 0xFFFFFFFF;
|
||||
if (chunk == startchunk)
|
||||
mask &= ~nbits(start & 0x1F);
|
||||
if (chunk == endchunk)
|
||||
mask &= nbits(end & 0x1F);
|
||||
ra->chunks[chunk] |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
void janetc_regalloc_freerange(JanetcRegisterAllocator *ra, int32_t start, int32_t n) {
|
||||
int32_t end = start + n - 1;
|
||||
int32_t startchunk = start / 32;
|
||||
int32_t endchunk = end / 32;
|
||||
for (int32_t chunk = startchunk; chunk <= endchunk; chunk++) {
|
||||
uint32_t mask = 0;
|
||||
if (chunk == startchunk)
|
||||
mask |= nbits(start & 0x1F);
|
||||
if (chunk == endchunk)
|
||||
mask |= ~nbits(end & 0x1F);
|
||||
ra->chunks[chunk] &= mask;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t janetc_regalloc_n(JanetcRegisterAllocator *ra, int32_t n) {
|
||||
int32_t start = 0, end = 0, next = 0;
|
||||
while (next >= 0) {
|
||||
start = next;
|
||||
end = start + n - 1;
|
||||
next = checkrange(ra, start, end);
|
||||
}
|
||||
markrange(ra, start, end);
|
||||
if (end > ra->max)
|
||||
ra->max = end;
|
||||
return start;
|
||||
}
|
||||
|
||||
int32_t janetc_regalloc_call(JanetcRegisterAllocator *ra, int32_t callee, int32_t nargs) {
|
||||
if (checkrange(ra, callee, callee + nargs) < 0) {
|
||||
markrange(ra, callee + 1, callee + nargs);
|
||||
return callee;
|
||||
}
|
||||
return janetc_regalloc_n(ra, nargs + 1);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -44,7 +44,7 @@ typedef struct {
|
||||
int32_t count; /* number of chunks in chunks */
|
||||
int32_t capacity; /* amount allocated for chunks */
|
||||
int32_t max; /* The maximum allocated register so far */
|
||||
int32_t regtemps; /* Hold which tempregistered are alloced. */
|
||||
int32_t regtemps; /* Hold which temp. registers are allocated. */
|
||||
} JanetcRegisterAllocator;
|
||||
|
||||
void janetc_regalloc_init(JanetcRegisterAllocator *ra);
|
||||
@@ -57,11 +57,4 @@ void janetc_regalloc_freetemp(JanetcRegisterAllocator *ra, int32_t reg, JanetcRe
|
||||
void janetc_regalloc_clone(JanetcRegisterAllocator *dest, JanetcRegisterAllocator *src);
|
||||
void janetc_regalloc_touch(JanetcRegisterAllocator *ra, int32_t reg);
|
||||
|
||||
/* Mutli-slot allocation disabled */
|
||||
/*
|
||||
int32_t janetc_regalloc_n(JanetcRegisterAllocator *ra, int32_t n);
|
||||
int32_t janetc_regalloc_call(JanetcRegisterAllocator *ra, int32_t callee, int32_t nargs);
|
||||
void janetc_regalloc_freerange(JanetcRegisterAllocator *ra, int32_t regstart, int32_t n);
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
133
src/core/run.c
133
src/core/run.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,122 +20,79 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "state.h"
|
||||
#include "vector.h"
|
||||
|
||||
/* Error reporting */
|
||||
void janet_stacktrace(JanetFiber *fiber, const char *errtype, Janet err) {
|
||||
int32_t fi;
|
||||
const char *errstr = (const char *)janet_to_string(err);
|
||||
JanetFiber **fibers = NULL;
|
||||
fprintf(stderr, "%s error: %s\n", errtype, errstr);
|
||||
|
||||
while (fiber) {
|
||||
janet_v_push(fibers, fiber);
|
||||
fiber = fiber->child;
|
||||
}
|
||||
|
||||
for (fi = janet_v_count(fibers) - 1; fi >= 0; fi--) {
|
||||
fiber = fibers[fi];
|
||||
int32_t i = fiber->frame;
|
||||
if (i > 0) fprintf(stderr, " (fiber)\n");
|
||||
while (i > 0) {
|
||||
JanetStackFrame *frame = (JanetStackFrame *)(fiber->data + i - JANET_FRAME_SIZE);
|
||||
JanetFuncDef *def = NULL;
|
||||
i = frame->prevframe;
|
||||
fprintf(stderr, " in");
|
||||
if (frame->func) {
|
||||
def = frame->func->def;
|
||||
fprintf(stderr, " %s", def->name ? (const char *)def->name : "<anonymous>");
|
||||
if (def->source) {
|
||||
fprintf(stderr, " [%s]", (const char *)def->source);
|
||||
}
|
||||
} else {
|
||||
JanetCFunction cfun = (JanetCFunction)(frame->pc);
|
||||
if (cfun) {
|
||||
Janet name = janet_table_get(janet_vm_registry, janet_wrap_cfunction(cfun));
|
||||
if (!janet_checktype(name, JANET_NIL))
|
||||
fprintf(stderr, " %s", (const char *)janet_to_string(name));
|
||||
else
|
||||
fprintf(stderr, " <cfunction>");
|
||||
}
|
||||
}
|
||||
if (frame->flags & JANET_STACKFRAME_TAILCALL)
|
||||
fprintf(stderr, " (tailcall)");
|
||||
if (frame->func && frame->pc) {
|
||||
int32_t off = (int32_t) (frame->pc - def->bytecode);
|
||||
if (def->sourcemap) {
|
||||
JanetSourceMapping mapping = def->sourcemap[off];
|
||||
fprintf(stderr, " on line %d, column %d", mapping.line, mapping.column);
|
||||
} else {
|
||||
fprintf(stderr, " pc=%d", off);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Run a string */
|
||||
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out) {
|
||||
JanetParser parser;
|
||||
int errflags = 0;
|
||||
FILE *errf = janet_dynfile("err", stderr);
|
||||
int errflags = 0, done = 0;
|
||||
int32_t index = 0;
|
||||
int dudeol = 0;
|
||||
int done = 0;
|
||||
Janet ret = janet_wrap_nil();
|
||||
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);
|
||||
|
||||
while (!errflags && !done) {
|
||||
switch (janet_parser_status(&parser)) {
|
||||
case JANET_PARSE_FULL:
|
||||
{
|
||||
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);
|
||||
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
|
||||
if (status != JANET_SIGNAL_OK) {
|
||||
janet_stacktrace(fiber, "runtime", ret);
|
||||
errflags |= 0x01;
|
||||
}
|
||||
} else {
|
||||
janet_stacktrace(cres.macrofiber, "compile",
|
||||
janet_wrap_string(cres.error));
|
||||
errflags |= 0x02;
|
||||
}
|
||||
/* While we haven't seen an error */
|
||||
while (!done) {
|
||||
|
||||
/* Evaluate parsed values */
|
||||
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->env = env;
|
||||
JanetSignal status = janet_continue(fiber, janet_wrap_nil(), &ret);
|
||||
if (status != JANET_SIGNAL_OK) {
|
||||
janet_stacktrace(fiber, ret);
|
||||
errflags |= 0x01;
|
||||
done = 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(errf, "compile error in %s: %s\n", sourcePath,
|
||||
(const char *)cres.error);
|
||||
errflags |= 0x02;
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispatch based on parse state */
|
||||
switch (janet_parser_status(&parser)) {
|
||||
case JANET_PARSE_DEAD:
|
||||
done = 1;
|
||||
break;
|
||||
case JANET_PARSE_ERROR:
|
||||
errflags |= 0x04;
|
||||
fprintf(stderr, "parse error: %s\n", janet_parser_error(&parser));
|
||||
fprintf(errf, "parse error in %s: %s\n",
|
||||
sourcePath, janet_parser_error(&parser));
|
||||
done = 1;
|
||||
break;
|
||||
case JANET_PARSE_PENDING:
|
||||
if (index >= len) {
|
||||
if (dudeol) {
|
||||
errflags |= 0x04;
|
||||
fprintf(stderr, "internal parse error: unexpected end of source\n");
|
||||
} else {
|
||||
dudeol = 1;
|
||||
janet_parser_consume(&parser, '\n');
|
||||
}
|
||||
if (index == len) {
|
||||
janet_parser_eof(&parser);
|
||||
} else {
|
||||
janet_parser_consume(&parser, bytes[index++]);
|
||||
}
|
||||
break;
|
||||
case JANET_PARSE_ROOT:
|
||||
if (index >= len) {
|
||||
done = 1;
|
||||
janet_parser_eof(&parser);
|
||||
} else {
|
||||
janet_parser_consume(&parser, bytes[index++]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Clean up and return errors */
|
||||
janet_parser_deinit(&parser);
|
||||
if (where) janet_gcunroot(janet_wrap_string(where));
|
||||
if (out) *out = ret;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,11 +20,13 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "compile.h"
|
||||
#include "util.h"
|
||||
#include "vector.h"
|
||||
#include "emit.h"
|
||||
#endif
|
||||
|
||||
static JanetSlot janetc_quote(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
if (argn != 1) {
|
||||
@@ -58,45 +60,44 @@ static JanetSlot quasiquote(JanetFopts opts, Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
return janetc_cslot(x);
|
||||
case JANET_TUPLE:
|
||||
{
|
||||
int32_t i, len;
|
||||
const Janet *tup = janet_unwrap_tuple(x);
|
||||
len = janet_tuple_length(tup);
|
||||
if (len > 1 && janet_checktype(tup[0], JANET_SYMBOL)) {
|
||||
const uint8_t *head = janet_unwrap_symbol(tup[0]);
|
||||
if (!janet_cstrcmp(head, "unquote"))
|
||||
return janetc_value(janetc_fopts_default(opts.compiler), tup[1]);
|
||||
}
|
||||
for (i = 0; i < len; i++)
|
||||
janet_v_push(slots, quasiquote(opts, tup[i]));
|
||||
return qq_slots(opts, slots, JOP_MAKE_TUPLE);
|
||||
}
|
||||
case JANET_ARRAY:
|
||||
{
|
||||
int32_t i;
|
||||
JanetArray *array = janet_unwrap_array(x);
|
||||
for (i = 0; i < array->count; i++)
|
||||
janet_v_push(slots, quasiquote(opts, array->data[i]));
|
||||
return qq_slots(opts, slots, JOP_MAKE_ARRAY);
|
||||
case JANET_TUPLE: {
|
||||
int32_t i, len;
|
||||
const Janet *tup = janet_unwrap_tuple(x);
|
||||
len = janet_tuple_length(tup);
|
||||
if (len > 1 && janet_checktype(tup[0], JANET_SYMBOL)) {
|
||||
const uint8_t *head = janet_unwrap_symbol(tup[0]);
|
||||
if (!janet_cstrcmp(head, "unquote"))
|
||||
return janetc_value(janetc_fopts_default(opts.compiler), tup[1]);
|
||||
}
|
||||
for (i = 0; i < len; i++)
|
||||
janet_v_push(slots, quasiquote(opts, tup[i]));
|
||||
return qq_slots(opts, slots, (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR)
|
||||
? JOP_MAKE_BRACKET_TUPLE
|
||||
: JOP_MAKE_TUPLE);
|
||||
}
|
||||
case JANET_ARRAY: {
|
||||
int32_t i;
|
||||
JanetArray *array = janet_unwrap_array(x);
|
||||
for (i = 0; i < array->count; i++)
|
||||
janet_v_push(slots, quasiquote(opts, array->data[i]));
|
||||
return qq_slots(opts, slots, JOP_MAKE_ARRAY);
|
||||
}
|
||||
case JANET_TABLE:
|
||||
case JANET_STRUCT:
|
||||
{
|
||||
const JanetKV *kv = NULL, *kvs = NULL;
|
||||
int32_t len, cap;
|
||||
janet_dictionary_view(x, &kvs, &len, &cap);
|
||||
while ((kv = janet_dictionary_next(kvs, cap, kv))) {
|
||||
JanetSlot key = quasiquote(opts, kv->key);
|
||||
JanetSlot value = quasiquote(opts, kv->value);
|
||||
key.flags &= ~JANET_SLOT_SPLICED;
|
||||
value.flags &= ~JANET_SLOT_SPLICED;
|
||||
janet_v_push(slots, key);
|
||||
janet_v_push(slots, value);
|
||||
}
|
||||
return qq_slots(opts, slots,
|
||||
janet_checktype(x, JANET_TABLE) ? JOP_MAKE_TABLE : JOP_MAKE_STRUCT);
|
||||
case JANET_STRUCT: {
|
||||
const JanetKV *kv = NULL, *kvs = NULL;
|
||||
int32_t len, cap = 0;
|
||||
janet_dictionary_view(x, &kvs, &len, &cap);
|
||||
while ((kv = janet_dictionary_next(kvs, cap, kv))) {
|
||||
JanetSlot key = quasiquote(opts, kv->key);
|
||||
JanetSlot value = quasiquote(opts, kv->value);
|
||||
key.flags &= ~JANET_SLOT_SPLICED;
|
||||
value.flags &= ~JANET_SLOT_SPLICED;
|
||||
janet_v_push(slots, key);
|
||||
janet_v_push(slots, value);
|
||||
}
|
||||
return qq_slots(opts, slots,
|
||||
janet_checktype(x, JANET_TABLE) ? JOP_MAKE_TABLE : JOP_MAKE_STRUCT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,17 +116,17 @@ static JanetSlot janetc_unquote(JanetFopts opts, int32_t argn, const Janet *argv
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
|
||||
/* Preform destructuring. Be careful to
|
||||
* keep the order registers are freed.
|
||||
/* Perform destructuring. Be careful to
|
||||
* keep the order registers are freed.
|
||||
* Returns if the slot 'right' can be freed. */
|
||||
static int destructure(JanetCompiler *c,
|
||||
Janet left,
|
||||
JanetSlot right,
|
||||
int (*leaf)(JanetCompiler *c,
|
||||
const uint8_t *sym,
|
||||
JanetSlot s,
|
||||
JanetTable *attr),
|
||||
JanetTable *attr) {
|
||||
Janet left,
|
||||
JanetSlot right,
|
||||
int (*leaf)(JanetCompiler *c,
|
||||
const uint8_t *sym,
|
||||
JanetSlot s,
|
||||
JanetTable *attr),
|
||||
JanetTable *attr) {
|
||||
switch (janet_type(left)) {
|
||||
default:
|
||||
janetc_cerror(c, "unexpected type in destructuring");
|
||||
@@ -134,58 +135,92 @@ static int destructure(JanetCompiler *c,
|
||||
/* Leaf, assign right to left */
|
||||
return leaf(c, janet_unwrap_symbol(left), right, attr);
|
||||
case JANET_TUPLE:
|
||||
case JANET_ARRAY:
|
||||
{
|
||||
int32_t i, len;
|
||||
const Janet *values;
|
||||
janet_indexed_view(left, &values, &len);
|
||||
for (i = 0; i < len; i++) {
|
||||
JanetSlot nextright = janetc_farslot(c);
|
||||
Janet subval = values[i];
|
||||
if (i < 0x100) {
|
||||
janetc_emit_ssu(c, JOP_GET_INDEX, nextright, right, (uint8_t) i, 1);
|
||||
} else {
|
||||
JanetSlot k = janetc_cslot(janet_wrap_integer(i));
|
||||
janetc_emit_sss(c, JOP_GET, nextright, right, k, 1);
|
||||
}
|
||||
if (destructure(c, subval, nextright, leaf, attr))
|
||||
janetc_freeslot(c, nextright);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
case JANET_TABLE:
|
||||
case JANET_STRUCT:
|
||||
{
|
||||
const JanetKV *kvs = NULL;
|
||||
int32_t i, cap, len;
|
||||
janet_dictionary_view(left, &kvs, &len, &cap);
|
||||
for (i = 0; i < cap; i++) {
|
||||
if (janet_checktype(kvs[i].key, JANET_NIL)) continue;
|
||||
JanetSlot nextright = janetc_farslot(c);
|
||||
JanetSlot k = janetc_value(janetc_fopts_default(c), kvs[i].key);
|
||||
case JANET_ARRAY: {
|
||||
int32_t len = 0;
|
||||
const Janet *values = NULL;
|
||||
janet_indexed_view(left, &values, &len);
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
JanetSlot nextright = janetc_farslot(c);
|
||||
Janet subval = values[i];
|
||||
if (i < 0x100) {
|
||||
janetc_emit_ssu(c, JOP_GET_INDEX, nextright, right, (uint8_t) i, 1);
|
||||
} else {
|
||||
JanetSlot k = janetc_cslot(janet_wrap_integer(i));
|
||||
janetc_emit_sss(c, JOP_GET, nextright, right, k, 1);
|
||||
if (destructure(c, kvs[i].value, nextright, leaf, attr))
|
||||
janetc_freeslot(c, nextright);
|
||||
}
|
||||
if (destructure(c, subval, nextright, leaf, attr))
|
||||
janetc_freeslot(c, nextright);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
case JANET_TABLE:
|
||||
case JANET_STRUCT: {
|
||||
const JanetKV *kvs = NULL;
|
||||
int32_t cap = 0, len = 0;
|
||||
janet_dictionary_view(left, &kvs, &len, &cap);
|
||||
for (int32_t i = 0; i < cap; i++) {
|
||||
if (janet_checktype(kvs[i].key, JANET_NIL)) continue;
|
||||
JanetSlot nextright = janetc_farslot(c);
|
||||
JanetSlot k = janetc_value(janetc_fopts_default(c), kvs[i].key);
|
||||
janetc_emit_sss(c, JOP_GET, nextright, right, k, 1);
|
||||
if (destructure(c, kvs[i].value, nextright, leaf, attr))
|
||||
janetc_freeslot(c, nextright);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a source map for definitions. */
|
||||
static const Janet *janetc_make_sourcemap(JanetCompiler *c) {
|
||||
Janet *tup = janet_tuple_begin(3);
|
||||
tup[0] = c->source ? janet_wrap_string(c->source) : janet_wrap_nil();
|
||||
tup[1] = janet_wrap_integer(c->current_mapping.start);
|
||||
tup[2] = janet_wrap_integer(c->current_mapping.end);
|
||||
return janet_tuple_end(tup);
|
||||
}
|
||||
|
||||
static JanetSlot janetc_varset(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
/*JanetFopts subopts = janetc_fopts_default(opts.compiler);*/
|
||||
/*JanetSlot ret, dest;*/
|
||||
Janet head;
|
||||
if (argn != 2) {
|
||||
janetc_cerror(opts.compiler, "expected 2 arguments");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
head = argv[0];
|
||||
if (!janet_checktype(head, JANET_SYMBOL)) {
|
||||
janetc_cerror(opts.compiler, "expected symbol");
|
||||
JanetFopts subopts = janetc_fopts_default(opts.compiler);
|
||||
if (janet_checktype(argv[0], JANET_SYMBOL)) {
|
||||
/* Normal var - (set a 1) */
|
||||
const uint8_t *sym = janet_unwrap_symbol(argv[0]);
|
||||
JanetSlot dest = janetc_resolve(opts.compiler, sym);
|
||||
if (!(dest.flags & JANET_SLOT_MUTABLE)) {
|
||||
janetc_cerror(opts.compiler, "cannot set constant");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
subopts.flags = JANET_FOPTS_HINT;
|
||||
subopts.hint = dest;
|
||||
JanetSlot ret = janetc_value(subopts, argv[1]);
|
||||
janetc_copy(opts.compiler, dest, ret);
|
||||
return ret;
|
||||
} else if (janet_checktype(argv[0], JANET_TUPLE)) {
|
||||
/* Set a field (setf behavior) - (set (tab :key) 2) */
|
||||
const Janet *tup = janet_unwrap_tuple(argv[0]);
|
||||
/* Tuple must have 2 elements */
|
||||
if (janet_tuple_length(tup) != 2) {
|
||||
janetc_cerror(opts.compiler, "expected 2 element tuple for l-value to set");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
JanetSlot ds = janetc_value(subopts, tup[0]);
|
||||
JanetSlot key = janetc_value(subopts, tup[1]);
|
||||
/* Can't be tail position because we will emit a PUT instruction afterwards */
|
||||
/* Also can't drop either */
|
||||
opts.flags &= ~(JANET_FOPTS_TAIL | JANET_FOPTS_DROP);
|
||||
JanetSlot rvalue = janetc_value(opts, argv[1]);
|
||||
/* Emit the PUT instruction */
|
||||
janetc_emit_sss(opts.compiler, JOP_PUT, ds, key, rvalue, 0);
|
||||
return rvalue;
|
||||
} else {
|
||||
/* Error */
|
||||
janetc_cerror(opts.compiler, "expected symbol or tuple for l-value to set");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
return janetc_sym_lvalue(opts, janet_unwrap_symbol(head), argv[1]);
|
||||
}
|
||||
|
||||
/* Add attributes to a global def or var table */
|
||||
@@ -198,11 +233,11 @@ static JanetTable *handleattr(JanetCompiler *c, int32_t argn, const Janet *argv)
|
||||
default:
|
||||
janetc_cerror(c, "could not add metadata to binding");
|
||||
break;
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
janet_table_put(tab, attr, janet_wrap_true());
|
||||
break;
|
||||
case JANET_STRING:
|
||||
janet_table_put(tab, janet_csymbolv(":doc"), attr);
|
||||
janet_table_put(tab, janet_ckeywordv("doc"), attr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -226,8 +261,8 @@ static JanetSlot dohead(JanetCompiler *c, JanetFopts opts, Janet *head, int32_t
|
||||
/* Def or var a symbol in a local scope */
|
||||
static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, JanetSlot ret) {
|
||||
int isUnnamedRegister = !(ret.flags & JANET_SLOT_NAMED) &&
|
||||
ret.index > 0 &&
|
||||
ret.envindex >= 0;
|
||||
ret.index > 0 &&
|
||||
ret.envindex >= 0;
|
||||
if (!isUnnamedRegister) {
|
||||
/* Slot is not able to be named */
|
||||
JanetSlot localslot = janetc_farslot(c);
|
||||
@@ -240,19 +275,20 @@ static int namelocal(JanetCompiler *c, const uint8_t *head, int32_t flags, Janet
|
||||
}
|
||||
|
||||
static int varleaf(
|
||||
JanetCompiler *c,
|
||||
const uint8_t *sym,
|
||||
JanetSlot s,
|
||||
JanetTable *attr) {
|
||||
JanetCompiler *c,
|
||||
const uint8_t *sym,
|
||||
JanetSlot s,
|
||||
JanetTable *reftab) {
|
||||
if (c->scope->flags & JANET_SCOPE_TOP) {
|
||||
/* Global var, generate var */
|
||||
JanetSlot refslot;
|
||||
JanetTable *reftab = janet_table(1);
|
||||
reftab->proto = attr;
|
||||
JanetTable *entry = janet_table_clone(reftab);
|
||||
JanetArray *ref = janet_array(1);
|
||||
janet_array_push(ref, janet_wrap_nil());
|
||||
janet_table_put(reftab, janet_csymbolv(":ref"), janet_wrap_array(ref));
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(reftab));
|
||||
janet_table_put(entry, janet_ckeywordv("ref"), janet_wrap_array(ref));
|
||||
janet_table_put(entry, janet_ckeywordv("source-map"),
|
||||
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(entry));
|
||||
refslot = janetc_cslot(janet_wrap_array(ref));
|
||||
janetc_emit_ssu(c, JOP_PUT_INDEX, refslot, s, 0, 0);
|
||||
return 1;
|
||||
@@ -272,18 +308,19 @@ static JanetSlot janetc_var(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
}
|
||||
|
||||
static int defleaf(
|
||||
JanetCompiler *c,
|
||||
const uint8_t *sym,
|
||||
JanetSlot s,
|
||||
JanetTable *attr) {
|
||||
JanetCompiler *c,
|
||||
const uint8_t *sym,
|
||||
JanetSlot s,
|
||||
JanetTable *tab) {
|
||||
if (c->scope->flags & JANET_SCOPE_TOP) {
|
||||
JanetTable *tab = janet_table(2);
|
||||
tab->proto = attr;
|
||||
JanetSlot valsym = janetc_cslot(janet_csymbolv(":value"));
|
||||
JanetSlot tabslot = janetc_cslot(janet_wrap_table(tab));
|
||||
JanetTable *entry = janet_table_clone(tab);
|
||||
janet_table_put(entry, janet_ckeywordv("source-map"),
|
||||
janet_wrap_tuple(janetc_make_sourcemap(c)));
|
||||
JanetSlot valsym = janetc_cslot(janet_ckeywordv("value"));
|
||||
JanetSlot tabslot = janetc_cslot(janet_wrap_table(entry));
|
||||
|
||||
/* Add env entry to env */
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(tab));
|
||||
janet_table_put(c->env, janet_wrap_symbol(sym), janet_wrap_table(entry));
|
||||
|
||||
/* Put value in table when evaulated */
|
||||
janetc_emit_sss(c, JOP_PUT, tabslot, valsym, s, 0);
|
||||
@@ -340,8 +377,8 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
|
||||
/* Set target for compilation */
|
||||
target = (drop || tail)
|
||||
? janetc_cslot(janet_wrap_nil())
|
||||
: janetc_gettarget(opts);
|
||||
? janetc_cslot(janet_wrap_nil())
|
||||
: janetc_gettarget(opts);
|
||||
|
||||
/* Compile condition */
|
||||
janetc_scope(&condscope, c, 0, "if");
|
||||
@@ -356,8 +393,9 @@ static JanetSlot janetc_if(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
falsebody = truebody;
|
||||
truebody = temp;
|
||||
}
|
||||
janetc_scope(&tempscope, c, 0, "if-body");
|
||||
target = janetc_value(bodyopts, truebody);
|
||||
janetc_scope(&tempscope, c, 0, "if-true");
|
||||
right = janetc_value(bodyopts, truebody);
|
||||
if (!drop && !tail) janetc_copy(c, target, right);
|
||||
janetc_popscope(c);
|
||||
janetc_throwaway(bodyopts, falsebody);
|
||||
janetc_popscope(c);
|
||||
@@ -433,6 +471,61 @@ static int32_t janetc_addfuncdef(JanetCompiler *c, JanetFuncDef *def) {
|
||||
return janet_v_count(scope->defs) - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* break
|
||||
*
|
||||
* jump :end or retn if in function
|
||||
*/
|
||||
static JanetSlot janetc_break(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
JanetCompiler *c = opts.compiler;
|
||||
JanetScope *scope = c->scope;
|
||||
if (argn > 1) {
|
||||
janetc_cerror(c, "expected at most 1 argument");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
|
||||
/* Find scope to break from */
|
||||
while (scope) {
|
||||
if (scope->flags & (JANET_SCOPE_FUNCTION | JANET_SCOPE_WHILE))
|
||||
break;
|
||||
scope = scope->parent;
|
||||
}
|
||||
if (NULL == scope) {
|
||||
janetc_cerror(c, "break must occur in while loop or closure");
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
|
||||
/* Emit code to break from that scope */
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
if (scope->flags & JANET_SCOPE_FUNCTION) {
|
||||
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;
|
||||
} else {
|
||||
/* while loop IIFE or no argument */
|
||||
if (argn) {
|
||||
subopts.flags |= JANET_FOPTS_DROP;
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
if (argn) {
|
||||
subopts.flags |= JANET_FOPTS_DROP;
|
||||
janetc_value(subopts, argv[0]);
|
||||
}
|
||||
/* Tag the instruction so the while special can turn it into a proper jump */
|
||||
janetc_emit(c, 0x80 | JOP_JUMP);
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* :whiletop
|
||||
* ...
|
||||
@@ -457,7 +550,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
|
||||
labelwt = janet_v_count(c->buffer);
|
||||
|
||||
janetc_scope(&tempscope, c, 0, "while");
|
||||
janetc_scope(&tempscope, c, JANET_SCOPE_WHILE, "while");
|
||||
|
||||
/* Compile condition */
|
||||
cond = janetc_value(subopts, argv[0]);
|
||||
@@ -475,8 +568,8 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
|
||||
/* Infinite loop does not need to check condition */
|
||||
labelc = infinite
|
||||
? 0
|
||||
: janetc_emit_si(c, JOP_JUMP_IF_NOT, cond, 0, 0);
|
||||
? 0
|
||||
: janetc_emit_si(c, JOP_JUMP_IF_NOT, cond, 0, 0);
|
||||
|
||||
/* Compile body */
|
||||
for (i = 1; i < argn; i++) {
|
||||
@@ -497,7 +590,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
/* Recompile in the function scope */
|
||||
cond = janetc_value(subopts, argv[0]);
|
||||
if (!(cond.flags & JANET_SLOT_CONSTANT)) {
|
||||
/* If not an infinte loop, return nil when condition false */
|
||||
/* If not an infinite loop, return nil when condition false */
|
||||
janetc_emit_si(c, JOP_JUMP_IF, cond, 2, 0);
|
||||
janetc_emit(c, JOP_RETURN_NIL);
|
||||
}
|
||||
@@ -522,14 +615,21 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
||||
return janetc_cslot(janet_wrap_nil());
|
||||
}
|
||||
|
||||
/* Compile jump to whiletop */
|
||||
/* Compile jump to :whiletop */
|
||||
labeljt = janet_v_count(c->buffer);
|
||||
janetc_emit(c, JOP_JUMP);
|
||||
|
||||
/* Calculate jumps */
|
||||
labeld = janet_v_count(c->buffer);
|
||||
if (!infinite) c->buffer[labelc] |= (labeld - labelc) << 16;
|
||||
c->buffer[labeljt] |= (labelwt - labeljt) << 8;
|
||||
if (!infinite) c->buffer[labelc] |= (uint32_t)(labeld - labelc) << 16;
|
||||
c->buffer[labeljt] |= (uint32_t)(labelwt - labeljt) << 8;
|
||||
|
||||
/* Calculate breaks */
|
||||
for (int32_t i = labelwt; i < labeld; i++) {
|
||||
if (c->buffer[i] == (0x80 | JOP_JUMP)) {
|
||||
c->buffer[i] = JOP_JUMP | ((labeld - i) << 8);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pop scope and return nil slot */
|
||||
janetc_popscope(c);
|
||||
@@ -543,16 +643,18 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
JanetSlot ret;
|
||||
Janet head;
|
||||
JanetScope fnscope;
|
||||
int32_t paramcount, argi, parami, arity, defindex, i;
|
||||
int32_t paramcount, argi, parami, arity, min_arity, max_arity, defindex, i;
|
||||
JanetFopts subopts = janetc_fopts_default(c);
|
||||
const Janet *params;
|
||||
const char *errmsg = NULL;
|
||||
|
||||
/* Function flags */
|
||||
int vararg = 0;
|
||||
int fixarity = 1;
|
||||
int structarg = 0;
|
||||
int allow_extra = 0;
|
||||
int selfref = 0;
|
||||
int seenamp = 0;
|
||||
int seenopt = 0;
|
||||
|
||||
/* Begin function */
|
||||
c->scope->flags |= JANET_SCOPE_CLOSURE;
|
||||
@@ -575,6 +677,9 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Keep track of destructured parameters */
|
||||
JanetSlot *destructed_params = NULL;
|
||||
|
||||
/* Compile function parameters */
|
||||
params = janet_unwrap_tuple(argv[parami]);
|
||||
paramcount = janet_tuple_length(params);
|
||||
@@ -583,27 +688,68 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
Janet param = params[i];
|
||||
if (janet_checktype(param, JANET_SYMBOL)) {
|
||||
/* Check for varargs and unfixed arity */
|
||||
if ((!seenamp) &&
|
||||
(0 == janet_cstrcmp(janet_unwrap_symbol(param), "&"))) {
|
||||
seenamp = 1;
|
||||
fixarity = 0;
|
||||
if (i == paramcount - 1) {
|
||||
if (!janet_cstrcmp(janet_unwrap_symbol(param), "&")) {
|
||||
if (seenamp) {
|
||||
errmsg = "& in unexpected location";
|
||||
goto error;
|
||||
} else if (i == paramcount - 1) {
|
||||
allow_extra = 1;
|
||||
arity--;
|
||||
} else if (i == paramcount - 2) {
|
||||
vararg = 1;
|
||||
arity -= 2;
|
||||
} else {
|
||||
errmsg = "variable argument symbol in unexpected location";
|
||||
errmsg = "& in unexpected location";
|
||||
goto error;
|
||||
}
|
||||
seenamp = 1;
|
||||
} else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&opt")) {
|
||||
if (seenopt) {
|
||||
errmsg = "only one &opt allowed";
|
||||
goto error;
|
||||
} else if (i == paramcount - 1) {
|
||||
errmsg = "&opt cannot be last item in parameter list";
|
||||
goto error;
|
||||
}
|
||||
min_arity = i;
|
||||
arity--;
|
||||
seenopt = 1;
|
||||
} else if (!janet_cstrcmp(janet_unwrap_symbol(param), "&keys")) {
|
||||
if (seenamp) {
|
||||
errmsg = "&keys in unexpected location";
|
||||
goto error;
|
||||
} else if (i == paramcount - 2) {
|
||||
vararg = 1;
|
||||
structarg = 1;
|
||||
arity -= 2;
|
||||
} else {
|
||||
errmsg = "&keys in unexpected location";
|
||||
goto error;
|
||||
}
|
||||
seenamp = 1;
|
||||
} else {
|
||||
janetc_nameslot(c, janet_unwrap_symbol(param), janetc_farslot(c));
|
||||
}
|
||||
} else {
|
||||
destructure(c, param, janetc_farslot(c), defleaf, NULL);
|
||||
janet_v_push(destructed_params, janetc_farslot(c));
|
||||
}
|
||||
}
|
||||
|
||||
/* Compile destructed params */
|
||||
int32_t j = 0;
|
||||
for (i = 0; i < paramcount; i++) {
|
||||
Janet param = params[i];
|
||||
if (!janet_checktype(param, JANET_SYMBOL)) {
|
||||
JanetSlot reg = destructed_params[j++];
|
||||
destructure(c, param, reg, defleaf, NULL);
|
||||
janetc_freeslot(c, reg);
|
||||
}
|
||||
}
|
||||
janet_v_free(destructed_params);
|
||||
|
||||
max_arity = (vararg || allow_extra) ? INT32_MAX : arity;
|
||||
if (!seenopt) min_arity = arity;
|
||||
|
||||
/* Check for self ref */
|
||||
if (selfref) {
|
||||
JanetSlot slot = janetc_farslot(c);
|
||||
@@ -615,18 +761,22 @@ static JanetSlot janetc_fn(JanetFopts opts, int32_t argn, const Janet *argv) {
|
||||
/* Compile function body */
|
||||
if (parami + 1 == argn) {
|
||||
janetc_emit(c, JOP_RETURN_NIL);
|
||||
} else for (argi = parami + 1; argi < argn; argi++) {
|
||||
subopts.flags = (argi == (argn - 1)) ? JANET_FOPTS_TAIL : JANET_FOPTS_DROP;
|
||||
janetc_value(subopts, argv[argi]);
|
||||
if (c->result.status == JANET_COMPILE_ERROR)
|
||||
goto error2;
|
||||
} else {
|
||||
for (argi = parami + 1; argi < argn; argi++) {
|
||||
subopts.flags = (argi == (argn - 1)) ? JANET_FOPTS_TAIL : JANET_FOPTS_DROP;
|
||||
janetc_value(subopts, argv[argi]);
|
||||
if (c->result.status == JANET_COMPILE_ERROR)
|
||||
goto error2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Build function */
|
||||
def = janetc_pop_funcdef(c);
|
||||
def->arity = arity;
|
||||
if (fixarity) def->flags |= JANET_FUNCDEF_FLAG_FIXARITY;
|
||||
def->min_arity = min_arity;
|
||||
def->max_arity = max_arity;
|
||||
if (vararg) def->flags |= JANET_FUNCDEF_FLAG_VARARG;
|
||||
if (structarg) def->flags |= JANET_FUNCDEF_FLAG_STRUCTARG;
|
||||
|
||||
if (selfref) def->name = janet_unwrap_symbol(head);
|
||||
defindex = janetc_addfuncdef(c, def);
|
||||
@@ -648,16 +798,16 @@ error2:
|
||||
|
||||
/* Keep in lexicographic order */
|
||||
static const JanetSpecial janetc_specials[] = {
|
||||
{":=", janetc_varset},
|
||||
{"break", janetc_break},
|
||||
{"def", janetc_def},
|
||||
{"do", janetc_do},
|
||||
{"fn", janetc_fn},
|
||||
{"if", janetc_if},
|
||||
{"quasiquote", janetc_quasiquote},
|
||||
{"quote", janetc_quote},
|
||||
{"set", janetc_varset},
|
||||
{"splice", janetc_splice},
|
||||
{"unquote", janetc_unquote},
|
||||
{"unquote", janetc_unquote},
|
||||
{"var", janetc_var},
|
||||
{"while", janetc_while}
|
||||
};
|
||||
@@ -665,9 +815,9 @@ static const JanetSpecial janetc_specials[] = {
|
||||
/* Find a special */
|
||||
const JanetSpecial *janetc_special(const uint8_t *name) {
|
||||
return janet_strbinsearch(
|
||||
&janetc_specials,
|
||||
sizeof(janetc_specials)/sizeof(JanetSpecial),
|
||||
sizeof(JanetSpecial),
|
||||
name);
|
||||
&janetc_specials,
|
||||
sizeof(janetc_specials) / sizeof(JanetSpecial),
|
||||
sizeof(JanetSpecial),
|
||||
name);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -27,10 +27,10 @@
|
||||
|
||||
/* The VM state. Rather than a struct that is passed
|
||||
* around, the vm state is global for simplicity. If
|
||||
* at some point a a global state object, or context,
|
||||
* is required to be passed around, this is waht would
|
||||
* be in it. However, thread local globals for interpreter
|
||||
* state should allow easy multithreading. */
|
||||
* at some point a global state object, or context,
|
||||
* is required to be passed around, this is what would
|
||||
* be in it. However, thread local global variables for interpreter
|
||||
* state should allow easy multi-threading. */
|
||||
|
||||
/* How many VM stacks have been entered */
|
||||
extern JANET_THREAD_LOCAL int janet_vm_stackn;
|
||||
@@ -39,7 +39,12 @@ extern JANET_THREAD_LOCAL int janet_vm_stackn;
|
||||
* Set and unset by janet_run. */
|
||||
extern JANET_THREAD_LOCAL JanetFiber *janet_vm_fiber;
|
||||
|
||||
/* The global registry for c functions. Used to store metadata
|
||||
/* The current pointer to the inner most jmp_buf. The current
|
||||
* return point for panics. */
|
||||
extern JANET_THREAD_LOCAL jmp_buf *janet_vm_jmp_buf;
|
||||
extern JANET_THREAD_LOCAL Janet *janet_vm_return_reg;
|
||||
|
||||
/* The global registry for c functions. Used to store meta-data
|
||||
* along with otherwise bare c function pointers. */
|
||||
extern JANET_THREAD_LOCAL JanetTable *janet_vm_registry;
|
||||
|
||||
@@ -60,4 +65,9 @@ extern JANET_THREAD_LOCAL Janet *janet_vm_roots;
|
||||
extern JANET_THREAD_LOCAL uint32_t janet_vm_root_count;
|
||||
extern JANET_THREAD_LOCAL uint32_t janet_vm_root_capacity;
|
||||
|
||||
/* Scratch memory */
|
||||
extern JANET_THREAD_LOCAL void **janet_scratch_mem;
|
||||
extern JANET_THREAD_LOCAL size_t janet_scratch_cap;
|
||||
extern JANET_THREAD_LOCAL size_t janet_scratch_len;
|
||||
|
||||
#endif /* JANET_STATE_H_defined */
|
||||
|
||||
1368
src/core/string.c
1368
src/core/string.c
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -21,142 +21,235 @@
|
||||
*/
|
||||
|
||||
/* Use a custom double parser instead of libc's strtod for better portability
|
||||
* and control. Also, uses a less strict rounding method than ieee to not incur
|
||||
* the cost of 4000 loc and dependence on arbitary precision arithmetic. There
|
||||
* is no plan to use arbitrary precision arithmetic for parsing numbers, and a
|
||||
* formal rounding mode has yet to be chosen (round towards 0 seems
|
||||
* reasonable).
|
||||
* and control.
|
||||
*
|
||||
* This version has been modified for much greater flexibility in parsing, such
|
||||
* as choosing the radix, supporting integer output, and returning Janets
|
||||
* directly.
|
||||
* as choosing the radix and supporting scientific notation with any radix.
|
||||
*
|
||||
* Numbers are of the form [-+]R[rR]I.F[eE&][-+]X where R is the radix, I is
|
||||
* the integer part, F is the fractional part, and X is the exponent. All
|
||||
* signs, radix, decimal point, fractional part, and exponent can be ommited.
|
||||
* The number will be considered and integer if the there is no decimal point
|
||||
* and no exponent. Any number greater the 2^32-1 or less than -(2^32) will be
|
||||
* coerced to a double. If there is an error, the function janet_scan_number will
|
||||
* return a janet nil. The radix is assumed to be 10 if omitted, and the E
|
||||
* Numbers are of the form [-+]R[rR]I.F[eE&][-+]X in pseudo-regex form, where R
|
||||
* is the radix, I is the integer part, F is the fractional part, and X is the
|
||||
* exponent. All signs, radix, decimal point, fractional part, and exponent can
|
||||
* be omitted. The radix is assumed to be 10 if omitted, and the E or e
|
||||
* separator for the exponent can only be used when the radix is 10. This is
|
||||
* because E is a vaid digit in bases 15 or greater. For bases greater than 10,
|
||||
* the letters are used as digitis. A through Z correspond to the digits 10
|
||||
* 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
|
||||
* '16rdeadbeef'. janet_scan_number also supports some c style syntax for
|
||||
* hexidecimal literals. The previous number could also be written
|
||||
* '0xdeadbeef'. Note that in this case, the number will actually be a double
|
||||
* as it will not fit in the range for a signed 32 bit integer. The string
|
||||
* '0xbeef' would parse to an integer as it is in the range of an int32_t. */
|
||||
* '0xdeadbeef'.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Lookup table for getting values of characters when parsing numbers. Handles
|
||||
* digits 0-9 and a-z (and A-Z). A-Z have values of 10 to 35. */
|
||||
static uint8_t digit_lookup[128] = {
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0,1,2,3,4,5,6,7,8,9,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
|
||||
25,26,27,28,29,30,31,32,33,34,35,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
|
||||
25,26,27,28,29,30,31,32,33,34,35,0xff,0xff,0xff,0xff,0xff
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
|
||||
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
#define BIGNAT_NBIT 31
|
||||
#define BIGNAT_BASE 0x80000000U
|
||||
|
||||
/* Allow for large mantissa. BigNat is a natural number. */
|
||||
struct BigNat {
|
||||
uint32_t first_digit; /* First digit so we don't need to allocate when not needed. */
|
||||
int32_t n; /* n digits */
|
||||
int32_t cap; /* allocated digit capacity */
|
||||
uint32_t *digits; /* Each digit is base (2 ^ 31). Digits are least significant first. */
|
||||
};
|
||||
|
||||
/* Initialize a bignat to 0 */
|
||||
static void bignat_zero(struct BigNat *x) {
|
||||
x->first_digit = 0;
|
||||
x->n = 0;
|
||||
x->cap = 0;
|
||||
x->digits = NULL;
|
||||
}
|
||||
|
||||
/* Allocate n more digits for mant. Return a pointer to these digits. */
|
||||
static uint32_t *bignat_extra(struct BigNat *mant, int32_t n) {
|
||||
int32_t oldn = mant->n;
|
||||
int32_t newn = oldn + n;
|
||||
if (mant->cap < newn) {
|
||||
int32_t newcap = 2 * newn;
|
||||
uint32_t *mem = realloc(mant->digits, newcap * sizeof(uint32_t));
|
||||
if (NULL == mem) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
mant->cap = newcap;
|
||||
mant->digits = mem;
|
||||
}
|
||||
mant->n = newn;
|
||||
return mant->digits + oldn;
|
||||
}
|
||||
|
||||
/* Append a digit */
|
||||
static void bignat_append(struct BigNat *mant, uint32_t dig) {
|
||||
bignat_extra(mant, 1)[0] = dig;
|
||||
}
|
||||
|
||||
/* Multiply the mantissa mant by a factor and the add a term
|
||||
* in one operation. factor will be between 2 and 36^4,
|
||||
* term will be between 0 and 36. */
|
||||
static void bignat_muladd(struct BigNat *mant, uint32_t factor, uint32_t term) {
|
||||
int32_t i;
|
||||
uint64_t carry = ((uint64_t) mant->first_digit) * factor + term;
|
||||
mant->first_digit = carry % BIGNAT_BASE;
|
||||
carry /= BIGNAT_BASE;
|
||||
for (i = 0; i < mant->n; i++) {
|
||||
carry += ((uint64_t) mant->digits[i]) * factor;
|
||||
mant->digits[i] = carry % BIGNAT_BASE;
|
||||
carry /= BIGNAT_BASE;
|
||||
}
|
||||
if (carry) bignat_append(mant, (uint32_t) carry);
|
||||
}
|
||||
|
||||
/* Divide the mantissa mant by a factor. Drop the remainder. */
|
||||
static void bignat_div(struct BigNat *mant, uint32_t divisor) {
|
||||
int32_t i;
|
||||
uint32_t quotient, remainder;
|
||||
uint64_t dividend;
|
||||
remainder = 0, quotient = 0;
|
||||
for (i = mant->n - 1; i >= 0; i--) {
|
||||
dividend = ((uint64_t)remainder * BIGNAT_BASE) + mant->digits[i];
|
||||
if (i < mant->n - 1) mant->digits[i + 1] = quotient;
|
||||
quotient = (uint32_t)(dividend / divisor);
|
||||
remainder = (uint32_t)(dividend % divisor);
|
||||
mant->digits[i] = remainder;
|
||||
}
|
||||
dividend = ((uint64_t)remainder * BIGNAT_BASE) + mant->first_digit;
|
||||
if (mant->n && mant->digits[mant->n - 1] == 0) mant->n--;
|
||||
mant->first_digit = (uint32_t)(dividend / divisor);
|
||||
}
|
||||
|
||||
/* Shift left by a multiple of BIGNAT_NBIT */
|
||||
static void bignat_lshift_n(struct BigNat *mant, int n) {
|
||||
if (!n) return;
|
||||
int32_t oldn = mant->n;
|
||||
bignat_extra(mant, n);
|
||||
memmove(mant->digits + n, mant->digits, sizeof(uint32_t) * oldn);
|
||||
memset(mant->digits, 0, sizeof(uint32_t) * (n - 1));
|
||||
mant->digits[n - 1] = mant->first_digit;
|
||||
mant->first_digit = 0;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define clz(x) __builtin_clz(x)
|
||||
#else
|
||||
static int clz(uint32_t x) {
|
||||
int n = 0;
|
||||
if (x <= 0x0000ffff) n += 16, x <<= 16;
|
||||
if (x <= 0x00ffffff) n += 8, x <<= 8;
|
||||
if (x <= 0x0fffffff) n += 4, x <<= 4;
|
||||
if (x <= 0x3fffffff) n += 2, x <<= 2;
|
||||
if (x <= 0x7fffffff) n ++;
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Extract double value from mantissa */
|
||||
static double bignat_extract(struct BigNat *mant, int32_t exponent2) {
|
||||
uint64_t top53;
|
||||
int32_t n = mant->n;
|
||||
/* Get most significant 53 bits from mant. Bit 52 (0 indexed) should
|
||||
* always be 1. This is essentially a large right shift on mant.*/
|
||||
if (n) {
|
||||
/* Two or more digits */
|
||||
uint64_t d1 = mant->digits[n - 1]; /* MSD (non-zero) */
|
||||
uint64_t d2 = (n == 1) ? mant->first_digit : mant->digits[n - 2];
|
||||
uint64_t d3 = (n > 2) ? mant->digits[n - 3] : (n == 2) ? mant->first_digit : 0;
|
||||
int lz = clz((uint32_t) d1);
|
||||
int nbits = 32 - lz;
|
||||
/* First get 54 bits */
|
||||
top53 = (d2 << (54 - BIGNAT_NBIT)) + (d3 >> (2 * BIGNAT_NBIT - 54));
|
||||
top53 >>= nbits;
|
||||
top53 |= (d1 << (54 - nbits));
|
||||
/* Rounding based on lowest bit of 54 */
|
||||
if (top53 & 1) top53++;
|
||||
top53 >>= 1;
|
||||
if (top53 > 0x1FffffFFFFffffUL) {
|
||||
top53 >>= 1;
|
||||
exponent2++;
|
||||
}
|
||||
/* Correct exponent - to correct for large right shift to mantissa. */
|
||||
exponent2 += (nbits - 53) + BIGNAT_NBIT * n;
|
||||
} else {
|
||||
/* One digit */
|
||||
top53 = mant->first_digit;
|
||||
}
|
||||
return ldexp((double)top53, exponent2);
|
||||
}
|
||||
|
||||
/* Read in a mantissa and exponent of a certain base, and give
|
||||
* back the double value. Should properly handle 0s, Inifinties, and
|
||||
* back the double value. Should properly handle 0s, infinities, and
|
||||
* denormalized numbers. (When the exponent values are too large) */
|
||||
static double convert(
|
||||
int negative,
|
||||
uint64_t mantissa,
|
||||
int32_t base,
|
||||
int32_t exponent) {
|
||||
int negative,
|
||||
struct BigNat *mant,
|
||||
int32_t base,
|
||||
int32_t exponent) {
|
||||
|
||||
int32_t exponent2 = 0;
|
||||
|
||||
/* Short circuit zero and huge numbers */
|
||||
if (mantissa == 0)
|
||||
return 0.0;
|
||||
if (exponent > 1022)
|
||||
if (mant->n == 0 && mant->first_digit == 0)
|
||||
return negative ? -0.0 : 0.0;
|
||||
if (exponent > 1023)
|
||||
return negative ? -INFINITY : INFINITY;
|
||||
|
||||
/* TODO add fast paths */
|
||||
/* Final value is X = mant * base ^ exponent * 2 ^ exponent2
|
||||
* Get exponent to zero while holding X constant. */
|
||||
|
||||
/* Convert exponent on the base into exponent2, the power of
|
||||
* 2 the will be used. Modify the mantissa as we convert. */
|
||||
if (exponent > 0) {
|
||||
/* Make the mantissa large enough so no precision is lost */
|
||||
while (mantissa <= 0x03ffffffffffffffULL && exponent > 0) {
|
||||
mantissa *= base;
|
||||
exponent--;
|
||||
}
|
||||
while (exponent > 0) {
|
||||
/* Allow 6 bits of room when multiplying. This is because
|
||||
* the largest base is 36, which is 6 bits. The space of 6 should
|
||||
* prevent overflow.*/
|
||||
mantissa >>= 1;
|
||||
exponent2++;
|
||||
if (mantissa <= 0x03ffffffffffffffULL) {
|
||||
mantissa *= base;
|
||||
exponent--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (exponent < 0) {
|
||||
mantissa <<= 1;
|
||||
exponent2--;
|
||||
/* Ensure that the last bit is set for minimum error
|
||||
* before dividing by the base */
|
||||
if (mantissa > 0x7fffffffffffffffULL) {
|
||||
mantissa /= base;
|
||||
exponent++;
|
||||
}
|
||||
}
|
||||
/* Positive exponents are simple */
|
||||
for (; exponent > 3; exponent -= 4) bignat_muladd(mant, base * base * base * base, 0);
|
||||
for (; exponent > 1; exponent -= 2) bignat_muladd(mant, base * base, 0);
|
||||
for (; exponent > 0; exponent -= 1) bignat_muladd(mant, base, 0);
|
||||
|
||||
/* Negative exponents are tricky - we don't want to loose bits
|
||||
* from integer division, so we need to premultiply. */
|
||||
if (exponent < 0) {
|
||||
int32_t shamt = 5 - exponent / 4;
|
||||
bignat_lshift_n(mant, shamt);
|
||||
exponent2 -= shamt * BIGNAT_NBIT;
|
||||
for (; exponent < -3; exponent += 4) bignat_div(mant, base * base * base * base);
|
||||
for (; exponent < -1; exponent += 2) bignat_div(mant, base * base);
|
||||
for (; exponent < 0; exponent += 1) bignat_div(mant, base);
|
||||
}
|
||||
|
||||
return negative
|
||||
? -ldexp((double) mantissa, exponent2)
|
||||
: ldexp((double) mantissa, exponent2);
|
||||
? -bignat_extract(mant, exponent2)
|
||||
: bignat_extract(mant, exponent2);
|
||||
}
|
||||
|
||||
/* Result of scanning a number source string. Will be further processed
|
||||
* depending on the desired resultant type. */
|
||||
struct JanetScanRes {
|
||||
uint64_t mant;
|
||||
int32_t ex;
|
||||
int error;
|
||||
int base;
|
||||
int seenpoint;
|
||||
int foundexp;
|
||||
int neg;
|
||||
};
|
||||
|
||||
/* Get the mantissa and exponent of decimal number. The
|
||||
* mantissa will be stored in a 64 bit unsigned integer (always positive).
|
||||
* The exponent will be in a signed 32 bit integer. Will also check if
|
||||
* the decimal point has been seen. Returns -1 if there is an invalid
|
||||
* number. */
|
||||
static struct JanetScanRes janet_scan_impl(
|
||||
const uint8_t *str,
|
||||
int32_t len) {
|
||||
|
||||
struct JanetScanRes res;
|
||||
/* Scan a real (double) from a string. If the string cannot be converted into
|
||||
* and integer, set *err to 1 and return 0. */
|
||||
int janet_scan_number(
|
||||
const uint8_t *str,
|
||||
int32_t len,
|
||||
double *out) {
|
||||
const uint8_t *end = str + len;
|
||||
|
||||
/* Initialize flags */
|
||||
int seenadigit = 0;
|
||||
int gotradix = 0;
|
||||
|
||||
/* Initialize result */
|
||||
res.mant = 0;
|
||||
res.ex = 0;
|
||||
res.error = 0;
|
||||
res.base = 10;
|
||||
res.seenpoint = 0;
|
||||
res.foundexp = 0;
|
||||
res.neg = 0;
|
||||
int ex = 0;
|
||||
int base = 10;
|
||||
int seenpoint = 0;
|
||||
int foundexp = 0;
|
||||
int neg = 0;
|
||||
struct BigNat mant;
|
||||
bignat_zero(&mant);
|
||||
|
||||
/* Prevent some kinds of overflow bugs relating to the exponent
|
||||
* overflowing. For example, if a string was passed 2GB worth of 0s after
|
||||
@@ -168,57 +261,60 @@ static struct JanetScanRes janet_scan_impl(
|
||||
/* Get sign */
|
||||
if (str >= end) goto error;
|
||||
if (*str == '-') {
|
||||
res.neg = 1;
|
||||
neg = 1;
|
||||
str++;
|
||||
} else if (*str == '+') {
|
||||
str++;
|
||||
}
|
||||
|
||||
/* Check for leading 0x or digit digit r */
|
||||
if (str + 1 < end && str[0] == '0' && str[1] == 'x') {
|
||||
base = 16;
|
||||
str += 2;
|
||||
} else if (str + 1 < end &&
|
||||
str[0] >= '0' && str[0] <= '9' &&
|
||||
str[1] == 'r') {
|
||||
base = str[0] - '0';
|
||||
str += 2;
|
||||
} else if (str + 2 < end &&
|
||||
str[0] >= '0' && str[0] <= '9' &&
|
||||
str[1] >= '0' && str[1] <= '9' &&
|
||||
str[2] == 'r') {
|
||||
base = 10 * (str[0] - '0') + (str[1] - '0');
|
||||
if (base < 2 || base > 36) goto error;
|
||||
str += 3;
|
||||
}
|
||||
|
||||
/* Skip leading zeros */
|
||||
while (str < end && (*str == '0' || *str == '.')) {
|
||||
if (res.seenpoint) res.ex--;
|
||||
if (seenpoint) ex--;
|
||||
if (*str == '.') {
|
||||
if (res.seenpoint) goto error;
|
||||
res.seenpoint = 1;
|
||||
if (seenpoint) goto error;
|
||||
seenpoint = 1;
|
||||
} else {
|
||||
seenadigit = 1;
|
||||
}
|
||||
seenadigit = 1;
|
||||
str++;
|
||||
}
|
||||
|
||||
/* Parse significant digits */
|
||||
while (str < end) {
|
||||
if (*str == '.') {
|
||||
if (res.seenpoint) goto error;
|
||||
res.seenpoint = 1;
|
||||
if (seenpoint) goto error;
|
||||
seenpoint = 1;
|
||||
} else if (*str == '&') {
|
||||
res.foundexp = 1;
|
||||
foundexp = 1;
|
||||
break;
|
||||
} else if (res.base == 10 && (*str == 'E' || *str == 'e')) {
|
||||
res.foundexp = 1;
|
||||
} else if (base == 10 && (*str == 'E' || *str == 'e')) {
|
||||
foundexp = 1;
|
||||
break;
|
||||
} else if (!gotradix && (*str == 'x' || *str == 'X')) {
|
||||
} else if (*str == '_') {
|
||||
if (!seenadigit) goto error;
|
||||
if (res.seenpoint || res.mant > 0) goto error;
|
||||
res.base = 16;
|
||||
res.mant = 0;
|
||||
seenadigit = 0;
|
||||
gotradix = 1;
|
||||
} else if (!gotradix && (*str == 'r' || *str == 'R')) {
|
||||
if (res.seenpoint) goto error;
|
||||
if (res.mant < 2 || res.mant > 36) goto error;
|
||||
res.base = (int) res.mant;
|
||||
res.mant = 0;
|
||||
seenadigit = 0;
|
||||
gotradix = 1;
|
||||
} else if (*str != '_') {
|
||||
/* underscores are ignored - can be used for separator */
|
||||
} else {
|
||||
int digit = digit_lookup[*str & 0x7F];
|
||||
if (*str > 127 || digit >= res.base) goto error;
|
||||
if (res.seenpoint) res.ex--;
|
||||
if (res.mant > 0x00ffffffffffffff)
|
||||
res.ex++;
|
||||
else
|
||||
res.mant = res.base * res.mant + digit;
|
||||
if (*str > 127 || digit >= base) goto error;
|
||||
if (seenpoint) ex--;
|
||||
bignat_muladd(&mant, base, digit);
|
||||
seenadigit = 1;
|
||||
}
|
||||
str++;
|
||||
@@ -228,7 +324,7 @@ static struct JanetScanRes janet_scan_impl(
|
||||
goto error;
|
||||
|
||||
/* Read exponent */
|
||||
if (str < end && res.foundexp) {
|
||||
if (str < end && foundexp) {
|
||||
int eneg = 0;
|
||||
int ee = 0;
|
||||
seenadigit = 0;
|
||||
@@ -241,90 +337,126 @@ static struct JanetScanRes janet_scan_impl(
|
||||
str++;
|
||||
}
|
||||
/* Skip leading 0s in exponent */
|
||||
while (str < end && *str == '0') str++;
|
||||
while (str < end && ee < (INT32_MAX / 40)) {
|
||||
int digit = digit_lookup[*str & 0x7F];
|
||||
if (*str == '_') {
|
||||
str++;
|
||||
continue;
|
||||
}
|
||||
if (*str > 127 || digit >= res.base) goto error;
|
||||
ee = res.base * ee + digit;
|
||||
while (str < end && *str == '0') {
|
||||
str++;
|
||||
seenadigit = 1;
|
||||
}
|
||||
if (eneg) res.ex -= ee; else res.ex += ee;
|
||||
while (str < end && ee < (INT32_MAX / 40)) {
|
||||
int digit = digit_lookup[*str & 0x7F];
|
||||
if (*str > 127 || digit >= base) goto error;
|
||||
ee = base * ee + digit;
|
||||
str++;
|
||||
seenadigit = 1;
|
||||
}
|
||||
if (eneg) ex -= ee;
|
||||
else ex += ee;
|
||||
}
|
||||
|
||||
if (!seenadigit)
|
||||
goto error;
|
||||
|
||||
return res;
|
||||
*out = convert(neg, &mant, base, ex);
|
||||
free(mant.digits);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
res.error = 1;
|
||||
return res;
|
||||
error:
|
||||
free(mant.digits);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Scan an integer from a string. If the string cannot be converted into
|
||||
* and integer, set *err to 1 and return 0. */
|
||||
int32_t janet_scan_integer(
|
||||
const uint8_t *str,
|
||||
int32_t len,
|
||||
int *err) {
|
||||
struct JanetScanRes res = janet_scan_impl(str, len);
|
||||
int64_t i64;
|
||||
if (res.error) goto error;
|
||||
if (res.seenpoint) goto error;
|
||||
if (res.ex < 0) goto error;
|
||||
i64 = res.neg ? -(int64_t)res.mant : (int64_t)res.mant;
|
||||
while (res.ex > 0) {
|
||||
i64 *= res.base;
|
||||
if (i64 > INT32_MAX || i64 < INT32_MIN) goto error;
|
||||
res.ex--;
|
||||
#ifdef JANET_INT_TYPES
|
||||
|
||||
static int scan_uint64(
|
||||
const uint8_t *str,
|
||||
int32_t len,
|
||||
uint64_t *out,
|
||||
int *neg) {
|
||||
const uint8_t *end = str + len;
|
||||
int seenadigit = 0;
|
||||
int base = 10;
|
||||
*neg = 0;
|
||||
*out = 0;
|
||||
uint64_t accum = 0;
|
||||
/* len max is INT64_MAX in base 2 with _ between each bits */
|
||||
/* '2r' + 64 bits + 63 _ + sign = 130 => 150 for some leading */
|
||||
/* zeros */
|
||||
if (len > 150) return 0;
|
||||
/* Get sign */
|
||||
if (str >= end) return 0;
|
||||
if (*str == '-') {
|
||||
*neg = 1;
|
||||
str++;
|
||||
} else if (*str == '+') {
|
||||
str++;
|
||||
}
|
||||
/* Check for leading 0x or digit digit r */
|
||||
if (str + 1 < end && str[0] == '0' && str[1] == 'x') {
|
||||
base = 16;
|
||||
str += 2;
|
||||
} else if (str + 1 < end &&
|
||||
str[0] >= '0' && str[0] <= '9' &&
|
||||
str[1] == 'r') {
|
||||
base = str[0] - '0';
|
||||
str += 2;
|
||||
} else if (str + 2 < end &&
|
||||
str[0] >= '0' && str[0] <= '9' &&
|
||||
str[1] >= '0' && str[1] <= '9' &&
|
||||
str[2] == 'r') {
|
||||
base = 10 * (str[0] - '0') + (str[1] - '0');
|
||||
if (base < 2 || base > 36) return 0;
|
||||
str += 3;
|
||||
}
|
||||
|
||||
/* Skip leading zeros */
|
||||
while (str < end && *str == '0') {
|
||||
seenadigit = 1;
|
||||
str++;
|
||||
}
|
||||
/* Parse significant digits */
|
||||
while (str < end) {
|
||||
if (*str == '_') {
|
||||
if (!seenadigit) return 0;
|
||||
} else {
|
||||
int digit = digit_lookup[*str & 0x7F];
|
||||
if (*str > 127 || digit >= base) return 0;
|
||||
if (accum > (UINT64_MAX - digit) / base) return 0;
|
||||
accum = accum * base + digit;
|
||||
seenadigit = 1;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
|
||||
if (!seenadigit) return 0;
|
||||
*out = accum;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int janet_scan_int64(const uint8_t *str, int32_t len, int64_t *out) {
|
||||
int neg;
|
||||
uint64_t bi;
|
||||
if (scan_uint64(str, len, &bi, &neg)) {
|
||||
if (neg && bi <= 0x8000000000000000ULL) {
|
||||
*out = -((int64_t) bi);
|
||||
return 1;
|
||||
}
|
||||
if (!neg && bi <= 0x7FFFFFFFFFFFFFFFULL) {
|
||||
*out = bi;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (i64 > INT32_MAX || i64 < INT32_MIN) goto error;
|
||||
if (NULL != err)
|
||||
*err = 0;
|
||||
return (int32_t) i64;
|
||||
error:
|
||||
if (NULL != err)
|
||||
*err = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Scan a real (double) from a string. If the string cannot be converted into
|
||||
* and integer, set *err to 1 and return 0. */
|
||||
double janet_scan_real(
|
||||
const uint8_t *str,
|
||||
int32_t len,
|
||||
int *err) {
|
||||
struct JanetScanRes res = janet_scan_impl(str, len);
|
||||
if (res.error) {
|
||||
if (NULL != err)
|
||||
*err = 1;
|
||||
return 0.0;
|
||||
} else {
|
||||
if (NULL != err)
|
||||
*err = 0;
|
||||
}
|
||||
return convert(res.neg, res.mant, res.base, res.ex);
|
||||
}
|
||||
|
||||
/* Scans a number from a string. Can return either an integer or a real if
|
||||
* the number cannot be represented as an integer. Will return nil in case of
|
||||
* an error. */
|
||||
Janet janet_scan_number(
|
||||
const uint8_t *str,
|
||||
int32_t len) {
|
||||
struct JanetScanRes res = janet_scan_impl(str, len);
|
||||
if (res.error)
|
||||
return janet_wrap_nil();
|
||||
if (!res.foundexp && !res.seenpoint) {
|
||||
int64_t i64 = res.neg ? -(int64_t)res.mant : (int64_t)res.mant;
|
||||
if (i64 <= INT32_MAX && i64 >= INT32_MIN) {
|
||||
return janet_wrap_integer((int32_t) i64);
|
||||
int janet_scan_uint64(const uint8_t *str, int32_t len, uint64_t *out) {
|
||||
int neg;
|
||||
uint64_t bi;
|
||||
if (scan_uint64(str, len, &bi, &neg)) {
|
||||
if (!neg) {
|
||||
*out = bi;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return janet_wrap_real(convert(res.neg, res.mant, res.base, res.ex));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,24 +20,27 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
/* Begin creation of a struct */
|
||||
JanetKV *janet_struct_begin(int32_t count) {
|
||||
|
||||
/* Calculate capacity as power of 2 after 2 * count. */
|
||||
int32_t capacity = janet_tablen(2 * count);
|
||||
if (capacity < 0) capacity = janet_tablen(count + 1);
|
||||
|
||||
size_t s = sizeof(int32_t) * 4 + (capacity * sizeof(JanetKV));
|
||||
char *data = janet_gcalloc(JANET_MEMORY_STRUCT, s);
|
||||
JanetKV *st = (JanetKV *) (data + 4 * sizeof(int32_t));
|
||||
size_t size = sizeof(JanetStructHead) + capacity * sizeof(JanetKV);
|
||||
JanetStructHead *head = janet_gcalloc(JANET_MEMORY_STRUCT, size);
|
||||
head->length = count;
|
||||
head->capacity = capacity;
|
||||
head->hash = 0;
|
||||
|
||||
JanetKV *st = (JanetKV *)(head->data);
|
||||
janet_memempty(st, capacity);
|
||||
janet_struct_length(st) = count;
|
||||
janet_struct_capacity(st) = capacity;
|
||||
janet_struct_hash(st) = 0;
|
||||
return st;
|
||||
}
|
||||
|
||||
@@ -62,7 +65,7 @@ const JanetKV *janet_struct_find(const JanetKV *st, Janet key) {
|
||||
*
|
||||
* Runs will be in sorted order, as the collisions resolver essentially
|
||||
* preforms an in-place insertion sort. This ensures the internal structure of the
|
||||
* hash map is independant of insertion order.
|
||||
* hash map is independent of insertion order.
|
||||
*/
|
||||
void janet_struct_put(JanetKV *st, Janet key, Janet value) {
|
||||
int32_t cap = janet_struct_capacity(st);
|
||||
@@ -71,59 +74,58 @@ void janet_struct_put(JanetKV *st, Janet key, Janet value) {
|
||||
int32_t i, j, dist;
|
||||
int32_t bounds[4] = {index, cap, 0, index};
|
||||
if (janet_checktype(key, JANET_NIL) || janet_checktype(value, JANET_NIL)) return;
|
||||
if (janet_checktype(key, JANET_NUMBER) && isnan(janet_unwrap_number(key))) return;
|
||||
/* Avoid extra items */
|
||||
if (janet_struct_hash(st) == janet_struct_length(st)) return;
|
||||
for (dist = 0, j = 0; j < 4; j += 2)
|
||||
for (i = bounds[j]; i < bounds[j + 1]; i++, dist++) {
|
||||
int status;
|
||||
int32_t otherhash;
|
||||
int32_t otherindex, otherdist;
|
||||
JanetKV *kv = st + i;
|
||||
/* We found an empty slot, so just add key and value */
|
||||
if (janet_checktype(kv->key, JANET_NIL)) {
|
||||
kv->key = key;
|
||||
kv->value = value;
|
||||
/* Update the temporary count */
|
||||
janet_struct_hash(st)++;
|
||||
return;
|
||||
for (i = bounds[j]; i < bounds[j + 1]; i++, dist++) {
|
||||
int status;
|
||||
int32_t otherhash;
|
||||
int32_t otherindex, otherdist;
|
||||
JanetKV *kv = st + i;
|
||||
/* We found an empty slot, so just add key and value */
|
||||
if (janet_checktype(kv->key, JANET_NIL)) {
|
||||
kv->key = key;
|
||||
kv->value = value;
|
||||
/* Update the temporary count */
|
||||
janet_struct_hash(st)++;
|
||||
return;
|
||||
}
|
||||
/* Robinhood hashing - check if colliding kv pair
|
||||
* is closer to their source than current. We use robinhood
|
||||
* hashing to ensure that equivalent structs that are constructed
|
||||
* with different order have the same internal layout, and therefor
|
||||
* will compare properly - i.e., {1 2 3 4} should equal {3 4 1 2}.
|
||||
* Collisions are resolved via an insertion sort insertion. */
|
||||
otherhash = janet_hash(kv->key);
|
||||
otherindex = janet_maphash(cap, otherhash);
|
||||
otherdist = (i + cap - otherindex) & (cap - 1);
|
||||
if (dist < otherdist)
|
||||
status = -1;
|
||||
else if (otherdist < dist)
|
||||
status = 1;
|
||||
else if (hash < otherhash)
|
||||
status = -1;
|
||||
else if (otherhash < hash)
|
||||
status = 1;
|
||||
else
|
||||
status = janet_compare(key, kv->key);
|
||||
/* If other is closer to their ideal slot */
|
||||
if (status == 1) {
|
||||
/* Swap current kv pair with pair in slot */
|
||||
JanetKV temp = *kv;
|
||||
kv->key = key;
|
||||
kv->value = value;
|
||||
key = temp.key;
|
||||
value = temp.value;
|
||||
/* Save dist and hash of new kv pair */
|
||||
dist = otherdist;
|
||||
hash = otherhash;
|
||||
} else if (status == 0) {
|
||||
/* A key was added to the struct more than once */
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Robinhood hashing - check if colliding kv pair
|
||||
* is closer to their source than current. We use robinhood
|
||||
* hashing to ensure that equivalent structs that are contsructed
|
||||
* with different order have the same internal layout, and therefor
|
||||
* will compare properly - i.e., {1 2 3 4} should equal {3 4 1 2}.
|
||||
* Collisions are resolved via an insertion sort insertion. */
|
||||
otherhash = janet_hash(kv->key);
|
||||
otherindex = janet_maphash(cap, otherhash);
|
||||
otherdist = (i + cap - otherindex) & (cap - 1);
|
||||
if (dist < otherdist)
|
||||
status = -1;
|
||||
else if (otherdist < dist)
|
||||
status = 1;
|
||||
else if (hash < otherhash)
|
||||
status = -1;
|
||||
else if (otherhash < hash)
|
||||
status = 1;
|
||||
else
|
||||
status = janet_compare(key, kv->key);
|
||||
/* If other is closer to their ideal slot */
|
||||
if (status == 1) {
|
||||
/* Swap current kv pair with pair in slot */
|
||||
JanetKV temp = *kv;
|
||||
kv->key = key;
|
||||
kv->value = value;
|
||||
key = temp.key;
|
||||
value = temp.value;
|
||||
/* Save dist and hash of new kv pair */
|
||||
dist = otherdist;
|
||||
hash = otherhash;
|
||||
} else if (status == 0) {
|
||||
/* This should not happen - it means
|
||||
* than a key was added to the struct more than once */
|
||||
janet_exit("struct double put fail");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finish building a struct */
|
||||
@@ -132,15 +134,8 @@ const JanetKV *janet_struct_end(JanetKV *st) {
|
||||
/* Error building struct, probably duplicate values. We need to rebuild
|
||||
* the struct using only the values that went in. The second creation should always
|
||||
* succeed. */
|
||||
int32_t i, realCount;
|
||||
JanetKV *newst;
|
||||
realCount = 0;
|
||||
for (i = 0; i < janet_struct_capacity(st); i++) {
|
||||
JanetKV *kv = st + i;
|
||||
realCount += janet_checktype(kv->key, JANET_NIL) ? 1 : 0;
|
||||
}
|
||||
newst = janet_struct_begin(realCount);
|
||||
for (i = 0; i < janet_struct_capacity(st); i++) {
|
||||
JanetKV *newst = janet_struct_begin(janet_struct_hash(st));
|
||||
for (int32_t i = 0; i < janet_struct_capacity(st); i++) {
|
||||
JanetKV *kv = st + i;
|
||||
if (!janet_checktype(kv->key, JANET_NIL)) {
|
||||
janet_struct_put(newst, kv->key, kv->value);
|
||||
@@ -158,17 +153,6 @@ Janet janet_struct_get(const JanetKV *st, Janet key) {
|
||||
return kv ? kv->value : janet_wrap_nil();
|
||||
}
|
||||
|
||||
/* Get the next key in a struct */
|
||||
const JanetKV *janet_struct_next(const JanetKV *st, const JanetKV *kv) {
|
||||
const JanetKV *end = st + janet_struct_capacity(st);
|
||||
kv = (kv == NULL) ? st : kv + 1;
|
||||
while (kv < end) {
|
||||
if (!janet_checktype(kv->key, JANET_NIL)) return kv;
|
||||
kv++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert struct to table */
|
||||
JanetTable *janet_struct_to_table(const JanetKV *st) {
|
||||
JanetTable *table = janet_table(janet_struct_capacity(st));
|
||||
@@ -229,5 +213,3 @@ int janet_struct_compare(const JanetKV *lhs, const JanetKV *rhs) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef janet_maphash
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 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,10 +25,15 @@
|
||||
* checks, all symbols are interned so that there is a single copy of it in the
|
||||
* whole program. Equality is then just a pointer check. */
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "state.h"
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include "symcache.h"
|
||||
#endif
|
||||
|
||||
/* Cache state */
|
||||
JANET_THREAD_LOCAL const uint8_t **janet_vm_cache = NULL;
|
||||
@@ -39,7 +44,7 @@ JANET_THREAD_LOCAL uint32_t janet_vm_cache_deleted = 0;
|
||||
/* Initialize the cache (allocate cache memory) */
|
||||
void janet_symcache_init() {
|
||||
janet_vm_cache_capacity = 1024;
|
||||
janet_vm_cache = calloc(1, janet_vm_cache_capacity * sizeof(const uint8_t **));
|
||||
janet_vm_cache = calloc(1, janet_vm_cache_capacity * sizeof(const uint8_t *));
|
||||
if (NULL == janet_vm_cache) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
@@ -63,10 +68,10 @@ static const uint8_t JANET_SYMCACHE_DELETED[1] = {0};
|
||||
* If the item is not found, return the location
|
||||
* where one would put it. */
|
||||
static const uint8_t **janet_symcache_findmem(
|
||||
const uint8_t *str,
|
||||
int32_t len,
|
||||
int32_t hash,
|
||||
int *success) {
|
||||
const uint8_t *str,
|
||||
int32_t len,
|
||||
int32_t hash,
|
||||
int *success) {
|
||||
uint32_t bounds[4];
|
||||
uint32_t i, j, index;
|
||||
const uint8_t **firstEmpty = NULL;
|
||||
@@ -79,7 +84,7 @@ static const uint8_t **janet_symcache_findmem(
|
||||
bounds[2] = 0;
|
||||
bounds[3] = index;
|
||||
for (j = 0; j < 4; j += 2)
|
||||
for (i = bounds[j]; i < bounds[j+1]; ++i) {
|
||||
for (i = bounds[j]; i < bounds[j + 1]; ++i) {
|
||||
const uint8_t *test = janet_vm_cache[i];
|
||||
/* Check empty spots */
|
||||
if (NULL == test) {
|
||||
@@ -104,7 +109,7 @@ static const uint8_t **janet_symcache_findmem(
|
||||
return janet_vm_cache + i;
|
||||
}
|
||||
}
|
||||
notfound:
|
||||
notfound:
|
||||
*success = 0;
|
||||
return firstEmpty;
|
||||
}
|
||||
@@ -116,7 +121,7 @@ static const uint8_t **janet_symcache_findmem(
|
||||
static void janet_cache_resize(uint32_t newCapacity) {
|
||||
uint32_t i, oldCapacity;
|
||||
const uint8_t **oldCache = janet_vm_cache;
|
||||
const uint8_t **newCache = calloc(1, newCapacity * sizeof(const uint8_t **));
|
||||
const uint8_t **newCache = calloc(1, newCapacity * sizeof(const uint8_t *));
|
||||
if (newCache == NULL) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
@@ -173,10 +178,10 @@ const uint8_t *janet_symbol(const uint8_t *str, int32_t len) {
|
||||
const uint8_t **bucket = janet_symcache_findmem(str, len, hash, &success);
|
||||
if (success)
|
||||
return *bucket;
|
||||
newstr = (uint8_t *) janet_gcalloc(JANET_MEMORY_SYMBOL, 2 * sizeof(int32_t) + len + 1)
|
||||
+ (2 * sizeof(int32_t));
|
||||
janet_string_hash(newstr) = hash;
|
||||
janet_string_length(newstr) = len;
|
||||
JanetStringHead *head = janet_gcalloc(JANET_MEMORY_SYMBOL, sizeof(JanetStringHead) + len + 1);
|
||||
head->hash = hash;
|
||||
head->length = len;
|
||||
newstr = (uint8_t *)(head->data);
|
||||
memcpy(newstr, str, len);
|
||||
newstr[len] = 0;
|
||||
janet_symcache_put((const uint8_t *)newstr, bucket);
|
||||
@@ -185,20 +190,7 @@ const uint8_t *janet_symbol(const uint8_t *str, int32_t len) {
|
||||
|
||||
/* Get a symbol from a cstring */
|
||||
const uint8_t *janet_csymbol(const char *cstr) {
|
||||
int32_t len = 0;
|
||||
while (cstr[len]) len++;
|
||||
return janet_symbol((const uint8_t *)cstr, len);
|
||||
}
|
||||
|
||||
/* Convert a string to a symbol */
|
||||
const uint8_t *janet_symbol_from_string(const uint8_t *str) {
|
||||
int success = 0;
|
||||
const uint8_t **bucket = janet_symcache_find(str, &success);
|
||||
if (success)
|
||||
return *bucket;
|
||||
janet_symcache_put((const uint8_t *)str, bucket);
|
||||
janet_gc_settype(janet_string_raw(str), JANET_MEMORY_SYMBOL);
|
||||
return str;
|
||||
return janet_symbol((const uint8_t *)cstr, (int32_t) strlen(cstr));
|
||||
}
|
||||
|
||||
/* Store counter for genysm to avoid quadratic behavior */
|
||||
@@ -234,21 +226,19 @@ const uint8_t *janet_symbol_gen(void) {
|
||||
* is enough for resolving collisions. */
|
||||
do {
|
||||
hash = janet_string_calchash(
|
||||
gensym_counter,
|
||||
sizeof(gensym_counter) - 1);
|
||||
gensym_counter,
|
||||
sizeof(gensym_counter) - 1);
|
||||
bucket = janet_symcache_findmem(
|
||||
gensym_counter,
|
||||
sizeof(gensym_counter) - 1,
|
||||
hash,
|
||||
&status);
|
||||
gensym_counter,
|
||||
sizeof(gensym_counter) - 1,
|
||||
hash,
|
||||
&status);
|
||||
} while (status && (inc_gensym(), 1));
|
||||
sym = (uint8_t *) janet_gcalloc(
|
||||
JANET_MEMORY_SYMBOL,
|
||||
2 * sizeof(int32_t) + sizeof(gensym_counter)) +
|
||||
(2 * sizeof(int32_t));
|
||||
JanetStringHead *head = janet_gcalloc(JANET_MEMORY_SYMBOL, sizeof(JanetStringHead) + sizeof(gensym_counter));
|
||||
head->length = sizeof(gensym_counter) - 1;
|
||||
head->hash = hash;
|
||||
sym = (uint8_t *)(head->data);
|
||||
memcpy(sym, gensym_counter, sizeof(gensym_counter));
|
||||
janet_string_length(sym) = sizeof(gensym_counter) - 1;
|
||||
janet_string_hash(sym) = hash;
|
||||
janet_symcache_put((const uint8_t *)sym, bucket);
|
||||
return (const uint8_t *)sym;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_SYMCACHE_H_defined
|
||||
#define JANET_SYMCACHE_H_defined
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#endif
|
||||
|
||||
/* Initialize the cache (allocate cache memory) */
|
||||
void janet_symcache_init(void);
|
||||
|
||||
218
src/core/table.c
218
src/core/table.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,18 +20,39 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
/* Initialize a table */
|
||||
JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
|
||||
#define JANET_TABLE_FLAG_STACK 0x10000
|
||||
|
||||
static void *janet_memalloc_empty_local(int32_t count) {
|
||||
int32_t i;
|
||||
void *mem = janet_smalloc(count * sizeof(JanetKV));
|
||||
JanetKV *mmem = (JanetKV *)mem;
|
||||
for (i = 0; i < count; i++) {
|
||||
JanetKV *kv = mmem + i;
|
||||
kv->key = janet_wrap_nil();
|
||||
kv->value = janet_wrap_nil();
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
static JanetTable *janet_table_init_impl(JanetTable *table, int32_t capacity, int stackalloc) {
|
||||
JanetKV *data;
|
||||
capacity = janet_tablen(capacity);
|
||||
if (stackalloc) table->gc.flags = JANET_TABLE_FLAG_STACK;
|
||||
if (capacity) {
|
||||
data = (JanetKV *) janet_memalloc_empty(capacity);
|
||||
if (NULL == data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
if (stackalloc) {
|
||||
data = janet_memalloc_empty_local(capacity);
|
||||
} else {
|
||||
data = (JanetKV *) janet_memalloc_empty(capacity);
|
||||
if (NULL == data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
table->data = data;
|
||||
table->capacity = capacity;
|
||||
@@ -45,15 +66,20 @@ JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
|
||||
return table;
|
||||
}
|
||||
|
||||
/* Initialize a table */
|
||||
JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
|
||||
return janet_table_init_impl(table, capacity, 1);
|
||||
}
|
||||
|
||||
/* Deinitialize a table */
|
||||
void janet_table_deinit(JanetTable *table) {
|
||||
free(table->data);
|
||||
janet_sfree(table->data);
|
||||
}
|
||||
|
||||
/* Create a new table */
|
||||
JanetTable *janet_table(int32_t capacity) {
|
||||
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
|
||||
return janet_table_init(table, capacity);
|
||||
return janet_table_init_impl(table, capacity, 0);
|
||||
}
|
||||
|
||||
/* Find the bucket that contains the given key. Will also return
|
||||
@@ -65,9 +91,15 @@ JanetKV *janet_table_find(JanetTable *t, Janet key) {
|
||||
/* Resize the dictionary table. */
|
||||
static void janet_table_rehash(JanetTable *t, int32_t size) {
|
||||
JanetKV *olddata = t->data;
|
||||
JanetKV *newdata = (JanetKV *) janet_memalloc_empty(size);
|
||||
if (NULL == newdata) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
JanetKV *newdata;
|
||||
int islocal = t->gc.flags & JANET_TABLE_FLAG_STACK;
|
||||
if (islocal) {
|
||||
newdata = (JanetKV *) janet_memalloc_empty_local(size);
|
||||
} else {
|
||||
newdata = (JanetKV *) janet_memalloc_empty(size);
|
||||
if (NULL == newdata) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
int32_t i, oldcapacity;
|
||||
oldcapacity = t->capacity;
|
||||
@@ -81,7 +113,11 @@ static void janet_table_rehash(JanetTable *t, int32_t size) {
|
||||
*newkv = *kv;
|
||||
}
|
||||
}
|
||||
free(olddata);
|
||||
if (islocal) {
|
||||
janet_sfree(olddata);
|
||||
} else {
|
||||
free(olddata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a value out of the table */
|
||||
@@ -129,6 +165,7 @@ Janet janet_table_remove(JanetTable *t, Janet key) {
|
||||
/* Put a value into the object */
|
||||
void janet_table_put(JanetTable *t, Janet key, Janet value) {
|
||||
if (janet_checktype(key, JANET_NIL)) return;
|
||||
if (janet_checktype(key, JANET_NUMBER) && isnan(janet_unwrap_number(key))) return;
|
||||
if (janet_checktype(value, JANET_NIL)) {
|
||||
janet_table_remove(t, key);
|
||||
} else {
|
||||
@@ -140,7 +177,7 @@ void janet_table_put(JanetTable *t, Janet key, Janet value) {
|
||||
janet_table_rehash(t, janet_tablen(2 * t->count + 2));
|
||||
}
|
||||
bucket = janet_table_find(t, key);
|
||||
if (janet_checktype(bucket->value, JANET_FALSE))
|
||||
if (janet_checktype(bucket->value, JANET_BOOLEAN))
|
||||
--t->deleted;
|
||||
bucket->key = key;
|
||||
bucket->value = value;
|
||||
@@ -158,18 +195,6 @@ void janet_table_clear(JanetTable *t) {
|
||||
t->deleted = 0;
|
||||
}
|
||||
|
||||
/* Find next key in an object. Returns NULL if no next key. */
|
||||
const JanetKV *janet_table_next(JanetTable *t, const JanetKV *kv) {
|
||||
JanetKV *end = t->data + t->capacity;
|
||||
kv = (kv == NULL) ? t->data : kv + 1;
|
||||
while (kv < end) {
|
||||
if (!janet_checktype(kv->key, JANET_NIL))
|
||||
return kv;
|
||||
kv++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Convert table to struct */
|
||||
const JanetKV *janet_table_to_struct(JanetTable *t) {
|
||||
JanetKV *st = janet_struct_begin(t->count);
|
||||
@@ -183,6 +208,18 @@ const JanetKV *janet_table_to_struct(JanetTable *t) {
|
||||
return janet_struct_end(st);
|
||||
}
|
||||
|
||||
/* Clone a table. */
|
||||
JanetTable *janet_table_clone(JanetTable *table) {
|
||||
JanetTable *newTable = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
|
||||
memcpy(newTable, table, sizeof(JanetTable));
|
||||
newTable->data = malloc(newTable->capacity * sizeof(JanetKV));
|
||||
if (NULL == newTable->data) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(newTable->data, table->data, table->capacity * sizeof(JanetKV));
|
||||
return newTable;
|
||||
}
|
||||
|
||||
/* Merge a table or struct into a table */
|
||||
static void janet_table_mergekv(JanetTable *table, const JanetKV *kvs, int32_t cap) {
|
||||
int32_t i;
|
||||
@@ -206,87 +243,92 @@ void janet_table_merge_struct(JanetTable *table, const JanetKV *other) {
|
||||
|
||||
/* C Functions */
|
||||
|
||||
static int cfun_new(JanetArgs args) {
|
||||
JanetTable *t;
|
||||
int32_t cap;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_INTEGER(cap, args, 0);
|
||||
t = janet_table(cap);
|
||||
JANET_RETURN_TABLE(args, t);
|
||||
static Janet cfun_table_new(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
int32_t cap = janet_getinteger(argv, 0);
|
||||
return janet_wrap_table(janet_table(cap));
|
||||
}
|
||||
|
||||
static int cfun_getproto(JanetArgs args) {
|
||||
JanetTable *t;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_TABLE(t, args, 0);
|
||||
JANET_RETURN(args, t->proto
|
||||
? janet_wrap_table(t->proto)
|
||||
: janet_wrap_nil());
|
||||
static Janet cfun_table_getproto(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *t = janet_gettable(argv, 0);
|
||||
return t->proto
|
||||
? janet_wrap_table(t->proto)
|
||||
: janet_wrap_nil();
|
||||
}
|
||||
|
||||
static int cfun_setproto(JanetArgs args) {
|
||||
JanetTable *table, *proto;
|
||||
JANET_FIXARITY(args, 2);
|
||||
JANET_ARG_TABLE(table, args, 0);
|
||||
if (janet_checktype(args.v[1], JANET_NIL)) {
|
||||
proto = NULL;
|
||||
} else {
|
||||
JANET_ARG_TABLE(proto, args, 1);
|
||||
static Janet cfun_table_setproto(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
JanetTable *proto = NULL;
|
||||
if (!janet_checktype(argv[1], JANET_NIL)) {
|
||||
proto = janet_gettable(argv, 1);
|
||||
}
|
||||
table->proto = proto;
|
||||
JANET_RETURN_TABLE(args, table);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static int cfun_tostruct(JanetArgs args) {
|
||||
JanetTable *t;
|
||||
JANET_FIXARITY(args, 1);
|
||||
JANET_ARG_TABLE(t, args, 0);
|
||||
JANET_RETURN_STRUCT(args, janet_table_to_struct(t));
|
||||
static Janet cfun_table_tostruct(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *t = janet_gettable(argv, 0);
|
||||
return janet_wrap_struct(janet_table_to_struct(t));
|
||||
}
|
||||
|
||||
static int cfun_rawget(JanetArgs args) {
|
||||
JanetTable *table;
|
||||
JANET_FIXARITY(args, 2);
|
||||
JANET_ARG_TABLE(table, args, 0);
|
||||
JANET_RETURN(args, janet_table_rawget(table, args.v[1]));
|
||||
static Janet cfun_table_rawget(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 2);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
return janet_table_rawget(table, argv[1]);
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"table/new", cfun_new,
|
||||
"(table/new capacity)\n\n"
|
||||
"Creates a new empty table with pre-allocated memory "
|
||||
"for capacity entries. This means that if one knows the number of "
|
||||
"entries going to go in a table on creation, extra memory allocation "
|
||||
"can be avoided. Returns the new table."
|
||||
static Janet cfun_table_clone(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTable *table = janet_gettable(argv, 0);
|
||||
return janet_wrap_table(janet_table_clone(table));
|
||||
}
|
||||
|
||||
static const JanetReg table_cfuns[] = {
|
||||
{
|
||||
"table/new", cfun_table_new,
|
||||
JDOC("(table/new capacity)\n\n"
|
||||
"Creates a new empty table with pre-allocated memory "
|
||||
"for capacity entries. This means that if one knows the number of "
|
||||
"entries going to go in a table on creation, extra memory allocation "
|
||||
"can be avoided. Returns the new table.")
|
||||
},
|
||||
{"table/to-struct", cfun_tostruct,
|
||||
"(table/to-struct tab)\n\n"
|
||||
"Convert a table to a struct. Returns a new struct. This function "
|
||||
"does not take into account prototype tables."
|
||||
{
|
||||
"table/to-struct", cfun_table_tostruct,
|
||||
JDOC("(table/to-struct tab)\n\n"
|
||||
"Convert a table to a struct. Returns a new struct. This function "
|
||||
"does not take into account prototype tables.")
|
||||
},
|
||||
{"table/getproto", cfun_getproto,
|
||||
"(table/getproto tab)\n\n"
|
||||
"Get the prototype table of a table. Returns nil if a table "
|
||||
"has no prototype, otherwise returns the prototype."
|
||||
{
|
||||
"table/getproto", cfun_table_getproto,
|
||||
JDOC("(table/getproto tab)\n\n"
|
||||
"Get the prototype table of a table. Returns nil if a table "
|
||||
"has no prototype, otherwise returns the prototype.")
|
||||
},
|
||||
{"table/setproto", cfun_setproto,
|
||||
"(table/setproto tab proto)\n\n"
|
||||
"Set the prototype of a table. Returns the original table tab."
|
||||
{
|
||||
"table/setproto", cfun_table_setproto,
|
||||
JDOC("(table/setproto tab proto)\n\n"
|
||||
"Set the prototype of a table. Returns the original table tab.")
|
||||
},
|
||||
{"table/rawget", cfun_rawget,
|
||||
"(table/rawget tab key)\n\n"
|
||||
"Gets a value from a table without looking at the prototype table. "
|
||||
"If a table tab does not contain t directly, the function will return "
|
||||
"nil without checking the prototype. Returns the value in the table."
|
||||
{
|
||||
"table/rawget", cfun_table_rawget,
|
||||
JDOC("(table/rawget tab key)\n\n"
|
||||
"Gets a value from a table without looking at the prototype table. "
|
||||
"If a table tab does not contain t directly, the function will return "
|
||||
"nil without checking the prototype. Returns the value in the table.")
|
||||
},
|
||||
{
|
||||
"table/clone", cfun_table_clone,
|
||||
JDOC("(table/clone tab)\n\n"
|
||||
"Create a copy of a table. Updates to the new table will not change the old table, "
|
||||
"and vice versa.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Load the table module */
|
||||
int janet_lib_table(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
return 0;
|
||||
void janet_lib_table(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, table_cfuns);
|
||||
}
|
||||
|
||||
#undef janet_maphash
|
||||
|
||||
163
src/core/tuple.c
163
src/core/tuple.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,21 +20,23 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "symcache.h"
|
||||
#include "gc.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Create a new empty tuple of the given size. This will return memory
|
||||
* which should be filled with Janets. The memory will not be collected until
|
||||
* janet_tuple_end is called. */
|
||||
Janet *janet_tuple_begin(int32_t length) {
|
||||
char *data = janet_gcalloc(JANET_MEMORY_TUPLE, 4 * sizeof(int32_t) + length * sizeof(Janet));
|
||||
Janet *tuple = (Janet *)(data + (4 * sizeof(int32_t)));
|
||||
janet_tuple_length(tuple) = length;
|
||||
janet_tuple_sm_line(tuple) = 0;
|
||||
janet_tuple_sm_col(tuple) = 0;
|
||||
return tuple;
|
||||
size_t size = sizeof(JanetTupleHead) + (length * sizeof(Janet));
|
||||
JanetTupleHead *head = janet_gcalloc(JANET_MEMORY_TUPLE, size);
|
||||
head->sm_start = -1;
|
||||
head->sm_end = -1;
|
||||
head->length = length;
|
||||
return (Janet *)(head->data);
|
||||
}
|
||||
|
||||
/* Finish building a tuple */
|
||||
@@ -91,95 +93,86 @@ int janet_tuple_compare(const Janet *lhs, const Janet *rhs) {
|
||||
|
||||
/* C Functions */
|
||||
|
||||
static int cfun_slice(JanetArgs args) {
|
||||
const Janet *vals;
|
||||
int32_t len;
|
||||
Janet *ret;
|
||||
int32_t start, end;
|
||||
JANET_MINARITY(args, 1);
|
||||
if (!janet_indexed_view(args.v[0], &vals, &len)) JANET_THROW(args, "expected array/tuple");
|
||||
/* Get start */
|
||||
if (args.n < 2) {
|
||||
start = 0;
|
||||
} else if (janet_checktype(args.v[1], JANET_INTEGER)) {
|
||||
start = janet_unwrap_integer(args.v[1]);
|
||||
} else {
|
||||
JANET_THROW(args, "expected integer");
|
||||
}
|
||||
/* Get end */
|
||||
if (args.n < 3) {
|
||||
end = -1;
|
||||
} else if (janet_checktype(args.v[2], JANET_INTEGER)) {
|
||||
end = janet_unwrap_integer(args.v[2]);
|
||||
} else {
|
||||
JANET_THROW(args, "expected integer");
|
||||
}
|
||||
if (start < 0) start = len + start;
|
||||
if (end < 0) end = len + end + 1;
|
||||
if (end < 0 || start < 0 || end > len || start > len)
|
||||
JANET_THROW(args, "slice range out of bounds");
|
||||
if (end >= start) {
|
||||
ret = janet_tuple_begin(end - start);
|
||||
memcpy(ret, vals + start, sizeof(Janet) * (end - start));
|
||||
} else {
|
||||
ret = janet_tuple_begin(0);
|
||||
}
|
||||
JANET_RETURN_TUPLE(args, janet_tuple_end(ret));
|
||||
static Janet cfun_tuple_brackets(int32_t argc, Janet *argv) {
|
||||
const Janet *tup = janet_tuple_n(argv, argc);
|
||||
janet_tuple_flag(tup) |= JANET_TUPLE_FLAG_BRACKETCTOR;
|
||||
return janet_wrap_tuple(tup);
|
||||
}
|
||||
|
||||
static int cfun_prepend(JanetArgs args) {
|
||||
const Janet *t;
|
||||
int32_t len, i;
|
||||
Janet *n;
|
||||
JANET_MINARITY(args, 1);
|
||||
if (!janet_indexed_view(args.v[0], &t, &len))
|
||||
JANET_THROW(args, "expected tuple/array");
|
||||
n = janet_tuple_begin(len - 1 + args.n);
|
||||
memcpy(n - 1 + args.n, t, sizeof(Janet) * len);
|
||||
for (i = 1; i < args.n; i++) {
|
||||
n[args.n - i - 1] = args.v[i];
|
||||
static Janet cfun_tuple_slice(int32_t argc, Janet *argv) {
|
||||
JanetRange range = janet_getslice(argc, argv);
|
||||
JanetView view = janet_getindexed(argv, 0);
|
||||
return janet_wrap_tuple(janet_tuple_n(view.items + range.start, range.end - range.start));
|
||||
}
|
||||
|
||||
static Janet cfun_tuple_type(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
const Janet *tup = janet_gettuple(argv, 0);
|
||||
if (janet_tuple_flag(tup) & JANET_TUPLE_FLAG_BRACKETCTOR) {
|
||||
return janet_ckeywordv("brackets");
|
||||
} else {
|
||||
return janet_ckeywordv("parens");
|
||||
}
|
||||
JANET_RETURN_TUPLE(args, janet_tuple_end(n));
|
||||
}
|
||||
|
||||
static int cfun_append(JanetArgs args) {
|
||||
const Janet *t;
|
||||
int32_t len;
|
||||
Janet *n;
|
||||
JANET_MINARITY(args, 1);
|
||||
if (!janet_indexed_view(args.v[0], &t, &len))
|
||||
JANET_THROW(args, "expected tuple/array");
|
||||
n = janet_tuple_begin(len - 1 + args.n);
|
||||
memcpy(n, t, sizeof(Janet) * len);
|
||||
memcpy(n + len, args.v + 1, sizeof(Janet) * (args.n - 1));
|
||||
JANET_RETURN_TUPLE(args, janet_tuple_end(n));
|
||||
static Janet cfun_tuple_sourcemap(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
const Janet *tup = janet_gettuple(argv, 0);
|
||||
Janet contents[2];
|
||||
contents[0] = janet_wrap_integer(janet_tuple_head(tup)->sm_start);
|
||||
contents[1] = janet_wrap_integer(janet_tuple_head(tup)->sm_end);
|
||||
return janet_wrap_tuple(janet_tuple_n(contents, 2));
|
||||
}
|
||||
|
||||
static const JanetReg cfuns[] = {
|
||||
{"tuple/slice", cfun_slice,
|
||||
"(tuple/slice arrtup [,start=0 [,end=(length arrtup)]])\n\n"
|
||||
"Take a sub sequence of an array or tuple from index start "
|
||||
"inclusive to index end exclusive. If start or end are not provided, "
|
||||
"they default to 0 and the length of arrtup respectively."
|
||||
"Returns the new tuple."
|
||||
static Janet cfun_tuple_setmap(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 3);
|
||||
const Janet *tup = janet_gettuple(argv, 0);
|
||||
janet_tuple_head(tup)->sm_start = janet_getinteger(argv, 1);
|
||||
janet_tuple_head(tup)->sm_end = janet_getinteger(argv, 2);
|
||||
return argv[0];
|
||||
}
|
||||
|
||||
static const JanetReg tuple_cfuns[] = {
|
||||
{
|
||||
"tuple/brackets", cfun_tuple_brackets,
|
||||
JDOC("(tuple/brackets & xs)\n\n"
|
||||
"Creates a new bracketed tuple containing the elements xs.")
|
||||
},
|
||||
{"tuple/append", cfun_append,
|
||||
"(tuple/append tup & items)\n\n"
|
||||
"Returns a new tuple that is the result of appending "
|
||||
"each element in items to tup."
|
||||
{
|
||||
"tuple/slice", cfun_tuple_slice,
|
||||
JDOC("(tuple/slice arrtup [,start=0 [,end=(length arrtup)]])\n\n"
|
||||
"Take a sub sequence of an array or tuple from index start "
|
||||
"inclusive to index end exclusive. If start or end are not provided, "
|
||||
"they default to 0 and the length of arrtup respectively."
|
||||
"Returns the new tuple.")
|
||||
},
|
||||
{"tuple/prepend", cfun_prepend,
|
||||
"(tuple/prepend tup & items)\n\n"
|
||||
"Prepends each element in items to tuple and "
|
||||
"returns a new tuple. Items are prepended such that the "
|
||||
"last element in items is the first element in the new tuple."
|
||||
{
|
||||
"tuple/type", cfun_tuple_type,
|
||||
JDOC("(tuple/type tup)\n\n"
|
||||
"Checks how the tuple was constructed. Will return the keyword "
|
||||
":brackets if the tuple was parsed with brackets, and :parens "
|
||||
"otherwise. The two types of tuples will behave the same most of "
|
||||
"the time, but will print differently and be treated differently by "
|
||||
"the compiler.")
|
||||
},
|
||||
{
|
||||
"tuple/sourcemap", cfun_tuple_sourcemap,
|
||||
JDOC("(tuple/sourcemap tup)\n\n"
|
||||
"Returns the sourcemap metadata attached to a tuple. "
|
||||
"The mapping is represented by a pair of byte offsets into the "
|
||||
"the source code representing the start and end byte indices where "
|
||||
"the tuple is. ")
|
||||
},
|
||||
{
|
||||
"tuple/setmap", cfun_tuple_setmap,
|
||||
JDOC("(tuple/setmap tup start end)\n\n"
|
||||
"Set the sourcemap metadata on a tuple. start and end should "
|
||||
"be integers representing byte offsets into the file. Returns tup.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Load the tuple module */
|
||||
int janet_lib_tuple(JanetArgs args) {
|
||||
JanetTable *env = janet_env(args);
|
||||
janet_cfuns(env, NULL, cfuns);
|
||||
return 0;
|
||||
void janet_lib_tuple(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, tuple_cfuns);
|
||||
}
|
||||
|
||||
561
src/core/typedarray.c
Normal file
561
src/core/typedarray.c
Normal file
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* Copyright (c) 2019 Calvin Rose & contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
#ifdef JANET_TYPED_ARRAY
|
||||
|
||||
static char *ta_type_names[] = {
|
||||
"uint8",
|
||||
"int8",
|
||||
"uint16",
|
||||
"int16",
|
||||
"uint32",
|
||||
"int32",
|
||||
"uint64",
|
||||
"int64",
|
||||
"float32",
|
||||
"float64",
|
||||
"?"
|
||||
};
|
||||
|
||||
static size_t ta_type_sizes[] = {
|
||||
sizeof(uint8_t),
|
||||
sizeof(int8_t),
|
||||
sizeof(uint16_t),
|
||||
sizeof(int16_t),
|
||||
sizeof(uint32_t),
|
||||
sizeof(int32_t),
|
||||
sizeof(uint64_t),
|
||||
sizeof(int64_t),
|
||||
sizeof(float),
|
||||
sizeof(double),
|
||||
0
|
||||
};
|
||||
|
||||
#define TA_COUNT_TYPES (JANET_TARRAY_TYPE_F64 + 1)
|
||||
#define TA_ATOM_MAXSIZE 8
|
||||
#define TA_FLAG_BIG_ENDIAN 1
|
||||
|
||||
static JanetTArrayType get_ta_type_by_name(const uint8_t *name) {
|
||||
for (int i = 0; i < TA_COUNT_TYPES; i++) {
|
||||
if (!janet_cstrcmp(name, ta_type_names[i]))
|
||||
return i;
|
||||
}
|
||||
janet_panicf("invalid typed array type %S", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JanetTArrayBuffer *ta_buffer_init(JanetTArrayBuffer *buf, size_t size) {
|
||||
buf->data = NULL;
|
||||
if (size > 0) {
|
||||
buf->data = (uint8_t *)calloc(size, sizeof(uint8_t));
|
||||
if (buf->data == NULL) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
buf->size = size;
|
||||
#ifdef JANET_BIG_ENDIAN
|
||||
buf->flags = TA_FLAG_BIG_ENDIAN;
|
||||
#else
|
||||
buf->flags = 0;
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int ta_buffer_gc(void *p, size_t s) {
|
||||
(void) s;
|
||||
JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p;
|
||||
free(buf->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ta_buffer_marshal(void *p, JanetMarshalContext *ctx) {
|
||||
JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p;
|
||||
janet_marshal_size(ctx, buf->size);
|
||||
janet_marshal_int(ctx, buf->flags);
|
||||
janet_marshal_bytes(ctx, buf->data, buf->size);
|
||||
}
|
||||
|
||||
static void ta_buffer_unmarshal(void *p, JanetMarshalContext *ctx) {
|
||||
JanetTArrayBuffer *buf = (JanetTArrayBuffer *)p;
|
||||
size_t size = janet_unmarshal_size(ctx);
|
||||
ta_buffer_init(buf, size);
|
||||
buf->flags = janet_unmarshal_int(ctx);
|
||||
janet_unmarshal_bytes(ctx, buf->data, size);
|
||||
}
|
||||
|
||||
static const JanetAbstractType ta_buffer_type = {
|
||||
"ta/buffer",
|
||||
ta_buffer_gc,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
ta_buffer_marshal,
|
||||
ta_buffer_unmarshal,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int ta_mark(void *p, size_t s) {
|
||||
(void) s;
|
||||
JanetTArrayView *view = (JanetTArrayView *)p;
|
||||
janet_mark(janet_wrap_abstract(view->buffer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ta_view_marshal(void *p, JanetMarshalContext *ctx) {
|
||||
JanetTArrayView *view = (JanetTArrayView *)p;
|
||||
size_t offset = (view->buffer->data - view->as.u8);
|
||||
janet_marshal_size(ctx, view->size);
|
||||
janet_marshal_size(ctx, view->stride);
|
||||
janet_marshal_int(ctx, view->type);
|
||||
janet_marshal_size(ctx, offset);
|
||||
janet_marshal_janet(ctx, janet_wrap_abstract(view->buffer));
|
||||
}
|
||||
|
||||
static void ta_view_unmarshal(void *p, JanetMarshalContext *ctx) {
|
||||
JanetTArrayView *view = (JanetTArrayView *)p;
|
||||
size_t offset;
|
||||
int32_t atype;
|
||||
Janet buffer;
|
||||
view->size = janet_unmarshal_size(ctx);
|
||||
view->stride = janet_unmarshal_size(ctx);
|
||||
atype = janet_unmarshal_int(ctx);
|
||||
if (atype < 0 || atype >= TA_COUNT_TYPES)
|
||||
janet_panic("bad typed array type");
|
||||
view->type = atype;
|
||||
offset = janet_unmarshal_size(ctx);
|
||||
buffer = janet_unmarshal_janet(ctx);
|
||||
if (!janet_checktype(buffer, JANET_ABSTRACT) ||
|
||||
(janet_abstract_type(janet_unwrap_abstract(buffer)) != &ta_buffer_type)) {
|
||||
janet_panicf("expected typed array buffer");
|
||||
}
|
||||
view->buffer = (JanetTArrayBuffer *)janet_unwrap_abstract(buffer);
|
||||
size_t buf_need_size = offset + (ta_type_sizes[view->type]) * ((view->size - 1) * view->stride + 1);
|
||||
if (view->buffer->size < buf_need_size)
|
||||
janet_panic("bad typed array offset in marshalled data");
|
||||
view->as.u8 = view->buffer->data + offset;
|
||||
}
|
||||
|
||||
static Janet ta_getter(void *p, Janet key) {
|
||||
Janet value;
|
||||
size_t index, i;
|
||||
JanetTArrayView *array = p;
|
||||
if (!janet_checksize(key)) janet_panic("expected size as key");
|
||||
index = (size_t) janet_unwrap_number(key);
|
||||
i = index * array->stride;
|
||||
if (index >= array->size) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
switch (array->type) {
|
||||
case JANET_TARRAY_TYPE_U8:
|
||||
value = janet_wrap_number(array->as.u8[i]);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_S8:
|
||||
value = janet_wrap_number(array->as.s8[i]);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_U16:
|
||||
value = janet_wrap_number(array->as.u16[i]);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_S16:
|
||||
value = janet_wrap_number(array->as.s16[i]);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_U32:
|
||||
value = janet_wrap_number(array->as.u32[i]);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_S32:
|
||||
value = janet_wrap_number(array->as.s32[i]);
|
||||
break;
|
||||
#ifdef JANET_INT_TYPES
|
||||
case JANET_TARRAY_TYPE_U64:
|
||||
value = janet_wrap_u64(array->as.u64[i]);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_S64:
|
||||
value = janet_wrap_s64(array->as.s64[i]);
|
||||
break;
|
||||
#endif
|
||||
case JANET_TARRAY_TYPE_F32:
|
||||
value = janet_wrap_number(array->as.f32[i]);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_F64:
|
||||
value = janet_wrap_number(array->as.f64[i]);
|
||||
break;
|
||||
default:
|
||||
janet_panicf("cannot get from typed array of type %s",
|
||||
ta_type_names[array->type]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void ta_setter(void *p, Janet key, Janet value) {
|
||||
size_t index, i;
|
||||
if (!janet_checksize(key)) janet_panic("expected size as key");
|
||||
index = (size_t) janet_unwrap_number(key);
|
||||
JanetTArrayView *array = p;
|
||||
i = index * array->stride;
|
||||
if (index >= array->size) {
|
||||
janet_panic("index out of bounds");
|
||||
}
|
||||
if (!janet_checktype(value, JANET_NUMBER) &&
|
||||
array->type != JANET_TARRAY_TYPE_U64 &&
|
||||
array->type != JANET_TARRAY_TYPE_S64) {
|
||||
janet_panic("expected number value");
|
||||
}
|
||||
switch (array->type) {
|
||||
case JANET_TARRAY_TYPE_U8:
|
||||
array->as.u8[i] = (uint8_t) janet_unwrap_number(value);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_S8:
|
||||
array->as.s8[i] = (int8_t) janet_unwrap_number(value);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_U16:
|
||||
array->as.u16[i] = (uint16_t) janet_unwrap_number(value);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_S16:
|
||||
array->as.s16[i] = (int16_t) janet_unwrap_number(value);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_U32:
|
||||
array->as.u32[i] = (uint32_t) janet_unwrap_number(value);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_S32:
|
||||
array->as.s32[i] = (int32_t) janet_unwrap_number(value);
|
||||
break;
|
||||
#ifdef JANET_INT_TYPES
|
||||
case JANET_TARRAY_TYPE_U64:
|
||||
array->as.u64[i] = janet_unwrap_u64(value);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_S64:
|
||||
array->as.s64[i] = janet_unwrap_s64(value);
|
||||
break;
|
||||
#endif
|
||||
case JANET_TARRAY_TYPE_F32:
|
||||
array->as.f32[i] = (float) janet_unwrap_number(value);
|
||||
break;
|
||||
case JANET_TARRAY_TYPE_F64:
|
||||
array->as.f64[i] = janet_unwrap_number(value);
|
||||
break;
|
||||
default:
|
||||
janet_panicf("cannot set typed array of type %s",
|
||||
ta_type_names[array->type]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const JanetAbstractType ta_view_type = {
|
||||
"ta/view",
|
||||
NULL,
|
||||
ta_mark,
|
||||
ta_getter,
|
||||
ta_setter,
|
||||
ta_view_marshal,
|
||||
ta_view_unmarshal,
|
||||
NULL
|
||||
};
|
||||
|
||||
JanetTArrayBuffer *janet_tarray_buffer(size_t size) {
|
||||
JanetTArrayBuffer *buf = janet_abstract(&ta_buffer_type, sizeof(JanetTArrayBuffer));
|
||||
ta_buffer_init(buf, size);
|
||||
return buf;
|
||||
}
|
||||
|
||||
JanetTArrayView *janet_tarray_view(
|
||||
JanetTArrayType type,
|
||||
size_t size,
|
||||
size_t stride,
|
||||
size_t offset,
|
||||
JanetTArrayBuffer *buffer) {
|
||||
|
||||
JanetTArrayView *view = janet_abstract(&ta_view_type, sizeof(JanetTArrayView));
|
||||
|
||||
if ((stride < 1) || (size < 1)) janet_panic("stride and size should be > 0");
|
||||
size_t buf_size = offset + ta_type_sizes[type] * ((size - 1) * stride + 1);
|
||||
|
||||
if (NULL == buffer) {
|
||||
buffer = janet_abstract(&ta_buffer_type, sizeof(JanetTArrayBuffer));
|
||||
ta_buffer_init(buffer, buf_size);
|
||||
}
|
||||
|
||||
if (buffer->size < buf_size) {
|
||||
janet_panicf("bad buffer size, %i bytes allocated < %i required",
|
||||
buffer->size,
|
||||
buf_size);
|
||||
}
|
||||
|
||||
view->buffer = buffer;
|
||||
view->stride = stride;
|
||||
view->size = size;
|
||||
view->as.u8 = buffer->data + offset;
|
||||
view->type = type;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
JanetTArrayBuffer *janet_gettarray_buffer(const Janet *argv, int32_t n) {
|
||||
return janet_getabstract(argv, n, &ta_buffer_type);
|
||||
}
|
||||
|
||||
JanetTArrayView *janet_gettarray_any(const Janet *argv, int32_t n) {
|
||||
return janet_getabstract(argv, n, &ta_view_type);
|
||||
}
|
||||
|
||||
JanetTArrayView *janet_gettarray_view(const Janet *argv, int32_t n, JanetTArrayType type) {
|
||||
JanetTArrayView *view = janet_getabstract(argv, n, &ta_view_type);
|
||||
if (view->type != type) {
|
||||
janet_panicf("bad slot #%d, expected typed array of type %s, got %v",
|
||||
n, ta_type_names[type], argv[n]);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
static Janet cfun_typed_array_new(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 2, 5);
|
||||
size_t offset = 0;
|
||||
size_t stride = 1;
|
||||
JanetTArrayBuffer *buffer = NULL;
|
||||
const uint8_t *keyw = janet_getkeyword(argv, 0);
|
||||
JanetTArrayType type = get_ta_type_by_name(keyw);
|
||||
size_t size = janet_getsize(argv, 1);
|
||||
if (argc > 2)
|
||||
stride = janet_getsize(argv, 2);
|
||||
if (argc > 3)
|
||||
offset = janet_getsize(argv, 3);
|
||||
if (argc > 4) {
|
||||
if (!janet_checktype(argv[4], JANET_ABSTRACT)) {
|
||||
janet_panicf("bad slot #%d, expected ta/view|ta/buffer, got %v",
|
||||
4, argv[4]);
|
||||
}
|
||||
void *p = janet_unwrap_abstract(argv[4]);
|
||||
if (janet_abstract_type(p) == &ta_view_type) {
|
||||
JanetTArrayView *view = (JanetTArrayView *)p;
|
||||
offset = (view->buffer->data - view->as.u8) + offset * ta_type_sizes[view->type];
|
||||
stride *= view->stride;
|
||||
buffer = view->buffer;
|
||||
} else {
|
||||
buffer = p;
|
||||
}
|
||||
}
|
||||
JanetTArrayView *view = janet_tarray_view(type, size, stride, offset, buffer);
|
||||
return janet_wrap_abstract(view);
|
||||
}
|
||||
|
||||
static JanetTArrayView *ta_is_view(Janet x) {
|
||||
if (!janet_checktype(x, JANET_ABSTRACT)) return NULL;
|
||||
void *abst = janet_unwrap_abstract(x);
|
||||
if (janet_abstract_type(abst) != &ta_view_type) return NULL;
|
||||
return (JanetTArrayView *)abst;
|
||||
}
|
||||
|
||||
static Janet cfun_typed_array_buffer(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTArrayView *view;
|
||||
if ((view = ta_is_view(argv[0]))) {
|
||||
return janet_wrap_abstract(view->buffer);
|
||||
}
|
||||
size_t size = janet_getsize(argv, 0);
|
||||
JanetTArrayBuffer *buf = janet_tarray_buffer(size);
|
||||
return janet_wrap_abstract(buf);
|
||||
}
|
||||
|
||||
static Janet cfun_typed_array_size(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTArrayView *view;
|
||||
if ((view = ta_is_view(argv[0]))) {
|
||||
return janet_wrap_number((double) view->size);
|
||||
}
|
||||
JanetTArrayBuffer *buf = (JanetTArrayBuffer *)janet_getabstract(argv, 0, &ta_buffer_type);
|
||||
return janet_wrap_number((double) buf->size);
|
||||
}
|
||||
|
||||
static Janet cfun_typed_array_properties(int32_t argc, Janet *argv) {
|
||||
janet_fixarity(argc, 1);
|
||||
JanetTArrayView *view;
|
||||
if ((view = ta_is_view(argv[0]))) {
|
||||
JanetTArrayView *view = janet_unwrap_abstract(argv[0]);
|
||||
JanetKV *props = janet_struct_begin(6);
|
||||
ptrdiff_t boffset = view->as.u8 - view->buffer->data;
|
||||
janet_struct_put(props, janet_ckeywordv("size"),
|
||||
janet_wrap_number((double) view->size));
|
||||
janet_struct_put(props, janet_ckeywordv("byte-offset"),
|
||||
janet_wrap_number((double) boffset));
|
||||
janet_struct_put(props, janet_ckeywordv("stride"),
|
||||
janet_wrap_number((double) view->stride));
|
||||
janet_struct_put(props, janet_ckeywordv("type"),
|
||||
janet_ckeywordv(ta_type_names[view->type]));
|
||||
janet_struct_put(props, janet_ckeywordv("type-size"),
|
||||
janet_wrap_number((double) ta_type_sizes[view->type]));
|
||||
janet_struct_put(props, janet_ckeywordv("buffer"),
|
||||
janet_wrap_abstract(view->buffer));
|
||||
return janet_wrap_struct(janet_struct_end(props));
|
||||
} else {
|
||||
JanetTArrayBuffer *buffer = janet_gettarray_buffer(argv, 0);
|
||||
JanetKV *props = janet_struct_begin(2);
|
||||
janet_struct_put(props, janet_ckeywordv("size"),
|
||||
janet_wrap_number((double) buffer->size));
|
||||
janet_struct_put(props, janet_ckeywordv("big-endian"),
|
||||
janet_wrap_boolean(buffer->flags & TA_FLAG_BIG_ENDIAN));
|
||||
return janet_wrap_struct(janet_struct_end(props));
|
||||
}
|
||||
}
|
||||
|
||||
static Janet cfun_typed_array_slice(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetTArrayView *src = janet_getabstract(argv, 0, &ta_view_type);
|
||||
JanetRange range;
|
||||
int32_t length = (int32_t)src->size;
|
||||
if (argc == 1) {
|
||||
range.start = 0;
|
||||
range.end = length;
|
||||
} else if (argc == 2) {
|
||||
range.start = janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = length;
|
||||
} else {
|
||||
range.start = janet_gethalfrange(argv, 1, length, "start");
|
||||
range.end = janet_gethalfrange(argv, 2, length, "end");
|
||||
if (range.end < range.start)
|
||||
range.end = range.start;
|
||||
}
|
||||
JanetArray *array = janet_array(range.end - range.start);
|
||||
if (array->data) {
|
||||
for (int32_t i = range.start; i < range.end; i++) {
|
||||
array->data[i - range.start] = ta_getter(src, janet_wrap_number(i));
|
||||
}
|
||||
}
|
||||
array->count = range.end - range.start;
|
||||
return janet_wrap_array(array);
|
||||
}
|
||||
|
||||
static Janet cfun_typed_array_copy_bytes(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 4, 5);
|
||||
JanetTArrayView *src = janet_getabstract(argv, 0, &ta_view_type);
|
||||
size_t index_src = janet_getsize(argv, 1);
|
||||
JanetTArrayView *dst = janet_getabstract(argv, 2, &ta_view_type);
|
||||
size_t index_dst = janet_getsize(argv, 3);
|
||||
size_t count = (argc == 5) ? janet_getsize(argv, 4) : 1;
|
||||
size_t src_atom_size = ta_type_sizes[src->type];
|
||||
size_t dst_atom_size = ta_type_sizes[dst->type];
|
||||
size_t step_src = src->stride * src_atom_size;
|
||||
size_t step_dst = dst->stride * dst_atom_size;
|
||||
size_t pos_src = (src->as.u8 - src->buffer->data) + (index_src * step_src);
|
||||
size_t pos_dst = (dst->as.u8 - dst->buffer->data) + (index_dst * step_dst);
|
||||
uint8_t *ps = src->buffer->data + pos_src, * pd = dst->buffer->data + pos_dst;
|
||||
if ((pos_dst + (count - 1)*step_dst + src_atom_size <= dst->buffer->size) &&
|
||||
(pos_src + (count - 1)*step_src + src_atom_size <= src->buffer->size)) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
memmove(pd, ps, src_atom_size);
|
||||
pd += step_dst;
|
||||
ps += step_src;
|
||||
}
|
||||
} else {
|
||||
janet_panic("typed array copy out of bounds");
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static Janet cfun_typed_array_swap_bytes(int32_t argc, Janet *argv) {
|
||||
janet_arity(argc, 4, 5);
|
||||
JanetTArrayView *src = janet_getabstract(argv, 0, &ta_view_type);
|
||||
size_t index_src = janet_getsize(argv, 1);
|
||||
JanetTArrayView *dst = janet_getabstract(argv, 2, &ta_view_type);
|
||||
size_t index_dst = janet_getsize(argv, 3);
|
||||
size_t count = (argc == 5) ? janet_getsize(argv, 4) : 1;
|
||||
size_t src_atom_size = ta_type_sizes[src->type];
|
||||
size_t dst_atom_size = ta_type_sizes[dst->type];
|
||||
size_t step_src = src->stride * src_atom_size;
|
||||
size_t step_dst = dst->stride * dst_atom_size;
|
||||
size_t pos_src = (src->as.u8 - src->buffer->data) + (index_src * step_src);
|
||||
size_t pos_dst = (dst->as.u8 - dst->buffer->data) + (index_dst * step_dst);
|
||||
uint8_t *ps = src->buffer->data + pos_src, * pd = dst->buffer->data + pos_dst;
|
||||
uint8_t temp[TA_ATOM_MAXSIZE];
|
||||
if ((pos_dst + (count - 1)*step_dst + src_atom_size <= dst->buffer->size) &&
|
||||
(pos_src + (count - 1)*step_src + src_atom_size <= src->buffer->size)) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
memcpy(temp, ps, src_atom_size);
|
||||
memcpy(ps, pd, src_atom_size);
|
||||
memcpy(pd, temp, src_atom_size);
|
||||
pd += step_dst;
|
||||
ps += step_src;
|
||||
}
|
||||
} else {
|
||||
janet_panic("typed array swap out of bounds");
|
||||
}
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
|
||||
static const JanetReg ta_cfuns[] = {
|
||||
{
|
||||
"tarray/new", cfun_typed_array_new,
|
||||
JDOC("(tarray/new type size &opt stride offset tarray|buffer)\n\n"
|
||||
"Create new typed array.")
|
||||
},
|
||||
{
|
||||
"tarray/buffer", cfun_typed_array_buffer,
|
||||
JDOC("(tarray/buffer array|size)\n\n"
|
||||
"Return typed array buffer or create a new buffer.")
|
||||
},
|
||||
{
|
||||
"tarray/length", cfun_typed_array_size,
|
||||
JDOC("(tarray/length array|buffer)\n\n"
|
||||
"Return typed array or buffer size.")
|
||||
},
|
||||
{
|
||||
"tarray/properties", cfun_typed_array_properties,
|
||||
JDOC("(tarray/properties array)\n\n"
|
||||
"Return typed array properties as a struct.")
|
||||
},
|
||||
{
|
||||
"tarray/copy-bytes", cfun_typed_array_copy_bytes,
|
||||
JDOC("(tarray/copy-bytes src sindex dst dindex &opt count)\n\n"
|
||||
"Copy count elements (default 1) of src array from index sindex "
|
||||
"to dst array at position dindex "
|
||||
"memory can overlap.")
|
||||
},
|
||||
{
|
||||
"tarray/swap-bytes", cfun_typed_array_swap_bytes,
|
||||
JDOC("(tarray/swap-bytes src sindex dst dindex &opt count)\n\n"
|
||||
"Swap count elements (default 1) between src array from index sindex "
|
||||
"and dst array at position dindex "
|
||||
"memory can overlap.")
|
||||
},
|
||||
{
|
||||
"tarray/slice", cfun_typed_array_slice,
|
||||
JDOC("(tarray/slice tarr &opt start end)\n\n"
|
||||
"Takes a slice of a typed array from start to end. The range is half "
|
||||
"open, [start, end). Indexes can also be negative, indicating indexing "
|
||||
"from the end of the end of the typed array. By default, start is 0 and end is "
|
||||
"the size of the typed array. Returns a new janet array.")
|
||||
},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* Module entry point */
|
||||
void janet_lib_typed_array(JanetTable *env) {
|
||||
janet_core_cfuns(env, NULL, ta_cfuns);
|
||||
janet_register_abstract_type(&ta_buffer_type);
|
||||
janet_register_abstract_type(&ta_view_type);
|
||||
}
|
||||
|
||||
#endif
|
||||
276
src/core/util.c
276
src/core/util.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,10 +20,14 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#include "state.h"
|
||||
#include "gc.h"
|
||||
#endif
|
||||
|
||||
/* Base 64 lookup table for digits */
|
||||
const char janet_base64[65] =
|
||||
@@ -35,22 +39,58 @@ const char janet_base64[65] =
|
||||
/* The JANET value types in order. These types can be used as
|
||||
* mnemonics instead of a bit pattern for type checking */
|
||||
const char *const janet_type_names[16] = {
|
||||
":nil",
|
||||
":boolean",
|
||||
":boolean",
|
||||
":fiber",
|
||||
":integer",
|
||||
":real",
|
||||
":string",
|
||||
":symbol",
|
||||
":array",
|
||||
":tuple",
|
||||
":table",
|
||||
":struct",
|
||||
":buffer",
|
||||
":function",
|
||||
":cfunction",
|
||||
":abstract"
|
||||
"number",
|
||||
"nil",
|
||||
"boolean",
|
||||
"fiber",
|
||||
"string",
|
||||
"symbol",
|
||||
"keyword",
|
||||
"array",
|
||||
"tuple",
|
||||
"table",
|
||||
"struct",
|
||||
"buffer",
|
||||
"function",
|
||||
"cfunction",
|
||||
"abstract",
|
||||
"pointer"
|
||||
};
|
||||
|
||||
const char *const janet_signal_names[14] = {
|
||||
"ok",
|
||||
"error",
|
||||
"debug",
|
||||
"yield",
|
||||
"user0",
|
||||
"user1",
|
||||
"user2",
|
||||
"user3",
|
||||
"user4",
|
||||
"user5",
|
||||
"user6",
|
||||
"user7",
|
||||
"user8",
|
||||
"user9"
|
||||
};
|
||||
|
||||
const char *const janet_status_names[16] = {
|
||||
"dead",
|
||||
"error",
|
||||
"debug",
|
||||
"pending",
|
||||
"user0",
|
||||
"user1",
|
||||
"user2",
|
||||
"user3",
|
||||
"user4",
|
||||
"user5",
|
||||
"user6",
|
||||
"user7",
|
||||
"user8",
|
||||
"user9",
|
||||
"new",
|
||||
"alive"
|
||||
};
|
||||
|
||||
/* Calculate hash for string */
|
||||
@@ -96,7 +136,7 @@ int32_t janet_tablen(int32_t n) {
|
||||
}
|
||||
|
||||
/* Helper to find a value in a Janet struct or table. Returns the bucket
|
||||
* containg the key, or the first empty bucket if there is no such key. */
|
||||
* containing the key, or the first empty bucket if there is no such key. */
|
||||
const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key) {
|
||||
int32_t index = janet_maphash(cap, janet_hash(key));
|
||||
int32_t i;
|
||||
@@ -151,7 +191,7 @@ const JanetKV *janet_dictionary_next(const JanetKV *kvs, int32_t cap, const Jane
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Compare a janet string with a cstring. more efficient than loading
|
||||
/* Compare a janet string with a cstring. More efficient than loading
|
||||
* c string as a janet string. */
|
||||
int janet_cstrcmp(const uint8_t *str, const char *other) {
|
||||
int32_t len = janet_string_length(str);
|
||||
@@ -168,12 +208,12 @@ int janet_cstrcmp(const uint8_t *str, const char *other) {
|
||||
|
||||
/* Do a binary search on a static array of structs. Each struct must
|
||||
* have a string as its first element, and the struct must be sorted
|
||||
* lexogrpahically by that element. */
|
||||
* lexicographically by that element. */
|
||||
const void *janet_strbinsearch(
|
||||
const void *tab,
|
||||
size_t tabcount,
|
||||
size_t itemsize,
|
||||
const uint8_t *key) {
|
||||
const void *tab,
|
||||
size_t tabcount,
|
||||
size_t itemsize,
|
||||
const uint8_t *key) {
|
||||
size_t low = 0;
|
||||
size_t hi = tabcount;
|
||||
const char *t = (const char *)tab;
|
||||
@@ -203,9 +243,9 @@ void janet_register(const char *name, JanetCFunction cfun) {
|
||||
/* Add a def to an environment */
|
||||
void janet_def(JanetTable *env, const char *name, Janet val, const char *doc) {
|
||||
JanetTable *subt = janet_table(2);
|
||||
janet_table_put(subt, janet_csymbolv(":value"), val);
|
||||
janet_table_put(subt, janet_ckeywordv("value"), val);
|
||||
if (doc)
|
||||
janet_table_put(subt, janet_csymbolv(":doc"), janet_cstringv(doc));
|
||||
janet_table_put(subt, janet_ckeywordv("doc"), janet_cstringv(doc));
|
||||
janet_table_put(env, janet_csymbolv(name), janet_wrap_table(subt));
|
||||
}
|
||||
|
||||
@@ -214,9 +254,9 @@ void janet_var(JanetTable *env, const char *name, Janet val, const char *doc) {
|
||||
JanetArray *array = janet_array(1);
|
||||
JanetTable *subt = janet_table(2);
|
||||
janet_array_push(array, val);
|
||||
janet_table_put(subt, janet_csymbolv(":ref"), janet_wrap_array(array));
|
||||
janet_table_put(subt, janet_ckeywordv("ref"), janet_wrap_array(array));
|
||||
if (doc)
|
||||
janet_table_put(subt, janet_csymbolv(":doc"), janet_cstringv(doc));
|
||||
janet_table_put(subt, janet_ckeywordv("doc"), janet_cstringv(doc));
|
||||
janet_table_put(env, janet_csymbolv(name), janet_wrap_table(subt));
|
||||
}
|
||||
|
||||
@@ -230,12 +270,13 @@ void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns)
|
||||
int32_t nmlen = 0;
|
||||
while (regprefix[reglen]) reglen++;
|
||||
while (cfuns->name[nmlen]) nmlen++;
|
||||
uint8_t *longname_buffer =
|
||||
janet_string_begin(reglen + 1 + nmlen);
|
||||
int32_t symlen = reglen + 1 + nmlen;
|
||||
uint8_t *longname_buffer = malloc(symlen);
|
||||
memcpy(longname_buffer, regprefix, reglen);
|
||||
longname_buffer[reglen] = '.';
|
||||
longname_buffer[reglen] = '/';
|
||||
memcpy(longname_buffer + reglen + 1, cfuns->name, nmlen);
|
||||
longname = janet_wrap_symbol(janet_string_end(longname_buffer));
|
||||
longname = janet_wrap_symbol(janet_symbol(longname_buffer, symlen));
|
||||
free(longname_buffer);
|
||||
}
|
||||
Janet fun = janet_wrap_cfunction(cfuns->cfun);
|
||||
janet_def(env, cfuns->name, fun, cfuns->documentation);
|
||||
@@ -244,6 +285,77 @@ void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns)
|
||||
}
|
||||
}
|
||||
|
||||
/* Abstract type introspection */
|
||||
|
||||
static const JanetAbstractType type_wrap = {
|
||||
"core/type-info",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const JanetAbstractType *at;
|
||||
} JanetAbstractTypeWrap;
|
||||
|
||||
void janet_register_abstract_type(const JanetAbstractType *at) {
|
||||
JanetAbstractTypeWrap *abstract = (JanetAbstractTypeWrap *)
|
||||
janet_abstract(&type_wrap, sizeof(JanetAbstractTypeWrap));
|
||||
abstract->at = at;
|
||||
Janet sym = janet_csymbolv(at->name);
|
||||
if (!(janet_checktype(janet_table_get(janet_vm_registry, sym), JANET_NIL))) {
|
||||
janet_panicf("cannot register abstract type %s, "
|
||||
"a type with the same name exists", at->name);
|
||||
}
|
||||
janet_table_put(janet_vm_registry, sym, janet_wrap_abstract(abstract));
|
||||
}
|
||||
|
||||
const JanetAbstractType *janet_get_abstract_type(Janet key) {
|
||||
Janet twrap = janet_table_get(janet_vm_registry, key);
|
||||
if (janet_checktype(twrap, JANET_NIL)) {
|
||||
return NULL;
|
||||
}
|
||||
if (!janet_checktype(twrap, JANET_ABSTRACT) ||
|
||||
(janet_abstract_type(janet_unwrap_abstract(twrap)) != &type_wrap)) {
|
||||
janet_panic("expected abstract type");
|
||||
}
|
||||
JanetAbstractTypeWrap *w = (JanetAbstractTypeWrap *)janet_unwrap_abstract(twrap);
|
||||
return w->at;
|
||||
}
|
||||
|
||||
#ifndef JANET_BOOTSTRAP
|
||||
void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p) {
|
||||
(void) p;
|
||||
Janet key = janet_csymbolv(name);
|
||||
Janet value;
|
||||
/* During init, allow replacing core library cfunctions with values from
|
||||
* the env. */
|
||||
Janet check = janet_table_get(env, key);
|
||||
if (janet_checktype(check, JANET_NIL)) {
|
||||
value = x;
|
||||
} else {
|
||||
value = check;
|
||||
}
|
||||
janet_table_put(env, key, value);
|
||||
if (janet_checktype(value, JANET_CFUNCTION)) {
|
||||
janet_table_put(janet_vm_registry, value, key);
|
||||
}
|
||||
}
|
||||
|
||||
void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns) {
|
||||
(void) regprefix;
|
||||
while (cfuns->name) {
|
||||
Janet fun = janet_wrap_cfunction(cfuns->cfun);
|
||||
janet_core_def(env, cfuns->name, fun, cfuns->documentation);
|
||||
cfuns++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Resolve a symbol in the environment */
|
||||
JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out) {
|
||||
Janet ref;
|
||||
@@ -253,32 +365,20 @@ JanetBindingType janet_resolve(JanetTable *env, const uint8_t *sym, Janet *out)
|
||||
return JANET_BINDING_NONE;
|
||||
entry_table = janet_unwrap_table(entry);
|
||||
if (!janet_checktype(
|
||||
janet_table_get(entry_table, janet_csymbolv(":macro")),
|
||||
JANET_NIL)) {
|
||||
*out = janet_table_get(entry_table, janet_csymbolv(":value"));
|
||||
janet_table_get(entry_table, janet_ckeywordv("macro")),
|
||||
JANET_NIL)) {
|
||||
*out = janet_table_get(entry_table, janet_ckeywordv("value"));
|
||||
return JANET_BINDING_MACRO;
|
||||
}
|
||||
ref = janet_table_get(entry_table, janet_csymbolv(":ref"));
|
||||
ref = janet_table_get(entry_table, janet_ckeywordv("ref"));
|
||||
if (janet_checktype(ref, JANET_ARRAY)) {
|
||||
*out = ref;
|
||||
return JANET_BINDING_VAR;
|
||||
}
|
||||
*out = janet_table_get(entry_table, janet_csymbolv(":value"));
|
||||
*out = janet_table_get(entry_table, janet_ckeywordv("value"));
|
||||
return JANET_BINDING_DEF;
|
||||
}
|
||||
|
||||
/* Get module from the arguments passed to library */
|
||||
JanetTable *janet_env(JanetArgs args) {
|
||||
JanetTable *module;
|
||||
if (args.n >= 1 && janet_checktype(args.v[0], JANET_TABLE)) {
|
||||
module = janet_unwrap_table(args.v[0]);
|
||||
} else {
|
||||
module = janet_table(0);
|
||||
}
|
||||
*args.ret = janet_wrap_table(module);
|
||||
return module;
|
||||
}
|
||||
|
||||
/* Read both tuples and arrays as c pointers + int32_t length. Return 1 if the
|
||||
* view can be constructed, 0 if an invalid type. */
|
||||
int janet_indexed_view(Janet seq, const Janet **data, int32_t *len) {
|
||||
@@ -288,7 +388,7 @@ int janet_indexed_view(Janet seq, const Janet **data, int32_t *len) {
|
||||
return 1;
|
||||
} else if (janet_checktype(seq, JANET_TUPLE)) {
|
||||
*data = janet_unwrap_tuple(seq);
|
||||
*len = janet_tuple_length(janet_unwrap_struct(seq));
|
||||
*len = janet_tuple_length(janet_unwrap_tuple(seq));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -297,7 +397,8 @@ int janet_indexed_view(Janet seq, const Janet **data, int32_t *len) {
|
||||
/* Read both strings and buffer as unsigned character array + int32_t len.
|
||||
* Returns 1 if the view can be constructed and 0 if the type is invalid. */
|
||||
int janet_bytes_view(Janet str, const uint8_t **data, int32_t *len) {
|
||||
if (janet_checktype(str, JANET_STRING) || janet_checktype(str, JANET_SYMBOL)) {
|
||||
if (janet_checktype(str, JANET_STRING) || janet_checktype(str, JANET_SYMBOL) ||
|
||||
janet_checktype(str, JANET_KEYWORD)) {
|
||||
*data = janet_unwrap_string(str);
|
||||
*len = janet_string_length(janet_unwrap_string(str));
|
||||
return 1;
|
||||
@@ -327,63 +428,24 @@ int janet_dictionary_view(Janet tab, const JanetKV **data, int32_t *len, int32_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get actual type name of a value for debugging purposes */
|
||||
static const char *typestr(JanetArgs args, int32_t n) {
|
||||
JanetType actual = n < args.n ? janet_type(args.v[n]) : JANET_NIL;
|
||||
return ((actual == JANET_ABSTRACT)
|
||||
? janet_abstract_type(janet_unwrap_abstract(args.v[n]))->name
|
||||
: janet_type_names[actual]) + 1;
|
||||
int janet_checkint(Janet x) {
|
||||
if (!janet_checktype(x, JANET_NUMBER))
|
||||
return 0;
|
||||
double dval = janet_unwrap_number(x);
|
||||
return janet_checkintrange(dval);
|
||||
}
|
||||
|
||||
int janet_type_err(JanetArgs args, int32_t n, JanetType expected) {
|
||||
const uint8_t *message = janet_formatc(
|
||||
"bad slot #%d, expected %t, got %s",
|
||||
n,
|
||||
expected,
|
||||
typestr(args, n));
|
||||
JANET_THROWV(args, janet_wrap_string(message));
|
||||
int janet_checkint64(Janet x) {
|
||||
if (!janet_checktype(x, JANET_NUMBER))
|
||||
return 0;
|
||||
double dval = janet_unwrap_number(x);
|
||||
return janet_checkint64range(dval);
|
||||
}
|
||||
|
||||
void janet_buffer_push_types(JanetBuffer *buffer, int types) {
|
||||
int first = 1;
|
||||
int i = 0;
|
||||
while (types) {
|
||||
if (1 & types) {
|
||||
if (first) {
|
||||
first = 0;
|
||||
} else {
|
||||
janet_buffer_push_u8(buffer, '|');
|
||||
}
|
||||
janet_buffer_push_cstring(buffer, janet_type_names[i] + 1);
|
||||
}
|
||||
i++;
|
||||
types >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
int janet_typemany_err(JanetArgs args, int32_t n, int expected) {
|
||||
const uint8_t *message;
|
||||
JanetBuffer buf;
|
||||
janet_buffer_init(&buf, 20);
|
||||
janet_buffer_push_string(&buf, janet_formatc("bad slot #%d, expected ", n));
|
||||
janet_buffer_push_types(&buf, expected);
|
||||
janet_buffer_push_cstring(&buf, ", got ");
|
||||
janet_buffer_push_cstring(&buf, typestr(args, n));
|
||||
message = janet_string(buf.data, buf.count);
|
||||
janet_buffer_deinit(&buf);
|
||||
JANET_THROWV(args, janet_wrap_string(message));
|
||||
}
|
||||
|
||||
int janet_arity_err(JanetArgs args, int32_t n, const char *prefix) {
|
||||
JANET_THROWV(args,
|
||||
janet_wrap_string(janet_formatc(
|
||||
"expected %s%d argument%s, got %d",
|
||||
prefix, n, n == 1 ? "" : "s", args.n)));
|
||||
}
|
||||
|
||||
int janet_typeabstract_err(JanetArgs args, int32_t n, const JanetAbstractType *at) {
|
||||
JANET_THROWV(args,
|
||||
janet_wrap_string(janet_formatc(
|
||||
"bad slot #%d, expected %s, got %s",
|
||||
n, at->name, typestr(args, n))));
|
||||
int janet_checksize(Janet x) {
|
||||
if (!janet_checktype(x, JANET_NUMBER))
|
||||
return 0;
|
||||
double dval = janet_unwrap_number(x);
|
||||
return dval == (double)((size_t) dval) &&
|
||||
dval <= SIZE_MAX;
|
||||
}
|
||||
|
||||
101
src/core/util.h
101
src/core/util.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -23,7 +23,42 @@
|
||||
#ifndef JANET_UTIL_H_defined
|
||||
#define JANET_UTIL_H_defined
|
||||
|
||||
#include <janet/janet.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#endif
|
||||
|
||||
/* Handle runtime errors */
|
||||
#ifndef janet_exit
|
||||
#include <stdio.h>
|
||||
#define janet_exit(m) do { \
|
||||
printf("C runtime error at line %d in file %s: %s\n",\
|
||||
__LINE__,\
|
||||
__FILE__,\
|
||||
(m));\
|
||||
exit(1);\
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define janet_assert(c, m) do { \
|
||||
if (!(c)) janet_exit((m)); \
|
||||
} while (0)
|
||||
|
||||
/* What to do when out of memory */
|
||||
#ifndef JANET_OUT_OF_MEMORY
|
||||
#include <stdio.h>
|
||||
#define JANET_OUT_OF_MEMORY do { printf("janet out of memory\n"); exit(1); } while (0)
|
||||
#endif
|
||||
|
||||
/* Omit docstrings in some builds */
|
||||
#ifndef JANET_BOOTSTRAP
|
||||
#define JDOC(x) NULL
|
||||
#define JANET_NO_BOOTSTRAP
|
||||
#else
|
||||
#define JDOC(x) x
|
||||
#endif
|
||||
|
||||
/* Utils */
|
||||
#define janet_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1))
|
||||
@@ -35,27 +70,55 @@ int32_t janet_tablen(int32_t n);
|
||||
void janet_buffer_push_types(JanetBuffer *buffer, int types);
|
||||
const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key);
|
||||
Janet janet_dict_get(const JanetKV *buckets, int32_t cap, Janet key);
|
||||
void janet_memempty(JanetKV *mem, int32_t count);
|
||||
void *janet_memalloc_empty(int32_t count);
|
||||
const void *janet_strbinsearch(
|
||||
const void *tab,
|
||||
size_t tabcount,
|
||||
size_t itemsize,
|
||||
const uint8_t *key);
|
||||
const void *tab,
|
||||
size_t tabcount,
|
||||
size_t itemsize,
|
||||
const uint8_t *key);
|
||||
void janet_buffer_format(
|
||||
JanetBuffer *b,
|
||||
const char *strfrmt,
|
||||
int32_t argstart,
|
||||
int32_t argc,
|
||||
Janet *argv);
|
||||
|
||||
/* Inside the janet core, defining globals is different
|
||||
* at bootstrap time and normal runtime */
|
||||
#ifdef JANET_BOOTSTRAP
|
||||
#define janet_core_def janet_def
|
||||
#define janet_core_cfuns janet_cfuns
|
||||
#else
|
||||
void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p);
|
||||
void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns);
|
||||
#endif
|
||||
|
||||
/* Initialize builtin libraries */
|
||||
int janet_lib_io(JanetArgs args);
|
||||
int janet_lib_math(JanetArgs args);
|
||||
int janet_lib_array(JanetArgs args);
|
||||
int janet_lib_tuple(JanetArgs args);
|
||||
int janet_lib_buffer(JanetArgs args);
|
||||
int janet_lib_table(JanetArgs args);
|
||||
int janet_lib_fiber(JanetArgs args);
|
||||
int janet_lib_os(JanetArgs args);
|
||||
int janet_lib_string(JanetArgs args);
|
||||
int janet_lib_marsh(JanetArgs args);
|
||||
int janet_lib_parse(JanetArgs args);
|
||||
void janet_lib_io(JanetTable *env);
|
||||
void janet_lib_math(JanetTable *env);
|
||||
void janet_lib_array(JanetTable *env);
|
||||
void janet_lib_tuple(JanetTable *env);
|
||||
void janet_lib_buffer(JanetTable *env);
|
||||
void janet_lib_table(JanetTable *env);
|
||||
void janet_lib_fiber(JanetTable *env);
|
||||
void janet_lib_os(JanetTable *env);
|
||||
void janet_lib_string(JanetTable *env);
|
||||
void janet_lib_marsh(JanetTable *env);
|
||||
void janet_lib_parse(JanetTable *env);
|
||||
#ifdef JANET_ASSEMBLER
|
||||
int janet_lib_asm(JanetArgs args);
|
||||
void janet_lib_asm(JanetTable *env);
|
||||
#endif
|
||||
void janet_lib_compile(JanetTable *env);
|
||||
void janet_lib_debug(JanetTable *env);
|
||||
#ifdef JANET_PEG
|
||||
void janet_lib_peg(JanetTable *env);
|
||||
#endif
|
||||
#ifdef JANET_TYPED_ARRAY
|
||||
void janet_lib_typed_array(JanetTable *env);
|
||||
#endif
|
||||
#ifdef JANET_INT_TYPES
|
||||
void janet_lib_inttypes(JanetTable *env);
|
||||
#endif
|
||||
int janet_lib_compile(JanetArgs args);
|
||||
|
||||
#endif
|
||||
|
||||
397
src/core/value.c
397
src/core/value.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,7 +20,9 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define a number of functions that can be used internally on ANY Janet.
|
||||
@@ -33,30 +35,28 @@ int janet_equals(Janet x, Janet y) {
|
||||
result = 0;
|
||||
} else {
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
case JANET_TRUE:
|
||||
case JANET_FALSE:
|
||||
result = 1;
|
||||
break;
|
||||
case JANET_REAL:
|
||||
result = (janet_unwrap_real(x) == janet_unwrap_real(y));
|
||||
break;
|
||||
case JANET_INTEGER:
|
||||
result = (janet_unwrap_integer(x) == janet_unwrap_integer(y));
|
||||
break;
|
||||
case JANET_STRING:
|
||||
result = janet_string_equal(janet_unwrap_string(x), janet_unwrap_string(y));
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
result = janet_tuple_equal(janet_unwrap_tuple(x), janet_unwrap_tuple(y));
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
result = janet_struct_equal(janet_unwrap_struct(x), janet_unwrap_struct(y));
|
||||
break;
|
||||
default:
|
||||
/* compare pointers */
|
||||
result = (janet_unwrap_pointer(x) == janet_unwrap_pointer(y));
|
||||
break;
|
||||
case JANET_NIL:
|
||||
result = 1;
|
||||
break;
|
||||
case JANET_BOOLEAN:
|
||||
result = (janet_unwrap_boolean(x) == janet_unwrap_boolean(y));
|
||||
break;
|
||||
case JANET_NUMBER:
|
||||
result = (janet_unwrap_number(x) == janet_unwrap_number(y));
|
||||
break;
|
||||
case JANET_STRING:
|
||||
result = janet_string_equal(janet_unwrap_string(x), janet_unwrap_string(y));
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
result = janet_tuple_equal(janet_unwrap_tuple(x), janet_unwrap_tuple(y));
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
result = janet_struct_equal(janet_unwrap_struct(x), janet_unwrap_struct(y));
|
||||
break;
|
||||
default:
|
||||
/* compare pointers */
|
||||
result = (janet_unwrap_pointer(x) == janet_unwrap_pointer(y));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -66,79 +66,69 @@ int janet_equals(Janet x, Janet y) {
|
||||
int32_t janet_hash(Janet x) {
|
||||
int32_t hash = 0;
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
hash = 0;
|
||||
break;
|
||||
case JANET_FALSE:
|
||||
hash = 1;
|
||||
break;
|
||||
case JANET_TRUE:
|
||||
hash = 2;
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
hash = janet_string_hash(janet_unwrap_string(x));
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
hash = janet_tuple_hash(janet_unwrap_tuple(x));
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
hash = janet_struct_hash(janet_unwrap_struct(x));
|
||||
break;
|
||||
case JANET_INTEGER:
|
||||
hash = janet_unwrap_integer(x);
|
||||
break;
|
||||
default:
|
||||
/* TODO - test performance with different hash functions */
|
||||
if (sizeof(double) == sizeof(void *)) {
|
||||
/* Assuming 8 byte pointer */
|
||||
uint64_t i = janet_u64(x);
|
||||
hash = (int32_t)(i & 0xFFFFFFFF);
|
||||
/* Get a bit more entropy by shifting the low bits out */
|
||||
hash >>= 3;
|
||||
hash ^= (int32_t) (i >> 32);
|
||||
} else {
|
||||
/* Assuming 4 byte pointer (or smaller) */
|
||||
hash = (int32_t) ((char *)janet_unwrap_pointer(x) - (char *)0);
|
||||
hash >>= 2;
|
||||
}
|
||||
break;
|
||||
case JANET_NIL:
|
||||
hash = 0;
|
||||
break;
|
||||
case JANET_BOOLEAN:
|
||||
hash = janet_unwrap_boolean(x);
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
hash = janet_string_hash(janet_unwrap_string(x));
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
hash = janet_tuple_hash(janet_unwrap_tuple(x));
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
hash = janet_struct_hash(janet_unwrap_struct(x));
|
||||
break;
|
||||
default:
|
||||
/* TODO - test performance with different hash functions */
|
||||
if (sizeof(double) == sizeof(void *)) {
|
||||
/* Assuming 8 byte pointer */
|
||||
uint64_t i = janet_u64(x);
|
||||
hash = (int32_t)(i & 0xFFFFFFFF);
|
||||
/* Get a bit more entropy by shifting the low bits out */
|
||||
hash >>= 3;
|
||||
hash ^= (int32_t)(i >> 32);
|
||||
} else {
|
||||
/* Assuming 4 byte pointer (or smaller) */
|
||||
hash = (int32_t)((char *)janet_unwrap_pointer(x) - (char *)0);
|
||||
hash >>= 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Compares x to y. If they are equal retuns 0. If x is less, returns -1.
|
||||
/* Compares x to y. If they are equal returns 0. If x is less, returns -1.
|
||||
* If y is less, returns 1. All types are comparable
|
||||
* and should have strict ordering. */
|
||||
int janet_compare(Janet x, Janet y) {
|
||||
if (janet_type(x) == janet_type(y)) {
|
||||
switch (janet_type(x)) {
|
||||
case JANET_NIL:
|
||||
case JANET_FALSE:
|
||||
case JANET_TRUE:
|
||||
return 0;
|
||||
case JANET_REAL:
|
||||
/* Check for nans to ensure total order */
|
||||
if (janet_unwrap_real(x) != janet_unwrap_real(x))
|
||||
return janet_unwrap_real(y) != janet_unwrap_real(y)
|
||||
? 0
|
||||
: -1;
|
||||
if (janet_unwrap_real(y) != janet_unwrap_real(y))
|
||||
case JANET_BOOLEAN:
|
||||
return janet_unwrap_boolean(x) - janet_unwrap_boolean(y);
|
||||
case JANET_NUMBER:
|
||||
/* Check for NaNs to ensure total order */
|
||||
if (janet_unwrap_number(x) != janet_unwrap_number(x))
|
||||
return janet_unwrap_number(y) != janet_unwrap_number(y)
|
||||
? 0
|
||||
: -1;
|
||||
if (janet_unwrap_number(y) != janet_unwrap_number(y))
|
||||
return 1;
|
||||
|
||||
if (janet_unwrap_real(x) == janet_unwrap_real(y)) {
|
||||
if (janet_unwrap_number(x) == janet_unwrap_number(y)) {
|
||||
return 0;
|
||||
} else {
|
||||
return janet_unwrap_real(x) > janet_unwrap_real(y) ? 1 : -1;
|
||||
}
|
||||
case JANET_INTEGER:
|
||||
if (janet_unwrap_integer(x) == janet_unwrap_integer(y)) {
|
||||
return 0;
|
||||
} else {
|
||||
return janet_unwrap_integer(x) > janet_unwrap_integer(y) ? 1 : -1;
|
||||
return janet_unwrap_number(x) > janet_unwrap_number(y) ? 1 : -1;
|
||||
}
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
return janet_string_compare(janet_unwrap_string(x), janet_unwrap_string(y));
|
||||
case JANET_TUPLE:
|
||||
return janet_tuple_compare(janet_unwrap_tuple(x), janet_unwrap_tuple(y));
|
||||
@@ -154,3 +144,248 @@ int janet_compare(Janet x, Janet y) {
|
||||
}
|
||||
return (janet_type(x) < janet_type(y)) ? -1 : 1;
|
||||
}
|
||||
|
||||
/* Gets a value and returns. Can panic. */
|
||||
Janet janet_get(Janet ds, Janet key) {
|
||||
Janet value;
|
||||
switch (janet_type(ds)) {
|
||||
default:
|
||||
janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, ds);
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
value = janet_struct_get(janet_unwrap_struct(ds), key);
|
||||
break;
|
||||
case JANET_TABLE:
|
||||
value = janet_table_get(janet_unwrap_table(ds), key);
|
||||
break;
|
||||
case JANET_ARRAY: {
|
||||
JanetArray *array = janet_unwrap_array(ds);
|
||||
int32_t index;
|
||||
if (!janet_checkint(key))
|
||||
janet_panic("expected integer key");
|
||||
index = janet_unwrap_integer(key);
|
||||
if (index < 0 || index >= array->count) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = array->data[index];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_TUPLE: {
|
||||
const Janet *tuple = janet_unwrap_tuple(ds);
|
||||
int32_t index;
|
||||
if (!janet_checkint(key))
|
||||
janet_panic("expected integer key");
|
||||
index = janet_unwrap_integer(key);
|
||||
if (index < 0 || index >= janet_tuple_length(tuple)) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = tuple[index];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_BUFFER: {
|
||||
JanetBuffer *buffer = janet_unwrap_buffer(ds);
|
||||
int32_t index;
|
||||
if (!janet_checkint(key))
|
||||
janet_panic("expected integer key");
|
||||
index = janet_unwrap_integer(key);
|
||||
if (index < 0 || index >= buffer->count) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = janet_wrap_integer(buffer->data[index]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD: {
|
||||
const uint8_t *str = janet_unwrap_string(ds);
|
||||
int32_t index;
|
||||
if (!janet_checkint(key))
|
||||
janet_panic("expected integer key");
|
||||
index = janet_unwrap_integer(key);
|
||||
if (index < 0 || index >= janet_string_length(str)) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = janet_wrap_integer(str[index]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JANET_ABSTRACT: {
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
|
||||
if (type->get) {
|
||||
value = (type->get)(janet_unwrap_abstract(ds), key);
|
||||
} else {
|
||||
janet_panicf("no getter for %v ", ds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Janet janet_getindex(Janet ds, int32_t index) {
|
||||
Janet value;
|
||||
if (index < 0) janet_panic("expected non-negative index");
|
||||
switch (janet_type(ds)) {
|
||||
default:
|
||||
janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, ds);
|
||||
break;
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
if (index >= janet_string_length(janet_unwrap_string(ds))) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = janet_wrap_integer(janet_unwrap_string(ds)[index]);
|
||||
}
|
||||
break;
|
||||
case JANET_ARRAY:
|
||||
if (index >= janet_unwrap_array(ds)->count) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = janet_unwrap_array(ds)->data[index];
|
||||
}
|
||||
break;
|
||||
case JANET_BUFFER:
|
||||
if (index >= janet_unwrap_buffer(ds)->count) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = janet_wrap_integer(janet_unwrap_buffer(ds)->data[index]);
|
||||
}
|
||||
break;
|
||||
case JANET_TUPLE:
|
||||
if (index >= janet_tuple_length(janet_unwrap_tuple(ds))) {
|
||||
value = janet_wrap_nil();
|
||||
} else {
|
||||
value = janet_unwrap_tuple(ds)[index];
|
||||
}
|
||||
break;
|
||||
case JANET_TABLE:
|
||||
value = janet_table_get(janet_unwrap_table(ds), janet_wrap_integer(index));
|
||||
break;
|
||||
case JANET_STRUCT:
|
||||
value = janet_struct_get(janet_unwrap_struct(ds), janet_wrap_integer(index));
|
||||
break;
|
||||
case JANET_ABSTRACT: {
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
|
||||
if (type->get) {
|
||||
value = (type->get)(janet_unwrap_abstract(ds), janet_wrap_integer(index));
|
||||
} else {
|
||||
janet_panicf("no getter for %v ", ds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
int32_t janet_length(Janet x) {
|
||||
switch (janet_type(x)) {
|
||||
default:
|
||||
janet_panicf("expected %T, got %v", JANET_TFLAG_LENGTHABLE, x);
|
||||
case JANET_STRING:
|
||||
case JANET_SYMBOL:
|
||||
case JANET_KEYWORD:
|
||||
return janet_string_length(janet_unwrap_string(x));
|
||||
case JANET_ARRAY:
|
||||
return janet_unwrap_array(x)->count;
|
||||
case JANET_BUFFER:
|
||||
return janet_unwrap_buffer(x)->count;
|
||||
case JANET_TUPLE:
|
||||
return janet_tuple_length(janet_unwrap_tuple(x));
|
||||
case JANET_STRUCT:
|
||||
return janet_struct_length(janet_unwrap_struct(x));
|
||||
case JANET_TABLE:
|
||||
return janet_unwrap_table(x)->count;
|
||||
}
|
||||
}
|
||||
|
||||
void janet_putindex(Janet ds, int32_t index, Janet value) {
|
||||
switch (janet_type(ds)) {
|
||||
default:
|
||||
janet_panicf("expected %T, got %v",
|
||||
JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds);
|
||||
case JANET_ARRAY: {
|
||||
JanetArray *array = janet_unwrap_array(ds);
|
||||
if (index >= array->count) {
|
||||
janet_array_ensure(array, index + 1, 2);
|
||||
array->count = index + 1;
|
||||
}
|
||||
array->data[index] = value;
|
||||
break;
|
||||
}
|
||||
case JANET_BUFFER: {
|
||||
JanetBuffer *buffer = janet_unwrap_buffer(ds);
|
||||
if (!janet_checkint(value))
|
||||
janet_panicf("can only put integers in buffers, got %v", value);
|
||||
if (index >= buffer->count) {
|
||||
janet_buffer_ensure(buffer, index + 1, 2);
|
||||
buffer->count = index + 1;
|
||||
}
|
||||
buffer->data[index] = janet_unwrap_integer(value);
|
||||
break;
|
||||
}
|
||||
case JANET_TABLE: {
|
||||
JanetTable *table = janet_unwrap_table(ds);
|
||||
janet_table_put(table, janet_wrap_integer(index), value);
|
||||
break;
|
||||
}
|
||||
case JANET_ABSTRACT: {
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
|
||||
if (type->put) {
|
||||
(type->put)(janet_unwrap_abstract(ds), janet_wrap_integer(index), value);
|
||||
} else {
|
||||
janet_panicf("no setter for %v ", ds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void janet_put(Janet ds, Janet key, Janet value) {
|
||||
switch (janet_type(ds)) {
|
||||
default:
|
||||
janet_panicf("expected %T, got %v",
|
||||
JANET_TFLAG_ARRAY | JANET_TFLAG_BUFFER | JANET_TFLAG_TABLE, ds);
|
||||
case JANET_ARRAY: {
|
||||
int32_t index;
|
||||
JanetArray *array = janet_unwrap_array(ds);
|
||||
if (!janet_checkint(key)) janet_panicf("expected integer key, got %v", key);
|
||||
index = janet_unwrap_integer(key);
|
||||
if (index < 0 || index == INT32_MAX) janet_panicf("bad integer key, got %v", key);
|
||||
if (index >= array->count) {
|
||||
janet_array_setcount(array, index + 1);
|
||||
}
|
||||
array->data[index] = value;
|
||||
break;
|
||||
}
|
||||
case JANET_BUFFER: {
|
||||
int32_t index;
|
||||
JanetBuffer *buffer = janet_unwrap_buffer(ds);
|
||||
if (!janet_checkint(key)) janet_panicf("expected integer key, got %v", key);
|
||||
index = janet_unwrap_integer(key);
|
||||
if (index < 0 || index == INT32_MAX) janet_panicf("bad integer key, got %v", key);
|
||||
if (!janet_checkint(value))
|
||||
janet_panicf("can only put integers in buffers, got %v", value);
|
||||
if (index >= buffer->count) {
|
||||
janet_buffer_setcount(buffer, index + 1);
|
||||
}
|
||||
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
|
||||
break;
|
||||
}
|
||||
case JANET_TABLE:
|
||||
janet_table_put(janet_unwrap_table(ds), key, value);
|
||||
break;
|
||||
case JANET_ABSTRACT: {
|
||||
JanetAbstractType *type = (JanetAbstractType *)janet_abstract_type(janet_unwrap_abstract(ds));
|
||||
if (type->put) {
|
||||
(type->put)(janet_unwrap_abstract(ds), key, value);
|
||||
} else {
|
||||
janet_panicf("no setter for %v ", ds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,40 +20,20 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef JANET_AMALG
|
||||
#include "vector.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Grow the buffer dynamically. Used for push operations. */
|
||||
void *janet_v_grow(void *v, int32_t increment, int32_t itemsize) {
|
||||
int32_t dbl_cur = (NULL != v) ? 2 * janet_v__cap(v) : 0;
|
||||
int32_t min_needed = janet_v_count(v) + increment;
|
||||
int32_t m = dbl_cur > min_needed ? dbl_cur : min_needed;
|
||||
int32_t *p = (int32_t *) realloc(v ? janet_v__raw(v) : 0, itemsize * m + sizeof(int32_t)*2);
|
||||
if (NULL != p) {
|
||||
if (!v) p[1] = 0;
|
||||
p[0] = m;
|
||||
return p + 2;
|
||||
} else {
|
||||
{
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
return (void *) (2 * sizeof(int32_t));
|
||||
}
|
||||
}
|
||||
|
||||
/* Clone a buffer. */
|
||||
void *janet_v_copymem(void *v, int32_t itemsize) {
|
||||
int32_t *p;
|
||||
if (NULL == v) return NULL;
|
||||
p = malloc(2 * sizeof(int32_t) + itemsize * janet_v__cap(v));
|
||||
if (NULL != p) {
|
||||
memcpy(p, janet_v__raw(v), 2 * sizeof(int32_t) + itemsize * janet_v__cnt(v));
|
||||
return p + 2;
|
||||
} else {
|
||||
{
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
return (void *) (2 * sizeof(int32_t));
|
||||
}
|
||||
int32_t *p = (int32_t *) janet_srealloc(v ? janet_v__raw(v) : 0, itemsize * m + sizeof(int32_t) * 2);
|
||||
if (!v) p[1] = 0;
|
||||
p[0] = m;
|
||||
return p + 2;
|
||||
}
|
||||
|
||||
/* Convert a buffer to normal allocated memory (forget capacity) */
|
||||
@@ -67,10 +47,10 @@ void *janet_v_flattenmem(void *v, int32_t itemsize) {
|
||||
memcpy(p, v, sizen);
|
||||
return p;
|
||||
} else {
|
||||
{
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
return NULL;
|
||||
{
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -23,7 +23,9 @@
|
||||
#ifndef JANET_VECTOR_H_defined
|
||||
#define JANET_VECTOR_H_defined
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* vector code modified from
|
||||
@@ -31,17 +33,15 @@
|
||||
*/
|
||||
|
||||
/* This is mainly used code such as the assembler or compiler, which
|
||||
* need vector like data structures that are not garbage collected
|
||||
* and used only from C */
|
||||
* need vector like data structures that are only garbage collected in case
|
||||
* of an error, and normally rely on malloc/free. */
|
||||
|
||||
#define janet_v_free(v) (((v) != NULL) ? (free(janet_v__raw(v)), 0) : 0)
|
||||
#define janet_v_free(v) (((v) != NULL) ? (janet_sfree(janet_v__raw(v)), 0) : 0)
|
||||
#define janet_v_push(v, x) (janet_v__maybegrow(v, 1), (v)[janet_v__cnt(v)++] = (x))
|
||||
#define janet_v_pop(v) (janet_v_count(v) ? janet_v__cnt(v)-- : 0)
|
||||
#define janet_v_count(v) (((v) != NULL) ? janet_v__cnt(v) : 0)
|
||||
#define janet_v_add(v, n) (janet_v__maybegrow(v, n), janet_v_cnt(v) += (n), &(v)[janet_v__cnt(v) - (n)])
|
||||
#define janet_v_last(v) ((v)[janet_v__cnt(v) - 1])
|
||||
#define janet_v_empty(v) (((v) != NULL) ? (janet_v__cnt(v) = 0) : 0)
|
||||
#define janet_v_copy(v) (janet_v_copymem((v), sizeof(*(v))))
|
||||
#define janet_v_flatten(v) (janet_v_flattenmem((v), sizeof(*(v))))
|
||||
|
||||
#define janet_v__raw(v) ((int32_t *)(v) - 2)
|
||||
@@ -54,7 +54,6 @@
|
||||
|
||||
/* Actual functions defined in vector.c */
|
||||
void *janet_v_grow(void *v, int32_t increment, int32_t itemsize);
|
||||
void *janet_v_copymem(void *v, int32_t itemsize);
|
||||
void *janet_v_flattenmem(void *v, int32_t itemsize);
|
||||
|
||||
#endif
|
||||
|
||||
1576
src/core/vm.c
1576
src/core/vm.c
File diff suppressed because it is too large
Load Diff
218
src/core/wrap.c
218
src/core/wrap.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
* Copyright (c) 2019 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
@@ -20,7 +20,165 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <janet/janet.h>
|
||||
#ifndef JANET_AMALG
|
||||
#include <janet.h>
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
/* Macro fills */
|
||||
|
||||
JanetType(janet_type)(Janet x) {
|
||||
return janet_type(x);
|
||||
}
|
||||
int (janet_checktype)(Janet x, JanetType type) {
|
||||
return janet_checktype(x, type);
|
||||
}
|
||||
int (janet_checktypes)(Janet x, int typeflags) {
|
||||
return janet_checktypes(x, typeflags);
|
||||
}
|
||||
int (janet_truthy)(Janet x) {
|
||||
return janet_truthy(x);
|
||||
}
|
||||
|
||||
const JanetKV *(janet_unwrap_struct)(Janet x) {
|
||||
return janet_unwrap_struct(x);
|
||||
}
|
||||
const Janet *(janet_unwrap_tuple)(Janet x) {
|
||||
return janet_unwrap_tuple(x);
|
||||
}
|
||||
JanetFiber *(janet_unwrap_fiber)(Janet x) {
|
||||
return janet_unwrap_fiber(x);
|
||||
}
|
||||
JanetArray *(janet_unwrap_array)(Janet x) {
|
||||
return janet_unwrap_array(x);
|
||||
}
|
||||
JanetTable *(janet_unwrap_table)(Janet x) {
|
||||
return janet_unwrap_table(x);
|
||||
}
|
||||
JanetBuffer *(janet_unwrap_buffer)(Janet x) {
|
||||
return janet_unwrap_buffer(x);
|
||||
}
|
||||
const uint8_t *(janet_unwrap_string)(Janet x) {
|
||||
return janet_unwrap_string(x);
|
||||
}
|
||||
const uint8_t *(janet_unwrap_symbol)(Janet x) {
|
||||
return janet_unwrap_symbol(x);
|
||||
}
|
||||
const uint8_t *(janet_unwrap_keyword)(Janet x) {
|
||||
return janet_unwrap_keyword(x);
|
||||
}
|
||||
void *(janet_unwrap_abstract)(Janet x) {
|
||||
return janet_unwrap_abstract(x);
|
||||
}
|
||||
void *(janet_unwrap_pointer)(Janet x) {
|
||||
return janet_unwrap_pointer(x);
|
||||
}
|
||||
JanetFunction *(janet_unwrap_function)(Janet x) {
|
||||
return janet_unwrap_function(x);
|
||||
}
|
||||
JanetCFunction(janet_unwrap_cfunction)(Janet x) {
|
||||
return janet_unwrap_cfunction(x);
|
||||
}
|
||||
int (janet_unwrap_boolean)(Janet x) {
|
||||
return janet_unwrap_boolean(x);
|
||||
}
|
||||
int32_t (janet_unwrap_integer)(Janet x) {
|
||||
return janet_unwrap_integer(x);
|
||||
}
|
||||
|
||||
#if defined(JANET_NANBOX_32) || defined(JANET_NANBOX_64)
|
||||
Janet(janet_wrap_nil)(void) {
|
||||
return janet_wrap_nil();
|
||||
}
|
||||
Janet(janet_wrap_true)(void) {
|
||||
return janet_wrap_true();
|
||||
}
|
||||
Janet(janet_wrap_false)(void) {
|
||||
return janet_wrap_false();
|
||||
}
|
||||
Janet(janet_wrap_boolean)(int x) {
|
||||
return janet_wrap_boolean(x);
|
||||
}
|
||||
Janet(janet_wrap_string)(const uint8_t *x) {
|
||||
return janet_wrap_string(x);
|
||||
}
|
||||
Janet(janet_wrap_symbol)(const uint8_t *x) {
|
||||
return janet_wrap_symbol(x);
|
||||
}
|
||||
Janet(janet_wrap_keyword)(const uint8_t *x) {
|
||||
return janet_wrap_keyword(x);
|
||||
}
|
||||
Janet(janet_wrap_array)(JanetArray *x) {
|
||||
return janet_wrap_array(x);
|
||||
}
|
||||
Janet(janet_wrap_tuple)(const Janet *x) {
|
||||
return janet_wrap_tuple(x);
|
||||
}
|
||||
Janet(janet_wrap_struct)(const JanetKV *x) {
|
||||
return janet_wrap_struct(x);
|
||||
}
|
||||
Janet(janet_wrap_fiber)(JanetFiber *x) {
|
||||
return janet_wrap_fiber(x);
|
||||
}
|
||||
Janet(janet_wrap_buffer)(JanetBuffer *x) {
|
||||
return janet_wrap_buffer(x);
|
||||
}
|
||||
Janet(janet_wrap_function)(JanetFunction *x) {
|
||||
return janet_wrap_function(x);
|
||||
}
|
||||
Janet(janet_wrap_cfunction)(JanetCFunction x) {
|
||||
return janet_wrap_cfunction(x);
|
||||
}
|
||||
Janet(janet_wrap_table)(JanetTable *x) {
|
||||
return janet_wrap_table(x);
|
||||
}
|
||||
Janet(janet_wrap_abstract)(void *x) {
|
||||
return janet_wrap_abstract(x);
|
||||
}
|
||||
Janet(janet_wrap_pointer)(void *x) {
|
||||
return janet_wrap_pointer(x);
|
||||
}
|
||||
Janet(janet_wrap_integer)(int32_t x) {
|
||||
return janet_wrap_integer(x);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef JANET_NANBOX_32
|
||||
double (janet_unwrap_number)(Janet x) {
|
||||
return janet_unwrap_number(x);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef JANET_NANBOX_64
|
||||
Janet(janet_wrap_number)(double x) {
|
||||
return janet_wrap_number(x);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****/
|
||||
|
||||
void *janet_memalloc_empty(int32_t count) {
|
||||
int32_t i;
|
||||
void *mem = malloc(count * sizeof(JanetKV));
|
||||
if (NULL == mem) {
|
||||
JANET_OUT_OF_MEMORY;
|
||||
}
|
||||
JanetKV *mmem = (JanetKV *)mem;
|
||||
for (i = 0; i < count; i++) {
|
||||
JanetKV *kv = mmem + i;
|
||||
kv->key = janet_wrap_nil();
|
||||
kv->value = janet_wrap_nil();
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
void janet_memempty(JanetKV *mem, int32_t count) {
|
||||
int32_t i;
|
||||
for (i = 0; i < count; i++) {
|
||||
mem[i].key = janet_wrap_nil();
|
||||
mem[i].value = janet_wrap_nil();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JANET_NANBOX_64
|
||||
|
||||
@@ -45,10 +203,7 @@ Janet janet_nanbox_from_cpointer(const void *p, uint64_t tagmask) {
|
||||
|
||||
Janet janet_nanbox_from_double(double d) {
|
||||
Janet ret;
|
||||
ret.real = d;
|
||||
/* Normalize NaNs */
|
||||
if (d != d)
|
||||
ret.u64 = janet_nanbox_tag(JANET_REAL);
|
||||
ret.number = d;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -58,31 +213,11 @@ Janet janet_nanbox_from_bits(uint64_t bits) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *janet_nanbox_memalloc_empty(int32_t count) {
|
||||
int32_t i;
|
||||
void *mem = malloc(count * sizeof(JanetKV));
|
||||
JanetKV *mmem = (JanetKV *)mem;
|
||||
for (i = 0; i < count; i++) {
|
||||
JanetKV *kv = mmem + i;
|
||||
kv->key = janet_wrap_nil();
|
||||
kv->value = janet_wrap_nil();
|
||||
}
|
||||
return mem;
|
||||
}
|
||||
|
||||
void janet_nanbox_memempty(JanetKV *mem, int32_t count) {
|
||||
int32_t i;
|
||||
for (i = 0; i < count; i++) {
|
||||
mem[i].key = janet_wrap_nil();
|
||||
mem[i].value = janet_wrap_nil();
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(JANET_NANBOX_32)
|
||||
|
||||
Janet janet_wrap_real(double x) {
|
||||
Janet janet_wrap_number(double x) {
|
||||
Janet ret;
|
||||
ret.real = x;
|
||||
ret.number = x;
|
||||
ret.tagged.type += JANET_DOUBLE_OFFSET;
|
||||
return ret;
|
||||
}
|
||||
@@ -101,20 +236,14 @@ Janet janet_nanbox32_from_tagp(uint32_t tag, void *pointer) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
double janet_unwrap_real(Janet x) {
|
||||
double janet_unwrap_number(Janet x) {
|
||||
x.tagged.type -= JANET_DOUBLE_OFFSET;
|
||||
return x.real;
|
||||
return x.number;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* Wrapper functions wrap a data type that is used from C into a
|
||||
* janet value, which can then be used in janet internal functions. Use
|
||||
* these functions sparingly, as these function will let the programmer
|
||||
* leak memory, where as the stack based API ensures that all values can
|
||||
* be collected by the garbage collector. */
|
||||
|
||||
Janet janet_wrap_nil() {
|
||||
Janet janet_wrap_nil(void) {
|
||||
Janet y;
|
||||
y.type = JANET_NIL;
|
||||
y.as.u64 = 0;
|
||||
@@ -123,22 +252,22 @@ Janet janet_wrap_nil() {
|
||||
|
||||
Janet janet_wrap_true(void) {
|
||||
Janet y;
|
||||
y.type = JANET_TRUE;
|
||||
y.as.u64 = 0;
|
||||
y.type = JANET_BOOLEAN;
|
||||
y.as.u64 = 1;
|
||||
return y;
|
||||
}
|
||||
|
||||
Janet janet_wrap_false(void) {
|
||||
Janet y;
|
||||
y.type = JANET_FALSE;
|
||||
y.type = JANET_BOOLEAN;
|
||||
y.as.u64 = 0;
|
||||
return y;
|
||||
}
|
||||
|
||||
Janet janet_wrap_boolean(int x) {
|
||||
Janet y;
|
||||
y.type = x ? JANET_TRUE : JANET_FALSE;
|
||||
y.as.u64 = 0;
|
||||
y.type = JANET_BOOLEAN;
|
||||
y.as.u64 = !!x;
|
||||
return y;
|
||||
}
|
||||
|
||||
@@ -151,10 +280,10 @@ Janet janet_wrap_##NAME(TYPE x) {\
|
||||
return y;\
|
||||
}
|
||||
|
||||
JANET_WRAP_DEFINE(real, double, JANET_REAL, real)
|
||||
JANET_WRAP_DEFINE(integer, int32_t, JANET_INTEGER, integer)
|
||||
JANET_WRAP_DEFINE(number, double, JANET_NUMBER, number)
|
||||
JANET_WRAP_DEFINE(string, const uint8_t *, JANET_STRING, cpointer)
|
||||
JANET_WRAP_DEFINE(symbol, const uint8_t *, JANET_SYMBOL, cpointer)
|
||||
JANET_WRAP_DEFINE(keyword, const uint8_t *, JANET_KEYWORD, cpointer)
|
||||
JANET_WRAP_DEFINE(array, JanetArray *, JANET_ARRAY, pointer)
|
||||
JANET_WRAP_DEFINE(tuple, const Janet *, JANET_TUPLE, cpointer)
|
||||
JANET_WRAP_DEFINE(struct, const JanetKV *, JANET_STRUCT, cpointer)
|
||||
@@ -164,6 +293,7 @@ JANET_WRAP_DEFINE(function, JanetFunction *, JANET_FUNCTION, pointer)
|
||||
JANET_WRAP_DEFINE(cfunction, JanetCFunction, JANET_CFUNCTION, pointer)
|
||||
JANET_WRAP_DEFINE(table, JanetTable *, JANET_TABLE, pointer)
|
||||
JANET_WRAP_DEFINE(abstract, void *, JANET_ABSTRACT, pointer)
|
||||
JANET_WRAP_DEFINE(pointer, void *, JANET_POINTER, pointer)
|
||||
|
||||
#undef JANET_WRAP_DEFINE
|
||||
|
||||
|
||||
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