mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-29 08:17:59 +03:00
Compare commits
1337 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cd4c9b4645 | ||
![]() |
05949c1cb2 | ||
![]() |
f8c0f1afda | ||
![]() |
ae7e38a9da | ||
![]() |
84a74a8057 | ||
![]() |
8fbf10b362 | ||
![]() |
6e5d23db6a | ||
![]() |
7fe9f7ab9f | ||
![]() |
5c83574a0e | ||
![]() |
410250bf2d | ||
![]() |
3e2a1ccf37 | ||
![]() |
74b7688c7b | ||
![]() |
83f71eaf3b | ||
![]() |
d4c8b72bbe | ||
![]() |
9fc370833b | ||
![]() |
7ba9370281 | ||
![]() |
9469a0e63a | ||
![]() |
8e5f045d02 | ||
![]() |
917a86863e | ||
![]() |
539c19bdee | ||
![]() |
22195b3267 | ||
![]() |
c2c6b5f072 | ||
![]() |
78e8d34c00 | ||
![]() |
ac77c5f9ad | ||
![]() |
382b9d1ef5 | ||
![]() |
8427ae7706 | ||
![]() |
e27ec86e85 | ||
![]() |
f0f733519f | ||
![]() |
d47e8d0e69 | ||
![]() |
075c438b5d | ||
![]() |
b1ee6a190e | ||
![]() |
8c26702678 | ||
![]() |
c608e6dec5 | ||
![]() |
64a2d60d2f | ||
![]() |
dfa8170597 | ||
![]() |
e5d9285033 | ||
![]() |
e0927a9415 | ||
![]() |
b07ee8eed7 | ||
![]() |
5bdff1bf3c | ||
![]() |
67f4fe3d1d | ||
![]() |
092346925e | ||
![]() |
4ed26b1c2d | ||
![]() |
cb37bda329 | ||
![]() |
74e13fc678 | ||
![]() |
a4b3019ce9 | ||
![]() |
9a08c05a19 | ||
![]() |
d9cc241502 | ||
![]() |
db3b9cc411 | ||
![]() |
e521e12a02 | ||
![]() |
6b1ca045d0 | ||
![]() |
33dd1461ad | ||
![]() |
edf3fbf68f | ||
![]() |
f2d7044dfb | ||
![]() |
8bbc2df8eb | ||
![]() |
92b8ed821f | ||
![]() |
b9c3856c09 | ||
![]() |
234192f9ed | ||
![]() |
f758bab187 | ||
![]() |
bed466be9d | ||
![]() |
d7d9aad0d9 | ||
![]() |
fa9a56f627 | ||
![]() |
6d4bc4207e | ||
![]() |
61ca421fb8 | ||
![]() |
66f5f67620 | ||
![]() |
e983bcb2d3 | ||
![]() |
6a71b857f0 | ||
![]() |
973e35c10a | ||
![]() |
28773e2387 | ||
![]() |
1daf39471a | ||
![]() |
ed342f089d | ||
![]() |
c49619d80e | ||
![]() |
5c230aaeae | ||
![]() |
f0ddc22da8 | ||
![]() |
1d60524269 | ||
![]() |
e7003e1ad5 | ||
![]() |
7babcb33a8 | ||
![]() |
506deb966d | ||
![]() |
892b69fc80 | ||
![]() |
0ea5f3cdc0 | ||
![]() |
74fa394bd7 | ||
![]() |
ed938bfeb9 | ||
![]() |
3046f5f4a2 | ||
![]() |
f01ad441d3 | ||
![]() |
e97c47315e | ||
![]() |
6f6a07e696 | ||
![]() |
b2ab5f65b1 | ||
![]() |
c3cdace9fb | ||
![]() |
fcba21dd34 | ||
![]() |
431b5e2fa7 | ||
![]() |
3064535f62 | ||
![]() |
df3fda8d33 | ||
![]() |
163594a6b9 | ||
![]() |
839d9a4719 | ||
![]() |
9d0fe6a1d8 | ||
![]() |
d33ebbe417 | ||
![]() |
f87d592ce6 | ||
![]() |
cb73477b90 | ||
![]() |
39a23fb755 | ||
![]() |
248fb11452 | ||
![]() |
019dccae57 | ||
![]() |
3acb07fe53 | ||
![]() |
45a58d4e5b | ||
![]() |
e85a1e63d1 | ||
![]() |
9b119d03a2 | ||
![]() |
e0b50439b3 | ||
![]() |
602acb4bae | ||
![]() |
d35bd90a12 | ||
![]() |
f1bb383baa | ||
![]() |
2f675b0069 | ||
![]() |
e047da7257 | ||
![]() |
e54fba1e4a | ||
![]() |
ba99f41fa0 | ||
![]() |
b3b8b73c55 | ||
![]() |
a9482054cd | ||
![]() |
7701fb5fd0 | ||
![]() |
7381ff0843 | ||
![]() |
e58ae8cf07 | ||
![]() |
8b6ea97cf6 | ||
![]() |
d19d56acee | ||
![]() |
d3d885e78f | ||
![]() |
1881e08c98 | ||
![]() |
6bf0ff2c43 | ||
![]() |
9b9c27937a | ||
![]() |
7a94f00180 | ||
![]() |
1e143c6e0a | ||
![]() |
44128eda43 | ||
![]() |
2c8af9fa20 | ||
![]() |
eff0f4d363 | ||
![]() |
9b6424f2d5 | ||
![]() |
ae71ba601a | ||
![]() |
1715fdb7b0 | ||
![]() |
1d884a4d62 | ||
![]() |
945ddd4738 | ||
![]() |
015a2b9be5 | ||
![]() |
77d0865c8a | ||
![]() |
92329741ad | ||
![]() |
562637f599 | ||
![]() |
a64825b6f1 | ||
![]() |
455d547de7 | ||
![]() |
918237113f | ||
![]() |
b78376b0ab | ||
![]() |
3e00302ade | ||
![]() |
f1c1fd2f63 | ||
![]() |
6ef9675bcb | ||
![]() |
ca56f62f54 | ||
![]() |
d37ac17a39 | ||
![]() |
62ce2f043d | ||
![]() |
0bb9af9894 | ||
![]() |
7d18d7506f | ||
![]() |
909f631c2f | ||
![]() |
a840c2200c | ||
![]() |
2c6331f583 | ||
![]() |
a31faffec5 | ||
![]() |
1f81ccf44d | ||
![]() |
34ff933e5b | ||
![]() |
5500b13659 | ||
![]() |
acb1bb1518 | ||
![]() |
89d5b74298 | ||
![]() |
8316062e3a | ||
![]() |
5f447d95c5 | ||
![]() |
599a651b6f | ||
![]() |
94ede801bc | ||
![]() |
be8048407e | ||
![]() |
106787d911 | ||
![]() |
737f5aa742 | ||
![]() |
2b41ae743a | ||
![]() |
437ce7c139 | ||
![]() |
237ceca0f4 | ||
![]() |
64e0c303ba | ||
![]() |
d2b692cb3b | ||
![]() |
ebb20121ac | ||
![]() |
4a6f30a152 | ||
![]() |
75144d877e | ||
![]() |
45d46e0e6b | ||
![]() |
903fdf288f | ||
![]() |
48902b00a9 | ||
![]() |
fa0e125f59 | ||
![]() |
bcbe216508 | ||
![]() |
06c33908d9 | ||
![]() |
c99b8abf25 | ||
![]() |
d8c646fabd | ||
![]() |
98b01ff64a | ||
![]() |
d163d1ec30 | ||
![]() |
6ef8a562e5 | ||
![]() |
d919e16790 | ||
![]() |
faf17cd03a | ||
![]() |
5f402e380b | ||
![]() |
ae20a49eb2 | ||
![]() |
fb97628ac9 | ||
![]() |
fc0c260ea1 | ||
![]() |
0dd0061a94 | ||
![]() |
7735f660ab | ||
![]() |
5764965230 | ||
![]() |
b55675b9ce | ||
![]() |
7be096f86a | ||
![]() |
db2649e936 | ||
![]() |
ff41900bd9 | ||
![]() |
2a89abe66d | ||
![]() |
a97548467e | ||
![]() |
bfe07f6e21 | ||
![]() |
4dce157665 | ||
![]() |
c25c7a1c96 | ||
![]() |
2da7401c28 | ||
![]() |
66d7e51f5d | ||
![]() |
f61c9cf4ff | ||
![]() |
1ba4924473 | ||
![]() |
cebc0175c9 | ||
![]() |
b81b28039c | ||
![]() |
5444ede2a8 | ||
![]() |
43d5bb9639 | ||
![]() |
fb7e4dce70 | ||
![]() |
9baf5222f8 | ||
![]() |
12ac1219e9 | ||
![]() |
7f071b5ec5 | ||
![]() |
06ee8fa4b8 | ||
![]() |
01129f62b5 | ||
![]() |
b353557c7f | ||
![]() |
610b7dc942 | ||
![]() |
cf8d642e4e | ||
![]() |
7bb719d337 | ||
![]() |
2b69daeb5e | ||
![]() |
9df0004e6b | ||
![]() |
92305a5d25 | ||
![]() |
62bf124830 | ||
![]() |
6b32f73f7a | ||
![]() |
b0d1b4d8bf | ||
![]() |
147aea42f5 | ||
![]() |
5597750c2c | ||
![]() |
b27ba26b6b | ||
![]() |
382f778fa2 | ||
![]() |
36fa14f5ac | ||
![]() |
9ced5c52e2 | ||
![]() |
957151cd56 | ||
![]() |
9b9699c134 | ||
![]() |
505f666545 | ||
![]() |
6b2797ee2e | ||
![]() |
be627f1005 | ||
![]() |
89183bbb4e | ||
![]() |
0c4402468b | ||
![]() |
2d9f34901b | ||
![]() |
315c5ebbf7 | ||
![]() |
0bc6648c78 | ||
![]() |
d028a5ffd0 | ||
![]() |
9100406a23 | ||
![]() |
74401de755 | ||
![]() |
c67e777271 | ||
![]() |
8fb4668dfc | ||
![]() |
575bf6cfb1 | ||
![]() |
6105f707db | ||
![]() |
a9af06e2fa | ||
![]() |
e888b9d7e1 | ||
![]() |
749d6626f9 | ||
![]() |
7f064da0d6 | ||
![]() |
a1c76af546 | ||
![]() |
ff62122c56 | ||
![]() |
74499ccef2 | ||
![]() |
d0527bf2dd | ||
![]() |
222177f45b | ||
![]() |
4e41bb97de | ||
![]() |
e3ecc3acdb | ||
![]() |
ee867fb9dc | ||
![]() |
3ea8978c67 | ||
![]() |
96f41ef9e8 | ||
![]() |
c757422f2d | ||
![]() |
6d38bb3617 | ||
![]() |
b62c8d7c40 | ||
![]() |
04b659b410 | ||
![]() |
0620e83125 | ||
![]() |
0ae3888456 | ||
![]() |
d75bba698f | ||
![]() |
13bad20803 | ||
![]() |
64c1777023 | ||
![]() |
625131fbb7 | ||
![]() |
078a2ab07b | ||
![]() |
88bbe48621 | ||
![]() |
dad63f98c5 | ||
![]() |
db717658fc | ||
![]() |
e9f057eb64 | ||
![]() |
0bf151b8cf | ||
![]() |
86161f0922 | ||
![]() |
5ba92095f6 | ||
![]() |
ae79247979 | ||
![]() |
117a4962b3 | ||
![]() |
f0ec62b888 | ||
![]() |
be820cac3d | ||
![]() |
0abb8981ba | ||
![]() |
51a6b741db | ||
![]() |
8d08a9feb5 | ||
![]() |
63a5464d00 | ||
![]() |
773c5bb7bb | ||
![]() |
becadbf046 | ||
![]() |
f97d257fd1 | ||
![]() |
4f66f24bbd | ||
![]() |
f8dc369ccb | ||
![]() |
050142d3b6 | ||
![]() |
f4763bd4a2 | ||
![]() |
ab7e25aa7a | ||
![]() |
19528ea3bd | ||
![]() |
2f6114deba | ||
![]() |
6198975bea | ||
![]() |
bddc52f632 | ||
![]() |
4fe20b2a92 | ||
![]() |
e674b85268 | ||
![]() |
3e5a3609cc | ||
![]() |
c7a1440131 | ||
![]() |
11d9661d6b | ||
![]() |
0a2d82ab11 | ||
![]() |
8d20d6f1ca | ||
![]() |
45f515c444 | ||
![]() |
a71cf7bced | ||
![]() |
8da4d36b54 | ||
![]() |
5df743ac9b | ||
![]() |
b582601abf | ||
![]() |
9a241dbc80 | ||
![]() |
59ebbc9e11 | ||
![]() |
d90c98cc60 | ||
![]() |
5d293cc75a | ||
![]() |
d96bf4ba8c | ||
![]() |
15aee1c5b0 | ||
![]() |
87373b4c78 | ||
![]() |
f9f1f59eba | ||
![]() |
4a76487620 | ||
![]() |
e6dbd363b6 | ||
![]() |
936d2ff98e | ||
![]() |
2ee37ea7de | ||
![]() |
d6cf25f16e | ||
![]() |
4f2a0b0554 | ||
![]() |
e17080c95c | ||
![]() |
ecddc1a330 | ||
![]() |
a2681cb0d3 | ||
![]() |
8679e63b89 | ||
![]() |
1f1d3b2aad | ||
![]() |
9642118cfa | ||
![]() |
dbaaf9fd5e | ||
![]() |
364458d68a | ||
![]() |
bc25908726 | ||
![]() |
bc20b9194f | ||
![]() |
5ab627af83 | ||
![]() |
dc0b746c31 | ||
![]() |
8edd446a6d | ||
![]() |
a4d620d7fd | ||
![]() |
12ea734bb7 | ||
![]() |
c0a93612ae | ||
![]() |
db7f864858 | ||
![]() |
554661d913 | ||
![]() |
4f09ca48f8 | ||
![]() |
ab9debfb94 | ||
![]() |
0395adfdc9 | ||
![]() |
27d3ca7bb9 | ||
![]() |
19be42911c | ||
![]() |
a74bb4e584 | ||
![]() |
4e934ee050 | ||
![]() |
202c94f02e | ||
![]() |
e5e625d78b | ||
![]() |
9f8ea9e13c | ||
![]() |
a7a28285ba | ||
![]() |
546a72c5c8 | ||
![]() |
df4a664020 | ||
![]() |
e093b07c8c | ||
![]() |
63277cc29d | ||
![]() |
71f6cf5b64 | ||
![]() |
b5fa51b2dc | ||
![]() |
3a649d612d | ||
![]() |
9fad7eec50 | ||
![]() |
f455dd1c91 | ||
![]() |
ddfeaf5fa2 | ||
![]() |
1695f4eeb8 | ||
![]() |
9e54c8dd0b | ||
![]() |
c484f8aa84 | ||
![]() |
81a8922f1d | ||
![]() |
1267effe08 | ||
![]() |
86cee87ecd | ||
![]() |
64b100561a | ||
![]() |
85a456a4f1 | ||
![]() |
bdf67b75dc | ||
![]() |
3bf00ad18c | ||
![]() |
91081c9f0d | ||
![]() |
88f21a98d4 | ||
![]() |
116c3d71fc | ||
![]() |
1de0f27511 | ||
![]() |
f218644703 | ||
![]() |
db68eeaca3 | ||
![]() |
2d0d13c620 | ||
![]() |
e3510c3204 | ||
![]() |
e7fd154fc9 | ||
![]() |
d070bc756f | ||
![]() |
545378d967 | ||
![]() |
534ac3876a | ||
![]() |
9ea15571a8 | ||
![]() |
483ead3203 | ||
![]() |
231bea0c1e | ||
![]() |
57a151af2a | ||
![]() |
f9c25a1862 | ||
![]() |
cfbba4b741 | ||
![]() |
bb975cda58 | ||
![]() |
59139b373c | ||
![]() |
68114b03e2 | ||
![]() |
b41d591d93 | ||
![]() |
c380a74bb0 | ||
![]() |
ab979b8487 | ||
![]() |
ec170b8aee | ||
![]() |
656158874c | ||
![]() |
540150a41a | ||
![]() |
26adcee7b1 | ||
![]() |
ef19187f83 | ||
![]() |
2624c5bed2 | ||
![]() |
99a4c68417 | ||
![]() |
7f4242521a | ||
![]() |
3bdb0b367b | ||
![]() |
08137298cc | ||
![]() |
e02e1db1ba | ||
![]() |
f39d2ce250 | ||
![]() |
8ff1e5ed0d | ||
![]() |
040fd06afe | ||
![]() |
2ddd482a80 | ||
![]() |
c74d110654 | ||
![]() |
eb0390a23b | ||
![]() |
d3e0cc30d8 | ||
![]() |
3cb6eec261 | ||
![]() |
ee6a661ed0 | ||
![]() |
03d93bcb2a | ||
![]() |
74f1d2640e | ||
![]() |
3737613b1a | ||
![]() |
43fd531689 | ||
![]() |
ee3df1680b | ||
![]() |
849e0557da | ||
![]() |
8bb955cc08 | ||
![]() |
d8b522343f | ||
![]() |
5254cc1daf | ||
![]() |
4aa1bb81d1 | ||
![]() |
8a82b0c891 | ||
![]() |
369e1ca967 | ||
![]() |
09e354a33a | ||
![]() |
0ab2e40257 | ||
![]() |
4761feb45d | ||
![]() |
3606f7637b | ||
![]() |
0d8b83cf42 | ||
![]() |
372afb4e8f | ||
![]() |
cd30a9e5e8 | ||
![]() |
5f884cd84c | ||
![]() |
3e605e1dda | ||
![]() |
d32c1350b7 | ||
![]() |
890dd5720f | ||
![]() |
1f89c23537 | ||
![]() |
5db8e98611 | ||
![]() |
5f43caf7a2 | ||
![]() |
84ac5652b9 | ||
![]() |
8a10fba156 | ||
![]() |
5825bdee09 | ||
![]() |
784f957596 | ||
![]() |
b79d662b13 | ||
![]() |
87b9c3caac | ||
![]() |
78c0ba3ff0 | ||
![]() |
abf69f120e | ||
![]() |
6c848dcfae | ||
![]() |
d562b9f433 | ||
![]() |
9494c999e7 | ||
![]() |
3b252848c5 | ||
![]() |
6eca0548c6 | ||
![]() |
f057770696 | ||
![]() |
908fc61283 | ||
![]() |
b5502103c0 | ||
![]() |
2b5e1cf448 | ||
![]() |
718997336b | ||
![]() |
ed434aaffe | ||
![]() |
50a7fa3b17 | ||
![]() |
506ee0f088 | ||
![]() |
06a70d364f | ||
![]() |
222e67e493 | ||
![]() |
43c8f8658b | ||
![]() |
b8684a9587 | ||
![]() |
542597ada2 | ||
![]() |
3153da6779 | ||
![]() |
516c340ac5 | ||
![]() |
3897c4e02d | ||
![]() |
efe34ad9c9 | ||
![]() |
aafb59eaab | ||
![]() |
1456b00907 | ||
![]() |
d256fe1efe | ||
![]() |
710f04596b | ||
![]() |
0b5ea0db77 | ||
![]() |
46d75a3e8f | ||
![]() |
04da97479d | ||
![]() |
1affcce9b9 | ||
![]() |
0712af9d5b | ||
![]() |
fae8744e4b | ||
![]() |
815498ec8a | ||
![]() |
c3365f3a38 | ||
![]() |
7c50d26ca8 | ||
![]() |
d81fce22ad | ||
![]() |
98d6463613 | ||
![]() |
82c2ba37cb | ||
![]() |
a962a17f66 | ||
![]() |
4aac07c0c4 | ||
![]() |
f51d499e68 | ||
![]() |
d72208cfb3 | ||
![]() |
4a8d66d077 | ||
![]() |
8a809877c4 | ||
![]() |
83981b369e | ||
![]() |
856dccad1f | ||
![]() |
f1f2d165c5 | ||
![]() |
79679554e3 | ||
![]() |
bc5df6af98 | ||
![]() |
2b020b664d | ||
![]() |
eec17d9f30 | ||
![]() |
7d4e29ee07 | ||
![]() |
45fca95385 | ||
![]() |
78cefae27b | ||
![]() |
7196210da9 | ||
![]() |
840b32fb71 | ||
![]() |
c20cfffabd | ||
![]() |
35319cc811 | ||
![]() |
132517993b | ||
![]() |
aac4316e71 | ||
![]() |
d49d322766 | ||
![]() |
e186f48b3f | ||
![]() |
8060cc8706 | ||
![]() |
b82054f1ad | ||
![]() |
c6165da100 | ||
![]() |
c6e7fae88f | ||
![]() |
f00576340a | ||
![]() |
00cc4b93a9 | ||
![]() |
55c5070453 | ||
![]() |
dbe1a59650 | ||
![]() |
61e82c0033 | ||
![]() |
3a928a99a9 | ||
![]() |
20c6c6f95c | ||
![]() |
cbcbde8e6d | ||
![]() |
30e053dc93 | ||
![]() |
8afb19cdd0 | ||
![]() |
7f584c76aa | ||
![]() |
5b4b60fcda | ||
![]() |
e936507b52 | ||
![]() |
9d31f26dd8 | ||
![]() |
f7f347986b | ||
![]() |
1ba72e669c | ||
![]() |
f9f727122b | ||
![]() |
29dff9016c | ||
![]() |
b3b8356731 | ||
![]() |
6b35cae804 | ||
![]() |
999c6e8314 | ||
![]() |
eb820fad63 | ||
![]() |
21f42c0a4c | ||
![]() |
2c93dad6f0 | ||
![]() |
91faf6406c | ||
![]() |
5f974935b4 | ||
![]() |
0faa11a882 | ||
![]() |
0a8eb65de3 | ||
![]() |
1e89416098 | ||
![]() |
4bbc30b0e5 | ||
![]() |
2ded586f1a | ||
![]() |
407ae147bd | ||
![]() |
1c2945de62 | ||
![]() |
20229be3b1 | ||
![]() |
23ab42cfa3 | ||
![]() |
f85376ddca | ||
![]() |
5acb5f6483 | ||
![]() |
317edbf5cb | ||
![]() |
67c8e33570 | ||
![]() |
627a30dc78 | ||
![]() |
47088ba978 | ||
![]() |
7b5129a6d8 | ||
![]() |
cfdf7af404 | ||
![]() |
b3ae526726 | ||
![]() |
e3ca14f84f | ||
![]() |
d08f881d1e | ||
![]() |
2e3fdc2942 | ||
![]() |
e89647d1b5 | ||
![]() |
e566319f44 | ||
![]() |
7b7eb8f49f | ||
![]() |
4e7fbf18ce | ||
![]() |
76dd26bb57 | ||
![]() |
c8a49e413f | ||
![]() |
ac63d0b178 | ||
![]() |
267fecb063 | ||
![]() |
60f3c3d8c5 | ||
![]() |
0c15fdbe61 | ||
![]() |
ff0b1cc085 | ||
![]() |
5f008f4562 | ||
![]() |
4bf3b1dfd7 | ||
![]() |
53e7ca0972 | ||
![]() |
e4098c458f | ||
![]() |
9a3166b9a1 | ||
![]() |
4a64e73648 | ||
![]() |
a2812d5b34 | ||
![]() |
6278b59a1d | ||
![]() |
4c42cb871a | ||
![]() |
b7d9d994c2 | ||
![]() |
3f33021869 | ||
![]() |
521fdbfa8b | ||
![]() |
f7a22ea1a7 | ||
![]() |
e5e001b090 | ||
![]() |
0638756d90 | ||
![]() |
b1bbd55328 | ||
![]() |
9ea62f711a | ||
![]() |
9cf2077051 | ||
![]() |
6f411f796d | ||
![]() |
c5e8ffc50c | ||
![]() |
4f3ea36bc1 | ||
![]() |
42d288b165 | ||
![]() |
a3bc674c25 | ||
![]() |
f49a803364 | ||
![]() |
e9be29bf9e | ||
![]() |
0ac19594f2 | ||
![]() |
deec54ff83 | ||
![]() |
f51eed1800 | ||
![]() |
cb0e8fb23e | ||
![]() |
a77d64b69b | ||
![]() |
9a6dfef475 | ||
![]() |
1e6c2158ea | ||
![]() |
78485fd3ec | ||
![]() |
bc6bf2c5a4 | ||
![]() |
5f7ac065d1 | ||
![]() |
061d152207 | ||
![]() |
ca87efa4fd | ||
![]() |
3a0e703d1d | ||
![]() |
ef751cf236 | ||
![]() |
63e0f254f6 | ||
![]() |
5c8ca6803e | ||
![]() |
5bc065c411 | ||
![]() |
3dafdf44e3 | ||
![]() |
90effcdc58 | ||
![]() |
1b95946db9 | ||
![]() |
f8f20a071c | ||
![]() |
aee7c26639 | ||
![]() |
d62ae232ee | ||
![]() |
29c2363141 | ||
![]() |
a98ed141cc | ||
![]() |
40b09be39b | ||
![]() |
015adfb9c6 | ||
![]() |
1695caa602 | ||
![]() |
9f45dd21aa | ||
![]() |
feee7c8285 | ||
![]() |
eca79130a0 | ||
![]() |
7fee647c84 | ||
![]() |
f20967dbec | ||
![]() |
b318179bb9 | ||
![]() |
a9516cd7f7 | ||
![]() |
cccd0d9bfb | ||
![]() |
768e5a559d | ||
![]() |
784684f2c0 | ||
![]() |
398e3364fa | ||
![]() |
60b9055d0a | ||
![]() |
5fa6b81ff3 | ||
![]() |
d23d68e1ba | ||
![]() |
36cf58ba16 | ||
![]() |
08f43e146d | ||
![]() |
dd2d8e8899 | ||
![]() |
39a52d26a4 | ||
![]() |
294a7592cd | ||
![]() |
b3210f93bd | ||
![]() |
b46641a2cf | ||
![]() |
806c052ff5 | ||
![]() |
f85769f3ca | ||
![]() |
5c834a0906 | ||
![]() |
7bc21f7d3a | ||
![]() |
3a345e9ccb | ||
![]() |
d8aced8ab4 | ||
![]() |
08d74d6608 | ||
![]() |
e4b3124a68 | ||
![]() |
349d7e5cdd | ||
![]() |
e3c1ccf5c2 | ||
![]() |
2439e7cd46 | ||
![]() |
bea2810408 | ||
![]() |
cce384d3de | ||
![]() |
6b18d46096 | ||
![]() |
fcf32b1ad0 | ||
![]() |
dcfdae10da | ||
![]() |
079a2bfc00 | ||
![]() |
4258b27c0c | ||
![]() |
a9c3474b1c | ||
![]() |
37f056bfaf | ||
![]() |
f2c52e61aa | ||
![]() |
b9b99af3d2 | ||
![]() |
cb7d5ddccc | ||
![]() |
8b4573787b | ||
![]() |
0628affb69 | ||
![]() |
046e0b26ac | ||
![]() |
16112505b6 | ||
![]() |
da05580213 | ||
![]() |
77af7e9e22 | ||
![]() |
587ce7d551 | ||
![]() |
943c552627 | ||
![]() |
9ae935b8bb | ||
![]() |
29e65d0d2e | ||
![]() |
89c4b0b22d | ||
![]() |
a46c1af417 | ||
![]() |
374c42b9fb | ||
![]() |
27b4e0c2ad | ||
![]() |
3ad7b1f5de | ||
![]() |
3a38e4c576 | ||
![]() |
33edb092a9 | ||
![]() |
f0603349e2 | ||
![]() |
8e9ed77e64 | ||
![]() |
0ad1eb9402 | ||
![]() |
a1675b577a | ||
![]() |
0c1c9d49a5 | ||
![]() |
ca92b1a78e | ||
![]() |
9791bd92e7 | ||
![]() |
5f95c3f0ec | ||
![]() |
f804c794f8 | ||
![]() |
c76b5cb0c8 | ||
![]() |
ef16d24450 | ||
![]() |
a492710f93 | ||
![]() |
acab964930 | ||
![]() |
9ca606c64e | ||
![]() |
438ed01b4f | ||
![]() |
485e384286 | ||
![]() |
8aae53c2c2 | ||
![]() |
4c4a0b0510 | ||
![]() |
2be193a997 | ||
![]() |
e39193497f | ||
![]() |
9f2cf860c5 | ||
![]() |
399c039f21 | ||
![]() |
ba6841bb18 | ||
![]() |
c70bba367e | ||
![]() |
8a8e63b8e6 | ||
![]() |
cb05e0eae8 | ||
![]() |
2a6a96e03a | ||
![]() |
7248091ca9 | ||
![]() |
2e6db4e01c | ||
![]() |
49b014bc88 | ||
![]() |
87b5e83901 | ||
![]() |
a34bfaa89b | ||
![]() |
22176d05f5 | ||
![]() |
6fa494b093 | ||
![]() |
d33d1ced19 | ||
![]() |
47e22abbe9 | ||
![]() |
063e994213 | ||
![]() |
838dab484b | ||
![]() |
1f4404d760 | ||
![]() |
01a4b69168 | ||
![]() |
903fbf62f8 | ||
![]() |
dd46927f62 | ||
![]() |
40966147c9 | ||
![]() |
891a6d97fa | ||
![]() |
192eb6c6f6 | ||
![]() |
c6dcc5d042 | ||
![]() |
4727417b93 | ||
![]() |
8f98b1c99d | ||
![]() |
c1817d9200 | ||
![]() |
97a31f5bb1 | ||
![]() |
be5c525fa2 | ||
![]() |
210cad57e1 | ||
![]() |
36133ea5e9 | ||
![]() |
978fa30d61 | ||
![]() |
e50715bd31 | ||
![]() |
4fd6cceecd | ||
![]() |
0b749b0c95 | ||
![]() |
be95d77d92 | ||
![]() |
815598f425 | ||
![]() |
b9d3f81253 | ||
![]() |
de91720fc9 | ||
![]() |
206fd59e55 | ||
![]() |
411c397027 | ||
![]() |
a39f7ccd2c | ||
![]() |
6dd320f010 | ||
![]() |
d5ed115d50 | ||
![]() |
23254c87ea | ||
![]() |
6b3a266133 | ||
![]() |
b78ba51d76 | ||
![]() |
2d670175b4 | ||
![]() |
a28cc919b7 | ||
![]() |
2681036afe | ||
![]() |
2f19cb6244 | ||
![]() |
085a25940d | ||
![]() |
cd2996cb06 | ||
![]() |
654e3a0381 | ||
![]() |
cec6e74475 | ||
![]() |
1e7f314869 | ||
![]() |
4e94f36361 | ||
![]() |
fff2c63584 | ||
![]() |
9124b3129e | ||
![]() |
1ef89739b7 | ||
![]() |
e7c5da4579 | ||
![]() |
7fe250ede3 | ||
![]() |
68337904e3 | ||
![]() |
d24d2114a4 | ||
![]() |
0b59e4f724 | ||
![]() |
09445ad0ba | ||
![]() |
20d591b74f | ||
![]() |
dc49d1c985 | ||
![]() |
eb7163a472 | ||
![]() |
3559e7d33f | ||
![]() |
350e86ed0b | ||
![]() |
9e45691869 | ||
![]() |
78b34345b1 | ||
![]() |
0ca392980d | ||
![]() |
9d8c484e11 | ||
![]() |
64d4b51060 | ||
![]() |
e9305dfd19 | ||
![]() |
c8e61f8b48 | ||
![]() |
490aebb55e | ||
![]() |
cd5db7cb93 | ||
![]() |
a6e26e7009 | ||
![]() |
84fccebf00 | ||
![]() |
361e1d64bb | ||
![]() |
06c250c3db | ||
![]() |
a92d7a09dd | ||
![]() |
7ddbfca0ee | ||
![]() |
30ca49448b | ||
![]() |
d8432e2e5a | ||
![]() |
2dfbace9a4 | ||
![]() |
6acdbfa6ee | ||
![]() |
4e8892278a | ||
![]() |
8bcccea7b4 | ||
![]() |
3c7f928e4d | ||
![]() |
a6cbef9f88 | ||
![]() |
5987e7ad8d | ||
![]() |
81d2e83521 | ||
![]() |
93549b7b07 | ||
![]() |
460241fdde | ||
![]() |
359df969e1 | ||
![]() |
00ad42a6e3 | ||
![]() |
356480b5dc | ||
![]() |
cc560727da | ||
![]() |
15da2fcc87 | ||
![]() |
04c0238936 | ||
![]() |
19e80d24bd | ||
![]() |
f9963f7940 | ||
![]() |
2a0f018247 | ||
![]() |
46b3b383a6 | ||
![]() |
53170e7eab | ||
![]() |
24a8dd3b47 | ||
![]() |
a7b697e756 | ||
![]() |
bb4e648121 | ||
![]() |
5bd68efc10 | ||
![]() |
98f4caec80 | ||
![]() |
796354e67a | ||
![]() |
09d756a75f | ||
![]() |
8aa34857e0 | ||
![]() |
48d1c9ca10 | ||
![]() |
9e29d08b92 | ||
![]() |
895660fe5a | ||
![]() |
715df20e1f | ||
![]() |
af5bce3cf8 | ||
![]() |
5239892743 | ||
![]() |
fa697cc26e | ||
![]() |
afab268a13 | ||
![]() |
1bd1832037 | ||
![]() |
caa13a659c | ||
![]() |
367cb77758 | ||
![]() |
ce723b5a7e | ||
![]() |
c398f4ae58 | ||
![]() |
272db66772 | ||
![]() |
460d2d9edb | ||
![]() |
6240cce4e8 | ||
![]() |
59e74212ad | ||
![]() |
4adf279e2f | ||
![]() |
3ff70b2915 | ||
![]() |
d49dd998be | ||
![]() |
e7889d2b88 | ||
![]() |
2eb2ff877d | ||
![]() |
fb0519eb49 | ||
![]() |
8995973c2a | ||
![]() |
9f27821ff6 | ||
![]() |
ee3bc5db0f | ||
![]() |
5bc44d4db7 | ||
![]() |
fdd4948ee2 | ||
![]() |
7b7473b660 | ||
![]() |
3faf25c92f | ||
![]() |
1bf60287e3 | ||
![]() |
83b6258de0 | ||
![]() |
57cf323d73 | ||
![]() |
46d2de40a0 | ||
![]() |
2c59e89fc4 | ||
![]() |
cd7583ab7a | ||
![]() |
4e9d4940a7 | ||
![]() |
0a28ddb7cf | ||
![]() |
63a8de13f8 | ||
![]() |
e3563bffb1 | ||
![]() |
2d09926ff9 | ||
![]() |
54346e4d85 | ||
![]() |
37267e99f1 | ||
![]() |
b868d6c518 | ||
![]() |
39d638d1a5 | ||
![]() |
07b4c1a997 | ||
![]() |
62d28a08d1 | ||
![]() |
cedad8c3ab | ||
![]() |
81a44919f0 | ||
![]() |
368f74e754 | ||
![]() |
c4f532268d | ||
![]() |
b2f7c83372 | ||
![]() |
2122a14d0e | ||
![]() |
d83a84036e | ||
![]() |
539bafc1b0 | ||
![]() |
47e37d3960 | ||
![]() |
80518e0abe | ||
![]() |
f5adbec7c8 | ||
![]() |
77cd0644f7 | ||
![]() |
33807e8198 | ||
![]() |
f2a55eec8b | ||
![]() |
08ca9bacef | ||
![]() |
6e551b97df | ||
![]() |
b3f8261c9b | ||
![]() |
b03480e926 | ||
![]() |
1d30d43aac | ||
![]() |
7f9f5c619a | ||
![]() |
9df06e0a63 | ||
![]() |
dbfaf6c7fd | ||
![]() |
1e38baf8fc | ||
![]() |
abb77ced54 | ||
![]() |
4d59cb6d03 | ||
![]() |
1d2f3dc64d | ||
![]() |
05376122b0 | ||
![]() |
571f87f35f | ||
![]() |
29a5fc7411 | ||
![]() |
ad82158301 | ||
![]() |
b73b31784c | ||
![]() |
0817b64926 | ||
![]() |
bcd689b8ba | ||
![]() |
546034ecc1 | ||
![]() |
15bd3d5217 | ||
![]() |
9dc4f9e24e | ||
![]() |
30a72eb71b | ||
![]() |
809d54d811 | ||
![]() |
46d1987816 | ||
![]() |
c13bd9ab19 | ||
![]() |
c241af1c70 | ||
![]() |
c55b252393 | ||
![]() |
29ebdf452f | ||
![]() |
612e7de243 | ||
![]() |
a7dcba12ce | ||
![]() |
cb3cd84f6d | ||
![]() |
fed654b06c | ||
![]() |
5cc7c24186 | ||
![]() |
83e2bac286 | ||
![]() |
064d08e4f4 | ||
![]() |
7b1eec808e | ||
![]() |
0d49a053ec | ||
![]() |
faf09d6f9c | ||
![]() |
e73bb7b92b | ||
![]() |
994dc69ce0 | ||
![]() |
c384a78e19 | ||
![]() |
5468734174 | ||
![]() |
063b6cb010 | ||
![]() |
374697f38b | ||
![]() |
91c00db0f2 | ||
![]() |
d53b3cf03d | ||
![]() |
a4227dddb0 | ||
![]() |
dbd164cf68 | ||
![]() |
4943fa22a0 | ||
![]() |
7880d275b1 | ||
![]() |
7d66d49742 | ||
![]() |
4bbf52abf5 | ||
![]() |
ce403506b5 | ||
![]() |
d0a451f866 | ||
![]() |
ff32cca745 | ||
![]() |
633d6dc4c5 | ||
![]() |
2632a28398 | ||
![]() |
65fe27c272 | ||
![]() |
c11eab5f58 | ||
![]() |
4a95305191 | ||
![]() |
b6d22844bf | ||
![]() |
ccf77beff3 | ||
![]() |
d37eb6a69b | ||
![]() |
d636d58c1b | ||
![]() |
146c89b888 | ||
![]() |
4aa91ea877 | ||
![]() |
c455fc6a39 | ||
![]() |
a4d0250cfb | ||
![]() |
3e9addf98a | ||
![]() |
178f3c96b5 | ||
![]() |
d5526be208 | ||
![]() |
1187910439 | ||
![]() |
f4a5644dc0 | ||
![]() |
f6cfb0508d | ||
![]() |
8d3eeb6879 | ||
![]() |
b0767730ed | ||
![]() |
3189b8093d | ||
![]() |
7b0ae84c22 | ||
![]() |
98c8f6df91 | ||
![]() |
3890f0d224 | ||
![]() |
1a94a53c56 | ||
![]() |
266f1b5b6a | ||
![]() |
35bf1470c9 | ||
![]() |
44c52201d0 | ||
![]() |
180f03e960 | ||
![]() |
30a2c20cd4 | ||
![]() |
62e2394a61 | ||
![]() |
72b3aa9b7e | ||
![]() |
d10423c0e0 | ||
![]() |
35c4ce79ee | ||
![]() |
2e5867407e | ||
![]() |
743022820d | ||
![]() |
6c7f7e7419 | ||
![]() |
1e9fcdf001 | ||
![]() |
036a846a69 | ||
![]() |
b8b76cd9c9 | ||
![]() |
e7b36e5692 | ||
![]() |
86f9cb86c7 | ||
![]() |
3c3acaeb82 | ||
![]() |
8ece609f4c | ||
![]() |
08b1bfa9ab | ||
![]() |
0511afab9f | ||
![]() |
9bd6bc09da | ||
![]() |
5b7816eb96 | ||
![]() |
369a6dd539 | ||
![]() |
414970ccff | ||
![]() |
9a98ce04c1 | ||
![]() |
2309c85374 | ||
![]() |
bd4a735c84 | ||
![]() |
0cb2bf9148 | ||
![]() |
75753286ea | ||
![]() |
fcf10a61d6 | ||
![]() |
bab278f9df | ||
![]() |
feeac00746 | ||
![]() |
4564084997 | ||
![]() |
5d635d93b0 | ||
![]() |
8e55fe316e | ||
![]() |
d9d89b4139 | ||
![]() |
e17543c497 | ||
![]() |
181c9cdc56 | ||
![]() |
fbb6236e40 | ||
![]() |
1ae94fcffc | ||
![]() |
bb0cc0d491 | ||
![]() |
1d0916a768 | ||
![]() |
bb74da582e | ||
![]() |
31b01122a6 | ||
![]() |
982eb38f15 | ||
![]() |
3bc9d08f17 | ||
![]() |
370f4269a2 | ||
![]() |
f74158a326 | ||
![]() |
d2ff29cf4d | ||
![]() |
ffd9c21d50 | ||
![]() |
c8cbb73ed9 | ||
![]() |
6c9f644cd3 | ||
![]() |
8cac8807aa | ||
![]() |
d2e3f596fe | ||
![]() |
50ea54dd95 | ||
![]() |
bf3d5849ac | ||
![]() |
5383dc0056 | ||
![]() |
4beded7cad | ||
![]() |
9d1102222d | ||
![]() |
b65bbd4b82 | ||
![]() |
f12353633a | ||
![]() |
7c0a72f4fe | ||
![]() |
2366bcbd1d | ||
![]() |
118c6d6105 | ||
![]() |
4fc4450c4b | ||
![]() |
067cc73dce | ||
![]() |
c44e4d40be | ||
![]() |
96d08f41e7 | ||
![]() |
f9589155d5 | ||
![]() |
e84ddfa36d | ||
![]() |
9f99115003 | ||
![]() |
9fc85346ac | ||
![]() |
a479606481 | ||
![]() |
389ba72c44 | ||
![]() |
4ee1dda75f | ||
![]() |
5a1b1c89bd | ||
![]() |
f38968b23b | ||
![]() |
ab40e95ae9 | ||
![]() |
3549107013 | ||
![]() |
fcd22caafb | ||
![]() |
60a89741c6 | ||
![]() |
35e411b9a8 | ||
![]() |
fec6828928 | ||
![]() |
b08f2fcbe2 | ||
![]() |
8a16b9db86 | ||
![]() |
878a34a85d | ||
![]() |
a645086112 | ||
![]() |
3f219f85d4 | ||
![]() |
3c4b6fcf3c | ||
![]() |
f9fd81266f | ||
![]() |
f286f70387 | ||
![]() |
bda2650862 | ||
![]() |
3001e3ccfe | ||
![]() |
4ac616bbc1 | ||
![]() |
b98995bf32 | ||
![]() |
2b2b06aebd | ||
![]() |
a77ada8f45 | ||
![]() |
6a863a86e9 | ||
![]() |
cc333e2f32 | ||
![]() |
acf385693e | ||
![]() |
05788b7978 | ||
![]() |
919ff5065a | ||
![]() |
1cdb4e0726 | ||
![]() |
4fdedc3b00 | ||
![]() |
52dff03342 | ||
![]() |
f2b4b6a893 | ||
![]() |
41508a2217 | ||
![]() |
dbeccb885e | ||
![]() |
0fdf829b3b | ||
![]() |
b72766b460 | ||
![]() |
253aa3816d | ||
![]() |
75c8ff2601 | ||
![]() |
4d6f22a948 | ||
![]() |
923790041d | ||
![]() |
e9012c619e | ||
![]() |
68899231f4 | ||
![]() |
bedc4ee37a | ||
![]() |
d0f0118d13 | ||
![]() |
85c35e4eb3 | ||
![]() |
7c21558f18 | ||
![]() |
38503b2620 | ||
![]() |
b3651cd5b6 | ||
![]() |
94bfadca5f | ||
![]() |
d5ac711da4 | ||
![]() |
31f8859cd4 | ||
![]() |
b29b68e36b | ||
![]() |
74425602ae | ||
![]() |
52de01b9f7 | ||
![]() |
f92fa759e2 | ||
![]() |
188b69e5aa | ||
![]() |
8df57a57c7 | ||
![]() |
1635f9048f | ||
![]() |
1d06a4c9b8 | ||
![]() |
ba648e1383 | ||
![]() |
6633e5512f | ||
![]() |
d1c0050d8d | ||
![]() |
94e807a4e6 | ||
![]() |
657e3fb2ee | ||
![]() |
6da51210de | ||
![]() |
7fc3191b4d | ||
![]() |
2e0835da2e | ||
![]() |
9f38943005 | ||
![]() |
28e49094d2 | ||
![]() |
860b44223b | ||
![]() |
9192981417 | ||
![]() |
a0a5ee68db | ||
![]() |
4ea26e5de7 | ||
![]() |
e4b286e41d | ||
![]() |
b63cf527bc | ||
![]() |
e6c92decf3 | ||
![]() |
48c2158c42 | ||
![]() |
0ad626c1d6 | ||
![]() |
07879bc744 | ||
![]() |
bfc7d1b3d4 | ||
![]() |
99f9acbd34 | ||
![]() |
7a1424b7f8 | ||
![]() |
d1573144ba | ||
![]() |
a70159d95b | ||
![]() |
b22584baf6 | ||
![]() |
737af2ecbd | ||
![]() |
9a6837865c | ||
![]() |
b7ed52c53d | ||
![]() |
72ae2842e7 | ||
![]() |
b94d21b20d | ||
![]() |
14fa1d39aa | ||
![]() |
dacc2c8f6a | ||
![]() |
fe6ee8ba1c | ||
![]() |
edce4087c9 | ||
![]() |
8ba30a102a | ||
![]() |
16a2358835 | ||
![]() |
8cb7ef6418 | ||
![]() |
59187f4db6 | ||
![]() |
90b2d00644 | ||
![]() |
6a3ede73eb | ||
![]() |
958e56642e | ||
![]() |
640cbefed8 | ||
![]() |
f96922956d | ||
![]() |
c921c0dd62 | ||
![]() |
bbee2abc67 | ||
![]() |
556c06332f | ||
![]() |
dbf69de04f | ||
![]() |
15b4ad2b46 | ||
![]() |
87da98178c | ||
![]() |
921dcb905a | ||
![]() |
969a0f0bed | ||
![]() |
a0f2cd973a | ||
![]() |
718bc86c4d | ||
![]() |
59104e388b | ||
![]() |
99a04a6718 | ||
![]() |
0c444e6434 | ||
![]() |
bd4d3cc58d | ||
![]() |
e78e21b8d9 | ||
![]() |
8ad6966c3b | ||
![]() |
0e3533023e | ||
![]() |
c1f78e8855 | ||
![]() |
2aa78da89a | ||
![]() |
397ffd56f8 | ||
![]() |
3c17ffe3c3 | ||
![]() |
c4cb594fc2 | ||
![]() |
626b6c587b | ||
![]() |
3e0dc4d021 | ||
![]() |
6ae964b731 | ||
![]() |
fb74011e69 | ||
![]() |
528b92c0d3 | ||
![]() |
432d9eb7c7 | ||
![]() |
2c8dd72f6f | ||
![]() |
ad7b05ddda | ||
![]() |
101ac9f76a | ||
![]() |
4ca16b887c | ||
![]() |
c5aa4c7fac | ||
![]() |
637218d74d | ||
![]() |
cf7ed097c2 | ||
![]() |
202380d696 | ||
![]() |
cc147e4a92 | ||
![]() |
710022ca4e | ||
![]() |
762889ed02 | ||
![]() |
81e09e77a6 | ||
![]() |
820f4bef5b | ||
![]() |
09e4f02e66 | ||
![]() |
d409db5f79 | ||
![]() |
1ce333108a | ||
![]() |
6dca91e2b2 | ||
![]() |
4ca4ed8178 | ||
![]() |
455903e172 | ||
![]() |
1c076dd2f9 | ||
![]() |
e418b84d6c | ||
![]() |
9fd737d1d8 | ||
![]() |
8e7384f5f3 | ||
![]() |
afa22619e0 | ||
![]() |
b63ef90d6c | ||
![]() |
19264a5ee1 | ||
![]() |
22cdf68fd0 | ||
![]() |
16a6e5001a | ||
![]() |
9c16f3d4c9 | ||
![]() |
7e8988a8eb | ||
![]() |
07d6d5d57f | ||
![]() |
55b3535b22 | ||
![]() |
d5bea6d820 | ||
![]() |
3533d49d9d | ||
![]() |
26a54e211c | ||
![]() |
bb21ea285e | ||
![]() |
b8a8d6f26b | ||
![]() |
35dfdcb5da | ||
![]() |
527ef6fcc5 | ||
![]() |
392f895142 | ||
![]() |
8e1cd2d0dd | ||
![]() |
2c042c42de | ||
![]() |
b5f9d1b179 | ||
![]() |
b3d3bf840e | ||
![]() |
aedba0a92d | ||
![]() |
0ea21d2c3c | ||
![]() |
7ea55baafa | ||
![]() |
2a83b014e0 | ||
![]() |
faec314ddd | ||
![]() |
d3f9a43f3b | ||
![]() |
9b7c040935 | ||
![]() |
dfabcbabf2 | ||
![]() |
8b5f475d33 | ||
![]() |
2bfde737c1 | ||
![]() |
481dd3bb82 | ||
![]() |
b6084589f5 | ||
![]() |
2a695dccf8 | ||
![]() |
3746d880db | ||
![]() |
3ae5fa613e | ||
![]() |
2f98192253 | ||
![]() |
e3753015fd | ||
![]() |
cb21a95917 | ||
![]() |
00a527a5ac | ||
![]() |
7783631573 | ||
![]() |
2fd5829325 | ||
![]() |
66cbade66e | ||
![]() |
61dce4195b | ||
![]() |
95d22a41d8 | ||
![]() |
18be86a183 | ||
![]() |
9a08e5b1f2 | ||
![]() |
96fd330b3f | ||
![]() |
3f5d4eb056 | ||
![]() |
66a9e88954 | ||
![]() |
c64a769f58 | ||
![]() |
b45d9d8bad | ||
![]() |
abb4c1a76c | ||
![]() |
59c90ad8d5 | ||
![]() |
8402ad2133 | ||
![]() |
c0877af85c | ||
![]() |
e5577a4354 | ||
![]() |
91cffc1c27 | ||
![]() |
b884c8e69b | ||
![]() |
15a9c0f76e | ||
![]() |
f868a922c8 | ||
![]() |
0135791354 | ||
![]() |
98d0da2370 | ||
![]() |
8d504d6907 | ||
![]() |
fbebfe8407 | ||
![]() |
0945ea5eed | ||
![]() |
8542e86963 | ||
![]() |
80a140e509 | ||
![]() |
b3560043ab | ||
![]() |
574a8fd802 | ||
![]() |
fe4a6263d3 | ||
![]() |
0409590ceb | ||
![]() |
e22501a646 | ||
![]() |
5e61a6613d | ||
![]() |
e2b9103cf4 | ||
![]() |
71f3e78e8f | ||
![]() |
c5253f7950 | ||
![]() |
9aca4187dc | ||
![]() |
371cc2e033 | ||
![]() |
26dc407148 | ||
![]() |
5a9a4c1de9 | ||
![]() |
ebbeb8b2bb | ||
![]() |
79a2300211 | ||
![]() |
4f67330b31 | ||
![]() |
ef2dd30d6d | ||
![]() |
d67b052ece | ||
![]() |
9f0d09729f | ||
![]() |
ceb789f899 | ||
![]() |
a788d0291c | ||
![]() |
60d882b564 | ||
![]() |
d0fe2444ab | ||
![]() |
45c3060841 | ||
![]() |
583ee31e77 | ||
![]() |
fb20bf5355 | ||
![]() |
7aa1a8338b | ||
![]() |
b35367339e | ||
![]() |
b38d9754d7 | ||
![]() |
ba3bd0b1d3 | ||
![]() |
00bea67107 | ||
![]() |
cb78e08d89 | ||
![]() |
a0eea51fbf | ||
![]() |
2a19651480 | ||
![]() |
53ad627f72 | ||
![]() |
8d80e272f6 | ||
![]() |
0d62bbb823 | ||
![]() |
2833c35ac9 | ||
![]() |
0d2d22fa8d | ||
![]() |
fde6c2827b | ||
![]() |
a286dbdd03 | ||
![]() |
a8b1f6575a | ||
![]() |
666fe3e605 | ||
![]() |
7acff7d912 | ||
![]() |
104066b915 | ||
![]() |
bacb5c99ce | ||
![]() |
be03784dda | ||
![]() |
a97f5954ce | ||
![]() |
8c47b06721 | ||
![]() |
426b99d75d | ||
![]() |
23efb49677 | ||
![]() |
b2da3a9d03 | ||
![]() |
70ffe72e1f | ||
![]() |
7da15230ee | ||
![]() |
14af4269e1 | ||
![]() |
bc212b041d | ||
![]() |
433f877d82 | ||
![]() |
d9e8275ca3 | ||
![]() |
102d317b08 | ||
![]() |
210b24fb6d | ||
![]() |
5433a11297 | ||
![]() |
ffe819e4a6 | ||
![]() |
cf4429f7d7 | ||
![]() |
a0aa4548b3 | ||
![]() |
de3ef51378 | ||
![]() |
5de236b76e |
972 changed files with 119060 additions and 50112 deletions
83
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
83
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
name: Bug report
|
||||
description: Create a report to help us understand and triage your issue.
|
||||
labels:
|
||||
- Awaiting Triage
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Bug Report
|
||||
|
||||
Please follow this document carefully to report a bug.
|
||||
|
||||
> **Important**: It is highly recommended that you use the latest version before submitting a bug report.
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Tomb Engine Version
|
||||
description: |
|
||||
Please select the TombEngine version you are using.
|
||||
options:
|
||||
- v1.8.1 (latest release)
|
||||
- v1.8.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Development Version
|
||||
description: Are you submitting this report from a development build that has not been officially released?
|
||||
options:
|
||||
- label: "I am using an unofficial development version."
|
||||
- label: "I am using an official release."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: |
|
||||
Please provide a clear and concise description of what the issue is.
|
||||
placeholder: |
|
||||
Your bug report here.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: To Reproduce
|
||||
description: |
|
||||
Please provide detailed steps to reproduce the issue.
|
||||
|
||||
**Note**: If the bug cannot be reproduced or the issue is not clearly explained, it may be closed without further investigation.
|
||||
placeholder: |
|
||||
Step-by-step reproduction instructions here.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behaviour
|
||||
description: |
|
||||
What did you expect to happen?
|
||||
|
||||
**Note**: If the bug cannot be reproduced or the issue is not clearly explained, it may be closed without further investigation.
|
||||
placeholder: |
|
||||
A description of what should happen here.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Minimal Reproduction Project
|
||||
description: |
|
||||
Please upload a .zip file (10 MB max) containing your level and all assets needed to compile the level, including a minimal version where the bug occurs.
|
||||
|
||||
Alternatively, provide a download link from a cloud storage service (e.g., Google Drive, Dropbox).
|
||||
|
||||
> **Important**: If you do not provide a minimal reproduction project, your issue may be rejected.
|
||||
placeholder: |
|
||||
Download link to your project or attach a .zip file.
|
||||
validations:
|
||||
required: true
|
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
blank_issues_enabled: false
|
||||
|
||||
# Disable blank issue
|
||||
|
||||
blank_issues_enabled: false
|
||||
|
||||
# Bug Report Form
|
||||
name: Bug Report
|
||||
description: File a bug report.
|
||||
title: "[Bug]: "
|
||||
labels: [ "Awaiting Triage" ]
|
||||
|
||||
# Initial Message
|
||||
body:
|
||||
type: markdown
|
||||
attributes:
|
||||
value: Thanks for taking the time to fill out this bug report! Please use the steps below to log the report for the development team.
|
34
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
Normal file
34
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
name: Feature Request
|
||||
description: Suggest an idea for TombEngine
|
||||
title: "[ Feature Request ] "
|
||||
labels: Awaiting Triage
|
||||
|
||||
body:
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
placeholder: A clear and concise description of what you want to happen.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
placeholder: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
placeholder: Add any other context or screenshots about the feature request here. (as a link to screenshot or drop the screenshot into this textbox)
|
||||
validations:
|
||||
required: false
|
53
AUTHORS.md
Normal file
53
AUTHORS.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Credit List
|
||||
|
||||
This is the credit list of **all** the people who contributed to TombEngine in any shape or form.
|
||||
|
||||
## Developers
|
||||
|
||||
- MontyTRC (Project Leader)
|
||||
|
||||
- Gancian (general coding
|
||||
- Krystian (general coding)
|
||||
- Kubsy (Some cleanups and fixes)
|
||||
- l.m. (general coding, Lua enhancements, bug fixing)
|
||||
- Lwmte (sound refactoring, general coding, code cleanups, bug fixing)
|
||||
- Moooonyeah (Jumanji) (entity decompilation)
|
||||
- Raildex (renderer refactoring, particle coding, general coding)
|
||||
- RicardoLuis0 (general coding)
|
||||
- Sezz (player state refactoring, general coding, code cleanups, bug fixing, assets)
|
||||
- Squidshire (Hispidence) (Lua implementation, bug fixing)
|
||||
- Stranger1992 (sound asset refactoring and organisation, assets)
|
||||
- TrainWreck (asset coding and Lua enhancements
|
||||
- TokyoSU (entity and vehicle decompilation)
|
||||
- Tomo (general coding, special FX coding, bug fixing)
|
||||
- Troye (general coding, refactoring)
|
||||
- Nickelony (general coding)
|
||||
- JesseG, aka WolfCheese (general coding)
|
||||
|
||||
## Testers
|
||||
- Adngel
|
||||
- Caesum
|
||||
- Dustie
|
||||
- GeckoKid
|
||||
- JoeyQuint
|
||||
- Kamillos
|
||||
- Kubsy
|
||||
- LGG_PRODUCTION
|
||||
- Lore
|
||||
- RemRem
|
||||
- Stranger1992
|
||||
- WolfCheese
|
||||
|
||||
## Assets and Miscellaneous
|
||||
|
||||
- Geckokid (Sprites and test level creator).
|
||||
|
||||
### Animations
|
||||
- SrDanielPonces (Diagonal shimmy transitions, backwards monkey swinging)
|
||||
- Krystian (Flexibility crawlspace, slope climbing animations)
|
||||
- Sezz (Additional Animations for player state refactoring)
|
||||
- Naotheia (Underwater puzzle placement, crouch 180° turn, crawl 180° turn, water surface 180° turn)
|
||||
- JoeyQuint (Standing 180° turn, monkey swing 180° turn)
|
||||
|
||||
### TombEngine Marketing
|
||||
- Stranger1992 (Tomb Engine website, Discord, Facebook, Instagram, Youtube, X, and Twitch)
|
|
@ -1,6 +1,366 @@
|
|||
Version 1.2
|
||||
===========
|
||||
# Changelog
|
||||
|
||||
The dates are in European standard format where date is presented as **YYYY-MM-DD**.
|
||||
TombEngine releases are located in this repository (alongside with Tomb Editor): https://github.com/TombEngine/TombEditorReleases
|
||||
|
||||
## [Version 1.8.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.8.1) - 2025-03-29
|
||||
|
||||
### Bug fixes
|
||||
* Fixed pathfinding for friendly NPCs such as monkeys.
|
||||
* Fixed particles remaining in the level after reloading from the savegame.
|
||||
* Fixed particles being canceled by fog bulbs.
|
||||
* Fixed crash in case hair object is the last object in a level.
|
||||
* Fixed crash with incorrectly applied animated textures on static meshes.
|
||||
* Fixed console window not hiding in non-debug mode on Windows 11.
|
||||
* Fixed key binding settings saving for the current play session after hitting Esc to cancel.
|
||||
* Fixed lensflare blending formula to avoid screen overbright.
|
||||
|
||||
### New features
|
||||
* Added Firefly Emitter object (ID 1099) with corresponding sprite slot (ID 1379).
|
||||
* Added live console input to perform Lua commands in realtime.
|
||||
|
||||
### Lua API changes
|
||||
* Added missing constructor for `Collision.Probe` without room number.
|
||||
* Added optional looping argument for `View.GetFlybyPosition` and `View.GetFlybyRotation` functions.
|
||||
|
||||
## [Version 1.8](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.8) - 2025-03-16
|
||||
|
||||
### Bug fixes
|
||||
* Improved engine performance up to 20%.
|
||||
* Fixed bridges moving the player when the player is underwater.
|
||||
* Fixed trigger triggerer not working.
|
||||
* Fixed display pickup numeric string not being interpolated in high framerate mode.
|
||||
* Fixed two block platform room portal traversal failing in some cases.
|
||||
* Fixed incorrect handling of dynamic light shadows.
|
||||
* Fixed ricochet flashes after using explosive weapons.
|
||||
* Fixed incorrect flare draw in crawl state.
|
||||
* Fixed starfield remaining active in the next level if it does not have a starfield specified.
|
||||
* Fixed underwater dust particles overflowing when camera is underwater.
|
||||
* Fixed wetness player attribute not being preserved in savegames.
|
||||
* Fixed invisible HK ammo in the inventory.
|
||||
* Fixed flickering rat emitter.
|
||||
* Fixed camera glitch when going into quicksand rooms with weapons drawn.
|
||||
* Fixed player model submerging into the floor while swimming underwater.
|
||||
* Fixed custom shatter sounds with custom sound IDs not playing correctly.
|
||||
* Fixed crashes with sound samples larger than 2 megabytes.
|
||||
|
||||
### New features
|
||||
* Added multithreading and an option for it to flow system settings.
|
||||
* Added ability to use floor trapdoors, keys and puzzle items underwater.
|
||||
- You must update your Lara object: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Lara/TEN_Lara.wad2
|
||||
* Added a particle based waterfall emitter object and associated sprite slots.
|
||||
- You must use this version: https://github.com/TombEngine/Resources/raw/refs/heads/main/Wad2%20Objects/Interactables/TEN_Waterfall_Emitter.wad2
|
||||
* Added TR1 Hammer.
|
||||
- You must use this version: <https://github.com/TombEngine/Resources/raw/refs/heads/main/Wad2%20Objects/Traps/TR1_Thor%20Hammer.wad2>
|
||||
* Added TR3 Moving Laser.
|
||||
* Added TR4 Statue Plinth.
|
||||
|
||||
### Lua API changes
|
||||
* Added diary module.
|
||||
* Added custom bar module.
|
||||
* Added `Collision.Probe` class for basic room collision detection.
|
||||
* Added `Flow.Horizon` class and two layers of horizons in a `Flow.Level` class.
|
||||
* Added `Effects.EmitAdvancedParticle` function, allowing animations and other effects.
|
||||
* Added `Effects.EmitAirBubble` function to spawn air bubbles.
|
||||
* Added `Effects.EmitStreamer` function to emit streamers.
|
||||
* Added `Flow.GetTotalSecretCount` function to get total amount of secrets in the game.
|
||||
* Added `View.GetFlyByPosition` and `View.GetFlyByRotation` functions to get flyby sequence parameters at a specified time point.
|
||||
* Added `Moveable:GetScale` and `Movebale:SetScale` methods to get or set visible scale of moveables.
|
||||
* Added `Static:GetCollidable` and `Static:SetCollidable` methods to get or set collision status of static meshes.
|
||||
* Added `Rotation:Lerp` function to allow linear interpolation between rotations.
|
||||
* Added ability to perform additive and subtractive operations on `Rotation` class and compare one `Rotation` to another.
|
||||
* Added various `Translate` methods to `Vec2` and `Vec3` script objects.
|
||||
* Added alpha transparency functionality for statics and moveables to be used with `SetColor` method.
|
||||
* Added extra arguments for sprite object slots and starting rotation value for `EmitParticle` function.
|
||||
* Added ability to dynamically change `Flow.Level` weather and environment parameters and save them to a savegame.
|
||||
* Added pickup count to `Flow.Statistics` class.
|
||||
* Changed `Flow.Starfield` and `Flow.LensFlare` primitive types to use parameters instead of getters and setters.
|
||||
* Fixed medipack level count in `Flow.Statistics` class.
|
||||
|
||||
## [Version 1.7.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.4) - 2025-04-01
|
||||
|
||||
### Bug fixes
|
||||
* Fixed static meshes with dynamic light mode not accepting room lights.
|
||||
* Fixed silent crashes if no Visual C++ runtimes are installed and provide a dialog box to download them instead.
|
||||
* Fixed issues with launching the engine from directories with non-Western characters in the path.
|
||||
* Fixed rare case of not being able to start a new game or exit game from the main menu on very slow GPUs.
|
||||
* Fixed occasional crashes with creatures stuck in a sector with no pathfinding set.
|
||||
* Fixed occasional cases of underwater switch animation not playing, if player spams jump key while pulling the switch.
|
||||
* Fixed player's blob shadows not rendering on moveables and static meshes.
|
||||
* Fixed antialiasing quality not changing after changing it in display settings.
|
||||
* Fixed endless explosion effect for Puna.
|
||||
* Fixed diary pick-up item inventory state not preserved in the savegame.
|
||||
* Fixed gravity being applied underwater when exiting the fly cheat.
|
||||
* Fixed gravity being applied when vaulting on the same frame as the player lands.
|
||||
|
||||
### New features
|
||||
* Added realtime shader reloading in debug mode by pressing F9 key.
|
||||
* Added load, save, stopwatch and compass as a functional pick-up items with ability to add or remove them from inventory.
|
||||
* Increased particle limit from 1024 to 4096.
|
||||
* Added ability for the player to more reliably stop at an edge when running at it while holding Walk.
|
||||
|
||||
### Lua API changes
|
||||
* Fixed Flow.FreezeMode.FULL drawing incorrect background.
|
||||
* Fixed DisplayString scale argument not being optional, as stated in documentation.
|
||||
|
||||
## [Version 1.7](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.3) - 2024-12-25
|
||||
|
||||
### Bug fixes
|
||||
* Significantly improved renderer performance.
|
||||
* Improved engine performance around bridges.
|
||||
* Improved engine performance if weather or bubble effects are active.
|
||||
* Improved engine start-up time.
|
||||
* Fixed silent crashes if loaded level is corrupted or in incorrect format.
|
||||
* Fixed occasional crashes if there are static meshes placed within room border walls.
|
||||
* Fixed climbable pushables clipping Lara under the bridges when pulled.
|
||||
* Fixed incorrect clipping of scaled off-centered static meshes.
|
||||
* Fixed incorrect collision detection for off-centered moveables.
|
||||
* Fixed incorrect slide directions for sub-click geometry.
|
||||
* Fixed stutter during jumps between cameras in a flyby sequence.
|
||||
* Fixed uzi targeting issues after using flycheat.
|
||||
* Fixed hair object vertices not always linking properly.
|
||||
* Fixed snow particles not always melting on the ground.
|
||||
* Fixed enemies not damaging Lara if she is staying on the sector where enemies were triggered.
|
||||
* Fixed enemy pickups dropping on death sectors.
|
||||
* Fixed Sarcophagus and Search Object pickup triggers.
|
||||
* Fixed vehicle transfer not happening for levels which were not previously visited.
|
||||
* Fixed audio tracks placed in subfolders not restoring after loading savegame.
|
||||
* Fixed initial position and lack of fade-in for looped audio track on level start.
|
||||
* Fixed shatter debris spawning on incorrect position for the first frame.
|
||||
* Fixed scripted input events not registering on the same game frame.
|
||||
* Fixed incorrect object camera position.
|
||||
* Fixed incorrect camera movement near walls after leaving look mode.
|
||||
* Fixed binocular or lasersight camera not switching off correctly after flyby.
|
||||
* Fixed binocular or lasersight camera transitions.
|
||||
* Fixed target highlighter still being active in binocular or lasersight mode.
|
||||
* Fixed Lara's Home entry not working.
|
||||
* Fixed exploding TR3 bosses.
|
||||
* Fixed original issue with deactivation of Dart Emitter.
|
||||
* Fixed original issue with weapon hotkeys available in binoculars or lasersight mode.
|
||||
* Fixed Electricity Wires object not doing instant kill when Lara is in close proximity.
|
||||
* Fixed Lens Flare object not functioning properly.
|
||||
* Fixed lens flares not being occluded by static meshes and moveables.
|
||||
* Fixed spotlight shadows.
|
||||
* Fixed Skeleton and Mummy not reacting to shotgun hits.
|
||||
|
||||
### New features
|
||||
* Added classic mirror effect with ability to reflect moveables and static meshes.
|
||||
* Added ability to customize many hardcoded parameters, such as flare, weapon, and hair settings.
|
||||
* Added dynamic shadow casting on objects and static meshes.
|
||||
* Added fast savegame reloading.
|
||||
* Added ricochet sounds and make the effect more prominent.
|
||||
* Allow camera shake during flybys.
|
||||
* Allow to run the engine without title level.
|
||||
* Allow more than 1024 objects in a level.
|
||||
* Allow more than 1000 static mesh slots in a level.
|
||||
|
||||
### Lua API changes
|
||||
* Added Flow.Statistics class, Flow.GetStatistics() and Flow.SetStatistics() functions.
|
||||
* Added Flow.GetFreezeMode() and Flow.SetFreezeMode() functions.
|
||||
* Added Effects.EmitSpotLight() function for directional spotlights.
|
||||
* Added optional cast shadow and name parameters for Effects.EmitLight() function.
|
||||
* Added Effects.GetWind() function to get current wind speed vector.
|
||||
* Added Moveable:GetCollidable() and Moveable:SetCollidable() functions.
|
||||
* Added Moveable:GetAnimSlot() and optional second argument for Moveable:SetAnim() to access different animation slots.
|
||||
* Added Rotation:Direction() method to get directional vector.
|
||||
* Added support for transparency value in Strings.DisplayString class.
|
||||
* Added extra argument for Sound.SetAmbientTrack() function to specify if new ambient track should play from the beginning.
|
||||
* Added new View.CameraType enum entries and return it by View.GetCameraType(), when flyby camera or binoculars/lasersight is active.
|
||||
* Added new primitive Time class, which allows to manipulate and format game time without precision loss.
|
||||
* Renamed Flow.WeaponType enumeration to Objects.WeaponType, and removed similar Objects.LaraWeaponType enumeration for consistency.
|
||||
* Renamed Objects.PlayerAmmoType to Objects.AmmoType for consistency.
|
||||
* Fixed Strings.DisplayString class not supporting some Unicode characters and empty lines in multiline strings.
|
||||
* Fixed Strings.DisplayString not being deallocated after showing.
|
||||
* Fixed GameVars not transferring between levels in hub mode.
|
||||
* Fixed incorrect return value of Moveable:GetAnim() function, if animation from another slot is currently playing.
|
||||
* Fixed incorrect behaviour of Moveable:GetJointRotation() function.
|
||||
* Fixed incorrect behaviour of Logic.EnableEvent() and Logic.DisableEvent() functions.
|
||||
* Fixed Util.HasLineOfSight() not taking static meshes into consideration.
|
||||
* Fixed collision callbacks not properly clearing after leveljump.
|
||||
* Fixed Flow.SetIntroImagePath() not using the correct path.
|
||||
|
||||
## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03
|
||||
|
||||
### Bug fixes
|
||||
* Fixed original issue with classic switch off trigger incorrectly activating some trigger actions.
|
||||
* Fixed moveable status after antitriggering.
|
||||
* Fixed leveljump vehicle transfer.
|
||||
* Fixed weapons not properly hitting enemies.
|
||||
* Fixed falling through dynamic bridges that are moving upwards.
|
||||
* Fixed Laserhead teleporting Lara and making her invisible on death.
|
||||
* Fixed pick-ups from Sarcophagus objects.
|
||||
* Fixed issue with Lara not rotating together with bridges while picking up items.
|
||||
* Fixed ghost collision with objects with zero bounds.
|
||||
* Fixed several binocular bugs.
|
||||
* Fixed faulty death sectors.
|
||||
* Fixed shimmy softlocks around static meshes with soft collision.
|
||||
* Fixed incorrect swing ledge grabs with steep grab angles.
|
||||
* Fixed incorrect climbing out of water on bridge objects and in front of static meshes.
|
||||
* Fixed incorrect diving animation when swandiving from a high place.
|
||||
* Fixed room clipping when flyby path goes out of room bounds.
|
||||
* Fixed camera rotating with the player's hips when climbing out of water.
|
||||
* Fixed camera behaviour on sloped surfaces after player's death.
|
||||
* Fixed camera position after loading a savegame.
|
||||
* Fixed broken ropes after loading a savegame.
|
||||
* Fixed AI for TR2 Skidoo driver and Worker with shotgun.
|
||||
* Fixed Ember Emitter crashing when ocb is between -1 and -10.
|
||||
* Fixed Electric Cleaner and Squishy Block not detecting collision with certain block heights.
|
||||
* Fixed Squishy Blocks crashing the level.
|
||||
* Fixed Larson and Pierre pathfinding.
|
||||
* Fixed Dart Emitters failing with antitrigger.
|
||||
* Fixed Homing Dart Emitter spawning darts continously when player is on its trigger.
|
||||
* Fixed Four Blade Trap floor and ceiling collision.
|
||||
* Fixed Joby Spikes collision and deformation.
|
||||
* Fixed Sentry Gun joint rotation.
|
||||
* Fixed Teeth Spikes not triggering the player impale animation.
|
||||
* Fixed TR4 Mine crash with OCB 1 when triggered.
|
||||
* Fixed cases where Atlantean Mutant's bombs cause the game to crash.
|
||||
* Fixed torch flame delay when the player throws or drops a torch.
|
||||
* Fixed display sprites and display strings rendering in the inventory background.
|
||||
* Fixed young Lara hair drawing. https://tombengine.com/docs/level-settings/#young_lara
|
||||
|
||||
### New features
|
||||
* Added high framerate mode (also known as 60 FPS mode).
|
||||
* Added a customisable global lensflare effect. https://tombengine.com/docs/level-settings/#lensflare
|
||||
* Added a customisable starry sky and meteor effect. https://tombengine.com/docs/level-settings/#stars
|
||||
* Added the ability to display "Lara's Home" entry in the main menu.
|
||||
* Added the ability to change pickup item count by modifying item hit points.
|
||||
* Added F12 as alternative to PrtSc for screenshots.
|
||||
* Added ability to invoke load game dialog after death by pressing any key.
|
||||
* Added visible mouse pointer in windowed mode.
|
||||
* Added portal debug mode.
|
||||
* Added new sound conditions: Quicksand and Underwater.
|
||||
- Quicksand - sound effect plays when a moveable is in quicksand.
|
||||
- Underwater - sound plays when the camera is submerged.
|
||||
* Added TR3 Seal Mutant. https://tombengine.com/docs/ocb-and-setup-instructions/#sealmutant
|
||||
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Seal_Mutant.wad2
|
||||
* Added TR4 Enemy Jeep. https://tombengine.com/docs/ocb-and-setup-instructions/#enemy_jeep
|
||||
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TR4_Enemy_Jeep.wad2
|
||||
* Changed TR5 Rome Hammer to not hurt player whilst deactivated.
|
||||
* Changed TR2 Statue with blade damage from 20 to 200.
|
||||
* Changed sound effect that is triggered when using the `level.rumble` feature in a level (ID 359 in the soundmap).
|
||||
* Changed hardcoded sound for Raising Block 1/2 back to the ID used in TRLE (ID 149).
|
||||
* Enhanced TR2 Rolling Spindle detection to avoid them going down through pits.
|
||||
* Enhanced Sentry Guns, with a new ItemFlags[3], to contain the ID of the inventory item that deactivates the sentry guns (Puzzle Item 5 by default).
|
||||
* Enhanced Dart Emitter, with a new ItemFlags[0], to contain the number of frames between shots (by default 32 in Dart Emitter, and 24 in Homing-Dart Emitter).
|
||||
* Enhanced Raptor behaviour and handling. https://tombengine.com/docs/ocb-and-setup-instructions/#raptor
|
||||
- You must use this version: https://github.com/TombEngine/Resources/raw/main/Wad2%20Objects/Enemies/TEN_Raptor.wad2
|
||||
* Removed original limit of 32 active Flame Emitters.
|
||||
|
||||
### Lua API changes
|
||||
* Added Flow.EnableHomeLevel() function.
|
||||
* Added Flow.IsStringPresent() function.
|
||||
* Added Flow.LensFlare() and Flow.Starfield() classes.
|
||||
* Added Inventory.GetUsedItem(), Inventory.SetUsedItem() and Inventory.ClearUsedItem() functions.
|
||||
* Added Input.KeyClearAll() function.
|
||||
* Added Moveable:GetJointRotation() and optional 'offset' parameter for Moveable.GetJointPosition().
|
||||
* Added Moveable:GetTargetState() function.
|
||||
* Added Room:GetRoomNumber() function.
|
||||
* Removed anims.monkeyAutoJump. It is now a player menu configuration.
|
||||
* Fixed Volume:GetActive() method.
|
||||
|
||||
## [Version 1.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.1) - 2024-04-21
|
||||
|
||||
### Bug fixes
|
||||
* Fixed drawing of display sprites in title level.
|
||||
* Fixed drawing of smoke sprites and various other sprites.
|
||||
* Fixed drawing of transparent surfaces when debris are present in scene.
|
||||
* Fixed player holster state and current vehicle not preserved correctly on level jump.
|
||||
* Fixed diving when swimming over sinks.
|
||||
* Fixed fire item effect not extinguishing in water.
|
||||
* Fixed fade-in and fade-out effects not canceling correctly when next level is loaded.
|
||||
* Fixed shadows still being visible after shattering a moveable.
|
||||
* Fixed FOV interpolation at the end of the flyby sequence.
|
||||
* Fixed sounds resuming in pause mode while switching between apps.
|
||||
* Fixed slide directions.
|
||||
* Fixed occasional collision warnings in a log when teeth spikes object was activated.
|
||||
* Fixed climbable pushables collision during continuous pulling action.
|
||||
* Fixed collision for solid static meshes with zero collision box.
|
||||
* Fixed bottom collision for solid static meshes.
|
||||
* Fixed T-Rex's head rotation.
|
||||
|
||||
### New features
|
||||
* Auto-switch to a crawl state if player start position is in a crawlspace.
|
||||
* Allow directional flame emitter (negative OCBs) to be rotated at any angle.
|
||||
* Revise wall spikes:
|
||||
- Wall spikes now stop when they touch a pushable, another spike wall or a normal wall.
|
||||
- Wall spikes will shatter any shatter in its path.
|
||||
- Wall spikes can be stopped by normal antitrigger or with a volume.
|
||||
* Added hub system to preserve level state on level jumps.
|
||||
* Added ember emitter.
|
||||
* Added fish emitter.
|
||||
* Added laser beam object.
|
||||
* Added TR2 dragon.
|
||||
* Added TR3 Winston (requires updated TEN .wad2 on TombEngine.com).
|
||||
* Added TR4 squishy blocks (requires updated TEN .wad2 on TombEngine.com).
|
||||
|
||||
### Lua API changes
|
||||
* Added resetHub flag to Flow.Level, which allows to reset hub data.
|
||||
* Added Flow.GetFlipMapStatus() function to get current flipmap status.
|
||||
* Added Moveable:GetMeshCount() function to get number of moveable meshes.
|
||||
* Added timeout parameter for Moveable:Enable() function.
|
||||
* Added Static:GetHP() and Static:SetHP() functions to change shatterable static mesh hit points.
|
||||
* Fixed Moveable:SetOnCollidedWithObject() callback.
|
||||
|
||||
## [Version 1.3](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7) - 2024-01-06
|
||||
|
||||
### Bug fixes
|
||||
* Fixed crash if title logo is removed from Textures folder.
|
||||
* Fixed crash if unknown player state ID is encountered.
|
||||
* Fixed bug with OCB 2 on pushables, and some other pushable bugs.
|
||||
* Fixed pushable camera bug during edge slip.
|
||||
* Fixed lever switch turn off alignment animation.
|
||||
* Fixed lack of water splash in certain scenarios.
|
||||
* Fixed hydra flame not showing when charging.
|
||||
* Fixed shockwave light for hammer god.
|
||||
* Fixed camera shaking in some cases when the player is in quicksand room.
|
||||
* Fixed certain flame emitter OCBs emitting fire in wrong directions.
|
||||
* Fixed player not being able to pick up a torch when crouching.
|
||||
* Fixed jittery camera when performing crawl-to-hang.
|
||||
* Fixed several issues with limited pistol ammo.
|
||||
* Fixed player not being able to crawl if two-handed weapon is currently equipped.
|
||||
* Fixed playback issues with audio tracks placed in subfolders.
|
||||
* Fixed thin caustics outline on the edge of the blocks.
|
||||
* Fixed big static objects affected wrongly by dynamic lights.
|
||||
* Fixed legacy trigger leveljumps ignoring provided level index.
|
||||
* Fixed incorrect light collection in some cases.
|
||||
* Fixed normal mapping for rooms, items, and statics.'
|
||||
|
||||
### New features
|
||||
* Added ambient occlusion (SSAO).
|
||||
* Added new post-process workflow (monochrome, negative, exclusion) with tinting.
|
||||
* Added SMAA antialiasing instead of MSAA.
|
||||
* Added previously missing player start position object functionality.
|
||||
* Added fast speed for fly cheat by holding Sprint input action.
|
||||
* Added speedometer to vehicles.
|
||||
* Added global node events.
|
||||
* Totally revised transparency handling.
|
||||
* Increased the maximum frames for animated sequences from 128 to 256.
|
||||
* Optimized the renderer.
|
||||
* Separate underwater wall and ceiling switch objects into two slots each.
|
||||
* Accurately rotate display sprites around the pivot defined by the align mode.
|
||||
* Allow walking on slopes when wading in water (similar to quicksand).
|
||||
* Allow player to pull certain levers with both hands when holding a flare.
|
||||
* Ported twin auto gun from TR3.
|
||||
* Revised keyhole OCBs to account for keeping or losing keys:
|
||||
- OCB 0: Play default animation and lose key.
|
||||
- Positive OCB: Play anim number and keep key.
|
||||
- Negative OCB: Play anim number and lose key.
|
||||
* Revised Wolf OCBs:
|
||||
- OCB 0: Wolf starts in walking animation, ready to chase Lara.
|
||||
- OCB 1: Wolf starts in sleeping animation.
|
||||
|
||||
### Lua API changes
|
||||
* Added Lara:GetInteractedMoveable() which returns currently interacted moveable by Lara.
|
||||
* Added Moveable:SetStatus() to set the current status of the moveable.
|
||||
* Added Room:GetColor() to get room's ambient light color.
|
||||
* Added Util.PickMoveableByDisplayPosition() and Util.PickStaticByDisplayPosition() functions.
|
||||
* Added View.GetCameraPosition(), View.GetCameraTarget() and View.GetCameraRoom() functions.
|
||||
* Added View.SetPostProcessMode(), View.SetPostProcessStrength() and View.SetPostProcessTint() functions.
|
||||
|
||||
## [Version 1.2](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.9) - 2023-11-11
|
||||
|
||||
### Bug fixes
|
||||
* Fix burning torch not working properly if there are more than 256 objects in a level.
|
||||
* Fix grenade and rocket projectiles smoke offset in certain directions.
|
||||
* Fix projectiles flying through animating objects.
|
||||
|
@ -24,30 +384,32 @@ Version 1.2
|
|||
* Fix non-elevated combat camera.
|
||||
* Fix camera snap when disengaging the look-around mode.
|
||||
* Fix TR4 mapper not being visible.
|
||||
|
||||
### New features
|
||||
* Improve head-on wall collision.
|
||||
* Overhaul pushables:
|
||||
- Separate climbable and non-climbable pushable object slots.
|
||||
- Add new pushable OCB to manipulate pushable properties.
|
||||
- Add new animations for pushing pushables off edgees (TR1-3 and TR4-5 versions).
|
||||
- Fix pushables not working with raising blocks.
|
||||
- Fix miscellaneous pushable bugs.
|
||||
- Separate climbable and non-climbable pushable object slots.
|
||||
- Add new pushable OCB to manipulate pushable properties.
|
||||
- Add new animations for pushing pushables off edgees (TR1-3 and TR4-5 versions).
|
||||
- Fix pushables not working with raising blocks.
|
||||
- Fix miscellaneous pushable bugs.
|
||||
* Overhaul look-around feature:
|
||||
- Allow for more consistent and wider viewing angles while crawling, crouching, and hanging.
|
||||
- Improve look camera movement and control.
|
||||
- Re-enable looking while performing up jump, backward jump, or backward crawl.
|
||||
- Add functionality to rotate slowly when holding Walk while using binoculars or lasersight.
|
||||
- Allow for more consistent and wider viewing angles while crawling, crouching, and hanging.
|
||||
- Improve look camera movement and control.
|
||||
- Re-enable looking while performing up jump, backward jump, or backward crawl.
|
||||
- Add functionality to rotate slowly when holding Walk while using binoculars or lasersight.
|
||||
* Add target highlighter system with toggle in Sound and Gameplay settings.
|
||||
* Add sprint slide state 191.
|
||||
* Add swinging blade.
|
||||
* Add crumbling platform and add new OCBs for behaviour:
|
||||
- OCB 0: Default behaviour. When the player steps on the platform, it will shake and crumble after 1.2 seconds.
|
||||
- OCB > 0: When the player steps on the platform, it will crumble after the number of frames set in the OCB.
|
||||
- A positive value results in activation via player collision.
|
||||
- A negative value requires a trigger to activate.
|
||||
- OCB 0: Default behaviour. When the player steps on the platform, it will shake and crumble after 1.2 seconds.
|
||||
- OCB > 0: When the player steps on the platform, it will crumble after the number of frames set in the OCB.
|
||||
- A positive value results in activation via player collision.
|
||||
- A negative value requires a trigger to activate.
|
||||
* Add basic mouse input handling. Allows for binding of mouse inputs in control settings.
|
||||
* Add settings for Mouse Sensitivity and Mouse Smoothing (not used in-game yet).
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Split and organize functions in `Misc` namespace to appropriate new namespaces.
|
||||
* Make Vec2 and Vec3 objects float-based instead of integer-based.
|
||||
* Add DisplaySprite object.
|
||||
|
@ -73,9 +435,9 @@ Lua API changes:
|
|||
* Add DisplayString::SetScale() function to resize text.
|
||||
* Add DisplayString::GetScale() function to get text scale.
|
||||
|
||||
Version 1.1.0
|
||||
==============
|
||||
## [Version 1.1.0](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.8) - 2023-07-29
|
||||
|
||||
### Bug fixes
|
||||
* Fix enemies shooting Lara through static meshes and moveables.
|
||||
* Fix skeletons and mummies not being affected by explosive weapons.
|
||||
* Fix crash on loading if static meshes with IDs above maximum are present.
|
||||
|
@ -105,6 +467,8 @@ Version 1.1.0
|
|||
* Fix room collector freezing game on some occasions.
|
||||
* Fix incorrect culling for scaled static meshes.
|
||||
* Fix normal mapping.
|
||||
|
||||
### New features
|
||||
* Add ability to save screenshot in the "Screenshots" subfolder by pressing the "Print screen" key.
|
||||
* Implement separate audio track channel for playing voiceovers with subtitles in .srt format.
|
||||
* Don't stop ambience when Lara dies.
|
||||
|
@ -122,15 +486,15 @@ Version 1.1.0
|
|||
* Add TR1 skateboard kid.
|
||||
* Add TR1 Kold.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Add soundtrack functions:
|
||||
- Misc::GetAudioTrackLoudness() for getting current loudness of a given track type.
|
||||
- Misc::IsAudioTrackPlaying() for checking if a given track type is playing.
|
||||
- Misc::GetCurrentSubtitle() for getting current subtitle string for the voice track.
|
||||
|
||||
Version 1.0.9
|
||||
=============
|
||||
## [Version 1.0.9](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.7) - 2023-06-03
|
||||
|
||||
### Bug fixes
|
||||
* Fix cold bar triggered in non-water rooms.
|
||||
* Fix spiky wall speed value and change it via OCB number or Lua (Moveable::SetItemFlags[0]).
|
||||
* Fix bats emitter crashing the game if little beetle object does not exist in wad.
|
||||
|
@ -151,6 +515,8 @@ Version 1.0.9
|
|||
* Fix grenade firing angle.
|
||||
* Fix rendering for static meshes with custom blending modes and alpha transparency.
|
||||
* Fix inconsistent multiline string spacing on different display modes.
|
||||
|
||||
### New features
|
||||
* Remove search object 4 hardcoded meshswap activated with a flipmap.
|
||||
* Add TR1 cowboy.
|
||||
* Add TR3 wall mounted blade.
|
||||
|
@ -176,7 +542,7 @@ Version 1.0.9
|
|||
* Add "Reset to defaults" entry to controls menu and automatically bind XBOX gamepad profile if connected.
|
||||
* Add 64-bit executable and place both 32-bit and 64-bit versions into /Bin subdirectory.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Add Vec2 class.
|
||||
* Add function String::SetTranslated().
|
||||
* Add function Misc::IsStringDisplaying().
|
||||
|
@ -186,9 +552,9 @@ Lua API changes:
|
|||
- PRESAVE, POSTSAVE
|
||||
- PRELOAD, POSTLOAD
|
||||
|
||||
Version 1.0.8
|
||||
=============
|
||||
## [Version 1.0.8](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.6) - 2023-04-10
|
||||
|
||||
### Bug fixes
|
||||
* Fix bubbles phasing through ceilings.
|
||||
* Fix object camera not clearing at level end.
|
||||
* Fix double breath sound effect when coming up for air.
|
||||
|
@ -207,6 +573,8 @@ Version 1.0.8
|
|||
- Imp is also scared of of the player if holding a lit torch.
|
||||
- Please note you must use the patched version found here: https://github.com/TombEngine/Resources/blob/main/Wad2%20Objects/tr5_Imp.wad2
|
||||
* Fix and improve wraith tails.
|
||||
|
||||
### New features/Amedments
|
||||
* Add dedicated WRAITH_TRAP object with enhanced effects.
|
||||
- OCB 0: Effect disabled.
|
||||
- OCB 1: Effect enabled.
|
||||
|
@ -227,13 +595,13 @@ Version 1.0.8
|
|||
* Restored inventory compass.
|
||||
* Allow dynamic segment count for hair object.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Add function Misc::IsSoundPlaying()
|
||||
* Add function DisplayString::SetFlags()
|
||||
|
||||
Version 1.0.7
|
||||
=============
|
||||
## [Version 1.0.7](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.5) - 2023-02-26
|
||||
|
||||
### Bug fixes
|
||||
* Fix spark particles not being cleared on level reload.
|
||||
* Fix visible but inactive enemies (e.g. Shiva or Xian guardians) taking damage.
|
||||
* Fix blockable LOT type enemies (e.g. T-Rex and Shiva) not being able to step up 1 click or drop 2 clicks.
|
||||
|
@ -254,6 +622,8 @@ Version 1.0.7
|
|||
- Killing move for spear used wrong value.
|
||||
* Fix TR3 big gun spawning rocket with 0 life which caused an immediate explosion.
|
||||
* Fix TR3 Tony and add boss effect for him.
|
||||
|
||||
### New features
|
||||
* Add TR3 civvy.
|
||||
* Add TR3 electric cleaner.
|
||||
* Add TR3 Sophia Leigh with following OCBs:
|
||||
|
@ -271,14 +641,14 @@ Version 1.0.7
|
|||
* Prevent Lara from drawing weapons during parallel bar swinging.
|
||||
* Further renderer performance optimizations and bugfixes.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Fix Camera:SetPosition not updating camera position when it is played simultaneously.
|
||||
* Add Moveable:GetAirborne and Moveable:SetAirborne.
|
||||
* Add Moveable:GetLocationAI and Moveable:SetLocationAI.
|
||||
|
||||
Version 1.0.6
|
||||
=============
|
||||
## [Version 1.0.6](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.4) - 2023-01-29
|
||||
|
||||
### Bug fixes
|
||||
* Fix major pathfinding bug which could have caused lots of issues with enemy behaviour.
|
||||
* Fix potential random crashes due to incorrect rendering behaviour.
|
||||
* Fix savegame crash for disabled enemies with partially set activation mask.
|
||||
|
@ -304,6 +674,8 @@ Version 1.0.6
|
|||
* Fix grenade launcher super ammo emitting too many fragments.
|
||||
* Fix grenade and rocket launcher lighting.
|
||||
* Fix ceiling trapdoor and floor trapdoor that Lara couldn't open manually.
|
||||
|
||||
### New features
|
||||
* Make enemies drop pickups at first available bounding box corner point, not centerpoint.
|
||||
* Restore original volumetric explosion effects.
|
||||
* Add TR3 lizard and Puna.
|
||||
|
@ -315,7 +687,7 @@ Version 1.0.6
|
|||
* Lua Moveable functions Enable and Disable now correctly trigger and antitrigger the moveable.
|
||||
* Improve level loading speed a lot.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Moveable:SetVisible has been added. MakeInvisible is now an alias for SetVisible(false).
|
||||
* Moveable:MeshIsVisible is now GetMeshVisible.
|
||||
* Moveable:SetMeshVisible has been added to replace ShowMesh/HideMesh.
|
||||
|
@ -327,9 +699,9 @@ Lua API changes:
|
|||
* Add new function Misc::GetCameraType()
|
||||
* Add new functions Moveable:GetAirborne() and Moveable:SetAirborne(bool input)
|
||||
|
||||
Version 1.0.5
|
||||
=============
|
||||
## [Version 1.0.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.3) - 2022-12-30
|
||||
|
||||
### Bug fixes
|
||||
* Fix combined items not existing in inventory upon game reload.
|
||||
* Fix classic rollingball not behaving properly in rooms beyond the distance of 32 blocks.
|
||||
* Fix rollingball not killing Lara under certain movement angles.
|
||||
|
@ -354,6 +726,8 @@ Version 1.0.5
|
|||
* Fix rare crash when smash item is inside a wall and add warning log for the scenario.
|
||||
* Fix bone rotations of some entities.
|
||||
* Fix Lara's animation for cog switch release.
|
||||
|
||||
### New features
|
||||
* Added new OCB to cog switch object:
|
||||
- Use OCB 0 to have the traditional behaviour.
|
||||
- Use any other OCB to can use the Cog Switch without need of any door linked.
|
||||
|
@ -362,12 +736,12 @@ Version 1.0.5
|
|||
* Draw real mesh for darts.
|
||||
* Added warning log when one slot requires another slot which is missing.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Add new Room class and several methods for it.
|
||||
|
||||
Version 1.0.4
|
||||
=============
|
||||
## [Version 1.0.4](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.2) - 2022-12-16
|
||||
|
||||
### New features
|
||||
* Add generic assignable effects for moveables - fire, sparks, smoke and laser / electric ignite.
|
||||
* Add ability to burn enemies with FLAME_EMITTER_1 and death blocks.
|
||||
* Add wireframe mode and other visual debug information (switch by F10/F11 debug page scroll hotkeys).
|
||||
|
@ -389,6 +763,8 @@ Version 1.0.4
|
|||
* SAS enhancements:
|
||||
- Fix grenade shooting.
|
||||
- Fix AI_MODIFY and AI_GUARD behaviour.
|
||||
|
||||
### Bug fixes
|
||||
* Fix choppy camera movement in several cases.
|
||||
* Fix Lara's vertical position when shimmying around steep slope corners.
|
||||
* Fix legacy pickup triggers not working in certain cases.
|
||||
|
@ -417,7 +793,7 @@ Version 1.0.4
|
|||
* Fix current soundtrack fading into silence if incoming one doesn't exist.
|
||||
* Fix crash if there is an attempt to display a string with missing characters.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Add new Volume class and several methods for it.
|
||||
* Add new Moveable functions: GetEffect, SetEffect and SetCustomEffect (for colored fire).
|
||||
* Add new Lara functions: GetTarget, GetVehicle and TorchIsLit.
|
||||
|
@ -435,9 +811,9 @@ Lua API changes:
|
|||
* Fix Rotation class using integers under the hood which prevented using fractional rotation values.
|
||||
* Fix distance tests failing on a very high distances.
|
||||
|
||||
Version 1.0.3
|
||||
=============
|
||||
## [Version 1.0.3](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6.1) - 2022-11-18
|
||||
|
||||
### New features
|
||||
* Add ledge jumps (Lara object must be updated with new animations to make it work).
|
||||
* Allow any object slot to be used as a meshswap.
|
||||
* Add OCB 1 for rollingball to make it silent.
|
||||
|
@ -450,6 +826,8 @@ Version 1.0.3
|
|||
* Improve game and inventory input handling.
|
||||
* Adjust sprint jump timing.
|
||||
* Backport DAMOCLES_SWORD from TR1.
|
||||
|
||||
### Bug fixes
|
||||
* Fix going into inventory and load/save dialogs during fade-ins and fade-outs.
|
||||
* Fix savegames not preserving save number and game timer.
|
||||
* Fix dodgy weapon lock angle constraints.
|
||||
|
@ -481,7 +859,7 @@ Version 1.0.3
|
|||
* Fix SAS_DRAG_BLOKE object interaction.
|
||||
* Fix KILLER_STATUE not triggering.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* A new class has been added, LaraObject, for Lara-specific functions. The built-in "Lara" variable now uses this class.
|
||||
* Add functions for Lara object:
|
||||
- GetPoison / SetPoison
|
||||
|
@ -501,9 +879,9 @@ Lua API changes:
|
|||
* Add SetTotalSecretCount option to gameflow script to set overall amount of secrets.
|
||||
* Raised the maximum value on Moveable.SetHP to 32767 (its internal numeric maximum).
|
||||
|
||||
Version 1.0.2
|
||||
=============
|
||||
## [Version 1.0.2](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.6) - 2022-09-16
|
||||
|
||||
### New features
|
||||
* Fix removing Pistols with TakeItem and SetItemCount.
|
||||
* Allow saving and loading of Vec3s in LevelVars and GameVars.
|
||||
* Support volume triggers made with node editor.
|
||||
|
@ -527,7 +905,8 @@ Version 1.0.2
|
|||
- 5 for valve turn
|
||||
- 6 for hole switch
|
||||
- any other OCBs play corresponding switch on anim or OCB+1 switch off anim.
|
||||
|
||||
|
||||
### Bug fixes
|
||||
* Fix incorrect pole mounting.
|
||||
* Fix zeroed forward velocity upon landing.
|
||||
* Fix incorrect behaviour when falling on statics from the top after monkeyswing.
|
||||
|
@ -541,7 +920,7 @@ Version 1.0.2
|
|||
* Fix stargate blades needlessly pushing the player around while hardly doing any damage.
|
||||
* Fix weapon hotkeys and add missing crossbow hotkey.
|
||||
|
||||
Lua API changes:
|
||||
### Lua API changes
|
||||
* Util.ShortenTENCalls no longer needs to be called; it is now automatic for both level scripts and Gameflow.lua.
|
||||
* Flow.InvID has been removed; any function taking a pickup (e.g. GiveItem) now takes an Objects.ObjID instead.
|
||||
* Add Enable, Disable, GetActive, Get/SetSolid functions for static meshes.
|
||||
|
@ -555,12 +934,14 @@ Lua API changes:
|
|||
* Rework GiveItem, TakeItem, and SetItemCount (e.g. SetItemCount with a value of -1 can give infinite ammo/consumables).
|
||||
|
||||
|
||||
Version 1.0.1
|
||||
=============
|
||||
## [Version 1.0.1](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.5.2) - 2022-08-16
|
||||
|
||||
### New features
|
||||
* Added antialiasing support.
|
||||
* Added static mesh scaling support.
|
||||
* Added free rotation for teeth spikes instead of using OCB codes.
|
||||
|
||||
### Bug fixes
|
||||
* Fix some issues with shimmying between diagonal ledges and walls.
|
||||
* Fix rope transparency.
|
||||
* Fix objects disappearing under certain angles at the edges of the screen.
|
||||
|
@ -578,6 +959,8 @@ Version 1.0.1
|
|||
* Fix falling through twoblock platform on room number change.
|
||||
* Fix falling block breaking too early if placed on a vertical portal.
|
||||
* Fix crashes when loading image files are missing.
|
||||
|
||||
### Amendments
|
||||
* Disable trigger check for puzzle holes.
|
||||
* Clear locusts and other swarm enemies on level reload.
|
||||
* Enhance cobra AI and fix targeting.
|
||||
|
@ -593,7 +976,6 @@ Version 1.0.1
|
|||
* EventSequence.lua has been added and documented.
|
||||
|
||||
|
||||
Version 1.0
|
||||
===========
|
||||
## [Version 1.0](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.5.1) - 2022-08-06
|
||||
|
||||
First beta release.
|
82
CODE_OF_CONDUCT.md
Normal file
82
CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
TombEngine Discord Server.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
401
CONTRIBUTING.md
Normal file
401
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,401 @@
|
|||
What you will read under has been agreed by the current active team in order to keep a good flow of work and proper management. You are welcome to put suggestions in the comments in order to discuss it with the TEN team.
|
||||
|
||||
# Contributor General rules
|
||||
|
||||
**1. Developer**
|
||||
|
||||
- If you wish to contribute to the TEN project, you must read and follow the coding convention used by the team, available here: https://github.com/MontyTRC89/TombEngine/blob/master/CONTRIBUTING.md#coding-conventions
|
||||
- Do not work directly on master nor any branch which may be developed by several members. Create your branch, and name it with a clear description of the feature/set of features you are working on. You may regularly merge/rebase master or the active development branch with yours.
|
||||
- Be respectful to other developers. Disagreements may occur on code style, changes, and should be discussed directly with the person who pushed or merged the commit/branch. If you are unsure of what some parts of the code means, or why a decision was made, contact the team as a whole so they can be explained to you.
|
||||
- Do not make a pull request until your branch and features are extensively tested by the QA team. You are invited to test your feature set as much as you can too.
|
||||
- If you are allowed to merge PRs, do not do such until code has been properly reviewed, preferably by more than one person.
|
||||
- Merge PRs using **squash and merge** mode to preserve main branch history in original state. For squash merge commit, leave list of commits in commit description (GitHub does it automatically in both desktop client and web interface).
|
||||
- Do not revert active development branch to a previous commit unless you are absolutely sure there will be no regressions. Do not revert parts of code to a previous state for the same reasons. In both of those cases, the entire team needs to be aware of it and agree on the decision.
|
||||
- There are **two key branches**: `master` and `develop`. `master` is to be used for releases, and nothing else. `develop` is based on `master` and is used for active development, **Every PR should use it as its base**. To keep in sync, for release period they are to be merged in the following order: `develop` -> `master`.
|
||||
|
||||
**2. Tester**
|
||||
|
||||
- Be respectful with the developer you are testing the feature of.
|
||||
- New features or bug fixes should be tested with several use cases, if not all possible use cases you can think of, of this feature. This avoids going back to it in the future because of missed test cases.
|
||||
- Pay attention to any regressions. Developer may have touched part of the code that could break other linked or unlinked features. Features or bug fixes can't be considered done if there are any regression elsewhere.
|
||||
|
||||
## Issues handling
|
||||
|
||||
**1. General**
|
||||
|
||||
- Preferably, write one issue for one specific bug. List of bugs should stay rare, unless they are closely related to each other.
|
||||
- Follow the progress of features/fixes on the project board.
|
||||
- Do not assign a dev to an issue. They will do so themselves
|
||||
- Carefully choose the labels you add to the issue
|
||||
|
||||
**2. Developer**
|
||||
|
||||
- Assign yourself to an issue you want to work on available in the To do column of the project board. Add this feature to the "In progress" column. Make the team know you are working on it both by adding a comment to it and via Discord, on the strategy channel of Tomb Engine.
|
||||
- Regularly update the status of the task by adding comments. If you are stuck, let other people know if they can help.
|
||||
- Do not pass an issue as done unless fixes or features are fully tested and merged with active dev branch
|
||||
|
||||
**3. Tester**
|
||||
|
||||
- Give the Developer as much detail as you can about the bug you're writing for, and an easy way to reproduce it.
|
||||
- Update and comment the issue with the tests you've done and your findings after fixes.
|
||||
|
||||
# Coding Conventions
|
||||
|
||||
## General Rules
|
||||
|
||||
* **Do not silently comment out any lines or change operators or conditional statements**, even for testing purposes. Should you comment out certain code, also leave a comment beginning with `TODO` prefix, and date-author signature, like this:
|
||||
|
||||
``` c
|
||||
// TODO: This code caused NPCs to run into walls, so it is disabled for now. -- Lwmte, 21.08.19
|
||||
// GetBoundsAccurate(blah);
|
||||
```
|
||||
|
||||
* **Do not modify existing code without a clear understanding of what it does.** Even if you think you are fixing bugs or restoring original behavior by reverting the code, consult the TombEngine discord before committing any changes. It is possible you may ruin the work of other people since the original Core Design codebase is very fragile and it is very easy to falsely identify code as bugged.
|
||||
|
||||
* **Do not introduce quick hacks to fix issues**. Hacks like `if (room == 314)` or `if (direction == WEST)` should be left in the history of bad Core Design coding practices.
|
||||
|
||||
* If code remains unimplemented or you need to visit another part of the code for a considerable amount of time to implement missing functionality for that first part of code, **leave a comment** prefixed `FIXME` and with a date-author signature, describing missing functionality that you are about to add:
|
||||
|
||||
``` c
|
||||
// FIXME: I need to precisely calculate floor height, but for now let's keep original. -- Lwmte, 21.08.19
|
||||
foo = GetFloor(blah);
|
||||
```
|
||||
|
||||
* Make sure that **any new code is warning-free** before committing. Exceptions to this guideline include cases where fixing warnings would require you to make many changes outside of the new code, where the warning originates from a 3rd-party library, or where fixing the warning would make the code significantly less readable or performant.
|
||||
|
||||
* Avoid using magic numbers. Use constants or enums instead.
|
||||
|
||||
* Use **American English**.
|
||||
|
||||
## Parenthesis and new lines
|
||||
|
||||
Please do not write like this:
|
||||
|
||||
``` c
|
||||
if (blah) {
|
||||
foo;
|
||||
foo_foo;
|
||||
} else {
|
||||
bar;
|
||||
bar_bar; }
|
||||
```
|
||||
|
||||
Write like this instead:
|
||||
|
||||
``` c
|
||||
if (blah)
|
||||
{
|
||||
foo;
|
||||
foo_foo;
|
||||
}
|
||||
else
|
||||
{
|
||||
bar;
|
||||
bar_bar;
|
||||
}
|
||||
```
|
||||
|
||||
However, if you have only one line enclosed (i. e. only `foo`), and there are no following `else` or `else if` conditions, you may omit brackets and do it like this:
|
||||
|
||||
``` c
|
||||
if (blah)
|
||||
foo;
|
||||
```
|
||||
|
||||
For one-line if statements, if the condition itself is multi-line, brackets should be added anyway to maintain readability:
|
||||
|
||||
```
|
||||
if (item.RoomNumber == 123 || IsHeld(In::Action) &&
|
||||
LaraHasEatenSandwich == SANDWICH_BACON)
|
||||
{
|
||||
CurrentLevel = 4;
|
||||
}
|
||||
```
|
||||
|
||||
Same rule applies to loops.
|
||||
|
||||
Avoid multiple nested conditional statements. Where possible, do an early exit from the code using the opposite statement instead of enclosing the statement body in an extra set of brackets. For example, instead of this:
|
||||
|
||||
``` c
|
||||
if (blah)
|
||||
{
|
||||
if (foo)
|
||||
bar;
|
||||
}
|
||||
```
|
||||
|
||||
Write this:
|
||||
|
||||
``` c
|
||||
if (!blah || !foo)
|
||||
return;
|
||||
|
||||
bar;
|
||||
```
|
||||
|
||||
## Spacing
|
||||
|
||||
Don't condense arguments, loop statements, or any other code which requires splitting with commas/semicolons or operators like this:
|
||||
|
||||
``` c
|
||||
void SomeLoop(int blah,int blah2)
|
||||
{
|
||||
for(int i=0;i<4;i++)
|
||||
{
|
||||
bar =blah;
|
||||
foo= blah2;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Instead, separate everything with spaces **on the both sides** of operator, and **after** the comma or semicolon, like this:
|
||||
|
||||
``` c
|
||||
void SomeLoop(int blah, int blah2)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
bar = blah;
|
||||
foo = blah2;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The same rule applies to any code body, not only function declarations or loop statements.
|
||||
|
||||
## Tabulation and indents
|
||||
|
||||
When you have numerous assignment operations in a row, and/or their names are somewhat of equal length, and their data types are similar, align "left" and "right" parts of the assignment using tabs most of the way and spaces the rest of the way, like this:
|
||||
|
||||
``` c
|
||||
float foo = 1.0;
|
||||
float bar_bar = 1.0;
|
||||
float foo_o_o = 1.0;
|
||||
```
|
||||
|
||||
Or:
|
||||
|
||||
``` c
|
||||
bar = foo-foo-foo;
|
||||
foo_o = bar;
|
||||
bar_r_r = foo;
|
||||
```
|
||||
|
||||
In case you have pointers defined along with "normal" variables, the asterisk symbol must be placed instead of the last tab's space symbol (this also applies for class declarations and/or implementations), like this:
|
||||
|
||||
``` c
|
||||
bar = foo_foo;
|
||||
*foo = &bar_bar;
|
||||
```
|
||||
|
||||
Of course, if one's left part is way longer than another one's left part, there's no need for such alignment, so you can leave it like this:
|
||||
|
||||
``` c
|
||||
*foo_foo = &bar_foo;
|
||||
bar->foo_foo_foo.blah_blah_bar_foo = 1.0;
|
||||
foo_bar = 1.0;
|
||||
```
|
||||
In a switch case, each case body must be one tab further case's own label. Like this:
|
||||
|
||||
``` c
|
||||
switch (blah)
|
||||
{
|
||||
case foo:
|
||||
foo - foo - foo;
|
||||
foo - foo - foo - foo;
|
||||
break;
|
||||
|
||||
case bar:
|
||||
bar - bar;
|
||||
bar - bar - bar;
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
If you need to enclose a particular case into a bracket block, put the body one tab further:
|
||||
|
||||
``` c
|
||||
switch (blah)
|
||||
{
|
||||
case foo:
|
||||
{
|
||||
float bar;
|
||||
bar = foo - foo - foo;
|
||||
foo - foo - foo - foo = bar;
|
||||
break;
|
||||
}
|
||||
|
||||
case bar:
|
||||
bar - bar;
|
||||
bar - bar - bar;
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
## Code splitting
|
||||
|
||||
If a code block becomes too large, it is recommended to split it into several "sub-blocks" with empty lines, taking each sub-block's meaning into account, like this:
|
||||
|
||||
``` c
|
||||
foo = foo_foo + foo_foo_foo;
|
||||
if (foo)
|
||||
foo - foo - foo - foo - foo;
|
||||
foo - foo = foo;
|
||||
|
||||
bar = (bar - bar > bar) ? (bar - bar) : 0;
|
||||
bar - bar - bar = bar - bar - bar;
|
||||
```
|
||||
|
||||
Conditional statements, if there are many, should be grouped according to their meaning. That is, if you are doing early exit from the function because of different conditions, group them as such:
|
||||
|
||||
``` c
|
||||
if (coll.Floor > CLICK(1) && coll.Ceiling < CLICK(3) && bounds.Y1 > WALL_SIZE)
|
||||
return false;
|
||||
|
||||
if (enemy.health <= 0 || lara.health <= 0 || collidedItems[0] == nullptr)
|
||||
return false;
|
||||
```
|
||||
|
||||
However, if there are few conditional statements, you can group them together. The rule of thumb is if there are more than three conditional statements and two of them are of a different kind, split them. This variant is allowed:
|
||||
|
||||
``` c
|
||||
if (coll.Floor > CLICK(2) && coll.Ceiling < CLICK(4) || lara.health <= 0)
|
||||
return false;
|
||||
```
|
||||
|
||||
Sometimes IDA decompiled output generates "pascal-styled" or "basic-styled" variable declarations at the beginning of the function or code block. Like this:
|
||||
|
||||
``` c
|
||||
int foo, bar = 0;
|
||||
float blah;
|
||||
...
|
||||
blah = foo;
|
||||
foo = bar;
|
||||
...
|
||||
```
|
||||
|
||||
**Please**, get rid of this style everywhere you see it and declare variables in the place narrowest to its actual usage, like this:
|
||||
|
||||
``` c
|
||||
int foo = foo;
|
||||
float blah = bar;
|
||||
...
|
||||
```
|
||||
|
||||
Let's cite [Google Style Guide](https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Local_Variables) here:
|
||||
|
||||
> Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
|
||||
|
||||
## Naming
|
||||
|
||||
* Use `auto` where possible and if the original type name is longer than `auto`. E.g. there is no point in changing `bool enabled = true` to `auto enabled = true`. Remember that C++ `auto` **is not similar to C#** `var`, and for referencing existing value with `auto`, you should add `&` to it in the end, e.g. `auto& item = g_Level.Items[itemIndex];` Also, for underlying pointer types, please write `auto*` instead of `auto`, even if it seems redundant.
|
||||
|
||||
* Avoid using Hungarian notation for function names. Currently, inconsistent notation indicates an unrefactored coding style, because the original code used inconsistent naming, like `SOUND_Stop()`, `have_i_got_object()` or `gar_SuperpackYXZ()`. These should eventually be eradicated along the course of code restyling. For new function names, `PascalCase` should be used.
|
||||
|
||||
* For global struct or class members or methods, `PascalCase` should be used. For local variable names, `camelCase` should be used. For global variables, which are temporary until full refactoring occurs, an exclusive case of Hungarian notation with the `g_` prefix (e.g. `g_Foo`) is permitted.
|
||||
|
||||
* Functions designed to take an enum argument should take the enum type itself instead of an int or short. `PassInfo(InfoEnum type)` is more readable and error-proof than `PassInfo(int type)`.
|
||||
|
||||
* Functions designed to return boolean value (0 or 1) should define return type as bool. Please don't use `int` return type and don't write `return 0` or `return 1` in bool return functions, use `return false` or `return true`.
|
||||
|
||||
* Use the following convention for Pointers and References: `Type* somePointer` / `Type& someReference`. Do not write this: `Type * somePointer` / `Type & someReference` or `Type *somePointer` / `Type &someReference`. Pointers and references are distinct types, hence why the notation for them should have the token on the side of the type.
|
||||
|
||||
* Avoid unscoped enum types. For scoped `enum class` types, use `PascalCase` without including enum prefix:
|
||||
|
||||
``` c
|
||||
enum class WeatherType
|
||||
{
|
||||
None,
|
||||
Rain,
|
||||
Snow,
|
||||
Cats,
|
||||
Dogs
|
||||
};
|
||||
```
|
||||
|
||||
`ENUM_ALL_CAPS` primarily indicates old C-styled Core notation. For C-styled (unscoped) enum values themselves, `ALL_CAPS` may be used for now, along with enum prefix:
|
||||
|
||||
``` c
|
||||
enum LaraWeaponType
|
||||
{
|
||||
WEAPON_NONE,
|
||||
WEAPON_PISTOLS,
|
||||
WEAPON_REVOLVER
|
||||
};
|
||||
```
|
||||
|
||||
## Data types
|
||||
|
||||
Avoid using `_t` data types, such as `uint8_t`, `int8_t`, `int16_t` etc. Use `unsigned char`, `signed char`, `short` etc. instead. If new variables or fields are introduced, prefer longer and more contemporary data types over deprecated ones. That is, if integer value is used, use `int` instead of `char` or `short`. If potentially fractional value is used (such as coordinates which are eventually multiplied or divided or transformed otherwise), prefer `float` over `int`.
|
||||
|
||||
For legacy functions and code paths, preserving original data types may be necessary. Special case are angle values - original games used weird `signed short` angle convention. So extra caution must be taken when writing code which operates on native TR angles, and it should always be kept in variables of `signed short` data type.
|
||||
|
||||
Prefer using references over pointers, both in function body and as arguments. When using references or pointers, prefix with `const` for read-only safety if the variable is not being written to.
|
||||
|
||||
## Casting
|
||||
|
||||
Prefer using C-styled casting instead of C++-styled casting where it is safe. While using it, avoid separating casting operator and variable with space:
|
||||
|
||||
`bar = (int)foo;`
|
||||
|
||||
For expressions, you can enclose expression into brackets to cast it:
|
||||
|
||||
`bar = int(foo + blah * 4);`
|
||||
|
||||
Using C++-styled casting is allowed when C-styled casting provides undefined or unacceptable behaviour.
|
||||
|
||||
## Includes
|
||||
|
||||
For header files from project itself, always use includes with quotes, not with brackets. Also avoid using Windows-specific `\` backslash symbols to specify subpaths, only use normal `/` slashes. Also please include full path to a header file and order includes alphabetically:
|
||||
|
||||
```c
|
||||
#include "Game/effects/lightning.h"
|
||||
#include "Specific/phd_math.h"
|
||||
```
|
||||
|
||||
Includes with brackets are only allowed when using external libraries:
|
||||
|
||||
```c
|
||||
#include <algorithm>
|
||||
#include "Game/collision/collide.h"
|
||||
```
|
||||
|
||||
## Namespaces
|
||||
|
||||
Don't shorten `std` namespace types and methods by using `using` directive. This is bad: `auto x = vector<int>();`
|
||||
Leave them as is. This is good: `auto x = std::vector<int>();`
|
||||
|
||||
## Comments
|
||||
|
||||
Use `//`-styled comments where possible.
|
||||
Only use `/* */` style in case you are about to temporarily comment certain block for testing purposes or when writing a comment that will serve as the source for generated documentation.
|
||||
|
||||
Use a `NOTE: ` prefix in your comment if you want to highlight something particularly noteworthy:
|
||||
```c
|
||||
// NOTE: Will not work for bones at ends of hierarchies.
|
||||
float GetBoneLength(GAME_OBJECT_ID objectID, int boneIndex)
|
||||
{
|
||||
const auto& object = Objects[objectID];
|
||||
|
||||
if (object.nmeshes == boneIndex)
|
||||
return 0.0f;
|
||||
|
||||
auto nextBoneOffset = GetJointOffset(objectID, boneIndex + 1);
|
||||
return nextBoneOffset.Length();
|
||||
}
|
||||
```
|
||||
|
||||
Use a `FAILSAFE: ` prefix in your comment if you want to highlight a particularly quirky solution without an obvious and clear purpose:
|
||||
```c
|
||||
if (portalRoomNumber != NO_VALUE &&
|
||||
rayRoomNumber != portalRoomNumber) // FAILSAFE: Prevent infinite loop if room portal leads back to itself.
|
||||
{
|
||||
player.Explode();
|
||||
}
|
||||
```
|
||||
|
||||
## Branches and pull requests
|
||||
|
||||
Make sure that epic branches (tens or hundreds of files changed due to renames, namespace wrappings, etc) **are focused on a single feature or task**. Don't jump in to others epic branches with another round of your epic changes. It masks bugs and makes review process very cumbersome.
|
||||
|
||||
Avoid making new branches based on unapproved epic PRs which are in the process of review. It may render your work useless if parent epic branch is unapproved and scrapped.
|
11
Documentation/compile.bat
Normal file
11
Documentation/compile.bat
Normal file
|
@ -0,0 +1,11 @@
|
|||
@echo off
|
||||
setlocal
|
||||
set DOC_DIR=.\doc
|
||||
set LDOC_DIR=.\compiler\ldoc
|
||||
set LUA_PATH=.\compiler\?.lua
|
||||
set LUA_CPATH=.\compiler\?.dll
|
||||
rmdir /s /q %DOC_DIR%
|
||||
mkdir %DOC_DIR%
|
||||
.\compiler\lua.exe %LDOC_DIR%\\ldoc.lua %*
|
||||
del output.xml
|
||||
exit /b %ERRORLEVEL%
|
22
Documentation/compiler/ldoc/COPYRIGHT
Normal file
22
Documentation/compiler/ldoc/COPYRIGHT
Normal file
|
@ -0,0 +1,22 @@
|
|||
LDoc License
|
||||
-----------
|
||||
Copyright (C) 2011 Steve Donovan.
|
||||
|
||||
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.
|
||||
|
66
Documentation/compiler/ldoc/ldoc-scm-3.rockspec
Normal file
66
Documentation/compiler/ldoc/ldoc-scm-3.rockspec
Normal file
|
@ -0,0 +1,66 @@
|
|||
package = "ldoc"
|
||||
version = "scm-3"
|
||||
|
||||
source = {
|
||||
dir="LDoc",
|
||||
url = "git+https://github.com/stevedonovan/LDoc.git"
|
||||
}
|
||||
|
||||
description = {
|
||||
summary = "A Lua Documentation Tool",
|
||||
detailed = [[
|
||||
LDoc is a LuaDoc-compatible documentation generator which can also
|
||||
process C extension source. Markdown may be optionally used to
|
||||
render comments, as well as integrated readme documentation and
|
||||
pretty-printed example files
|
||||
]],
|
||||
homepage='https://github.com/lunarmodules/LDoc',
|
||||
maintainer='steve.j.donovan@gmail.com',
|
||||
license = "MIT/X11",
|
||||
}
|
||||
|
||||
dependencies = {
|
||||
"penlight","markdown"
|
||||
}
|
||||
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["ldoc.tools"] = "ldoc/tools.lua",
|
||||
["ldoc.lang"] = "ldoc/lang.lua",
|
||||
["ldoc.parse"] = "ldoc/parse.lua",
|
||||
["ldoc.html"] = "ldoc/html.lua",
|
||||
["ldoc.lexer"] = "ldoc/lexer.lua",
|
||||
["ldoc.markup"] = "ldoc/markup.lua",
|
||||
["ldoc.prettify"] = "ldoc/prettify.lua",
|
||||
["ldoc.markdown"] = "ldoc/markdown.lua",
|
||||
["ldoc.doc"] = "ldoc/doc.lua",
|
||||
["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua",
|
||||
["ldoc.html.ldoc_md_ltp"] = "ldoc/html/ldoc_md_ltp.lua",
|
||||
["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua",
|
||||
["ldoc.html._code_css"] = "ldoc/html/_code_css.lua",
|
||||
["ldoc.html._reset_css"] = "ldoc/html/_reset_css.lua",
|
||||
["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua",
|
||||
["ldoc.html.ldoc_pale_css"] = "ldoc/html/ldoc_pale_css.lua",
|
||||
["ldoc.html.ldoc_new_css"] = "ldoc/html/ldoc_new_css.lua",
|
||||
["ldoc.html.ldoc_fixed_css"] = "ldoc/html/ldoc_fixed_css.lua",
|
||||
["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua",
|
||||
["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua",
|
||||
["ldoc.builtin.global"] = "ldoc/builtin/global.lua",
|
||||
["ldoc.builtin.debug"] = "ldoc/builtin/debug.lua",
|
||||
["ldoc.builtin.io"] = "ldoc/builtin/io.lua",
|
||||
["ldoc.builtin.lfs"] = "ldoc/builtin/lfs.lua",
|
||||
["ldoc.builtin.lpeg"] = "ldoc/builtin/lpeg.lua",
|
||||
["ldoc.builtin.math"] = "ldoc/builtin/math.lua",
|
||||
["ldoc.builtin.os"] = "ldoc/builtin/os.lua",
|
||||
["ldoc.builtin.package"] = "ldoc/builtin/package.lua",
|
||||
["ldoc.builtin.string"] = "ldoc/builtin/string.lua",
|
||||
["ldoc.builtin.table"] = "ldoc/builtin/table.lua",
|
||||
},
|
||||
copy_directories = {'doc','tests'},
|
||||
install = {
|
||||
bin = {
|
||||
ldoc = "ldoc.lua"
|
||||
}
|
||||
}
|
||||
}
|
866
Documentation/compiler/ldoc/ldoc.lua
Normal file
866
Documentation/compiler/ldoc/ldoc.lua
Normal file
|
@ -0,0 +1,866 @@
|
|||
#!/usr/bin/env lua
|
||||
---------------
|
||||
-- ## ldoc, a Lua documentation generator.
|
||||
--
|
||||
-- Compatible with luadoc-style annotations, but providing
|
||||
-- easier customization options.
|
||||
--
|
||||
-- C/C++ support for Lua extensions is provided.
|
||||
--
|
||||
-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.4.3.zip)
|
||||
--
|
||||
-- [Github Page](https://github.com/stevedonovan/ldoc)
|
||||
--
|
||||
-- @author Steve Donovan
|
||||
-- @copyright 2011
|
||||
-- @license MIT/X11
|
||||
-- @script ldoc
|
||||
|
||||
local class = require 'pl.class'
|
||||
local app = require 'pl.app'
|
||||
local path = require 'pl.path'
|
||||
local dir = require 'pl.dir'
|
||||
local utils = require 'pl.utils'
|
||||
local List = require 'pl.List'
|
||||
local stringx = require 'pl.stringx'
|
||||
local tablex = require 'pl.tablex'
|
||||
|
||||
-- Penlight compatibility
|
||||
utils.unpack = utils.unpack or unpack or table.unpack
|
||||
local lapp = require 'pl.lapp'
|
||||
|
||||
local version = '1.4.6'
|
||||
|
||||
-- so we can find our private modules
|
||||
app.require_here()
|
||||
|
||||
--- @usage
|
||||
local usage = [[
|
||||
ldoc, a documentation generator for Lua, v]]..version..[[
|
||||
|
||||
Invocation:
|
||||
ldoc [options] <file>
|
||||
ldoc --version
|
||||
|
||||
Options:
|
||||
-d,--dir (default doc) output directory
|
||||
-o,--output (default 'index') output name
|
||||
-v,--verbose verbose
|
||||
-a,--all show local functions, etc, in docs
|
||||
-q,--quiet suppress output
|
||||
-m,--module module docs as text
|
||||
-s,--style (default !) directory for style sheet (ldoc.css)
|
||||
-l,--template (default !) directory for template (ldoc.ltp)
|
||||
-p,--project (default ldoc) project name
|
||||
-t,--title (default Reference) page title
|
||||
-f,--format (default plain) formatting - can be markdown, discount or plain
|
||||
-b,--package (default .) top-level package basename (needed for module(...))
|
||||
-x,--ext (default html) output file extension
|
||||
-c,--config (default config.ld) configuration name
|
||||
-u,--unqualified don't show package name in sidebar links
|
||||
-i,--ignore ignore any 'no doc comment or no module' warnings
|
||||
-X,--not_luadoc break LuaDoc compatibility. Descriptions may continue after tags.
|
||||
-D,--define (default none) set a flag to be used in config.ld
|
||||
-C,--colon use colon style
|
||||
-N,--no_args_infer don't infer arguments from source
|
||||
-B,--boilerplate ignore first comment in source files
|
||||
-M,--merge allow module merging
|
||||
-S,--simple no return or params, no summary
|
||||
-O,--one one-column output layout
|
||||
-V,--version show version information
|
||||
--date (default system) use this date in generated doc
|
||||
--dump debug output dump
|
||||
--filter (default none) filter output as Lua data (e.g pl.pretty.dump)
|
||||
--tags (default none) show all references to given tags, comma-separated
|
||||
--fatalwarnings non-zero exit status on any warning
|
||||
--testing reproducible build; no date or version on output
|
||||
--icon (default none) an image that will be displayed under the project name on all pages
|
||||
|
||||
<file> (string) source file or directory containing source
|
||||
|
||||
`ldoc .` reads options from an `config.ld` file in same directory;
|
||||
`ldoc -c path/to/myconfig.ld <file>` reads options from `path/to/myconfig.ld`
|
||||
and processes <file> if 'file' was not defined in the ld file.
|
||||
]]
|
||||
local args = lapp(usage)
|
||||
local lfs = require 'lfs'
|
||||
local doc = require 'ldoc.doc'
|
||||
local lang = require 'ldoc.lang'
|
||||
local tools = require 'ldoc.tools'
|
||||
local global = require 'ldoc.builtin.globals'
|
||||
local markup = require 'ldoc.markup'
|
||||
local parse = require 'ldoc.parse'
|
||||
local KindMap = tools.KindMap
|
||||
local Item,File = doc.Item,doc.File
|
||||
local quit = utils.quit
|
||||
|
||||
if args.version then
|
||||
print('LDoc v' .. version)
|
||||
os.exit(0)
|
||||
end
|
||||
|
||||
|
||||
local ModuleMap = class(KindMap)
|
||||
doc.ModuleMap = ModuleMap
|
||||
|
||||
function ModuleMap:_init ()
|
||||
self.klass = ModuleMap
|
||||
self.fieldname = 'section'
|
||||
end
|
||||
|
||||
local ProjectMap = class(KindMap)
|
||||
ProjectMap.project_level = true
|
||||
|
||||
function ProjectMap:_init ()
|
||||
self.klass = ProjectMap
|
||||
self.fieldname = 'type'
|
||||
end
|
||||
|
||||
local lua, cc = lang.lua, lang.cc
|
||||
|
||||
local file_types = {
|
||||
['.lua'] = lua,
|
||||
['.ldoc'] = lua,
|
||||
['.luadoc'] = lua,
|
||||
['.c'] = cc,
|
||||
['.h'] = cc,
|
||||
['.cpp'] = cc,
|
||||
['.cxx'] = cc,
|
||||
['.C'] = cc,
|
||||
['.mm'] = cc,
|
||||
['.cs'] = cc,
|
||||
['.moon'] = lang.moon,
|
||||
}
|
||||
------- ldoc external API ------------
|
||||
|
||||
-- the ldoc table represents the API available in `config.ld`.
|
||||
local ldoc = { charset = 'UTF-8', version = version }
|
||||
|
||||
local known_types, kind_names = {}
|
||||
|
||||
local function lookup (itype,igroup,isubgroup)
|
||||
local kn = kind_names[itype]
|
||||
known_types[itype] = true
|
||||
if kn then
|
||||
if type(kn) == 'string' then
|
||||
igroup = kn
|
||||
else
|
||||
igroup = kn[1]
|
||||
isubgroup = kn[2]
|
||||
end
|
||||
end
|
||||
return itype, igroup, isubgroup
|
||||
end
|
||||
|
||||
local function setup_kinds ()
|
||||
kind_names = ldoc.kind_names or {}
|
||||
|
||||
ModuleMap:add_kind(lookup('function','Functions','Parameters'))
|
||||
ModuleMap:add_kind(lookup('table','Tables','Fields'))
|
||||
ModuleMap:add_kind(lookup('field','Fields'))
|
||||
ModuleMap:add_kind(lookup('type','Types'))
|
||||
ModuleMap:add_kind(lookup('lfunction','Local Functions','Parameters'))
|
||||
ModuleMap:add_kind(lookup('annotation','Issues'))
|
||||
|
||||
ProjectMap:add_kind(lookup('module','Modules'))
|
||||
ProjectMap:add_kind(lookup('script','Scripts'))
|
||||
ProjectMap:add_kind(lookup('classmod','Classes'))
|
||||
ProjectMap:add_kind(lookup('topic','Topics'))
|
||||
ProjectMap:add_kind(lookup('example','Examples'))
|
||||
ProjectMap:add_kind(lookup('file','Source'))
|
||||
|
||||
for k in pairs(kind_names) do
|
||||
if not known_types[k] then
|
||||
quit("unknown item type "..tools.quote(k).." in kind_names")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- hacky way for doc module to be passed options...
|
||||
doc.ldoc = ldoc
|
||||
|
||||
-- if the corresponding argument was the default, then any ldoc field overrides
|
||||
local function override (field,defval)
|
||||
defval = defval or false
|
||||
if args[field] == defval and ldoc[field] ~= nil then args[field] = ldoc[field] end
|
||||
end
|
||||
|
||||
-- aliases to existing tags can be defined. E.g. just 'p' for 'param'
|
||||
function ldoc.alias (a,tag)
|
||||
doc.add_alias(a,tag)
|
||||
end
|
||||
|
||||
-- standard aliases --
|
||||
|
||||
ldoc.alias('tparam',{'param',modifiers={type="$1"}})
|
||||
ldoc.alias('treturn',{'return',modifiers={type="$1"}})
|
||||
ldoc.alias('tfield',{'field',modifiers={type="$1"}})
|
||||
|
||||
function ldoc.tparam_alias (name,type)
|
||||
type = type or name
|
||||
ldoc.alias(name,{'param',modifiers={type=type}})
|
||||
end
|
||||
|
||||
ldoc.alias ('error',doc.error_macro)
|
||||
|
||||
ldoc.tparam_alias 'string'
|
||||
ldoc.tparam_alias 'number'
|
||||
ldoc.tparam_alias 'int'
|
||||
ldoc.tparam_alias 'bool'
|
||||
ldoc.tparam_alias 'func'
|
||||
ldoc.tparam_alias 'tab'
|
||||
ldoc.tparam_alias 'thread'
|
||||
|
||||
function ldoc.add_language_extension(ext, lang)
|
||||
lang = (lang=='c' and cc) or (lang=='lua' and lua) or quit('unknown language')
|
||||
if ext:sub(1,1) ~= '.' then ext = '.'..ext end
|
||||
file_types[ext] = lang
|
||||
end
|
||||
|
||||
function ldoc.add_section (name, title, subname)
|
||||
ModuleMap:add_kind(name,title,subname)
|
||||
end
|
||||
|
||||
-- new tags can be added, which can be on a project level.
|
||||
function ldoc.new_type (tag, header, project_level,subfield)
|
||||
doc.add_tag(tag,doc.TAG_TYPE,project_level)
|
||||
if project_level then
|
||||
ProjectMap:add_kind(tag,header,subfield)
|
||||
else
|
||||
ModuleMap:add_kind(tag,header,subfield)
|
||||
end
|
||||
end
|
||||
|
||||
function ldoc.manual_url (url)
|
||||
global.set_manual_url(url)
|
||||
end
|
||||
|
||||
function ldoc.custom_see_handler(pat, handler)
|
||||
doc.add_custom_see_handler(pat, handler)
|
||||
end
|
||||
|
||||
local ldoc_contents = {
|
||||
'alias','add_language_extension','custom_tags','new_type','add_section', 'tparam_alias',
|
||||
'file','project','title','package', 'icon','format','output','dir','ext', 'topics',
|
||||
'one','style','template','description','examples', 'pretty', 'charset', 'plain',
|
||||
'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars',
|
||||
'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups',
|
||||
'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler',
|
||||
'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles',
|
||||
'unqualified', 'custom_display_name_handler', 'kind_names', 'custom_references',
|
||||
'dont_escape_underscore','global_lookup','prettify_files','convert_opt', 'user_keywords',
|
||||
'postprocess_html',
|
||||
'custom_css','version',
|
||||
'no_args_infer',
|
||||
'keep_menu_order'
|
||||
}
|
||||
ldoc_contents = tablex.makeset(ldoc_contents)
|
||||
|
||||
local function loadstr (ldoc,txt)
|
||||
local chunk, err
|
||||
-- Penlight's Lua 5.2 compatibility has wobbled over the years...
|
||||
if not rawget(_G,'loadin') then -- Penlight 0.9.5
|
||||
-- Penlight 0.9.7; no more global load() override
|
||||
local load = load or utils.load
|
||||
chunk,err = load(txt,'config',nil,ldoc)
|
||||
else
|
||||
-- luacheck: push ignore 113
|
||||
chunk,err = loadin(ldoc,txt)
|
||||
-- luacheck: pop
|
||||
end
|
||||
return chunk, err
|
||||
end
|
||||
|
||||
-- any file called 'config.ld' found in the source tree will be
|
||||
-- handled specially. It will be loaded using 'ldoc' as the environment.
|
||||
local function read_ldoc_config (fname)
|
||||
local directory = path.dirname(fname)
|
||||
if directory == '' then
|
||||
directory = '.'
|
||||
end
|
||||
local chunk, err, _
|
||||
if args.filter == 'none' then
|
||||
print('reading configuration from '..fname)
|
||||
end
|
||||
local txt,not_found = utils.readfile(fname)
|
||||
if txt then
|
||||
chunk, err = loadstr(ldoc,txt)
|
||||
if chunk then
|
||||
if args.define ~= 'none' then ldoc[args.define] = true end
|
||||
_,err = pcall(chunk)
|
||||
end
|
||||
end
|
||||
if err then quit('error loading config file '..fname..': '..err) end
|
||||
for k in pairs(ldoc) do
|
||||
if not ldoc_contents[k] then
|
||||
quit("this config file field/function is unrecognized: "..k)
|
||||
end
|
||||
end
|
||||
return directory, not_found
|
||||
end
|
||||
|
||||
local quote = tools.quote
|
||||
--- processing command line and preparing for output ---
|
||||
|
||||
local file_list = List()
|
||||
File.list = file_list
|
||||
local config_dir
|
||||
|
||||
|
||||
local ldoc_dir = arg[0]:gsub('[^/\\]+$','')
|
||||
local doc_path = ldoc_dir..'/ldoc/builtin/?.lua'
|
||||
|
||||
-- ldoc -m is expecting a Lua package; this converts this to a file path
|
||||
if args.module then
|
||||
-- first check if we've been given a global Lua lib function
|
||||
if args.file:match '^%a+$' and global.functions[args.file] then
|
||||
args.file = 'global.'..args.file
|
||||
end
|
||||
local fullpath,mod,_ = tools.lookup_existing_module_or_function (args.file, doc_path)
|
||||
if not fullpath then
|
||||
quit(mod)
|
||||
else
|
||||
args.file = fullpath
|
||||
args.module = mod
|
||||
end
|
||||
end
|
||||
|
||||
local abspath = tools.abspath
|
||||
|
||||
-- a special case: 'ldoc .' can get all its parameters from config.ld
|
||||
if args.file == '.' then
|
||||
local err
|
||||
config_dir,err = read_ldoc_config(args.config)
|
||||
if err then quit("no "..quote(args.config).." found") end
|
||||
local config_path = path.dirname(args.config)
|
||||
if config_path ~= '' then
|
||||
print('changing to directory',config_path)
|
||||
lfs.chdir(config_path)
|
||||
end
|
||||
args.file = ldoc.file or '.'
|
||||
if args.file == '.' then
|
||||
args.file = lfs.currentdir()
|
||||
elseif type(args.file) == 'table' then
|
||||
for i,f in ipairs(args.file) do
|
||||
args.file[i] = abspath(f)
|
||||
end
|
||||
else
|
||||
args.file = abspath(args.file)
|
||||
end
|
||||
else
|
||||
-- user-provided config file
|
||||
if args.config ~= 'config.ld' then
|
||||
local err
|
||||
config_dir,err = read_ldoc_config(args.config)
|
||||
if err then quit("no "..quote(args.config).." found") end
|
||||
end
|
||||
-- with user-provided file
|
||||
if args.file == nil then
|
||||
lapp.error('missing required parameter: file')
|
||||
end
|
||||
args.file = abspath(args.file)
|
||||
end
|
||||
|
||||
if type(ldoc.custom_tags) == 'table' then -- custom tags
|
||||
for i, custom in ipairs(ldoc.custom_tags) do
|
||||
if type(custom) == 'string' then
|
||||
custom = {custom}
|
||||
ldoc.custom_tags[i] = custom
|
||||
end
|
||||
doc.add_tag(custom[1], 'ML')
|
||||
end
|
||||
end -- custom tags
|
||||
|
||||
local source_dir = args.file
|
||||
if type(source_dir) == 'table' then
|
||||
source_dir = source_dir[1]
|
||||
end
|
||||
if type(source_dir) == 'string' and path.isfile(source_dir) then
|
||||
source_dir = path.splitpath(source_dir)
|
||||
end
|
||||
source_dir = source_dir:gsub('[/\\]%.$','')
|
||||
|
||||
---------- specifying the package for inferring module names --------
|
||||
-- If you use module(...), or forget to explicitly use @module, then
|
||||
-- ldoc has to infer the module name. There are three sensible values for
|
||||
-- `args.package`:
|
||||
--
|
||||
-- * '.' the actual source is in an immediate subdir of the path given
|
||||
-- * '..' the path given points to the source directory
|
||||
-- * 'NAME' explicitly give the base module package name
|
||||
--
|
||||
|
||||
override ('package','.')
|
||||
|
||||
local function setup_package_base()
|
||||
if ldoc.package then args.package = ldoc.package end
|
||||
if args.package == '.' then
|
||||
args.package = source_dir
|
||||
elseif args.package == '..' then
|
||||
args.package = path.splitpath(source_dir)
|
||||
elseif not args.package:find '[\\/]' then
|
||||
local subdir,dir = path.splitpath(source_dir)
|
||||
if dir == args.package then
|
||||
args.package = subdir
|
||||
elseif path.isdir(path.join(source_dir,args.package)) then
|
||||
args.package = source_dir
|
||||
else
|
||||
quit("args.package is not the name of the source directory")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--------- processing files ---------------------
|
||||
-- ldoc may be given a file, or a directory. `args.file` may also be specified in config.ld
|
||||
-- where it is a list of files or directories. If specified on the command-line, we have
|
||||
-- to find an optional associated config.ld, if not already loaded.
|
||||
|
||||
if ldoc.ignore then args.ignore = true end
|
||||
|
||||
local function process_file (f, flist)
|
||||
local ext = path.extension(f)
|
||||
local ftype = file_types[ext]
|
||||
if ftype then
|
||||
if args.verbose then print(f) end
|
||||
ftype.extra = ldoc.parse_extra or {}
|
||||
local F,err = parse.file(f,ftype,args)
|
||||
if err then
|
||||
if F then
|
||||
F:warning("internal LDoc error")
|
||||
end
|
||||
quit(err)
|
||||
end
|
||||
flist:append(F)
|
||||
end
|
||||
end
|
||||
|
||||
local process_file_list = tools.process_file_list
|
||||
|
||||
setup_package_base()
|
||||
|
||||
override 'no_args_infer'
|
||||
override 'colon'
|
||||
override 'merge'
|
||||
override 'not_luadoc'
|
||||
override 'module_file'
|
||||
override 'boilerplate'
|
||||
override 'all'
|
||||
|
||||
setup_kinds()
|
||||
|
||||
-- LDoc is doing plain ole C, don't want random Lua references!
|
||||
if ldoc.parse_extra and ldoc.parse_extra.C then
|
||||
ldoc.no_lua_ref = true
|
||||
end
|
||||
|
||||
if ldoc.merge_error_groups == nil then
|
||||
ldoc.merge_error_groups = 'Error Message'
|
||||
end
|
||||
|
||||
-- ldoc.module_file establishes a partial ordering where the
|
||||
-- master module files are processed first.
|
||||
local function reorder_module_file ()
|
||||
if args.module_file then
|
||||
local mf = {}
|
||||
for mname, f in pairs(args.module_file) do
|
||||
local fullpath = abspath(f)
|
||||
mf[fullpath] = true
|
||||
end
|
||||
return function(x,y)
|
||||
return mf[x] and not mf[y]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- process files, optionally in order that respects master module files
|
||||
local function process_all_files(files)
|
||||
local sortfn = reorder_module_file()
|
||||
local files = tools.expand_file_list(files,'*.*')
|
||||
if sortfn then files:sort(sortfn) end
|
||||
for f in files:iter() do
|
||||
process_file(f, file_list)
|
||||
end
|
||||
if #file_list == 0 then quit "no source files found" end
|
||||
end
|
||||
|
||||
if type(args.file) == 'table' then
|
||||
-- this can only be set from config file so we can assume config is already read
|
||||
process_all_files(args.file)
|
||||
|
||||
elseif path.isdir(args.file) then
|
||||
-- use any configuration file we find, if not already specified
|
||||
if not config_dir then
|
||||
local files = List(dir.getallfiles(args.file,'*.*'))
|
||||
local config_files = files:filter(function(f)
|
||||
return path.basename(f) == args.config
|
||||
end)
|
||||
if #config_files > 0 then
|
||||
config_dir = read_ldoc_config(config_files[1])
|
||||
if #config_files > 1 then
|
||||
print('warning: other config files found: '..config_files[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
process_all_files({args.file})
|
||||
|
||||
elseif path.isfile(args.file) then
|
||||
-- a single file may be accompanied by a config.ld in the same dir
|
||||
if not config_dir then
|
||||
config_dir = path.dirname(args.file)
|
||||
if config_dir == '' then config_dir = '.' end
|
||||
local config = path.join(config_dir,args.config)
|
||||
if path.isfile(config) then
|
||||
read_ldoc_config(config)
|
||||
end
|
||||
end
|
||||
process_file(args.file, file_list)
|
||||
if #file_list == 0 then quit "unsupported file extension" end
|
||||
else
|
||||
quit ("file or directory does not exist: "..quote(args.file))
|
||||
end
|
||||
|
||||
|
||||
-- create the function that renders text (descriptions and summaries)
|
||||
-- (this also will initialize the code prettifier used)
|
||||
override ('format','plain')
|
||||
override 'pretty'
|
||||
ldoc.markup = markup.create(ldoc, args.format, args.pretty, ldoc.user_keywords)
|
||||
|
||||
------ 'Special' Project-level entities ---------------------------------------
|
||||
-- Examples and Topics do not contain code to be processed for doc comments.
|
||||
-- Instead, they are intended to be rendered nicely as-is, whether as pretty-lua
|
||||
-- or as Markdown text. Treating them as 'modules' does stretch the meaning of
|
||||
-- of the term, but allows them to be treated much as modules or scripts.
|
||||
-- They define an item 'body' field (containing the file's text) and a 'postprocess'
|
||||
-- field which is used later to convert them into HTML. They may contain @{ref}s.
|
||||
|
||||
local function add_special_project_entity (f,tags,process)
|
||||
local F = File(f)
|
||||
tags.name = path.basename(f)
|
||||
local text = utils.readfile(f)
|
||||
local item = F:new_item(tags,1)
|
||||
if process then
|
||||
text = process(F, text)
|
||||
end
|
||||
F:finish()
|
||||
file_list:append(F)
|
||||
item.body = text
|
||||
return item, F
|
||||
end
|
||||
|
||||
local function prettify_source_files(files,class,linemap)
|
||||
local prettify = require 'ldoc.prettify'
|
||||
|
||||
process_file_list (files, '*.*', function(f)
|
||||
local ext = path.extension(f)
|
||||
local ftype = file_types[ext]
|
||||
if ftype then
|
||||
local item = add_special_project_entity(f,{
|
||||
class = class,
|
||||
})
|
||||
-- wrap prettify for this example so it knows which file to blame
|
||||
-- if there's a problem
|
||||
local lang = ext:sub(2)
|
||||
item.postprocess = function(code)
|
||||
return '<h2>'..path.basename(f)..'</h2>\n' ..
|
||||
prettify.lua(lang,f,code,0,true,linemap and linemap[f])
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if type(ldoc.examples) == 'string' then
|
||||
ldoc.examples = {ldoc.examples}
|
||||
end
|
||||
if type(ldoc.examples) == 'table' then
|
||||
prettify_source_files(ldoc.examples,"example")
|
||||
end
|
||||
|
||||
ldoc.is_file_prettified = {}
|
||||
|
||||
if ldoc.prettify_files then
|
||||
local files = List()
|
||||
local linemap = {}
|
||||
for F in file_list:iter() do
|
||||
files:append(F.filename)
|
||||
local mod = F.modules[1]
|
||||
if mod then
|
||||
local ls = List()
|
||||
for item in mod.items:iter() do
|
||||
ls:append(item.lineno)
|
||||
end
|
||||
linemap[F.filename] = ls
|
||||
end
|
||||
end
|
||||
|
||||
if type(ldoc.prettify_files) == 'table' then
|
||||
files = tools.expand_file_list(ldoc.prettify_files, '*.*')
|
||||
elseif type(ldoc.prettify_files) == 'string' then
|
||||
-- the gotcha is that if the person has a folder called 'show', only the contents
|
||||
-- of that directory will be converted. So, we warn of this amibiguity
|
||||
if ldoc.prettify_files == 'show' then
|
||||
-- just fall through with all module files collected above
|
||||
if path.exists 'show' then
|
||||
print("Notice: if you only want to prettify files in `show`, then set prettify_files to `show/`")
|
||||
end
|
||||
else
|
||||
files = tools.expand_file_list({ldoc.prettify_files}, '*.*')
|
||||
end
|
||||
end
|
||||
|
||||
ldoc.is_file_prettified = tablex.makeset(files)
|
||||
prettify_source_files(files,"file",linemap)
|
||||
end
|
||||
|
||||
if args.simple then
|
||||
ldoc.no_return_or_parms=true
|
||||
ldoc.no_summary=true
|
||||
end
|
||||
|
||||
ldoc.readme = ldoc.readme or ldoc.topics
|
||||
if type(ldoc.readme) == 'string' then
|
||||
ldoc.readme = {ldoc.readme}
|
||||
end
|
||||
if type(ldoc.readme) == 'table' then
|
||||
process_file_list(ldoc.readme, '*.md', function(f)
|
||||
local item, F = add_special_project_entity(f,{
|
||||
class = 'topic'
|
||||
}, markup.add_sections)
|
||||
-- add_sections above has created sections corresponding to the 2nd level
|
||||
-- headers in the readme, which are attached to the File. So
|
||||
-- we pass the File to the postprocesser, which will insert the section markers
|
||||
-- and resolve inline @ references.
|
||||
if ldoc.use_markdown_titles then
|
||||
item.display_name = F.display_name
|
||||
end
|
||||
item.postprocess = function(txt) return ldoc.markup(txt,F) end
|
||||
end)
|
||||
end
|
||||
|
||||
-- extract modules from the file objects, resolve references and sort appropriately ---
|
||||
|
||||
local first_module
|
||||
local project = ProjectMap()
|
||||
local module_list = List()
|
||||
module_list.by_name = {}
|
||||
|
||||
local modcount = 0
|
||||
|
||||
for F in file_list:iter() do
|
||||
for mod in F.modules:iter() do
|
||||
if not first_module then first_module = mod end
|
||||
if doc.code_tag(mod.type) then modcount = modcount + 1 end
|
||||
module_list:append(mod)
|
||||
module_list.by_name[mod.name] = mod
|
||||
end
|
||||
end
|
||||
|
||||
local handle = io.open("output.xml", "w")
|
||||
io.output(handle)
|
||||
for mod in module_list:iter() do
|
||||
mod:dumpToXML()
|
||||
end
|
||||
io.close(handle)
|
||||
|
||||
for mod in module_list:iter() do
|
||||
if not args.module then -- no point if we're just showing docs on the console
|
||||
mod:resolve_references(module_list)
|
||||
end
|
||||
project:add(mod,module_list)
|
||||
end
|
||||
|
||||
|
||||
if ldoc.sort_modules then
|
||||
table.sort(module_list,function(m1,m2)
|
||||
return m1.name < m2.name
|
||||
end)
|
||||
end
|
||||
|
||||
ldoc.single = modcount == 1 and first_module or nil
|
||||
|
||||
--do return end
|
||||
|
||||
-------- three ways to dump the object graph after processing -----
|
||||
|
||||
-- ldoc -m will give a quick & dirty dump of the module's documentation;
|
||||
-- using -v will make it more verbose
|
||||
if args.module then
|
||||
if #module_list == 0 then quit("no modules found") end
|
||||
if args.module == true then
|
||||
file_list[1]:dump(args.verbose)
|
||||
else
|
||||
local M,name = module_list[1], args.module
|
||||
local fun = M.items.by_name[name]
|
||||
if not fun then
|
||||
fun = M.items.by_name[M.mod_name..':'..name]
|
||||
end
|
||||
if not fun then quit(quote(name).." is not part of "..quote(args.file)) end
|
||||
fun:dump(true)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- ldoc --dump will do the same as -m, except for the currently specified files
|
||||
if args.dump then
|
||||
for mod in module_list:iter() do
|
||||
mod:dump(true)
|
||||
end
|
||||
os.exit()
|
||||
end
|
||||
if args.tags ~= 'none' then
|
||||
local tagset = {}
|
||||
for t in stringx.split(args.tags,','):iter() do
|
||||
tagset[t] = true
|
||||
end
|
||||
for mod in module_list:iter() do
|
||||
mod:dump_tags(tagset)
|
||||
end
|
||||
os.exit()
|
||||
end
|
||||
|
||||
-- ldoc --filter mod.name will load the module `mod` and pass the object graph
|
||||
-- to the function `name`. As a special case --filter dump will use pl.pretty.dump.
|
||||
if args.filter ~= 'none' then
|
||||
doc.filter_objects_through_function(args.filter, module_list)
|
||||
os.exit()
|
||||
end
|
||||
|
||||
-- can specify format, output, dir and ext in config.ld
|
||||
override ('output','index')
|
||||
override ('dir','doc')
|
||||
override ('ext','html')
|
||||
override 'one'
|
||||
|
||||
-- handling styling and templates --
|
||||
ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp'
|
||||
|
||||
-- special case: user wants to generate a .md file from a .lua file
|
||||
if args.ext == 'md' then
|
||||
if #module_list ~= 1 then
|
||||
quit("can currently only generate Markdown output from one module only")
|
||||
end
|
||||
if not ldoc.template or ldoc.template == '!' then
|
||||
ldoc.template = '!md'
|
||||
end
|
||||
args.output = module_list[1].name
|
||||
args.dir = '.'
|
||||
ldoc.template_escape = '>'
|
||||
ldoc.style = false
|
||||
args.ext = '.md'
|
||||
end
|
||||
|
||||
local function match_bang (s)
|
||||
if type(s) ~= 'string' then return end
|
||||
return s:match '^!(.*)'
|
||||
end
|
||||
|
||||
local function style_dir (sname)
|
||||
local style = ldoc[sname]
|
||||
local dir
|
||||
if style==false and sname == 'style' then
|
||||
args.style = false
|
||||
ldoc.css = false
|
||||
end
|
||||
if style then
|
||||
if style == true then
|
||||
dir = config_dir
|
||||
elseif type(style) == 'string' and (path.isdir(style) or match_bang(style)) then
|
||||
dir = style
|
||||
else
|
||||
quit(quote(tostring(style)).." is not a directory")
|
||||
end
|
||||
args[sname] = dir
|
||||
end
|
||||
end
|
||||
|
||||
-- the directories for template and stylesheet can be specified
|
||||
-- either by command-line '--template','--style' arguments or by 'template and
|
||||
-- 'style' fields in config.ld.
|
||||
-- The assumption here is that if these variables are simply true then the directory
|
||||
-- containing config.ld contains a ldoc.css and a ldoc.ltp respectively. Otherwise
|
||||
-- they must be a valid subdirectory.
|
||||
|
||||
style_dir 'style'
|
||||
style_dir 'template'
|
||||
|
||||
if not args.ext:find '^%.' then
|
||||
args.ext = '.'..args.ext
|
||||
end
|
||||
|
||||
if args.one then
|
||||
ldoc.style = '!one'
|
||||
end
|
||||
|
||||
local builtin_style, builtin_template = match_bang(args.style),match_bang(args.template)
|
||||
if builtin_style or builtin_template then
|
||||
-- '!' here means 'use built-in templates'
|
||||
local user = path.expanduser('~'):gsub('[/\\: ]','_')
|
||||
local tmpdir = path.join(path.is_windows and os.getenv('TMP') or (os.getenv('TMPDIR') or '/tmp'),'ldoc'..user)
|
||||
if not path.isdir(tmpdir) then
|
||||
lfs.mkdir(tmpdir)
|
||||
end
|
||||
local function tmpwrite (name)
|
||||
local ok,text = pcall(require,'ldoc.html.'..name:gsub('%.','_'))
|
||||
if not ok then
|
||||
quit("cannot find builtin template "..name.." ("..text..")")
|
||||
end
|
||||
if not utils.writefile(path.join(tmpdir,name),text) then
|
||||
quit("cannot write to temp directory "..tmpdir)
|
||||
end
|
||||
end
|
||||
if builtin_style then
|
||||
if builtin_style ~= '' then
|
||||
ldoc.css = 'ldoc_'..builtin_style..'.css'
|
||||
end
|
||||
tmpwrite(ldoc.css)
|
||||
args.style = tmpdir
|
||||
end
|
||||
if builtin_template then
|
||||
if builtin_template ~= '' then
|
||||
ldoc.templ = 'ldoc_'..builtin_template..'.ltp'
|
||||
end
|
||||
tmpwrite(ldoc.templ)
|
||||
args.template = tmpdir
|
||||
end
|
||||
end
|
||||
|
||||
-- default icon to nil
|
||||
if args.icon == 'none' then args.icon = nil end
|
||||
|
||||
ldoc.log = print
|
||||
ldoc.kinds = project
|
||||
ldoc.modules = module_list
|
||||
ldoc.title = ldoc.title or args.title
|
||||
ldoc.project = ldoc.project or args.project
|
||||
ldoc.package = args.package:match '%a+' and args.package or nil
|
||||
ldoc.icon = ldoc.icon or args.icon
|
||||
|
||||
local source_date_epoch = os.getenv("SOURCE_DATE_EPOCH")
|
||||
if args.testing then
|
||||
ldoc.updatetime = "2015-01-01 12:00:00"
|
||||
ldoc.version = 'TESTING'
|
||||
elseif source_date_epoch == nil then
|
||||
if args.date == 'system' then
|
||||
ldoc.updatetime = os.date("%Y-%m-%d %H:%M:%S")
|
||||
else
|
||||
ldoc.updatetime = args.date
|
||||
end
|
||||
else
|
||||
ldoc.updatetime = os.date("!%Y-%m-%d %H:%M:%S",source_date_epoch)
|
||||
end
|
||||
|
||||
local html = require 'ldoc.html'
|
||||
|
||||
html.generate_output(ldoc, args, project)
|
||||
|
||||
if args.verbose then
|
||||
print 'modules'
|
||||
for k in pairs(module_list.by_name) do print(k) end
|
||||
end
|
||||
|
||||
if args.fatalwarnings and Item.had_warning then
|
||||
os.exit(1)
|
||||
end
|
3
Documentation/compiler/ldoc/ldoc/SciTE.properties
Normal file
3
Documentation/compiler/ldoc/ldoc/SciTE.properties
Normal file
|
@ -0,0 +1,3 @@
|
|||
tabsize=3
|
||||
indent.size=3
|
||||
use.tabs=0
|
50
Documentation/compiler/ldoc/ldoc/builtin/coroutine.lua
Normal file
50
Documentation/compiler/ldoc/ldoc/builtin/coroutine.lua
Normal file
|
@ -0,0 +1,50 @@
|
|||
--- creating and controlling coroutines.
|
||||
-- @module coroutine
|
||||
|
||||
local coroutine = {}
|
||||
|
||||
---
|
||||
-- Creates a new coroutine, with body `f`. `f` must be a Lua
|
||||
-- function. Returns this new coroutine, an object with type `"thread"`.
|
||||
function coroutine.create(f) end
|
||||
|
||||
---
|
||||
-- Starts or continues the execution of coroutine `co`. The first time
|
||||
-- you resume a coroutine, it starts running its body. The values
|
||||
-- ... are passed as the arguments to the body function. If the coroutine
|
||||
-- has yielded, `resume` restarts it; the values ... are passed
|
||||
-- as the results from the yield.
|
||||
-- If the coroutine runs without any errors, `resume` returns true plus any
|
||||
-- values passed to `yield` (if the coroutine yields) or any values returned
|
||||
-- by the body function (if the coroutine terminates). If there is any error,
|
||||
-- `resume` returns false plus the error message.
|
||||
function coroutine.resume(co , ...) end
|
||||
|
||||
---
|
||||
-- Returns the running coroutine. Or nil when called by the main thread.
|
||||
function coroutine.running() end
|
||||
|
||||
---
|
||||
-- Returns the status of coroutine `co`. Result is a string: `"running"`, if
|
||||
-- the coroutine is running (that is, it called `status`); `"suspended"`, if
|
||||
-- the coroutine is suspended in a call to `yield`, or if it has not started
|
||||
-- running yet; `"normal"` if the coroutine is active but not running (that
|
||||
-- is, it has resumed another coroutine); and `"dead"` if the coroutine has
|
||||
-- finished its body function, or if it has stopped with an error.
|
||||
function coroutine.status(co) end
|
||||
|
||||
---
|
||||
-- Creates a new coroutine, with body `f`. `f` must be a Lua
|
||||
-- function. Returns a function that resumes the coroutine each time it is
|
||||
-- called. Any arguments passed to the function behave as the extra arguments to
|
||||
-- `resume`. Returns the same values returned by `resume`, except the first
|
||||
-- boolean. In case of error, propagates the error.
|
||||
function coroutine.wrap(f) end
|
||||
|
||||
---
|
||||
-- Suspends the execution of the calling coroutine. The coroutine cannot
|
||||
-- be running a C function, a metamethod, or an iterator. Any arguments to
|
||||
-- `yield` are passed as extra results to `resume`.
|
||||
function coroutine.yield(...) end
|
||||
|
||||
return coroutine
|
124
Documentation/compiler/ldoc/ldoc/builtin/debug.lua
Normal file
124
Documentation/compiler/ldoc/ldoc/builtin/debug.lua
Normal file
|
@ -0,0 +1,124 @@
|
|||
--- getting runtime debug information.
|
||||
-- @module debug
|
||||
|
||||
local debug = {}
|
||||
---
|
||||
-- Enters an interactive mode with the user, running each string that
|
||||
-- the user enters. Using simple commands and other debug facilities,
|
||||
-- the user can inspect global and local variables, change their values,
|
||||
-- evaluate expressions, and so on. A line containing only the word `cont`
|
||||
-- finishes this function, so that the caller continues its execution.
|
||||
-- Note that commands for `debug.debug` are not lexically nested within any
|
||||
-- function, and so have no direct access to local variables.
|
||||
function debug.debug() end
|
||||
|
||||
---
|
||||
-- Returns the environment of object `o`.
|
||||
function debug.getfenv(o) end
|
||||
|
||||
---
|
||||
-- Returns the current hook settings of the thread, as three values: the
|
||||
-- current hook function, the current hook mask, and the current hook count
|
||||
-- (as set by the `debug.sethook` function).
|
||||
function debug.gethook(thread) end
|
||||
|
||||
---
|
||||
-- Returns a table with information about a function. You can give the
|
||||
-- function directly, or you can give a number as the value of `function`,
|
||||
-- which means the function running at level `function` of the call stack
|
||||
-- of the given thread: level 0 is the current function (`getinfo` itself);
|
||||
-- level 1 is the function that called `getinfo`; and so on. If `function`
|
||||
-- is a number larger than the number of active functions, then `getinfo`
|
||||
-- returns nil.
|
||||
--
|
||||
-- `thread` and `what` are optional.
|
||||
--
|
||||
-- The returned table can contain all the fields returned by `lua_getinfo`,
|
||||
-- with the string `what` describing which fields to fill in. The default for
|
||||
-- `what` is to get all information available, except the table of valid
|
||||
-- lines. If present, the option '`f`' adds a field named `func` with
|
||||
-- the function itself. If present, the option '`L`' adds a field named
|
||||
-- `activelines` with the table of valid lines.
|
||||
-- For instance, the expression `debug.getinfo(1,"n").name` returns a table
|
||||
-- with a name for the current function, if a reasonable name can be found,
|
||||
-- and the expression `debug.getinfo(print)` returns a table with all available
|
||||
-- information about the `print` function.
|
||||
function debug.getinfo(thread, func , what) end
|
||||
|
||||
---
|
||||
-- This function returns the name and the value of the local variable with
|
||||
-- index `loc` of the function at level `level` of the stack. (The first
|
||||
-- parameter or local variable has index 1, and so on, until the last active
|
||||
-- local variable.) The function returns nil if there is no local variable
|
||||
-- with the given index, and raises an error when called with a `level` out
|
||||
-- of range. (You can call `debug.getinfo` to check whether the level is valid.)
|
||||
-- Variable names starting with '`(`' (open parentheses) represent internal
|
||||
-- variables (loop control variables, temporaries, and C function locals).
|
||||
function debug.getlocal(thread, level, loc) end
|
||||
|
||||
---
|
||||
-- Returns the metatable of the given `object` or nil if it does not have
|
||||
-- a metatable.
|
||||
function debug.getmetatable(object) end
|
||||
|
||||
---
|
||||
-- Returns the registry table (see §3.5).
|
||||
function debug.getregistry() end
|
||||
|
||||
---
|
||||
-- This function returns the name and the value of the upvalue with index
|
||||
-- `up` of the function `func`. The function returns nil if there is no
|
||||
-- upvalue with the given index.
|
||||
function debug.getupvalue(func, up) end
|
||||
|
||||
---
|
||||
-- Sets the environment of the given `object` to the given `table`. Returns
|
||||
-- `object`.
|
||||
function debug.setfenv(object, table) end
|
||||
|
||||
---
|
||||
-- Sets the given function as a hook. The string `mask` and the number
|
||||
-- `count` describe when the hook will be called. The string mask may have
|
||||
-- the following characters, with the given meaning:
|
||||
--
|
||||
-- * `"c"`: the hook is called every time Lua calls a function;
|
||||
-- * `"r"`: the hook is called every time Lua returns from a function;
|
||||
-- * `"l"`: the hook is called every time Lua enters a new line of code.
|
||||
--
|
||||
-- With a `count` different from zero, the hook is called after every `count`
|
||||
-- instructions.
|
||||
--
|
||||
-- When called without arguments, `debug.sethook` turns off the hook.
|
||||
--
|
||||
-- When the hook is called, its first parameter is a string describing
|
||||
-- the event that has triggered its call: `"call"`, `"return"` (or `"tail
|
||||
-- return"`, when simulating a return from a tail call), `"line"`, and
|
||||
-- `"count"`. For line events, the hook also gets the new line number as its
|
||||
-- second parameter. Inside a hook, you can call `getinfo` with level 2 to
|
||||
-- get more information about the running function (level 0 is the `getinfo`
|
||||
-- function, and level 1 is the hook function), unless the event is `"tail
|
||||
-- return"`. In this case, Lua is only simulating the return, and a call to
|
||||
-- `getinfo` will return invalid data.
|
||||
function debug.sethook(thread, hook, mask , count) end
|
||||
|
||||
---
|
||||
-- This function assigns the value `value` to the local variable with
|
||||
-- index `loc` of the function at level `level` of the stack. The function
|
||||
-- returns nil if there is no local variable with the given index, and raises
|
||||
-- an error when called with a `level` out of range. (You can call `getinfo`
|
||||
-- to check whether the level is valid.) Otherwise, it returns the name of
|
||||
-- the local variable.
|
||||
function debug.setlocal(thread, level, loc, value) end
|
||||
|
||||
---
|
||||
-- Sets the metatable for the given `object` to the given `table` (which
|
||||
-- can be nil).
|
||||
function debug.setmetatable(object, table) end
|
||||
|
||||
---
|
||||
-- This function assigns the value `value` to the upvalue with index `up`
|
||||
-- of the function `func`. The function returns nil if there is no upvalue
|
||||
-- with the given index. Otherwise, it returns the name of the upvalue.
|
||||
function debug.setupvalue(func, up, value) end
|
||||
|
||||
return debug
|
243
Documentation/compiler/ldoc/ldoc/builtin/global.lua
Normal file
243
Documentation/compiler/ldoc/ldoc/builtin/global.lua
Normal file
|
@ -0,0 +1,243 @@
|
|||
--- Lua global functions.
|
||||
|
||||
module 'global'
|
||||
|
||||
-- luacheck: ignore 121
|
||||
|
||||
---
|
||||
-- Issues an error when its argument `v` is false.
|
||||
-- That is, nil or false. otherwise, returns all its arguments.
|
||||
-- `message` is an error when absent, it defaults to "assertion failed!"
|
||||
function assert(v , message) end
|
||||
|
||||
---
|
||||
-- This function is a generic interface to the garbage collector. It
|
||||
-- performs different functions according to its first argument, `opt`:
|
||||
--
|
||||
-- * "stop": stops the garbage collector.
|
||||
-- * "restart": restarts the garbage collector.
|
||||
-- * "collect": performs a full garbage-collection cycle.
|
||||
-- * "count": returns the total memory in use by Lua (in Kbytes).
|
||||
-- * "step": performs a garbage-collection step. The step "size" is controlled
|
||||
-- by `arg` (larger values mean more steps) in a non-specified way. If you
|
||||
-- want to control the step size you must experimentally tune the value of
|
||||
-- * "arg". Returns true if the step finished a collection cycle.
|
||||
-- * "setpause": sets `arg` as the new value for the *pause* of the collector
|
||||
-- (see 2.10). Returns the previous value for *pause*.
|
||||
-- * "setstepmul": sets `arg` as the new value for the *step multiplier*
|
||||
-- of the collector (see 2.10). Returns the previous value for *step*.
|
||||
--
|
||||
function collectgarbage(opt , arg) end
|
||||
|
||||
---
|
||||
-- Opens the named file and executes its contents as a Lua chunk. When
|
||||
-- called without arguments,
|
||||
-- `dofile` executes the contents of the standard input (`stdin`). Returns
|
||||
-- all values returned by the chunk. In case of errors, `dofile` propagates
|
||||
-- the error to its caller (that is, `dofile` does not run in protected mode).
|
||||
function dofile(filename) end
|
||||
|
||||
---
|
||||
-- Terminates the last protected function called.
|
||||
-- Returns `message` as the error message.
|
||||
-- Function `error` never returns.
|
||||
-- Usually, `error` adds some information about the error position at the
|
||||
-- beginning of the message. The `level` argument specifies how to get the
|
||||
-- error position. With level 1 (the default), the error position is where the
|
||||
-- `error` function was called. Level 2 points the error to where the function
|
||||
-- that called `error` was called; and so on. Passing a level 0 avoids the
|
||||
-- addition of error position information to the message.
|
||||
function error(message , level) end
|
||||
|
||||
---
|
||||
-- A global variable (not a function) that holds the global environment
|
||||
-- (that is, `_G._G = _G`). Lua itself does not use this variable; changing
|
||||
-- its value does not affect any environment, nor vice-versa. (Set `__ENV`
|
||||
-- to change environments in functions)
|
||||
-- @table _G
|
||||
|
||||
---
|
||||
-- If `object` does not have a metatable, returns nil. Otherwise, if the
|
||||
-- object's metatable has a `"__metatable"` field, returns the associated
|
||||
-- value. Otherwise, returns the metatable of the given object.
|
||||
function getmetatable(object) end
|
||||
|
||||
---
|
||||
-- For iterating over sequences. Returns three values: an iterator function, the table `t`, and 0,
|
||||
-- so that the construction
|
||||
-- for i,v in ipairs(t) do *body* end
|
||||
-- will iterate over the pairs (`1,t[1]`), (`2,t[2]`), ..., up to the
|
||||
-- first integer key absent from the table.
|
||||
function ipairs(t) end
|
||||
|
||||
---
|
||||
-- Loads a chunk.
|
||||
-- If `ld` is a string, the chunk is this string.
|
||||
-- If `ld` is a function, load calls it repeatedly to get the chunk pieces. Each call to `ld` must return a
|
||||
-- string that concatenates with previous results. A return of an empty string, nil, or no value
|
||||
-- signals the end of the chunk.
|
||||
-- If there are no syntactic errors, returns the compiled chunk as a function;
|
||||
-- otherwise, returns nil plus the error message.
|
||||
-- If the resulting function has upvalues, the first upvalue is set to the value of the global environment or to `env`,
|
||||
-- if that parameter is given. When loading main chunks, the first upvalue will be the`_ENV` variable (see 2.2).
|
||||
-- `source` is used as the source of the chunk for error messages and debug information (see 4.9).
|
||||
-- When absent, it defaults to `ld`, if `ld` is a string, or to "=(load)" otherwise.
|
||||
-- The string `mode` controls whether the chunk can be text or binary (that is, a precompiled chunk).
|
||||
-- It may be the string "b" (only binary chunks), "t" (only text chunks), or "bt" (both binary and text).
|
||||
-- The default is "bt"
|
||||
function load (ld , source , mode , env) end
|
||||
|
||||
---
|
||||
-- Similar to `load`, but gets the chunk from file `filename`. Or from the
|
||||
-- standard input, if no file name is given.
|
||||
function loadfile (filename , mode , env) end
|
||||
|
||||
---
|
||||
-- Allows a program to traverse all fields of a table. Its first argument is
|
||||
-- a table and its second argument is an index in this table. `next` returns
|
||||
-- the next index of the table and its associated value.
|
||||
--
|
||||
-- When called with nil
|
||||
-- as its second argument, `next` returns an initial index and its associated
|
||||
-- value. When called with the last index, or with nil in an empty table, `next`
|
||||
-- returns nil.
|
||||
--
|
||||
-- If the second argument is absent, then it is interpreted as
|
||||
-- nil. In particular, you can use `next(t)` to check whether a table is empty.
|
||||
-- The order in which the indices are enumerated is not specified, *even for
|
||||
-- numeric indices*. (To traverse a table in numeric order, use a numerical
|
||||
-- for or the `ipairs` function.)
|
||||
--
|
||||
-- The behavior of `next` is *undefined* if, during the traversal, you assign
|
||||
-- any value to a non-existent field in the table. You may however modify
|
||||
-- existing fields. In particular, you may clear existing fields.
|
||||
function next(table , index) end
|
||||
|
||||
---
|
||||
-- For iterating over all key-value pairs of a table.
|
||||
-- Returns three values: the `next` function, the table `t`, and nil,
|
||||
-- so that the construction
|
||||
-- for k,v in pairs(t) do *body* end
|
||||
-- will iterate over all key-value pairs of table `t`.
|
||||
-- See function `next` for the caveats of modifying the table during its
|
||||
-- traversal.
|
||||
function pairs(t) end
|
||||
|
||||
---
|
||||
-- Calls function `f` with the given arguments in *protected mode*. This
|
||||
-- means that any error inside `f` is not propagated; instead, `pcall` catches
|
||||
-- the error and returns a status code. Its first result is the status code (a
|
||||
-- boolean), which is true if the call succeeds without errors. In such case,
|
||||
-- `pcall` also returns all results from the call, after this first result. In
|
||||
-- case of any error, `pcall` returns false plus the error message.
|
||||
function pcall(f, arg1, ...) end
|
||||
|
||||
---
|
||||
--Prints any number of values to `stdout`.
|
||||
-- Uses the `tostring` function to convert them to strings. `print` is not
|
||||
-- intended for formatted output, but only as a quick way to show a value,
|
||||
-- typically for debugging. For formatted output, use `string.format`.
|
||||
function print(...) end
|
||||
|
||||
---
|
||||
-- Checks whether `v1` is equal to `v2`. Does not invoke any
|
||||
-- metamethod. Returns a boolean.
|
||||
function rawequal(v1, v2) end
|
||||
|
||||
---
|
||||
-- Gets the real value of `table[index]`. Does not invoke any
|
||||
-- metamethod. `table` must be a table; `index` may be any value.
|
||||
function rawget(table, index) end
|
||||
|
||||
---
|
||||
-- Sets the real value of `table[index]` to `value`. Does not invoke any
|
||||
-- metamethod. `table` must be a table, `index` any value different from nil,
|
||||
-- and `value` any Lua value.
|
||||
-- This function returns `table`.
|
||||
function rawset(table, index, value) end
|
||||
|
||||
---
|
||||
-- Returns all arguments after argument number
|
||||
-- `index`. Otherwise, `index` must be the string `"#"`, and `select` returns
|
||||
-- the total number of extra arguments it received.
|
||||
function select(index, ...) end
|
||||
|
||||
---
|
||||
-- Sets the metatable for the given table. (You cannot change the metatable
|
||||
-- of other types from Lua, only from C.) If `metatable` is nil, removes the
|
||||
-- metatable of the given table. If the original metatable has a `"__metatable"`
|
||||
-- field, raises an error.
|
||||
-- This function returns `table`.
|
||||
function setmetatable(table, metatable) end
|
||||
|
||||
---
|
||||
-- Tries to convert its argument to a number. If the argument is already
|
||||
-- a number or a string convertible to a number, then `tonumber` returns this
|
||||
-- number; otherwise, it returns nil.
|
||||
-- An optional argument specifies the base to interpret the numeral. The base
|
||||
-- may be any integer between 2 and 36, inclusive. In bases above 10, the
|
||||
-- letter '`A`' (in either upper or lower case) represents 10, '`B`' represents
|
||||
-- 11, and so forth, with '`Z`' representing 35. In base 10 (the default),
|
||||
-- the number can have a decimal part, as well as an optional exponent part
|
||||
-- (see 2.1). In other bases, only unsigned integers are accepted.
|
||||
function tonumber(e , base) end
|
||||
|
||||
---
|
||||
-- Converts any value to a string in a reasonable format.
|
||||
-- For complete control of how numbers are converted, use `string.format`.
|
||||
-- If the metatable of `e` has a `"__tostring"` field, then `tostring` calls
|
||||
-- the corresponding value with `e` as argument, and uses the result of the
|
||||
-- call as its result.
|
||||
function tostring(e) end
|
||||
|
||||
---
|
||||
-- Returns the type of its only argument, coded as a string. The possible
|
||||
-- results of this function are "
|
||||
-- `nil`" (a string, not the value nil), "`number`", "`string`", "`boolean`",
|
||||
-- "`table`", "`function`", "`thread`", and "`userdata`".
|
||||
function type(v) end
|
||||
|
||||
---
|
||||
-- A global variable (not a function) that holds a string containing the
|
||||
-- current interpreter version. The current contents of this variable is
|
||||
-- "`Lua 5.1`".
|
||||
-- @table _VERSION
|
||||
|
||||
---
|
||||
-- This function is similar to `pcall`, except that you can set a new
|
||||
-- error handler.
|
||||
-- `xpcall` calls function `f` in protected mode, using `err` as the error
|
||||
-- handler. Any error inside `f` is not propagated; instead, `xpcall` catches
|
||||
-- the error, calls the `err` function with the original error object, and
|
||||
-- returns a status code. Its first result is the status code (a boolean),
|
||||
-- which is true if the call succeeds without errors. In this case, `xpcall`
|
||||
-- also returns all results from the call, after this first result. In case
|
||||
-- of any error, `xpcall` returns false plus the result from `err`.
|
||||
function xpcall(f, err) end
|
||||
|
||||
---
|
||||
-- Loads the given module. The function starts by looking into the
|
||||
-- `package.loaded` table to determine whether `modname` is already
|
||||
-- loaded. If it is, then `require` returns the value stored at
|
||||
-- `package.loaded[modname]`. Otherwise, it tries to find a *loader* for
|
||||
-- the module.
|
||||
-- To find a loader, `require` is guided by the `package.loaders` array. By
|
||||
-- changing this array, we can change how `require` looks for a module. The
|
||||
-- following explanation is based on the default configuration for
|
||||
-- `package.loaders`.
|
||||
-- First `require` queries `package.preload[modname]`. If it has a value,
|
||||
-- this value (which should be a function) is the loader. Otherwise `require`
|
||||
-- searches for a Lua loader using the path stored in `package.path`. If
|
||||
-- that also fails, it searches for a C loader using the path stored in
|
||||
-- `package.cpath`. If that also fails, it tries an *all-in-one* loader (see
|
||||
-- `package.loaders`).
|
||||
-- Once a loader is found, `require` calls the loader with a single argument,
|
||||
-- `modname`. If the loader returns any value, `require` assigns the returned
|
||||
-- value to `package.loaded[modname]`. If the loader returns no value and
|
||||
-- has not assigned any value to `package.loaded[modname]`, then `require`
|
||||
-- assigns true to this entry. In any case, `require` returns the final value
|
||||
-- of `package.loaded[modname]`.
|
||||
-- If there is any error loading or running the module, or if it cannot find
|
||||
-- any loader for the module, then `require` signals an error.
|
||||
function require(modname) end
|
||||
|
186
Documentation/compiler/ldoc/ldoc/builtin/globals.lua
Normal file
186
Documentation/compiler/ldoc/ldoc/builtin/globals.lua
Normal file
|
@ -0,0 +1,186 @@
|
|||
-------
|
||||
-- global functions and tables
|
||||
local tools = require 'ldoc.tools'
|
||||
local globals = {}
|
||||
local lua52 = _VERSION:match '5.2'
|
||||
local lua53 = _VERSION:match '5.3'
|
||||
local lua54 = _VERSION:match '5.4'
|
||||
|
||||
|
||||
globals.functions = {
|
||||
assert = true,
|
||||
collectgarbage = true,
|
||||
dofile = true,
|
||||
error = true,
|
||||
getmetatable = true,
|
||||
setmetatable = true,
|
||||
pairs = true,
|
||||
ipairs = true,
|
||||
load = true,
|
||||
loadfile = true,
|
||||
loadstring = true,
|
||||
next = true,
|
||||
pcall = true,
|
||||
print = true,
|
||||
rawequal = true,
|
||||
rawget = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
tonumber = true,
|
||||
tostring = true,
|
||||
type = true,
|
||||
xpcall = true,
|
||||
module = true,
|
||||
require = true,
|
||||
}
|
||||
local functions = globals.functions
|
||||
|
||||
if lua54 then
|
||||
functions.warn = true
|
||||
functions.rawlen = true
|
||||
elseif lua52 or lua53 then
|
||||
functions.rawlen = true
|
||||
else
|
||||
functions.setfenv = true
|
||||
functions.getfenv = true
|
||||
functions.unpack = true
|
||||
end
|
||||
|
||||
local manual, fun_ref
|
||||
|
||||
function globals.set_manual_url(url)
|
||||
manual = url .. '#'
|
||||
fun_ref = manual..'pdf-'
|
||||
end
|
||||
|
||||
if lua54 then
|
||||
globals.tables = {
|
||||
io = '6.8',
|
||||
package = '6.3',
|
||||
math = '6.7',
|
||||
os = '6.9',
|
||||
string = '6.4',
|
||||
table = '6.6',
|
||||
coroutine = '6.2',
|
||||
debug = '6.10'
|
||||
}
|
||||
globals.set_manual_url 'https://www.lua.org/manual/5.4/manual.html'
|
||||
elseif lua53 then
|
||||
globals.tables = {
|
||||
io = '6.8',
|
||||
package = '6.3',
|
||||
math = '6.7',
|
||||
os = '6.9',
|
||||
string = '6.4',
|
||||
table = '6.6',
|
||||
coroutine = '6.2',
|
||||
debug = '6.10'
|
||||
}
|
||||
globals.set_manual_url 'https://www.lua.org/manual/5.3/manual.html'
|
||||
elseif lua52 then
|
||||
globals.tables = {
|
||||
io = '6.8',
|
||||
package = '6.3',
|
||||
math = '6.6',
|
||||
os = '6.9',
|
||||
string = '6.4',
|
||||
table = '6.5',
|
||||
coroutine = '6.2',
|
||||
debug = '6.10'
|
||||
}
|
||||
globals.set_manual_url 'https://www.lua.org/manual/5.2/manual.html'
|
||||
else
|
||||
globals.tables = {
|
||||
io = '5.7',
|
||||
package = '5.3',
|
||||
math = '5.6',
|
||||
os = '5.8',
|
||||
string = '5.4',
|
||||
table = '5.5',
|
||||
coroutine = '5.2',
|
||||
debug = '5.9'
|
||||
}
|
||||
globals.set_manual_url 'https://www.lua.org/manual/5.1/manual.html'
|
||||
end
|
||||
|
||||
local file_methods = {
|
||||
close = true,
|
||||
flush = true,
|
||||
lines = true,
|
||||
read = true,
|
||||
seek = true,
|
||||
setvbuf = true,
|
||||
write = true,
|
||||
}
|
||||
|
||||
-- external libs tracked by LDoc using LDoc style
|
||||
local xlibs = {
|
||||
lfs='lfs.html', lpeg='lpeg.html',
|
||||
}
|
||||
local xlib_url = 'http://stevedonovan.github.io/lua-stdlibs/modules/'
|
||||
|
||||
local tables = globals.tables
|
||||
|
||||
local function function_ref (name,tbl)
|
||||
local href
|
||||
if not tbl then -- can only be a standard Lua global function
|
||||
if globals.functions[name] then
|
||||
return {href = fun_ref..name, label = name}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
if tbl == 'file' then -- special case: file objects!
|
||||
if not file_methods[name] then
|
||||
return nil
|
||||
end
|
||||
name = 'file:'..name
|
||||
href = fun_ref..name
|
||||
elseif tables[tbl] then -- function inside standard Lua table
|
||||
local t = rawget(_G,tbl) -- do a quick sanity check
|
||||
if not rawget(t,name) then
|
||||
return nil
|
||||
end
|
||||
name = tbl..'.'..name
|
||||
href = fun_ref..name
|
||||
elseif xlibs[tbl] then -- in external libs, use LDoc style
|
||||
local t = require('ldoc.builtin.'..tbl)
|
||||
if not rawget(t,name) then
|
||||
return nil
|
||||
end
|
||||
href = xlib_url..xlibs[tbl]..'#'..name
|
||||
name = tbl..'.'..name
|
||||
else
|
||||
return nil
|
||||
end
|
||||
return {href = href, label = name}
|
||||
end
|
||||
|
||||
local function module_ref (tbl)
|
||||
local href
|
||||
if tables[tbl] ~= nil then -- standard Lua table
|
||||
href = manual..tables[tbl]
|
||||
elseif xlibs[tbl] then -- external lib
|
||||
href = xlib_url..xlibs[tbl]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
return {href = href, label = tbl}
|
||||
end
|
||||
|
||||
function globals.lua_manual_ref (name)
|
||||
local tbl,fname = tools.split_dotted_name(name)
|
||||
local ref
|
||||
if not tbl then -- plain symbol
|
||||
ref = function_ref(name)
|
||||
if ref then return ref end
|
||||
ref = module_ref(name)
|
||||
if ref then return ref end
|
||||
else
|
||||
ref = function_ref(fname,tbl)
|
||||
if ref then return ref end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
return globals
|
162
Documentation/compiler/ldoc/ldoc/builtin/io.lua
Normal file
162
Documentation/compiler/ldoc/ldoc/builtin/io.lua
Normal file
|
@ -0,0 +1,162 @@
|
|||
--- Reading and Writing Files.
|
||||
-- @module io
|
||||
|
||||
local io = {}
|
||||
|
||||
-- luacheck: ignore 241
|
||||
local file = {}
|
||||
|
||||
---
|
||||
-- Equivalent to `file:close()`. Without a `file`, closes the default
|
||||
-- output file.
|
||||
function io.close(file) end
|
||||
|
||||
---
|
||||
-- Equivalent to `file:flush` over the default output file.
|
||||
function io.flush() end
|
||||
|
||||
---
|
||||
-- When called with a file name, it opens the named file (in text mode),
|
||||
-- and sets its handle as the default input file. When called with a file
|
||||
-- handle, it simply sets this file handle as the default input file. When
|
||||
-- called without parameters, it returns the current default input file.
|
||||
-- In case of errors this function raises the error, instead of returning an
|
||||
-- error code.
|
||||
function io.input(file) end
|
||||
|
||||
---
|
||||
-- Opens the given file name in read mode and returns an iterator function
|
||||
-- that, each time it is called, returns a new line from the file. Therefore,
|
||||
-- the construction
|
||||
-- for line in io.lines(filename) do *body* end
|
||||
-- will iterate over all lines of the file. When the iterator function detects
|
||||
-- the end of file, it returns nil (to finish the loop) and automatically
|
||||
-- closes the file.
|
||||
-- The call `io.lines()` (with no file name) is equivalent to
|
||||
-- `io.input():lines()`; that is, it iterates over the lines of the default
|
||||
-- input file. In this case it does not close the file when the loop ends.
|
||||
function io.lines(filename) end
|
||||
|
||||
---
|
||||
-- This function opens a file, in the mode specified in the string `mode`. It
|
||||
-- returns a new file handle, or, in case of errors, nil plus an error message.
|
||||
-- The `mode` string can be any of the following:
|
||||
-- "r": read mode (the default);
|
||||
-- "w": write mode;
|
||||
-- "a": append mode;
|
||||
-- "r+": update mode, all previous data is preserved;
|
||||
-- "w+": update mode, all previous data is erased;
|
||||
-- "a+": append update mode, previous data is preserved, writing is only
|
||||
-- allowed at the end of file.
|
||||
-- The `mode` string can also have a '`b`' at the end, which is needed in
|
||||
-- some systems to open the file in binary mode. This string is exactly what
|
||||
-- is used in the standard C function `fopen`.
|
||||
function io.open(filename , mode) end
|
||||
|
||||
---
|
||||
-- Similar to `io.input`, but operates over the default output file.
|
||||
function io.output(file) end
|
||||
|
||||
---
|
||||
-- Starts program `prog` in a separated process and returns a file handle
|
||||
-- that you can use to read data from this program (if `mode` is `"r"`,
|
||||
-- the default) or to write data to this program (if `mode` is `"w"`).
|
||||
-- This function is system dependent and is not available on all platforms.
|
||||
function io.popen(prog , mode) end
|
||||
|
||||
---
|
||||
-- Equivalent to `io.input():read`.
|
||||
function io.read(...) end
|
||||
|
||||
-- * `io.stderr`: Standard error.
|
||||
-- * `io.stdin`: Standard in.
|
||||
-- * `io.stdout`: Standard out.
|
||||
|
||||
---
|
||||
-- Returns a handle for a temporary file. This file is opened in update
|
||||
-- mode and it is automatically removed when the program ends.
|
||||
function io.tmpfile() end
|
||||
|
||||
---
|
||||
-- Checks whether `obj` is a valid file handle. Returns the string `"file"`
|
||||
-- if `obj` is an open file handle, `"closed file"` if `obj` is a closed file
|
||||
-- handle, or nil if `obj` is not a file handle.
|
||||
function io.type(obj) end
|
||||
|
||||
---
|
||||
-- Equivalent to `io.output():write`.
|
||||
function io.write(...) end
|
||||
|
||||
---
|
||||
-- Closes `file`. Note that files are automatically closed when their
|
||||
-- handles are garbage collected, but that takes an unpredictable amount of
|
||||
-- time to happen.
|
||||
function file:close() end
|
||||
|
||||
---
|
||||
-- Saves any written data to `file`.
|
||||
function file:flush() end
|
||||
|
||||
---
|
||||
-- Returns an iterator function that, each time it is called, returns a
|
||||
-- new line from the file. Therefore, the construction
|
||||
-- for line in file:lines() do *body* end
|
||||
-- will iterate over all lines of the file. (Unlike `io.lines`, this function
|
||||
-- does not close the file when the loop ends.)
|
||||
function file:lines() end
|
||||
|
||||
---
|
||||
-- Reads the file `file`, according to the given formats, which specify
|
||||
-- what to read. For each format, the function returns a string (or a number)
|
||||
-- with the characters read, or nil if it cannot read data with the specified
|
||||
-- format. When called without formats, it uses a default format that reads
|
||||
-- the entire next line (see below).
|
||||
-- The available formats are
|
||||
-- "*n": reads a number; this is the only format that returns a number
|
||||
-- instead of a string.
|
||||
-- "*a": reads the whole file, starting at the current position. On end of
|
||||
-- file, it returns the empty string.
|
||||
-- "*l": reads the next line (skipping the end of line), returning nil on
|
||||
-- end of file. This is the default format.
|
||||
-- *number*: reads a string with up to this number of characters, returning
|
||||
-- nil on end of file. If number is zero, it reads nothing and returns an
|
||||
-- empty string, or nil on end of file.
|
||||
function file:read(...) end
|
||||
|
||||
---
|
||||
-- Sets and gets the file position, measured from the beginning of the
|
||||
-- file, to the position given by `offset` plus a base specified by the string
|
||||
-- `whence`, as follows:
|
||||
-- "set": base is position 0 (beginning of the file);
|
||||
-- "cur": base is current position;
|
||||
-- "end": base is end of file;
|
||||
-- In case of success, function `seek` returns the final file position,
|
||||
-- measured in bytes from the beginning of the file. If this function fails,
|
||||
-- it returns nil, plus a string describing the error.
|
||||
-- The default value for `whence` is `"cur"`, and for `offset` is 0. Therefore,
|
||||
-- the call `file:seek()` returns the current file position, without changing
|
||||
-- it; the call `file:seek("set")` sets the position to the beginning of the
|
||||
-- file (and returns 0); and the call `file:seek("end")` sets the position
|
||||
-- to the end of the file, and returns its size.
|
||||
function file:seek(whence , offset) end
|
||||
|
||||
---
|
||||
-- Sets the buffering mode for an output file. There are three available
|
||||
-- modes:
|
||||
--
|
||||
-- * "no": no buffering; the result of any output operation appears immediately.
|
||||
-- * "full": full buffering; output operation is performed only when the
|
||||
-- buffer is full (or when you explicitly `flush` the file (see `io.flush`)).
|
||||
-- * "line": line buffering; output is buffered until a newline is output or
|
||||
-- there is any input from some special files (such as a terminal device).
|
||||
-- For the last two cases, `size` specifies the size of the buffer, in
|
||||
-- bytes. The default is an appropriate size.
|
||||
function file:setvbuf(mode , size) end
|
||||
|
||||
---
|
||||
-- Writes the value of each of its arguments to the `file`. The arguments
|
||||
-- must be strings or numbers. To write other values, use `tostring` or
|
||||
-- `string.format` before `write`.
|
||||
function file:write(...) end
|
||||
|
||||
return io
|
125
Documentation/compiler/ldoc/ldoc/builtin/lfs.lua
Normal file
125
Documentation/compiler/ldoc/ldoc/builtin/lfs.lua
Normal file
|
@ -0,0 +1,125 @@
|
|||
--- File and Directory manipulation
|
||||
-- @module lfs
|
||||
|
||||
local lfs = {}
|
||||
|
||||
---
|
||||
-- Returns a table with the file attributes corresponding to filepath (or nil
|
||||
-- followed by an error message in case of error). If the second optional
|
||||
-- argument is given, then only the value of the named attribute is returned
|
||||
-- (this use is equivalent to lfs.attributes(filepath).aname, but the table is
|
||||
-- not created and only one attribute is retrieved from the O.S.). The
|
||||
-- attributes are described as follows; attribute mode is a string, all the
|
||||
-- others are numbers, and the time related attributes use the same time
|
||||
-- reference of os.time:
|
||||
--
|
||||
-- - dev: on Unix systems, this represents the device that the inode resides on.
|
||||
-- On Windows systems, represents the drive number of the disk containing
|
||||
-- the file
|
||||
-- - ino: on Unix systems, this represents the inode number. On Windows systems
|
||||
-- this has no meaning
|
||||
-- - mode: string representing the associated protection mode (the values could
|
||||
-- be file, directory, link, socket, named pipe, char device, block
|
||||
-- device or other)
|
||||
-- - nlink: number of hard links to the file
|
||||
-- - uid: user-id of owner (Unix only, always 0 on Windows)
|
||||
-- - gid: group-id of owner (Unix only, always 0 on Windows)
|
||||
-- - rdev: on Unix systems, represents the device type, for special file inodes.
|
||||
-- On Windows systems represents the same as dev
|
||||
-- - access: time of last access
|
||||
-- - modification: time of last data modification
|
||||
-- - change: time of last file status change
|
||||
-- - size: file size, in bytes
|
||||
-- - blocks: block allocated for file; (Unix only)
|
||||
-- - blksize: optimal file system I/O blocksize; (Unix only)
|
||||
-- This function uses stat internally thus if the given filepath is a symbolic
|
||||
-- link, it is followed (if it points to another link the chain is followed
|
||||
-- recursively) and the information is about the file it refers to. To obtain
|
||||
-- information about the link itself, see function lfs.symlinkattributes.
|
||||
function lfs.attributes(filepath , aname) end
|
||||
|
||||
---
|
||||
-- Changes the current working directory to the given path.
|
||||
-- Returns true in case of success or nil plus an error string.
|
||||
function lfs.chdir(path) end
|
||||
|
||||
---
|
||||
-- Creates a lockfile (called lockfile.lfs) in path if it does not exist and
|
||||
-- returns the lock. If the lock already exists checks it it's stale, using the
|
||||
-- second parameter (default for the second parameter is INT_MAX, which in
|
||||
-- practice means the lock will never be stale. To free the the lock call
|
||||
-- lock:free().
|
||||
-- In case of any errors it returns nil and the error message. In particular,
|
||||
-- if the lock exists and is not stale it returns the "File exists" message.
|
||||
function lfs.lock_dir(path, seconds_stale) end
|
||||
|
||||
---
|
||||
-- Returns a string with the current working directory or nil plus an error
|
||||
-- string.
|
||||
function lfs.currentdir() end
|
||||
|
||||
---
|
||||
-- Lua iterator over the entries of a given directory. Each time the iterator is
|
||||
-- called with dir_obj it returns a directory entry's name as a string, or nil
|
||||
-- if there are no more entries. You can also iterate by calling `dir_obj:next()`,
|
||||
-- and explicitly close the directory before the iteration finished with
|
||||
-- `dir_obj:close()`. Raises an error if path is not a directory.
|
||||
function lfs.dir(path) end
|
||||
|
||||
---
|
||||
-- Locks a file or a part of it. This function works on open files; the file
|
||||
-- handle should be specified as the first argument. The string mode could be
|
||||
-- either r (for a read/shared lock) or w (for a write/exclusive lock). The
|
||||
-- optional arguments start and length can be used to specify a starting point
|
||||
-- and its length; both should be numbers.
|
||||
-- Returns true if the operation was successful; in case of error, it returns
|
||||
-- nil plus an error string.
|
||||
function lfs.lock(filehandle, mode, start, length) end
|
||||
|
||||
---
|
||||
-- Creates a new directory. The argument is the name of the new directory.
|
||||
-- Returns true if the operation was successful; in case of error, it returns
|
||||
-- nil plus an error string.
|
||||
function lfs.mkdir(dirname) end
|
||||
|
||||
---
|
||||
-- Removes an existing directory. The argument is the name of the directory.
|
||||
-- Returns true if the operation was successful; in case of error, it returns
|
||||
-- nil plus an error string.
|
||||
function lfs.rmdir(dirname) end
|
||||
|
||||
---
|
||||
-- Sets the writing mode for a file. The mode string can be either binary or
|
||||
-- text. Returns the previous mode string for the file. This function is only
|
||||
-- available in Windows, so you may want to make sure that lfs.setmode exists
|
||||
-- before using it.
|
||||
function lfs.setmode(file, mode) end
|
||||
|
||||
---
|
||||
-- Identical to lfs.attributes except that it obtains information about the link
|
||||
-- itself (not the file it refers to). This function is not available in Windows
|
||||
-- so you may want to make sure that lfs.symlinkattributes exists before using
|
||||
-- it.
|
||||
function lfs.symlinkattributes(filepath , aname) end
|
||||
|
||||
---
|
||||
-- Set access and modification times of a file. This function is a bind to utime
|
||||
-- function. The first argument is the filename, the second argument (atime) is
|
||||
-- the access time, and the third argument (mtime) is the modification time.
|
||||
-- Both times are provided in seconds (which should be generated with Lua
|
||||
-- standard function os.time). If the modification time is omitted, the access
|
||||
-- time provided is used; if both times are omitted, the current time is used.
|
||||
-- Returns true if the operation was successful; in case of error, it returns
|
||||
-- nil plus an error string.
|
||||
function lfs.touch(filepath , atime , mtime) end
|
||||
|
||||
---
|
||||
-- Unlocks a file or a part of it. This function works on open files; the file
|
||||
-- handle should be specified as the first argument. The optional arguments
|
||||
-- start and length can be used to specify a starting point and its length; both
|
||||
-- should be numbers.
|
||||
-- Returns true if the operation was successful; in case of error, it returns
|
||||
-- nil plus an error string.
|
||||
function lfs.unlock(filehandle, start, length) end
|
||||
|
||||
return lfs
|
214
Documentation/compiler/ldoc/ldoc/builtin/lpeg.lua
Normal file
214
Documentation/compiler/ldoc/ldoc/builtin/lpeg.lua
Normal file
|
@ -0,0 +1,214 @@
|
|||
--- LPeg PEG pattern matching.
|
||||
-- @module lpeg
|
||||
|
||||
local lpeg = {}
|
||||
|
||||
---
|
||||
-- The matching function. It attempts to match the given pattern against the
|
||||
-- subject string. If the match succeeds, returns the index in the subject of
|
||||
-- the first character after the match, or the captured values (if the pattern
|
||||
-- captured any value).
|
||||
--
|
||||
-- An optional numeric argument init makes the match starts at that position in
|
||||
-- the subject string. As usual in Lua libraries, a negative value counts from
|
||||
-- the end.
|
||||
--
|
||||
-- Unlike typical pattern-matching functions, match works only in anchored mode;
|
||||
-- that is, it tries to match the pattern with a prefix of the given subject
|
||||
-- string (at position init), not with an arbitrary substring of the subject.
|
||||
-- So, if we want to find a pattern anywhere in a string, we must either write a
|
||||
-- loop in Lua or write a pattern that matches anywhere. This second approach is
|
||||
-- easy and quite efficient; see examples.
|
||||
function lpeg.match(pattern, subject , init) end
|
||||
|
||||
---
|
||||
-- If the given value is a pattern, returns the string "pattern". Otherwise
|
||||
-- returns nil.
|
||||
function lpeg.type(value) end
|
||||
|
||||
---
|
||||
-- Returns a string with the running version of LPeg.
|
||||
function lpeg.version() end
|
||||
|
||||
---
|
||||
-- Sets the maximum size for the backtrack stack used by LPeg to track calls and
|
||||
-- choices. Most well-written patterns need little backtrack levels and
|
||||
-- therefore you seldom need to change this maximum; but a few useful patterns
|
||||
-- may need more space. Before changing this maximum you should try to rewrite
|
||||
-- your pattern to avoid the need for extra space.
|
||||
function lpeg.setmaxstack(max) end
|
||||
|
||||
---
|
||||
-- Converts the given value into a proper pattern, according to the following
|
||||
-- rules:
|
||||
-- * If the argument is a pattern, it is returned unmodified.
|
||||
-- * If the argument is a string, it is translated to a pattern that matches
|
||||
-- literally the string.
|
||||
-- * If the argument is a non-negative number n, the result is a pattern that
|
||||
-- matches exactly n characters.
|
||||
-- * If the argument is a negative number -n, the result is a pattern that
|
||||
-- succeeds only if the input string does not have n characters: lpeg.P(-n)
|
||||
-- is equivalent to -lpeg.P(n) (see the unary minus operation).
|
||||
-- * If the argument is a boolean, the result is a pattern that always
|
||||
-- succeeds or always fails (according to the boolean value), without
|
||||
-- consuming any input.
|
||||
-- * If the argument is a table, it is interpreted as a grammar (see
|
||||
-- Grammars).
|
||||
-- * If the argument is a function, returns a pattern equivalent to a
|
||||
-- match-time capture over the empty string.
|
||||
function lpeg.P(value) end
|
||||
|
||||
---
|
||||
-- Returns a pattern that matches any single character belonging to one of the
|
||||
-- given ranges. Each range is a string xy of length 2, representing all
|
||||
-- characters with code between the codes of x and y (both inclusive).
|
||||
-- As an example, the pattern `lpeg.R("09")` matches any digit, and `lpeg.R("az",
|
||||
-- "AZ")` matches any ASCII letter.
|
||||
function lpeg.R(range) end
|
||||
|
||||
---
|
||||
-- Returns a pattern that matches any single character that appears in the given
|
||||
-- string. (The S stands for Set.)
|
||||
-- As an example, the pattern lpeg.S("+-*/") matches any arithmetic operator.
|
||||
-- Note that, if s is a character (that is, a string of length 1), then
|
||||
-- lpeg.P(s) is equivalent to lpeg.S(s) which is equivalent to lpeg.R(s..s).
|
||||
-- Note also that both lpeg.S("") and lpeg.R() are patterns that always fail.
|
||||
function lpeg.S(string) end
|
||||
|
||||
---
|
||||
-- This operation creates a non-terminal (a variable) for a grammar. The created
|
||||
-- non-terminal refers to the rule indexed by v in the enclosing grammar. (See
|
||||
-- Grammars for details.)
|
||||
function lpeg.V(v) end
|
||||
|
||||
---
|
||||
-- Returns a table with patterns for matching some character classes according
|
||||
-- to the current locale. The table has fields:
|
||||
--
|
||||
-- * alnum
|
||||
-- * alpha
|
||||
-- * cntrl
|
||||
-- * digit
|
||||
-- * graph
|
||||
-- * lower
|
||||
-- * print
|
||||
-- * punct
|
||||
-- * space
|
||||
-- * upper
|
||||
-- * xdigit
|
||||
--
|
||||
-- each one containing a
|
||||
-- correspondent pattern. Each pattern matches any single character that belongs
|
||||
-- to its class.
|
||||
--
|
||||
-- If called with an argument table, then it creates those fields inside the
|
||||
-- given table and returns that table.
|
||||
function lpeg.locale(table) end
|
||||
|
||||
---
|
||||
-- Creates a simple capture, which captures the substring of the subject that
|
||||
-- matches patt. The captured value is a string. If patt has other captures,
|
||||
-- their values are returned after this one.
|
||||
function lpeg.C(patt) end
|
||||
|
||||
---
|
||||
-- Creates an argument capture. This pattern matches the empty string and
|
||||
-- produces the value given as the nth extra argument given in the call to
|
||||
-- lpeg.match.
|
||||
function lpeg.Carg(n) end
|
||||
|
||||
---
|
||||
-- Creates a back capture. This pattern matches the empty string and produces
|
||||
-- the values produced by the most recent group capture named name.
|
||||
-- Most recent means the last complete outermost group capture with the given
|
||||
-- name. A Complete capture means that the entire pattern corresponding to the
|
||||
-- capture has matched. An Outermost capture means that the capture is not
|
||||
-- inside another complete capture.
|
||||
function lpeg.Cb(name) end
|
||||
|
||||
---
|
||||
-- Creates a constant capture. This pattern matches the empty string and
|
||||
-- produces all given values as its captured values.
|
||||
function lpeg.Cc(...) end
|
||||
|
||||
---
|
||||
-- Creates a fold capture. If patt produces a list of captures C1 C2 ... Cn,
|
||||
-- this capture will produce the value func(...func(func(C1, C2), C3)..., Cn),
|
||||
-- that is, it will fold (or accumulate, or reduce) the captures from patt using
|
||||
-- function func.
|
||||
--
|
||||
-- This capture assumes that patt should produce at least one capture with at
|
||||
-- least one value (of any type), which becomes the initial value of an
|
||||
-- accumulator. (If you need a specific initial value, you may prefix a constant
|
||||
-- capture to patt.) For each subsequent capture LPeg calls func with this
|
||||
-- accumulator as the first argument and all values produced by the capture as
|
||||
-- extra arguments; the value returned by this call becomes the new value for
|
||||
-- the accumulator. The final value of the accumulator becomes the captured
|
||||
-- value.
|
||||
--
|
||||
-- As an example, the following pattern matches a list of numbers separated by
|
||||
-- commas and returns their addition:
|
||||
--
|
||||
-- -- matches a numeral and captures its value
|
||||
-- number = lpeg.R"09"^1 / tonumber
|
||||
-- -- matches a list of numbers, captures their values
|
||||
-- list = number * ("," * number)^0
|
||||
-- -- auxiliary function to add two numbers
|
||||
-- function add (acc, newvalue) return acc + newvalue end
|
||||
-- -- folds the list of numbers adding them
|
||||
-- sum = lpeg.Cf(list, add)
|
||||
-- -- example of use
|
||||
-- print(sum:match("10,30,43")) --> 83
|
||||
--
|
||||
function lpeg.Cf(patt, func) end
|
||||
|
||||
---
|
||||
-- Creates a group capture. It groups all values returned by patt into a single
|
||||
-- capture. The group may be anonymous (if no name is given) or named with the
|
||||
-- given name.
|
||||
-- An anonymous group serves to join values from several captures into a single
|
||||
-- capture. A named group has a different behavior. In most situations, a named
|
||||
-- group returns no values at all. Its values are only relevant for a following
|
||||
-- back capture or when used inside a table capture.
|
||||
function lpeg.Cg(patt , name) end
|
||||
|
||||
---
|
||||
-- Creates a position capture. It matches the empty string and captures the
|
||||
-- position in the subject where the match occurs. The captured value is a
|
||||
-- number.
|
||||
function lpeg.Cp() end
|
||||
|
||||
---
|
||||
-- Creates a substitution capture, which captures the substring of the subject
|
||||
-- that matches patt, with substitutions. For any capture inside patt with a
|
||||
-- value, the substring that matched the capture is replaced by the capture
|
||||
-- value (which should be a string). The final captured value is the string
|
||||
-- resulting from all replacements.
|
||||
function lpeg.Cs(patt) end
|
||||
|
||||
---
|
||||
-- Creates a table capture. This capture creates a table and puts all values
|
||||
-- from all anonymous captures made by patt inside this table in successive
|
||||
-- integer keys, starting at 1. Moreover, for each named capture group created
|
||||
-- by patt, the first value of the group is put into the table with the group
|
||||
-- name as its key. The captured value is only the table.
|
||||
function lpeg.Ct(patt) end
|
||||
|
||||
---
|
||||
-- Creates a match-time capture. Unlike all other captures, this one is
|
||||
-- evaluated immediately when a match occurs. It forces the immediate evaluation
|
||||
-- of all its nested captures and then calls func.
|
||||
-- The given function gets as arguments the entire subject, the current position
|
||||
-- (after the match of patt), plus any capture values produced by patt.
|
||||
-- The first value returned by function defines how the match happens. If the
|
||||
-- call returns a number, the match succeeds and the returned number becomes the
|
||||
-- new current position. (Assuming a subject s and current position i, the
|
||||
-- returned number must be in the range [i, len(s) + 1].) If the call returns
|
||||
-- true, the match succeeds without consuming any input. (So, to return true is
|
||||
-- equivalent to return i.) If the call returns false, nil, or no value, the
|
||||
-- match fails.
|
||||
-- Any extra values returned by the function become the values produced by the
|
||||
-- capture.
|
||||
function lpeg.Cmt(patt, func) end
|
||||
|
||||
return lpeg
|
144
Documentation/compiler/ldoc/ldoc/builtin/math.lua
Normal file
144
Documentation/compiler/ldoc/ldoc/builtin/math.lua
Normal file
|
@ -0,0 +1,144 @@
|
|||
--- standard mathematical functions.
|
||||
-- @module math
|
||||
|
||||
local math = {}
|
||||
|
||||
---
|
||||
-- Returns the absolute value of `x`.
|
||||
function math.abs(x) end
|
||||
|
||||
---
|
||||
-- Returns the arc cosine of `x` (in radians).
|
||||
function math.acos(x) end
|
||||
|
||||
---
|
||||
-- Returns the arc sine of `x` (in radians).
|
||||
function math.asin(x) end
|
||||
|
||||
---
|
||||
-- Returns the arc tangent of `x` (in radians).
|
||||
function math.atan(x) end
|
||||
|
||||
---
|
||||
-- Returns the arc tangent of `y/x` (in radians), but uses the signs
|
||||
-- of both parameters to find the quadrant of the result. (It also handles
|
||||
-- correctly the case of `x` being zero.)
|
||||
function math.atan2(y, x) end
|
||||
|
||||
---
|
||||
-- Returns the smallest integer larger than or equal to `x`.
|
||||
function math.ceil(x) end
|
||||
|
||||
---
|
||||
-- Returns the cosine of `x` (assumed to be in radians).
|
||||
function math.cos(x) end
|
||||
|
||||
---
|
||||
-- Returns the hyperbolic cosine of `x`.
|
||||
function math.cosh(x) end
|
||||
|
||||
---
|
||||
-- Returns the angle `x` (given in radians) in degrees.
|
||||
function math.deg(x) end
|
||||
|
||||
---
|
||||
-- Returns the value *e^x*.
|
||||
function math.exp(x) end
|
||||
|
||||
---
|
||||
-- Returns the largest integer smaller than or equal to `x`.
|
||||
function math.floor(x) end
|
||||
|
||||
---
|
||||
-- Returns the remainder of the division of `x` by `y` that rounds the
|
||||
-- quotient towards zero.
|
||||
function math.fmod(x, y) end
|
||||
|
||||
---
|
||||
-- Returns `m` and `e` such that *x = m2^e*, `e` is an integer and the
|
||||
-- absolute value of `m` is in the range *[0.5, 1)* (or zero when `x` is zero).
|
||||
function math.frexp(x) end
|
||||
|
||||
---
|
||||
-- The value `HUGE_VAL`, a value larger than or equal to any other
|
||||
-- numerical value.
|
||||
-- function math.huge end
|
||||
-- * `math.HUGE_VAL`: math.HUGE_VAL
|
||||
|
||||
---
|
||||
-- Returns *m2^e* (`e` should be an integer).
|
||||
function math.ldexp(m, e) end
|
||||
|
||||
---
|
||||
-- Returns the natural logarithm of `x`.
|
||||
function math.log(x) end
|
||||
|
||||
---
|
||||
-- Returns the base-10 logarithm of `x`.
|
||||
function math.log10(x) end
|
||||
|
||||
---
|
||||
-- Returns the maximum value among its arguments.
|
||||
function math.max(x, ...) end
|
||||
|
||||
---
|
||||
-- Returns the minimum value among its arguments.
|
||||
function math.min(x, ...) end
|
||||
|
||||
---
|
||||
-- Returns two numbers, the integral part of `x` and the fractional part of
|
||||
-- `x`.
|
||||
function math.modf(x) end
|
||||
|
||||
---
|
||||
-- The value of *pi*.
|
||||
-- function math.pi end
|
||||
-- * `math.pi`: math.pi
|
||||
|
||||
---
|
||||
-- Returns *x^y*. (You can also use the expression `x^y` to compute this
|
||||
-- value.)
|
||||
function math.pow(x, y) end
|
||||
|
||||
---
|
||||
-- Returns the angle `x` (given in degrees) in radians.
|
||||
function math.rad(x) end
|
||||
|
||||
---
|
||||
-- This function is an interface to the simple pseudo-random generator
|
||||
-- function `rand` provided by ANSI C. (No guarantees can be given for its
|
||||
-- statistical properties.)
|
||||
-- When called without arguments, returns a uniform pseudo-random real
|
||||
-- number in the range *[0,1)*. When called with an integer number `m`,
|
||||
-- `math.random` returns a uniform pseudo-random integer in the range *[1,
|
||||
-- m]*. When called with two integer numbers `m` and `n`, `math.random`
|
||||
-- returns a uniform pseudo-random integer in the range *[m, n]*.
|
||||
function math.random(m , n) end
|
||||
|
||||
---
|
||||
-- Sets `x` as the "seed" for the pseudo-random generator: equal seeds
|
||||
-- produce equal sequences of numbers.
|
||||
function math.randomseed(x) end
|
||||
|
||||
---
|
||||
-- Returns the sine of `x` (assumed to be in radians).
|
||||
function math.sin(x) end
|
||||
|
||||
---
|
||||
-- Returns the hyperbolic sine of `x`.
|
||||
function math.sinh(x) end
|
||||
|
||||
---
|
||||
-- Returns the square root of `x`. (You can also use the expression `x^0.5`
|
||||
-- to compute this value.)
|
||||
function math.sqrt(x) end
|
||||
|
||||
---
|
||||
-- Returns the tangent of `x` (assumed to be in radians).
|
||||
function math.tan(x) end
|
||||
|
||||
---
|
||||
-- Returns the hyperbolic tangent of `x`.
|
||||
function math.tanh(x) end
|
||||
|
||||
return math
|
112
Documentation/compiler/ldoc/ldoc/builtin/os.lua
Normal file
112
Documentation/compiler/ldoc/ldoc/builtin/os.lua
Normal file
|
@ -0,0 +1,112 @@
|
|||
--- Operating System facilities like date, time and program execution.
|
||||
-- @module os
|
||||
|
||||
local os = {}
|
||||
|
||||
---
|
||||
-- Returns an approximation of the amount in seconds of CPU time used by
|
||||
-- the program.
|
||||
function os.clock() end
|
||||
|
||||
---
|
||||
-- Returns a string or a table containing date and time, formatted according
|
||||
-- to the given string `format`.
|
||||
--
|
||||
-- If the `time` argument is present, this is the time to be formatted
|
||||
-- (see the `os.time` function for a description of this value). Otherwise,
|
||||
-- `date` formats the current time.
|
||||
--
|
||||
-- If `format` starts with '`!`', then the date is formatted in Coordinated
|
||||
-- Universal Time. After this optional character, if `format` is the string
|
||||
-- "`*t`", then `date` returns a table with the following fields:
|
||||
--
|
||||
-- * `year` (four digits)
|
||||
-- * `month` (1--12)
|
||||
-- * `day` (1--31)
|
||||
-- * `hour` (0--23)
|
||||
-- * `min` (0--59)
|
||||
-- * `sec` (0--61)
|
||||
-- * `wday` (weekday, Sunday is 1)
|
||||
-- * `yday` (day of the year)
|
||||
-- * `isdst` (daylight saving flag, a boolean).
|
||||
--
|
||||
-- If `format` is not "`*t`", then `date` returns the date as a string,
|
||||
-- formatted according to the same rules as the C function `strftime`.
|
||||
-- When called without arguments, `date` returns a reasonable date and time
|
||||
-- representation that depends on the host system and on the current locale
|
||||
-- (that is, `os.date()` is equivalent to `os.date("%c")`).
|
||||
function os.date(format , time) end
|
||||
|
||||
---
|
||||
-- Returns the number of seconds from time `t1` to time `t2`. In POSIX,
|
||||
-- Windows, and some other systems, this value is exactly `t2`*-*`t1`.
|
||||
function os.difftime(t2, t1) end
|
||||
|
||||
---
|
||||
-- This function is equivalent to the C function `system`. It passes
|
||||
-- `command` to be executed by an operating system shell. It returns a status
|
||||
-- code, which is system-dependent. If `command` is absent, then it returns
|
||||
-- nonzero if a shell is available and zero otherwise.
|
||||
function os.execute(command) end
|
||||
|
||||
---
|
||||
-- Calls the C function `exit`, with an optional `code`, to terminate the
|
||||
-- host program. The default value for `code` is the success code.
|
||||
function os.exit(code) end
|
||||
|
||||
---
|
||||
-- Returns the value of the process environment variable `varname`, or
|
||||
-- nil if the variable is not defined.
|
||||
function os.getenv(varname) end
|
||||
|
||||
---
|
||||
-- Deletes the file or directory with the given name. Directories must be
|
||||
-- empty to be removed. If this function fails, it returns nil, plus a string
|
||||
-- describing the error.
|
||||
function os.remove(filename) end
|
||||
|
||||
---
|
||||
-- Renames file or directory named `oldname` to `newname`. If this function
|
||||
-- fails, it returns nil, plus a string describing the error.
|
||||
function os.rename(oldname, newname) end
|
||||
|
||||
---
|
||||
-- Sets the current locale of the program. `locale` is a string specifying
|
||||
-- a locale; `category` is an optional string describing which category to
|
||||
-- change: `"all"`, `"collate"`, `"ctype"`, `"monetary"`, `"numeric"`, or
|
||||
-- `"time"`; the default category is `"all"`. The function returns the name
|
||||
-- of the new locale, or nil if the request cannot be honored.
|
||||
-- If `locale` is the empty string, the current locale is set to an
|
||||
-- implementation-defined native locale. If `locale` is the string "`C`",
|
||||
-- the current locale is set to the standard C locale.
|
||||
-- When called with nil as the first argument, this function only returns
|
||||
-- the name of the current locale for the given category.
|
||||
function os.setlocale(locale , category) end
|
||||
|
||||
---
|
||||
-- Returns the current time when called without arguments, or a time
|
||||
-- representing the date and time specified by the given table. This table
|
||||
-- must have fields `year`, `month`, and `day`, and may have fields `hour`,
|
||||
-- `min`, `sec`, and `isdst` (for a description of these fields, see the
|
||||
-- `os.date` function).
|
||||
-- The returned value is a number, whose meaning depends on your system. In
|
||||
-- POSIX, Windows, and some other systems, this number counts the number
|
||||
-- of seconds since some given start time (the "epoch"). In other systems,
|
||||
-- the meaning is not specified, and the number returned by `time` can be
|
||||
-- used only as an argument to `date` and `difftime`.
|
||||
function os.time(table) end
|
||||
|
||||
---
|
||||
-- Returns a string with a file name that can be used for a temporary
|
||||
-- file. The file must be explicitly opened before its use and explicitly
|
||||
-- removed when no longer needed.
|
||||
-- On some systems (POSIX), this function also creates a file with that
|
||||
-- name, to avoid security risks. (Someone else might create the file with
|
||||
-- wrong permissions in the time between getting the name and creating the
|
||||
-- file.) You still have to open the file to use it and to remove it (even
|
||||
-- if you do not use it).
|
||||
-- When possible, you may prefer to use `io.tmpfile`, which automatically
|
||||
-- removes the file when the program ends.
|
||||
function os.tmpname() end
|
||||
|
||||
return os
|
97
Documentation/compiler/ldoc/ldoc/builtin/package.lua
Normal file
97
Documentation/compiler/ldoc/ldoc/builtin/package.lua
Normal file
|
@ -0,0 +1,97 @@
|
|||
--- controlling how `require` finds packages.
|
||||
-- @module package
|
||||
|
||||
local package = {}
|
||||
|
||||
---
|
||||
-- The path used by `require` to search for a C loader.
|
||||
-- Lua initializes the C path `package.cpath` in the same way it initializes
|
||||
-- the Lua path `package.path`, using the environment variable `LUA_CPATH`
|
||||
-- or a default path defined in `luaconf.h`.
|
||||
-- function package.cpath end
|
||||
-- * `package.cpath`: package.cpath
|
||||
|
||||
---
|
||||
-- A table used by `require` to control which modules are already
|
||||
-- loaded. When you require a module `modname` and `package.loaded[modname]`
|
||||
-- is not false, `require` simply returns the value stored there.
|
||||
-- function package.loaded end
|
||||
-- * `package.loaded`: package.loaded
|
||||
|
||||
---
|
||||
-- A table used by `require` to control how to load modules.
|
||||
-- Each entry in this table is a *searcher function*. When looking for a module,
|
||||
-- `require` calls each of these searchers in ascending order, with the module
|
||||
-- name (the argument given to `require`) as its sole parameter. The function
|
||||
-- can return another function (the module *loader*) or a string explaining
|
||||
-- why it did not find that module (or nil if it has nothing to say). Lua
|
||||
-- initializes this table with four functions.
|
||||
-- The first searcher simply looks for a loader in the `package.preload` table.
|
||||
-- The second searcher looks for a loader as a Lua library, using the path
|
||||
-- stored at `package.path`. A path is a sequence of *templates* separated by
|
||||
-- semicolons. For each template, the searcher will change each interrogation
|
||||
-- mark in the template by `filename`, which is the module name with each dot
|
||||
-- replaced by a "directory separator" (such as "`/`" in Unix); then it will
|
||||
-- try to open the resulting file name. So, for instance, if the Lua path is
|
||||
-- the string
|
||||
-- "./?.lua;./?.lc;/usr/local/?/init.lua"
|
||||
-- the search for a Lua file for module `foo` will try to open the files
|
||||
-- `./foo.lua`, `./foo.lc`, and `/usr/local/foo/init.lua`, in that order.
|
||||
-- The third searcher looks for a loader as a C library, using the path given
|
||||
-- by the variable `package.cpath`. For instance, if the C path is the string
|
||||
-- "./?.so;./?.dll;/usr/local/?/init.so"
|
||||
-- the searcher for module `foo` will try to open the files `./foo.so`,
|
||||
-- `./foo.dll`, and `/usr/local/foo/init.so`, in that order. Once it finds
|
||||
-- a C library, this searcher first uses a dynamic link facility to link the
|
||||
-- application with the library. Then it tries to find a C function inside the
|
||||
-- library to be used as the loader. The name of this C function is the string
|
||||
-- "`luaopen_`" concatenated with a copy of the module name where each dot
|
||||
-- is replaced by an underscore. Moreover, if the module name has a hyphen,
|
||||
-- its prefix up to (and including) the first hyphen is removed. For instance,
|
||||
-- if the module name is `a.v1-b.c`, the function name will be `luaopen_b_c`.
|
||||
-- The fourth searcher tries an *all-in-one loader*. It searches the C
|
||||
-- path for a library for the root name of the given module. For instance,
|
||||
-- when requiring `a.b.c`, it will search for a C library for `a`. If found,
|
||||
-- it looks into it for an open function for the submodule; in our example,
|
||||
-- that would be `luaopen_a_b_c`. With this facility, a package can pack
|
||||
-- several C submodules into one single library, with each submodule keeping
|
||||
-- its original open function.
|
||||
-- function package.loaders end
|
||||
-- * `package.loaders`: package.loaders
|
||||
|
||||
---
|
||||
-- Dynamically links the host program with the C library `libname`. Inside
|
||||
-- this library, looks for a function `funcname` and returns this function as a
|
||||
-- C function. (So, `funcname` must follow the protocol (see `lua_CFunction`)).
|
||||
-- This is a low-level function. It completely bypasses the package and module
|
||||
-- system. Unlike `require`, it does not perform any path searching and does
|
||||
-- not automatically adds extensions. `libname` must be the complete file name
|
||||
-- of the C library, including if necessary a path and extension. `funcname`
|
||||
-- must be the exact name exported by the C library (which may depend on the
|
||||
-- C compiler and linker used).
|
||||
-- This function is not supported by ANSI C. As such, it is only available
|
||||
-- on some platforms (Windows, Linux, Mac OS X, Solaris, BSD, plus other Unix
|
||||
-- systems that support the `dlfcn` standard).
|
||||
function package.loadlib(libname, funcname) end
|
||||
|
||||
---
|
||||
-- The path used by `require` to search for a Lua loader.
|
||||
-- At start-up, Lua initializes this variable with the value of the environment
|
||||
-- variable `LUA_PATH` or with a default path defined in `luaconf.h`, if
|
||||
-- the environment variable is not defined. Any "`;;`" in the value of the
|
||||
-- environment variable is replaced by the default path.
|
||||
-- function package.path end
|
||||
-- * `package.path`: package.path
|
||||
|
||||
---
|
||||
-- A table to store loaders for specific modules (see `require`).
|
||||
-- function package.preload end
|
||||
-- * `package.preload`: package.preload
|
||||
|
||||
---
|
||||
-- Sets a metatable for `module` with its `__index` field referring to the
|
||||
-- global environment, so that this module inherits values from the global
|
||||
-- environment. To be used as an option to function `module`.
|
||||
function package.seeall(module) end
|
||||
|
||||
return package
|
191
Documentation/compiler/ldoc/ldoc/builtin/string.lua
Normal file
191
Documentation/compiler/ldoc/ldoc/builtin/string.lua
Normal file
|
@ -0,0 +1,191 @@
|
|||
--- string operations like searching and matching.
|
||||
-- @module string
|
||||
|
||||
local string = {}
|
||||
|
||||
---
|
||||
-- Returns the internal numerical codes of the characters `s[i]`, `s[i+1]`,
|
||||
-- ..., `s[j]`. The default value for `i` is 1; the default value for `j`
|
||||
-- is `i`.
|
||||
-- Note that numerical codes are not necessarily portable across platforms.
|
||||
function string.byte(s , i , j) end
|
||||
|
||||
---
|
||||
-- Receives zero or more integers. Returns a string with length equal to
|
||||
-- the number of arguments, in which each character has the internal numerical
|
||||
-- code equal to its corresponding argument.
|
||||
-- Note that numerical codes are not necessarily portable across platforms.
|
||||
function string.char(...) end
|
||||
|
||||
---
|
||||
-- Returns a string containing a binary representation of the given
|
||||
-- function, so that a later `loadstring` on this string returns a copy of
|
||||
-- the function. `function` must be a Lua function without upvalues.
|
||||
function string.dump(func) end
|
||||
|
||||
---
|
||||
-- Looks for the first match of `pattern` in the string `s`. If it finds a
|
||||
-- match, then `find` returns the indices of `s` where this occurrence starts
|
||||
-- and ends; otherwise, it returns nil. A third, optional numerical argument
|
||||
-- `init` specifies where to start the search; its default value is 1 and
|
||||
-- can be negative. A value of true as a fourth, optional argument `plain`
|
||||
-- turns off the pattern matching facilities, so the function does a plain
|
||||
-- "find substring" operation, with no characters in `pattern` being considered
|
||||
-- "magic". Note that if `plain` is given, then `init` must be given as well.
|
||||
-- If the pattern has captures, then in a successful match the captured values
|
||||
-- are also returned, after the two indices.
|
||||
function string.find(s, pattern , init , plain) end
|
||||
|
||||
---
|
||||
-- Returns a formatted version of its variable number of arguments following
|
||||
-- the description given in its first argument (which must be a string). The
|
||||
-- format string follows the same rules as the `printf` family of standard C
|
||||
-- functions. The only differences are that the options/modifiers `*`, `l`,
|
||||
-- `L`, `n`, `p`, and `h` are not supported and that there is an extra option,
|
||||
-- `q`. The `q` option formats a string in a form suitable to be safely read
|
||||
-- back by the Lua interpreter: the string is written between double quotes,
|
||||
-- and all double quotes, newlines, embedded zeros, and backslashes in the
|
||||
-- string are correctly escaped when written. For instance, the call
|
||||
--
|
||||
-- string.format('%q', 'a string with "quotes" and \n new line')
|
||||
--
|
||||
-- will produce the string:
|
||||
--
|
||||
-- "a string with \"quotes\" and \
|
||||
-- new line"
|
||||
--
|
||||
-- The options `c`, `d`, `E`, `e`, `f`, `g`, `G`, `i`, `o`, `u`, `X`, and
|
||||
-- `x` all expect a number as argument, whereas `q` and `s` expect a string.
|
||||
-- This function does not accept string values containing embedded zeros,
|
||||
-- except as arguments to the `q` option.
|
||||
function string.format(formatstring, ...) end
|
||||
|
||||
---
|
||||
-- Returns an iterator function that, each time it is called, returns the
|
||||
-- next captures from `pattern` over string `s`. If `pattern` specifies no
|
||||
-- captures, then the whole match is produced in each call.
|
||||
-- As an example, the following loop
|
||||
--
|
||||
-- s = "hello world from Lua"
|
||||
-- for w in string.gmatch(s, "%a+") do
|
||||
-- print(w)
|
||||
-- end
|
||||
--
|
||||
-- will iterate over all the words from string `s`, printing one per line. The
|
||||
-- next example collects all pairs `key=value` from the given string into
|
||||
-- a table:
|
||||
--
|
||||
-- t = {}
|
||||
-- s = "from=world, to=Lua"
|
||||
-- for k, v in string.gmatch(s, "(%w+)=(%w+)") do
|
||||
-- t[k] = v
|
||||
-- end
|
||||
--
|
||||
-- For this function, a '`^`' at the start of a pattern does not work as an
|
||||
-- anchor, as this would prevent the iteration.
|
||||
function string.gmatch(s, pattern) end
|
||||
|
||||
---
|
||||
-- Returns a copy of `s` in which all (or the first `n`, if given)
|
||||
-- occurrences of the `pattern` have been replaced by a replacement string
|
||||
-- specified by `repl`, which can be a string, a table, or a function. `gsub`
|
||||
-- also returns, as its second value, the total number of matches that occurred.
|
||||
--
|
||||
-- If `repl` is a string, then its value is used for replacement. The character
|
||||
-- `%` works as an escape character: any sequence in `repl` of the form `%n`,
|
||||
-- with *n* between 1 and 9, stands for the value of the *n*-th captured
|
||||
-- substring (see below). The sequence `%0` stands for the whole match. The
|
||||
-- sequence `%%` stands for a single `%`.
|
||||
--
|
||||
-- If `repl` is a table, then the table is queried for every match, using
|
||||
-- the first capture as the key; if the pattern specifies no captures, then
|
||||
-- the whole match is used as the key.
|
||||
--
|
||||
-- If `repl` is a function, then this function is called every time a match
|
||||
-- occurs, with all captured substrings passed as arguments, in order; if
|
||||
-- the pattern specifies no captures, then the whole match is passed as a
|
||||
-- sole argument.
|
||||
--
|
||||
-- If the value returned by the table query or by the function call is a
|
||||
-- string or a number, then it is used as the replacement string; otherwise,
|
||||
-- if it is false or nil, then there is no replacement (that is, the original
|
||||
-- match is kept in the string).
|
||||
--
|
||||
-- Here are some examples:
|
||||
-- x = string.gsub("hello world", "(%w+)", "%1 %1")
|
||||
-- --> x="hello hello world world"
|
||||
-- x = string.gsub("hello world", "%w+", "%0 %0", 1)
|
||||
-- --> x="hello hello world"
|
||||
-- x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
|
||||
-- --> x="world hello Lua from"
|
||||
-- x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
|
||||
-- --> x="home = /home/roberto, user = roberto"
|
||||
-- x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
|
||||
-- return loadstring(s)()
|
||||
-- end)
|
||||
-- --> x="4+5 = 9"
|
||||
-- local t = {name="lua", version="5.1"}
|
||||
-- x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
|
||||
-- --> x="lua-5.1.tar.gz"
|
||||
function string.gsub(s, pattern, repl , n) end
|
||||
|
||||
---
|
||||
-- Receives a string and returns its length. The empty string `""` has
|
||||
-- length 0. Embedded zeros are counted, so `"a\000bc\000"` has length 5.
|
||||
function string.len(s) end
|
||||
|
||||
---
|
||||
-- Receives a string and returns a copy of this string with all uppercase
|
||||
-- letters changed to lowercase. All other characters are left unchanged. The
|
||||
-- definition of what an uppercase letter is depends on the current locale.
|
||||
function string.lower(s) end
|
||||
|
||||
---
|
||||
-- Looks for the first *match* of `pattern` in the string `s`. If it
|
||||
-- finds one, then `match` returns the captures from the pattern; otherwise
|
||||
-- it returns nil. If `pattern` specifies no captures, then the whole match
|
||||
-- is returned. A third, optional numerical argument `init` specifies where
|
||||
-- to start the search; its default value is 1 and can be negative.
|
||||
function string.match(s, pattern , init) end
|
||||
|
||||
---
|
||||
-- Returns a string that is the concatenation of `n` copies of the string
|
||||
-- `s`.
|
||||
function string.rep(s, n) end
|
||||
|
||||
---
|
||||
-- Returns a string that is the string `s` reversed.
|
||||
function string.reverse(s) end
|
||||
|
||||
---
|
||||
-- Returns the substring of `s` that starts at `i` and continues until
|
||||
-- `j`; `i` and `j` can be negative. If `j` is absent, then it is assumed to
|
||||
-- be equal to -1 (which is the same as the string length). In particular,
|
||||
-- the call `string.sub(s,1,j)` returns a prefix of `s` with length `j`, and
|
||||
-- `string.sub(s, -i)` returns a suffix of `s` with length `i`.
|
||||
function string.sub(s, i , j) end
|
||||
|
||||
---
|
||||
-- Receives a string and returns a copy of this string with all lowercase
|
||||
-- letters changed to uppercase. All other characters are left unchanged. The
|
||||
-- definition of what a lowercase letter is depends on the current locale.
|
||||
function string.upper(s) end
|
||||
|
||||
---
|
||||
-- (5.3) Returns a binary string containing the values v1, v2, etc. packed (that is, serialized in binary form)
|
||||
--- according to the format string fmt (see 6.4.2).
|
||||
function string.pack (fmt, v1, v2, ...) end
|
||||
|
||||
---
|
||||
-- (5.3) Returns the size of a string resulting from string.pack with the given format.
|
||||
-- The format string cannot have the variable-length options 's' or 'z' (see 6.4.2).
|
||||
function string.packsize (fmt) end
|
||||
|
||||
---
|
||||
-- (5.3) Returns the values packed in string s (see string.pack) according to the format string fmt (see 6.4.2).
|
||||
-- An optional pos marks where to start reading in s (default is 1)
|
||||
-- After the read values, this function also returns the index of the first unread byte in s.
|
||||
function string.unpack (fmt, s , pos) end
|
||||
|
||||
return string
|
||||
|
52
Documentation/compiler/ldoc/ldoc/builtin/table.lua
Normal file
52
Documentation/compiler/ldoc/ldoc/builtin/table.lua
Normal file
|
@ -0,0 +1,52 @@
|
|||
--- manipulating Lua tables.
|
||||
-- @module table
|
||||
|
||||
local table = {}
|
||||
|
||||
---
|
||||
-- Given an array where all elements are strings or numbers, returns
|
||||
-- `table[i]..sep..table[i+1] ... sep..table[j]`. The default value for
|
||||
-- `sep` is the empty string, the default for `i` is 1, and the default for
|
||||
-- `j` is the length of the table. If `i` is greater than `j`, returns the
|
||||
-- empty string.
|
||||
function table.concat(table , sep , i , j) end
|
||||
|
||||
---
|
||||
-- Inserts element `value` at position `pos` in `table`, shifting up
|
||||
-- other elements to open space, if necessary. The default value for `pos` is
|
||||
-- `n+1`, where `n` is the length of the table (see §2.5.5), so that a call
|
||||
-- `table.insert(t,x)` inserts `x` at the end of table `t`.
|
||||
function table.insert(table, pos, value) end
|
||||
|
||||
---
|
||||
-- Removes from `table` the element at position `pos`, shifting down other
|
||||
-- elements to close the space, if necessary. Returns the value of the removed
|
||||
-- element. The default value for `pos` is `n`, where `n` is the length of the
|
||||
-- table, so that a call `table.remove(t)` removes the last element of table
|
||||
-- `t`.
|
||||
function table.remove(table , pos) end
|
||||
|
||||
---
|
||||
-- Returns a new table with all parameters stored into keys 1, 2, etc. and with a field "n" with
|
||||
-- the total number of parameters. Note that the resulting table may not be a sequence.
|
||||
function table.pack (...) end
|
||||
---
|
||||
-- Sorts table elements in a given order,
|
||||
-- *in-place*, from `table[1]` to `table[n]`, where `n` is the length of the
|
||||
-- table. If `comp` is given, then it must be a function that receives two
|
||||
-- table elements, and returns true when the first is less than the second
|
||||
-- (so that `not comp(a[i+1],a[i])` will be true after the sort). If `comp`
|
||||
-- is not given, then the '<' operator will be used.
|
||||
function table.sort(table , comp) end
|
||||
|
||||
-- luacheck: ignore 121
|
||||
|
||||
---
|
||||
-- Returns the elements from the given table. This function is equivalent to
|
||||
-- return list[i], list[i+1], ..., list[j]
|
||||
-- except that the above code can be written only for a fixed number of
|
||||
-- elements. By default, `i` is 1 and `j` is the length of the list, as
|
||||
-- defined by the length operator (see §2.5.5).
|
||||
function unpack(list , i , j) end
|
||||
|
||||
return table
|
48
Documentation/compiler/ldoc/ldoc/builtin/utf8.lua
Normal file
48
Documentation/compiler/ldoc/ldoc/builtin/utf8.lua
Normal file
|
@ -0,0 +1,48 @@
|
|||
--- This library provides basic support for UTF-8 encoding.
|
||||
-- @module utf8
|
||||
|
||||
local utf8 = {}
|
||||
|
||||
---
|
||||
-- Receives zero or more integers, converts each one to its corresponding UTF-8 byte sequence and returns
|
||||
-- a string with the concatenation of all these sequences.
|
||||
function utf8.char (...) end
|
||||
|
||||
---
|
||||
-- The pattern "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" , which matches exactly one
|
||||
-- UTF-8 byte sequence, assuming that the subject is a valid UTF-8 string.
|
||||
-- @field charpattern
|
||||
|
||||
---
|
||||
-- Iterate over all characters in string.
|
||||
--
|
||||
-- for p, c in utf8.codes(s) do body end
|
||||
--
|
||||
-- will iterate over all characters in string s, with p being the position (in bytes) and c the code point
|
||||
-- of each character. It raises an error if it meets any invalid byte sequence.
|
||||
function utf8.codes (s) end
|
||||
|
||||
---
|
||||
-- Returns the codepoints (as integers) from all characters in s that start between byte position i and j (both included).
|
||||
-- The default for i is 1 and for j is i. It raises an error if it meets any invalid byte sequence.
|
||||
function utf8.codepoint (s , i , j) end
|
||||
|
||||
---
|
||||
-- Returns the number of UTF-8 characters in string s that start between positions i and j (both inclusive).
|
||||
-- The default for i is 1 and for j is -1. If it finds any invalid byte sequence, returns a false value plus
|
||||
-- the position of the first invalid byte.
|
||||
function utf8.len (s , i , j) end
|
||||
|
||||
---
|
||||
-- Returns the position (in bytes) where the encoding of the n-th character of s (counting from position i) starts.
|
||||
-- A negative n gets characters before position i. The default for i is 1 when n is non-negative
|
||||
-- and #s + 1 otherwise, so that utf8.offset(s, -n) gets the offset of the n-th character from the end
|
||||
-- of the string.
|
||||
-- If the specified character is neither in the subject nor right after its end, the function returns nil.
|
||||
--
|
||||
-- As a special case, when n is 0 the function returns the start of the encoding of the character that contains the i-th byte of s.
|
||||
--
|
||||
-- This function assumes that s is a valid UTF-8 string.
|
||||
function utf8.offset (s, n , i) end
|
||||
|
||||
return utf8
|
11
Documentation/compiler/ldoc/ldoc/config.ld
Normal file
11
Documentation/compiler/ldoc/ldoc/config.ld
Normal file
|
@ -0,0 +1,11 @@
|
|||
project = 'Lua'
|
||||
description = 'Lua Standard Libraries'
|
||||
full_description = [[
|
||||
These are the built-in libraries of Lua 5.1
|
||||
|
||||
Plus documentation for lpeg and luafilesystem.
|
||||
]]
|
||||
file = {'builtin',exclude = {'builtin/globals.lua'}}
|
||||
no_summary = true
|
||||
no_return_or_parms = true
|
||||
format = 'discount'
|
1515
Documentation/compiler/ldoc/ldoc/doc.lua
Normal file
1515
Documentation/compiler/ldoc/ldoc/doc.lua
Normal file
File diff suppressed because it is too large
Load diff
402
Documentation/compiler/ldoc/ldoc/html.lua
Normal file
402
Documentation/compiler/ldoc/ldoc/html.lua
Normal file
|
@ -0,0 +1,402 @@
|
|||
------ generating HTML output ---------
|
||||
-- Although this can be generalized for outputting any format, since the template
|
||||
-- is language-agnostic, this implementation concentrates on HTML.
|
||||
-- This does the actual generation of HTML, and provides support functions in the ldoc
|
||||
-- table for the template
|
||||
--
|
||||
-- A fair amount of the complexity comes from operating in two basic modes; first, where
|
||||
-- there is a number of modules (classic LuaDoc) or otherwise, where there is only one
|
||||
-- module and the index contains the documentation for that module.
|
||||
--
|
||||
-- Like LuaDoc, LDoc puts similar kinds of documentation files in their own directories.
|
||||
-- So module docs go into 'modules/', scripts go into 'scripts/', and so forth. LDoc
|
||||
-- generalizes the idea of these project-level categories and in fact custom categories
|
||||
-- can be created (refered to as 'kinds' in the code)
|
||||
|
||||
local List = require 'pl.List'
|
||||
local utils = require 'pl.utils'
|
||||
local path = require 'pl.path'
|
||||
local stringx = require 'pl.stringx'
|
||||
local template = require 'pl.template'
|
||||
local tablex = require 'pl.tablex'
|
||||
local OrderedMap = require 'pl.OrderedMap'
|
||||
local tools = require 'ldoc.tools'
|
||||
local markup = require 'ldoc.markup'
|
||||
local prettify = require 'ldoc.prettify'
|
||||
local doc = require 'ldoc.doc'
|
||||
local unpack = utils.unpack
|
||||
local html = {}
|
||||
|
||||
|
||||
local quit = utils.quit
|
||||
|
||||
local function cleanup_whitespaces(text)
|
||||
local lines = stringx.splitlines(text)
|
||||
for i = 1, #lines do
|
||||
lines[i] = stringx.rstrip(lines[i])
|
||||
end
|
||||
lines[#lines + 1] = "" -- Little trick: file should end with newline
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
local function get_module_info(m)
|
||||
local info = OrderedMap()
|
||||
for tag in doc.module_info_tags() do
|
||||
local val = m.tags[tag]
|
||||
if type(val)=='table' then
|
||||
val = table.concat(val,',')
|
||||
end
|
||||
tag = stringx.title(tag)
|
||||
info:set(tag,val)
|
||||
end
|
||||
if #info:keys() > 0 then
|
||||
return info
|
||||
end
|
||||
end
|
||||
|
||||
local escape_table = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" }
|
||||
|
||||
function html.generate_output(ldoc, args, project)
|
||||
local check_directory, check_file, writefile = tools.check_directory, tools.check_file, tools.writefile
|
||||
local original_ldoc
|
||||
|
||||
local function save_and_set_ldoc (set)
|
||||
if not set then return end
|
||||
if not original_ldoc then
|
||||
original_ldoc = tablex.copy(ldoc)
|
||||
end
|
||||
for s in set:iter() do
|
||||
local var,val = s:match('([^=]+)=(.+)')
|
||||
local num = tonumber(val)
|
||||
if num then val = num
|
||||
elseif val == 'true' then val = true
|
||||
elseif val == 'false' then val = false
|
||||
end
|
||||
print('setting',var,val)
|
||||
ldoc[var] = val
|
||||
end
|
||||
end
|
||||
|
||||
local function restore_ldoc ()
|
||||
if original_ldoc then
|
||||
ldoc = original_ldoc
|
||||
end
|
||||
end
|
||||
|
||||
function ldoc.escape(str)
|
||||
return (str:gsub("['&<>\"]", escape_table))
|
||||
end
|
||||
|
||||
function ldoc.prettify(str)
|
||||
return prettify.code('lua','usage',str,0,false)
|
||||
end
|
||||
|
||||
-- Item descriptions come from combining the summary and description fields
|
||||
function ldoc.descript(item)
|
||||
return tools.join(' ', item.summary, item.description)
|
||||
end
|
||||
|
||||
function ldoc.module_name (mod)
|
||||
local name = mod.name
|
||||
if args.unqualified and (mod.type == 'module' or mod.type == 'classmod') then -- leave out package
|
||||
name = name:gsub('^.-%.','')
|
||||
elseif mod.type == 'topic' then
|
||||
if mod.display_name then
|
||||
name = mod.display_name
|
||||
else -- leave out md extension
|
||||
name = name:gsub('%..*$','')
|
||||
end
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
-- this generates the internal module/function references
|
||||
function ldoc.href(see)
|
||||
if see.href then -- explict reference, e.g. to Lua manual
|
||||
return see.href
|
||||
elseif doc.Module:class_of(see) then
|
||||
return ldoc.ref_to_module(see)
|
||||
else
|
||||
return ldoc.ref_to_module(see.mod)..'#'..see.name
|
||||
end
|
||||
end
|
||||
|
||||
-- this is either called from the 'root' (index or single module) or
|
||||
-- from the 'modules' etc directories. If we are in one of those directories,
|
||||
-- then linking to another kind is `../kind/name`; to the same kind is just `name`.
|
||||
-- If we are in the root, then it is `kind/name`.
|
||||
function ldoc.ref_to_module (mod)
|
||||
local base = "" -- default: same directory
|
||||
mod = mod or ldoc.module
|
||||
local kind, module = mod.kind, ldoc.module
|
||||
local name = mod.name -- default: name of module
|
||||
if not ldoc.single then
|
||||
if module then -- we are in kind/
|
||||
if module.type ~= type then -- cross ref to ../kind/
|
||||
base = "../"..kind.."/"
|
||||
end
|
||||
else -- we are in root: index
|
||||
base = kind..'/'
|
||||
end
|
||||
else -- single module
|
||||
if mod == ldoc.single then
|
||||
name = ldoc.output
|
||||
if not ldoc.root then base = '../' end
|
||||
elseif ldoc.root then -- ref to other kinds (like examples)
|
||||
base = kind..'/'
|
||||
else
|
||||
if module.type ~= type then -- cross ref to ../kind/
|
||||
base = "../"..kind.."/"
|
||||
end
|
||||
end
|
||||
end
|
||||
return base..name..'.html'
|
||||
end
|
||||
|
||||
function ldoc.include_file (file)
|
||||
local text,_ = utils.readfile(file)
|
||||
if not text then quit("unable to include "..file)
|
||||
else
|
||||
return text
|
||||
end
|
||||
end
|
||||
|
||||
-- these references are never from the index...?
|
||||
function ldoc.source_ref (fun)
|
||||
local modname = fun.module.name
|
||||
local pack,name = tools.split_dotted_name(modname)
|
||||
if not pack then
|
||||
name = modname
|
||||
end
|
||||
return (ldoc.single and "" or "../").."source/"..name..'.lua.html#'..fun.lineno
|
||||
end
|
||||
|
||||
function ldoc.use_li(ls)
|
||||
if #ls > 1 then return '<li>','</li>' else return '','' end
|
||||
end
|
||||
|
||||
function ldoc.default_display_name(item)
|
||||
-- Project-level items:
|
||||
if doc.project_level(item.type) then
|
||||
return ldoc.module_name(item)
|
||||
end
|
||||
-- Module-level items:
|
||||
local name = item.display_name or item.name
|
||||
if item.type == 'function' or item.type == 'lfunction' then
|
||||
if not ldoc.no_space_before_args then
|
||||
name = name..' '
|
||||
end
|
||||
return name..item.args
|
||||
else
|
||||
return name
|
||||
end
|
||||
end
|
||||
|
||||
function ldoc.display_name(item)
|
||||
if ldoc.custom_display_name_handler then
|
||||
return ldoc.custom_display_name_handler(item, ldoc.default_display_name)
|
||||
else
|
||||
return ldoc.default_display_name(item)
|
||||
end
|
||||
end
|
||||
|
||||
function ldoc.no_spaces(s)
|
||||
s = s:gsub('%s*$','')
|
||||
return (s:gsub('%W','_'))
|
||||
end
|
||||
|
||||
function ldoc.module_typename(m)
|
||||
return doc.presentation_name(m.type)
|
||||
end
|
||||
|
||||
function ldoc.is_list (t)
|
||||
return type(t) == 'table' and t.append
|
||||
end
|
||||
|
||||
function ldoc.strip_header (s)
|
||||
if not s then return s end
|
||||
return s:gsub('^%s*#+%s+','')
|
||||
end
|
||||
|
||||
function ldoc.typename (tp)
|
||||
if not tp or tp == '' or tp:match '^@' then return '' end
|
||||
local optional
|
||||
-- ?<type> is short for ?nil|<type>
|
||||
if tp:match("^%?") and not tp:match '|' then
|
||||
tp = '?|'..tp:sub(2)
|
||||
end
|
||||
local tp2 = tp:match("%?|?(.*)")
|
||||
if tp2 then
|
||||
optional = true
|
||||
tp = tp2
|
||||
end
|
||||
|
||||
local types = {}
|
||||
for name in tp:gmatch("[^|]+") do
|
||||
local sym = name:match '([%w%.%:]+)'
|
||||
local ref,_ = markup.process_reference(sym,true)
|
||||
if ref then
|
||||
if ref.label and sym == name then
|
||||
name = ref.label
|
||||
end
|
||||
local shortName = name:match("%.(%w*)$")
|
||||
if shortName then
|
||||
name = shortName
|
||||
end
|
||||
types[#types+1] = ('<a class="type" href="%s">%s</a>'):format(ldoc.href(ref),name)
|
||||
else
|
||||
types[#types+1] = '<span class="type">'..name..'</span>'
|
||||
end
|
||||
end
|
||||
local names = table.concat(types, ", ", 1, math.max(#types-1, 1))
|
||||
if #types > 1 then names = names.." or "..types[#types] end
|
||||
if optional then
|
||||
if names ~= '' then
|
||||
if #types == 1 then names = "optional "..names end
|
||||
else
|
||||
names = "optional"
|
||||
end
|
||||
end
|
||||
return names
|
||||
end
|
||||
|
||||
-- the somewhat tangled logic that controls whether a type appears in the
|
||||
-- navigation sidebar. (At least it's no longer in the template ;))
|
||||
function ldoc.allowed_in_contents(type,module)
|
||||
local allowed = true
|
||||
if ldoc.kinds_allowed then
|
||||
allowed = ldoc.kinds_allowed[type]
|
||||
elseif ldoc.prettify_files and type == 'file' then
|
||||
allowed = ldoc.prettify_files == 'show' or (module and module.type == 'file')
|
||||
end
|
||||
return allowed
|
||||
end
|
||||
|
||||
local function set_charset (ldoc,m)
|
||||
m = m or ldoc.module
|
||||
ldoc.doc_charset = (m and m.tags.charset) or ldoc.charset
|
||||
end
|
||||
|
||||
local module_template,_ = utils.readfile (path.join(args.template,ldoc.templ))
|
||||
if not module_template then
|
||||
quit("template not found at '"..args.template.."' Use -l to specify directory containing ldoc.ltp")
|
||||
end
|
||||
|
||||
-- Runs a template on a module to generate HTML page.
|
||||
local function templatize(template_str, ldoc, module)
|
||||
local out, err = template.substitute(template_str, {
|
||||
ldoc = ldoc,
|
||||
module = module,
|
||||
_escape = ldoc.template_escape
|
||||
})
|
||||
if not out then
|
||||
quit(("template failed for %s: %s"):format(
|
||||
module and module.name or ldoc.output or "index",
|
||||
err))
|
||||
end
|
||||
if ldoc.postprocess_html then
|
||||
out = ldoc.postprocess_html(out, module)
|
||||
end
|
||||
return cleanup_whitespaces(out)
|
||||
end
|
||||
|
||||
local css, custom_css = ldoc.css, ldoc.custom_css
|
||||
ldoc.output = args.output
|
||||
ldoc.ipairs = ipairs
|
||||
ldoc.pairs = pairs
|
||||
ldoc.print = print
|
||||
|
||||
-- Bang out the index.
|
||||
-- in single mode there is one module and the 'index' is the
|
||||
-- documentation for that module.
|
||||
ldoc.module = ldoc.single
|
||||
if ldoc.single and args.one then
|
||||
ldoc.kinds_allowed = {module = true, topic = true}
|
||||
ldoc.one = true
|
||||
end
|
||||
ldoc.root = true
|
||||
if ldoc.module then
|
||||
ldoc.module.info = get_module_info(ldoc.module)
|
||||
ldoc.module.ldoc = ldoc
|
||||
save_and_set_ldoc(ldoc.module.tags.set)
|
||||
end
|
||||
set_charset(ldoc)
|
||||
local out = templatize(module_template, ldoc, ldoc.module)
|
||||
ldoc.root = false
|
||||
restore_ldoc()
|
||||
|
||||
check_directory(args.dir) -- make sure output directory is ok
|
||||
|
||||
-- project icon
|
||||
if ldoc.icon then
|
||||
local dir_data = args.dir .. '/data'
|
||||
if not path.isdir(dir_data) then
|
||||
-- luacheck: push ignore lfs
|
||||
lfs.mkdir(dir_data)
|
||||
-- luacheck: pop
|
||||
end
|
||||
local file = require 'pl.file'
|
||||
file.copy(ldoc.icon, dir_data)
|
||||
end
|
||||
|
||||
args.dir = args.dir .. path.sep
|
||||
|
||||
if css then -- has CSS been copied?
|
||||
check_file(args.dir..css, path.join(args.style,css))
|
||||
end
|
||||
|
||||
if custom_css then -- has custom CSS been copied?
|
||||
check_file(args.dir..custom_css, custom_css)
|
||||
end
|
||||
|
||||
-- write out the module index
|
||||
out = cleanup_whitespaces(out)
|
||||
writefile(args.dir..args.output..args.ext,out)
|
||||
|
||||
-- in single mode, we exclude any modules since the module has been done;
|
||||
-- ext step is then only for putting out any examples or topics
|
||||
local mods = List()
|
||||
for kind, modules in project() do
|
||||
local lkind = kind:lower()
|
||||
if not ldoc.single or ldoc.single and lkind ~= 'modules' then
|
||||
mods:append {kind, lkind, modules}
|
||||
end
|
||||
end
|
||||
|
||||
-- write out the per-module documentation
|
||||
-- note that we reset the internal ordering of the 'kinds' so that
|
||||
-- e.g. when reading a topic the other Topics will be listed first.
|
||||
if css then
|
||||
ldoc.css = '../'..css
|
||||
end
|
||||
if custom_css then
|
||||
ldoc.custom_css = '../'..custom_css
|
||||
end
|
||||
for m in mods:iter() do
|
||||
local kind, lkind, modules = unpack(m)
|
||||
check_directory(args.dir..lkind)
|
||||
if not ldoc.keep_menu_order then
|
||||
project:put_kind_first(kind)
|
||||
end
|
||||
for m in modules() do
|
||||
ldoc.module = m
|
||||
ldoc.body = m.body
|
||||
m.ldoc = ldoc
|
||||
if m.tags.set then
|
||||
save_and_set_ldoc(m.tags.set)
|
||||
end
|
||||
set_charset(ldoc)
|
||||
m.info = get_module_info(m)
|
||||
if ldoc.body and m.postprocess then
|
||||
ldoc.body = m.postprocess(ldoc.body)
|
||||
end
|
||||
local out = templatize(module_template, ldoc, m)
|
||||
writefile(args.dir..lkind..'/'..m.name..args.ext,out)
|
||||
restore_ldoc()
|
||||
end
|
||||
end
|
||||
if not args.quiet then print('output written to '..tools.abspath(args.dir)) end
|
||||
end
|
||||
|
||||
return html
|
||||
|
19
Documentation/compiler/ldoc/ldoc/html/_code_css.lua
Normal file
19
Documentation/compiler/ldoc/ldoc/html/_code_css.lua
Normal file
|
@ -0,0 +1,19 @@
|
|||
return [[
|
||||
|
||||
/* styles for prettification of source */
|
||||
pre .comment { color: #558817; }
|
||||
pre .constant { color: #a8660d; }
|
||||
pre .escape { color: #844631; }
|
||||
pre .keyword { color: #aa5050; font-weight: bold; }
|
||||
pre .library { color: #0e7c6b; }
|
||||
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
|
||||
pre .string { color: #8080ff; }
|
||||
pre .number { color: #f8660d; }
|
||||
pre .operator { color: #2239a8; font-weight: bold; }
|
||||
pre .preprocessor, pre .prepro { color: #a33243; }
|
||||
pre .global { color: #800080; }
|
||||
pre .user-keyword { color: #800080; }
|
||||
pre .prompt { color: #558817; }
|
||||
pre .url { color: #272fc2; text-decoration: underline; }
|
||||
|
||||
]]
|
66
Documentation/compiler/ldoc/ldoc/html/_reset_css.lua
Normal file
66
Documentation/compiler/ldoc/ldoc/html/_reset_css.lua
Normal file
|
@ -0,0 +1,66 @@
|
|||
return [[
|
||||
/* BEGIN RESET
|
||||
|
||||
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
|
||||
Code licensed under the BSD License:
|
||||
http://developer.yahoo.com/yui/license.html
|
||||
version: 2.8.2r1
|
||||
*/
|
||||
html {
|
||||
color: #000;
|
||||
background: #FFF;
|
||||
}
|
||||
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
fieldset,img {
|
||||
border: 0;
|
||||
}
|
||||
address,caption,cite,code,dfn,em,strong,th,var,optgroup {
|
||||
font-style: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
del,ins {
|
||||
text-decoration: none;
|
||||
}
|
||||
li {
|
||||
margin-left: 20px;
|
||||
}
|
||||
caption,th {
|
||||
text-align: left;
|
||||
}
|
||||
h1,h2,h3,h4,h5,h6 {
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
}
|
||||
q:before,q:after {
|
||||
content: '';
|
||||
}
|
||||
abbr,acronym {
|
||||
border: 0;
|
||||
font-variant: normal;
|
||||
}
|
||||
sup {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
sub {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
legend {
|
||||
color: #000;
|
||||
}
|
||||
input,button,textarea,select,optgroup,option {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-style: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
input,button,textarea,select {*font-size:100%;
|
||||
}
|
||||
/* END RESET */
|
||||
]]
|
225
Documentation/compiler/ldoc/ldoc/html/ldoc_css.lua
Normal file
225
Documentation/compiler/ldoc/ldoc/html/ldoc_css.lua
Normal file
|
@ -0,0 +1,225 @@
|
|||
return require('ldoc.html._reset_css') .. [[
|
||||
|
||||
body {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
font-family: arial, helvetica, geneva, sans-serif;
|
||||
background-color: #ffffff; margin: 0px;
|
||||
}
|
||||
|
||||
code, tt { font-family: monospace; font-size: 1.1em; }
|
||||
span.parameter { font-family:monospace; }
|
||||
span.parameter:after { content:":"; }
|
||||
span.types:before { content:"("; }
|
||||
span.types:after { content:")"; }
|
||||
.type { font-weight: bold; font-style:italic }
|
||||
|
||||
body, p, td, th { font-size: .95em; line-height: 1.2em;}
|
||||
|
||||
p, ul { margin: 10px 0 0 0px;}
|
||||
|
||||
strong { font-weight: bold;}
|
||||
|
||||
em { font-style: italic;}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 20px 0 20px 0;
|
||||
}
|
||||
h2, h3, h4 { margin: 15px 0 10px 0; }
|
||||
h2 { font-size: 1.25em; }
|
||||
h3 { font-size: 1.15em; }
|
||||
h4 { font-size: 1.06em; }
|
||||
|
||||
a:link { font-weight: bold; color: #004080; text-decoration: none; }
|
||||
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
|
||||
a:link:hover { text-decoration: underline; }
|
||||
|
||||
hr {
|
||||
color:#cccccc;
|
||||
background: #00007f;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
blockquote { margin-left: 3em; }
|
||||
|
||||
ul { list-style-type: disc; }
|
||||
|
||||
p.name {
|
||||
font-family: "Andale Mono", monospace;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: rgb(245, 245, 245);
|
||||
border: 1px solid #C0C0C0; /* silver */
|
||||
padding: 10px;
|
||||
margin: 10px 0 10px 0;
|
||||
overflow: auto;
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
pre.example {
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
table.index { border: 1px #00007f; }
|
||||
table.index td { text-align: left; vertical-align: top; }
|
||||
|
||||
#container {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#product {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product big {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#main {
|
||||
background-color: #f0f0f0;
|
||||
border-left: 2px solid #cccccc;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
float: left;
|
||||
width: 14em;
|
||||
vertical-align: top;
|
||||
background-color: #f0f0f0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#navigation h2 {
|
||||
background-color:#e7e7e7;
|
||||
font-size:1.1em;
|
||||
color:#000000;
|
||||
text-align: left;
|
||||
padding:0.2em;
|
||||
border-top:1px solid #dddddd;
|
||||
border-bottom:1px solid #dddddd;
|
||||
}
|
||||
|
||||
#navigation ul
|
||||
{
|
||||
font-size:1em;
|
||||
list-style-type: none;
|
||||
margin: 1px 1px 10px 1px;
|
||||
}
|
||||
|
||||
#navigation li {
|
||||
text-indent: -1em;
|
||||
display: block;
|
||||
margin: 3px 0px 0px 22px;
|
||||
}
|
||||
|
||||
#navigation li li a {
|
||||
margin: 0px 3px 0px -1em;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 14em;
|
||||
padding: 1em;
|
||||
width: 700px;
|
||||
border-left: 2px solid #cccccc;
|
||||
border-right: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#about {
|
||||
clear: both;
|
||||
padding: 5px;
|
||||
border-top: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font: 12pt "Times New Roman", "TimeNR", Times, serif;
|
||||
}
|
||||
a { font-weight: bold; color: #004080; text-decoration: underline; }
|
||||
|
||||
#main {
|
||||
background-color: #ffffff;
|
||||
border-left: 0px;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-left: 2%;
|
||||
margin-right: 2%;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
display: none;
|
||||
}
|
||||
pre.example {
|
||||
font-family: "Andale Mono", monospace;
|
||||
font-size: 10pt;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
table.module_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.module_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.module_list td.name { background-color: #f0f0f0; min-width: 200px; }
|
||||
table.module_list td.summary { width: 100%; }
|
||||
|
||||
|
||||
table.function_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.function_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.function_list td.name { background-color: #f0f0f0; min-width: 200px; }
|
||||
table.function_list td.summary { width: 100%; }
|
||||
|
||||
ul.nowrap {
|
||||
overflow:auto;
|
||||
white-space:nowrap;
|
||||
}
|
||||
|
||||
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
|
||||
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
|
||||
dl.table h3, dl.function h3 {font-size: .95em;}
|
||||
|
||||
/* stop sublists from having initial vertical space */
|
||||
ul ul { margin-top: 0px; }
|
||||
ol ul { margin-top: 0px; }
|
||||
ol ol { margin-top: 0px; }
|
||||
ul ol { margin-top: 0px; }
|
||||
|
||||
/* make the target distinct; helps when we're navigating to a function */
|
||||
a:target + * {
|
||||
background-color: #FF9;
|
||||
}
|
||||
|
||||
]]
|
||||
.. require('ldoc.html._code_css')
|
233
Documentation/compiler/ldoc/ldoc/html/ldoc_fixed_css.lua
Normal file
233
Documentation/compiler/ldoc/ldoc/html/ldoc_fixed_css.lua
Normal file
|
@ -0,0 +1,233 @@
|
|||
return require('ldoc.html._reset_css') .. [[
|
||||
|
||||
body {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
font-family: arial, helvetica, geneva, sans-serif;
|
||||
background-color: #ffffff; margin: 0px;
|
||||
}
|
||||
|
||||
code, tt { font-family: monospace; font-size: 1.1em; }
|
||||
span.parameter { font-family:monospace; }
|
||||
span.parameter:after { content:":"; }
|
||||
span.types:before { content:"("; }
|
||||
span.types:after { content:")"; }
|
||||
.type { font-weight: bold; font-style:italic }
|
||||
|
||||
body, p, td, th { font-size: .95em; line-height: 1.2em;}
|
||||
|
||||
p, ul { margin: 10px 0 0 0px;}
|
||||
|
||||
strong { font-weight: bold;}
|
||||
|
||||
em { font-style: italic;}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
h2, h3, h4 { margin: 15px 0 10px 0; }
|
||||
h2 { font-size: 1.25em; }
|
||||
h3 { font-size: 1.15em; }
|
||||
h4 { font-size: 1.06em; }
|
||||
|
||||
a:link { font-weight: bold; color: #004080; text-decoration: none; }
|
||||
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
|
||||
a:link:hover { text-decoration: underline; }
|
||||
|
||||
hr {
|
||||
color:#cccccc;
|
||||
background: #00007f;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
blockquote { margin-left: 3em; }
|
||||
|
||||
ul { list-style-type: disc; }
|
||||
|
||||
p.name {
|
||||
font-family: "Andale Mono", monospace;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: rgb(245, 245, 245);
|
||||
border: 1px solid #C0C0C0; /* silver */
|
||||
padding: 10px;
|
||||
margin: 10px 0 10px 0;
|
||||
overflow: auto;
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
pre.example {
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
table.index { border: 1px #00007f; }
|
||||
table.index td { text-align: left; vertical-align: top; }
|
||||
|
||||
#container {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product big {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#main {
|
||||
background-color:#FFFFFF; // #f0f0f0;
|
||||
border-left: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
float: left;
|
||||
width: 14em;
|
||||
vertical-align: top;
|
||||
background-color:#FFFFFF; // #f0f0f0;
|
||||
border-right: 2px solid #cccccc;
|
||||
overflow: visible;
|
||||
overflow-y: scroll;
|
||||
height: 100%;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
#navigation h2 {
|
||||
background-color:#FFFFFF;//:#e7e7e7;
|
||||
font-size:1.1em;
|
||||
color:#000000;
|
||||
text-align: left;
|
||||
padding:0.2em;
|
||||
border-bottom:1px solid #dddddd;
|
||||
}
|
||||
|
||||
#navigation ul
|
||||
{
|
||||
font-size:1em;
|
||||
list-style-type: none;
|
||||
margin: 1px 1px 10px 1px;
|
||||
}
|
||||
|
||||
#navigation li {
|
||||
text-indent: -1em;
|
||||
display: block;
|
||||
margin: 3px 0px 0px 22px;
|
||||
}
|
||||
|
||||
#navigation li li a {
|
||||
margin: 0px 3px 0px -1em;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 14em;
|
||||
padding: 1em;
|
||||
padding-left: 2em;
|
||||
width: 700px;
|
||||
border-left: 2px solid #cccccc;
|
||||
// border-right: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#about {
|
||||
clear: both;
|
||||
padding-left: 1em;
|
||||
margin-left: 14em; // avoid the damn sidebar!
|
||||
border-top: 2px solid #cccccc;
|
||||
border-left: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font: 12pt "Times New Roman", "TimeNR", Times, serif;
|
||||
}
|
||||
a { font-weight: bold; color: #004080; text-decoration: underline; }
|
||||
|
||||
#main {
|
||||
background-color: #ffffff;
|
||||
border-left: 0px;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-left: 2%;
|
||||
margin-right: 2%;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
display: none;
|
||||
}
|
||||
pre.example {
|
||||
font-family: "Andale Mono", monospace;
|
||||
font-size: 10pt;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
table.module_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.module_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; }
|
||||
table.module_list td.summary { width: 100%; }
|
||||
|
||||
table.function_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.function_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; }
|
||||
table.function_list td.summary { width: 100%; }
|
||||
|
||||
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
|
||||
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
|
||||
dl.table h3, dl.function h3 {font-size: .95em;}
|
||||
|
||||
ul.nowrap {
|
||||
overflow:auto;
|
||||
whitespace:nowrap;
|
||||
}
|
||||
|
||||
/* stop sublists from having initial vertical space */
|
||||
ul ul { margin-top: 0px; }
|
||||
ol ul { margin-top: 0px; }
|
||||
ol ol { margin-top: 0px; }
|
||||
ul ol { margin-top: 0px; }
|
||||
|
||||
/* make the target distinct; helps when we're navigating to a function */
|
||||
a:target + * {
|
||||
background-color: #FF9;
|
||||
}
|
||||
|
||||
]]
|
||||
.. require('ldoc.html._code_css')
|
324
Documentation/compiler/ldoc/ldoc/html/ldoc_ltp.lua
Normal file
324
Documentation/compiler/ldoc/ldoc/html/ldoc_ltp.lua
Normal file
|
@ -0,0 +1,324 @@
|
|||
return [==[
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=$(ldoc.doc_charset)"/>
|
||||
<head>
|
||||
<title>$(ldoc.title)</title>
|
||||
<link rel="stylesheet" href="$(ldoc.css)" type="text/css" />
|
||||
# if ldoc.custom_css then -- add custom CSS file if configured.
|
||||
<link rel="stylesheet" href="$(ldoc.custom_css)" type="text/css" />
|
||||
# end
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
# local no_spaces = ldoc.no_spaces
|
||||
# local use_li = ldoc.use_li
|
||||
# local display_name = ldoc.display_name
|
||||
# local iter = ldoc.modules.iter
|
||||
# local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end
|
||||
# local nowrap = ldoc.wrap and '' or 'nowrap'
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>$(ldoc.project)</h1>
|
||||
|
||||
# if ldoc.icon then
|
||||
# if module then
|
||||
<img src="../data/$(ldoc.icon)" />
|
||||
# else
|
||||
<img src="data/$(ldoc.icon)" />
|
||||
# end
|
||||
# end
|
||||
|
||||
# if not ldoc.single and module then -- reference back to project index
|
||||
<ul>
|
||||
<li><a href="../$(ldoc.output).html">Index</a></li>
|
||||
</ul>
|
||||
# end
|
||||
|
||||
# --------- contents of module -------------
|
||||
# if module and not ldoc.no_summary and #module.items > 0 then
|
||||
<h2>Contents</h2>
|
||||
<ul>
|
||||
# for kind,items in module.kinds() do
|
||||
<li><a href="#$(no_spaces(kind))">$(kind)</a></li>
|
||||
# end
|
||||
</ul>
|
||||
# end
|
||||
|
||||
|
||||
# if ldoc.no_summary and module and not ldoc.one then -- bang out the functions on the side
|
||||
# for kind, items in module.kinds() do
|
||||
<h2>$(kind)</h2>
|
||||
<ul class="nowrap">
|
||||
# for item in items() do
|
||||
<li><a href="#$(item.name)">$(display_name(item))</a></li>
|
||||
# end
|
||||
</ul>
|
||||
# end
|
||||
# end
|
||||
# -------- contents of project ----------
|
||||
# local this_mod = module and module.name
|
||||
# for kind, mods, type in ldoc.kinds() do
|
||||
# if ldoc.allowed_in_contents(type,module) then
|
||||
<h2>$(kind)</h2>
|
||||
<ul class="$(kind=='Topics' and '' or 'nowrap')">
|
||||
# for mod in mods() do local name = display_name(mod)
|
||||
# if mod.name == this_mod then
|
||||
<li><strong>$(name)</strong></li>
|
||||
# else
|
||||
<li><a href="$(ldoc.ref_to_module(mod))">$(name)</a></li>
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
</ul>
|
||||
# end
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
# if ldoc.body then -- verbatim HTML as contents; 'non-code' entries
|
||||
$(ldoc.body)
|
||||
# elseif module then -- module documentation
|
||||
<h1>$(ldoc.module_typename(module)) <code>$(module.name)</code></h1>
|
||||
<p>$(M(module.summary,module))</p>
|
||||
<p>$(M(module.description,module))</p>
|
||||
# if module.tags.include then
|
||||
$(M(ldoc.include_file(module.tags.include)))
|
||||
# end
|
||||
# if module.see then
|
||||
# local li,il = use_li(module.see)
|
||||
<h3>See also:</h3>
|
||||
<ul>
|
||||
# for see in iter(module.see) do
|
||||
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if see
|
||||
# if module.usage then
|
||||
# local li,il = use_li(module.usage)
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
# for usage in iter(module.usage) do
|
||||
$(li)<pre class="example">$(ldoc.escape(usage))</pre>$(il)
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if usage
|
||||
# if module.info then
|
||||
<h3>Info:</h3>
|
||||
<ul>
|
||||
# for tag, value in module.info:iter() do
|
||||
<li><strong>$(tag)</strong>: $(M(value,module))</li>
|
||||
# end
|
||||
</ul>
|
||||
# end -- if module.info
|
||||
|
||||
|
||||
# if not ldoc.no_summary then
|
||||
# -- bang out the tables of item types for this module (e.g Functions, Tables, etc)
|
||||
# for kind,items in module.kinds() do
|
||||
<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2>
|
||||
<table class="function_list">
|
||||
# for item in items() do
|
||||
<tr>
|
||||
<td class="name" $(nowrap)><a href="#$(item.name)">$(display_name(item))</a></td>
|
||||
<td class="summary">$(M(item.summary,item))</td>
|
||||
</tr>
|
||||
# end -- for items
|
||||
</table>
|
||||
#end -- for kinds
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
#end -- if not no_summary
|
||||
|
||||
# --- currently works for both Functions and Tables. The params field either contains
|
||||
# --- function parameters or table fields.
|
||||
# local show_return = not ldoc.no_return_or_parms
|
||||
# local show_parms = show_return
|
||||
# for kind, items in module.kinds() do
|
||||
# local kitem = module.kinds:get_item(kind)
|
||||
# local has_description = kitem and ldoc.descript(kitem) ~= ""
|
||||
<h2 class="section-header $(has_description and 'has-description')"><a name="$(no_spaces(kind))"></a>$(kind)</h2>
|
||||
$(M(module.kinds:get_section_description(kind),nil))
|
||||
# if kitem then
|
||||
# if has_description then
|
||||
<div class="section-description">
|
||||
$(M(ldoc.descript(kitem),kitem))
|
||||
</div>
|
||||
# end
|
||||
# if kitem.usage then
|
||||
<h3>Usage:</h3>
|
||||
<pre class="example">$(ldoc.prettify(kitem.usage[1]))</pre>
|
||||
# end
|
||||
# end
|
||||
<dl class="function">
|
||||
# for item in items() do
|
||||
<dt>
|
||||
<a name = "$(item.name)"></a>
|
||||
<strong>$(display_name(item))</strong>
|
||||
# if ldoc.prettify_files and ldoc.is_file_prettified[item.module.file.filename] then
|
||||
<a style="float:right;" href="$(ldoc.source_ref(item))">line $(item.lineno)</a>
|
||||
# end
|
||||
</dt>
|
||||
<dd>
|
||||
$(M(ldoc.descript(item),item))
|
||||
|
||||
# if ldoc.custom_tags then
|
||||
# for custom in iter(ldoc.custom_tags) do
|
||||
# local tag = item.tags[custom[1]]
|
||||
# if tag and not custom.hidden then
|
||||
# local li,il = use_li(tag)
|
||||
<h3>$(custom.title or custom[1]):</h3>
|
||||
<ul>
|
||||
# for value in iter(tag) do
|
||||
$(li)$(custom.format and custom.format(value) or M(value))$(il)
|
||||
# end -- for
|
||||
# end -- if tag
|
||||
</ul>
|
||||
# end -- iter tags
|
||||
# end
|
||||
|
||||
# if show_parms and item.params and #item.params > 0 then
|
||||
# local subnames = module.kinds:type_of(item).subnames
|
||||
# if subnames then
|
||||
<h3>$(subnames):</h3>
|
||||
# end
|
||||
<ul>
|
||||
# for parm in iter(item.params) do
|
||||
# local param,sublist = item:subparam(parm)
|
||||
# if sublist then
|
||||
<li><span class="parameter">$(sublist)</span>$(M(item.params.map[sublist],item))
|
||||
<ul>
|
||||
# end
|
||||
# for p in iter(param) do
|
||||
# local name,tp,def = item:display_name_of(p), ldoc.typename(item:type_of_param(p)), item:default_of_param(p)
|
||||
<li><span class="parameter">$(name)</span>
|
||||
# if tp ~= '' then
|
||||
<span class="types">$(tp)</span>
|
||||
# end
|
||||
$(M(item.params.map[p],item))
|
||||
# if def == true then
|
||||
(<em>optional</em>)
|
||||
# elseif def then
|
||||
(<em>default</em> $(def))
|
||||
# end
|
||||
# if item:readonly(p) then
|
||||
<em>readonly</em>
|
||||
# end
|
||||
</li>
|
||||
# end
|
||||
# if sublist then
|
||||
</li></ul>
|
||||
# end
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if params
|
||||
|
||||
# if show_return and item.retgroups then local groups = item.retgroups
|
||||
<h3>Returns:</h3>
|
||||
# for i,group in ldoc.ipairs(groups) do local li,il = use_li(group)
|
||||
<ol>
|
||||
# for r in group:iter() do local type, ctypes = item:return_type(r); local rt = ldoc.typename(type)
|
||||
$(li)
|
||||
# if rt ~= '' then
|
||||
<span class="types">$(rt)</span>
|
||||
# end
|
||||
$(M(r.text,item))$(il)
|
||||
# if ctypes then
|
||||
<ul>
|
||||
# for c in ctypes:iter() do
|
||||
<li><span class="parameter">$(c.name)</span>
|
||||
<span class="types">$(ldoc.typename(c.type))</span>
|
||||
$(M(c.comment,item))</li>
|
||||
# end
|
||||
</ul>
|
||||
# end -- if ctypes
|
||||
# end -- for r
|
||||
</ol>
|
||||
# if i < #groups then
|
||||
<h3>Or</h3>
|
||||
# end
|
||||
# end -- for group
|
||||
# end -- if returns
|
||||
|
||||
# if show_return and item.raise then
|
||||
<h3>Raises:</h3>
|
||||
$(M(item.raise,item))
|
||||
# end
|
||||
|
||||
# if item.see then
|
||||
# local li,il = use_li(item.see)
|
||||
<h3>See also:</h3>
|
||||
<ul>
|
||||
# for see in iter(item.see) do
|
||||
$(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if see
|
||||
|
||||
# if item.usage then
|
||||
# local li,il = use_li(item.usage)
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
# for usage in iter(item.usage) do
|
||||
$(li)<pre class="example">$(ldoc.prettify(usage))</pre>$(il)
|
||||
# end -- for
|
||||
</ul>
|
||||
# end -- if usage
|
||||
|
||||
</dd>
|
||||
# end -- for items
|
||||
</dl>
|
||||
# end -- for kinds
|
||||
|
||||
# else -- if module; project-level contents
|
||||
|
||||
# if ldoc.description then
|
||||
<h2>$(M(ldoc.description,nil))</h2>
|
||||
# end
|
||||
# if ldoc.full_description then
|
||||
<p>$(M(ldoc.full_description,nil))</p>
|
||||
# end
|
||||
|
||||
# for kind, mods in ldoc.kinds() do
|
||||
<h2>$(kind)</h2>
|
||||
# kind = kind:lower()
|
||||
<table class="module_list">
|
||||
# for m in mods() do
|
||||
<tr>
|
||||
<td class="name" $(nowrap)><a href="$(no_spaces(kind))/$(m.name).html">$(m.name)</a></td>
|
||||
<td class="summary">$(M(ldoc.strip_header(m.summary),m))</td>
|
||||
</tr>
|
||||
# end -- for modules
|
||||
</table>
|
||||
# end -- for kinds
|
||||
# end -- if module
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc $(ldoc.version)</a></i>
|
||||
<i style="float:right;">Last updated $(ldoc.updatetime) </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
||||
]==]
|
||||
|
17
Documentation/compiler/ldoc/ldoc/html/ldoc_md_ltp.lua
Normal file
17
Documentation/compiler/ldoc/ldoc/html/ldoc_md_ltp.lua
Normal file
|
@ -0,0 +1,17 @@
|
|||
return [[
|
||||
> local lev = ldoc.level or 2
|
||||
> local lev1,lev2 = ('#'):rep(lev),('#'):rep(lev+1)
|
||||
> for kind, items in module.kinds() do
|
||||
> local kitem = module.kinds:get_item(kind)
|
||||
> if kitem then
|
||||
$(lev1) $(ldoc.descript(kitem))
|
||||
|
||||
> end
|
||||
> for item in items() do
|
||||
$(lev2) $(ldoc.display_name(item))
|
||||
|
||||
$(ldoc.descript(item))
|
||||
|
||||
> end
|
||||
> end
|
||||
]]
|
292
Documentation/compiler/ldoc/ldoc/html/ldoc_new_css.lua
Normal file
292
Documentation/compiler/ldoc/ldoc/html/ldoc_new_css.lua
Normal file
|
@ -0,0 +1,292 @@
|
|||
return [[
|
||||
body {
|
||||
color: #47555c;
|
||||
font-size: 16px;
|
||||
font-family: "Open Sans", sans-serif;
|
||||
margin: 0;
|
||||
background: #eff4ff;
|
||||
}
|
||||
|
||||
a:link { color: #008fee; }
|
||||
a:visited { color: #008fee; }
|
||||
a:hover { color: #22a7ff; }
|
||||
|
||||
h1 { font-size:26px; font-weight: normal; }
|
||||
h2 { font-size:22px; font-weight: normal; }
|
||||
h3 { font-size:18px; font-weight: normal; }
|
||||
h4 { font-size:16px; font-weight: bold; }
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
background: #c1cce4;
|
||||
border: 0px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
code, tt {
|
||||
font-family: monospace;
|
||||
}
|
||||
span.parameter {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
color: rgb(99, 115, 131);
|
||||
}
|
||||
span.parameter:after {
|
||||
content:":";
|
||||
}
|
||||
span.types:before {
|
||||
content:"(";
|
||||
}
|
||||
span.types:after {
|
||||
content:")";
|
||||
}
|
||||
.type {
|
||||
font-weight: bold; font-style:italic
|
||||
}
|
||||
|
||||
p.name {
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
float: left;
|
||||
background-color: white;
|
||||
border-right: 1px solid #d3dbec;
|
||||
border-bottom: 1px solid #d3dbec;
|
||||
|
||||
width: 14em;
|
||||
vertical-align: top;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#navigation br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#navigation h1 {
|
||||
background-color: white;
|
||||
border-bottom: 1px solid #d3dbec;
|
||||
padding: 15px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#navigation h2 {
|
||||
font-size: 18px;
|
||||
background-color: white;
|
||||
border-bottom: 1px solid #d3dbec;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#content h1 {
|
||||
background-color: #2c3e67;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#content h2 {
|
||||
background-color: #6c7ea7;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
#content h2 a {
|
||||
background-color: #6c7ea7;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#content h2 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#content h3 {
|
||||
font-style: italic;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 4px;
|
||||
margin-right: 15px;
|
||||
margin-left: 15px;
|
||||
margin-bottom: 5px;
|
||||
border-bottom: solid 1px #bcd;
|
||||
}
|
||||
|
||||
#content h4 {
|
||||
margin-right: 15px;
|
||||
margin-left: 15px;
|
||||
border-bottom: solid 1px #bcd;
|
||||
}
|
||||
|
||||
#content pre {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: rgb(50, 55, 68);
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
/* border: 1px solid #C0C0C0; /* silver */
|
||||
padding: 15px;
|
||||
overflow: auto;
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
#content ul pre.example {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
table.index {
|
||||
/* border: 1px #00007f; */
|
||||
}
|
||||
table.index td { text-align: left; vertical-align: top; }
|
||||
|
||||
#navigation ul
|
||||
{
|
||||
font-size:1em;
|
||||
list-style-type: none;
|
||||
margin: 1px 1px 10px 1px;
|
||||
}
|
||||
|
||||
#navigation li {
|
||||
text-indent: -1em;
|
||||
display: block;
|
||||
margin: 3px 0px 0px 22px;
|
||||
}
|
||||
|
||||
#navigation li li a {
|
||||
margin: 0px 3px 0px -1em;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 14em;
|
||||
}
|
||||
|
||||
#content p {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
#content table {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#content p, #content table, #content ol, #content ul, #content dl {
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
#about {
|
||||
padding: 15px;
|
||||
padding-left: 16em;
|
||||
background-color: white;
|
||||
border-top: 1px solid #d3dbec;
|
||||
border-bottom: 1px solid #d3dbec;
|
||||
}
|
||||
|
||||
table.module_list, table.function_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
margin: 15px;
|
||||
}
|
||||
table.module_list td, table.function_list td {
|
||||
border-width: 1px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
border: solid 1px rgb(193, 204, 228);
|
||||
}
|
||||
table.module_list td.name, table.function_list td.name {
|
||||
background-color: white; min-width: 200px; border-right-width: 0px;
|
||||
}
|
||||
table.module_list td.summary, table.function_list td.summary {
|
||||
background-color: white; width: 100%; border-left-width: 0px;
|
||||
}
|
||||
|
||||
dl.function {
|
||||
margin-right: 15px;
|
||||
margin-left: 15px;
|
||||
border-bottom: solid 1px rgb(193, 204, 228);
|
||||
border-left: solid 1px rgb(193, 204, 228);
|
||||
border-right: solid 1px rgb(193, 204, 228);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
dl.function dt {
|
||||
color: rgb(99, 123, 188);
|
||||
font-family: monospace;
|
||||
border-top: solid 1px rgb(193, 204, 228);
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
dl.function dd {
|
||||
margin-left: 15px;
|
||||
margin-right: 15px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#content dl.function dd h3 {
|
||||
margin-top: 0px;
|
||||
margin-left: 0px;
|
||||
padding-left: 0px;
|
||||
font-size: 16px;
|
||||
color: rgb(128, 128, 128);
|
||||
border-bottom: solid 1px #def;
|
||||
}
|
||||
|
||||
#content dl.function dd ul, #content dl.function dd ol {
|
||||
padding: 0px;
|
||||
padding-left: 15px;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
ul.nowrap {
|
||||
overflow:auto;
|
||||
white-space:nowrap;
|
||||
}
|
||||
|
||||
.section-description {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
/* stop sublists from having initial vertical space */
|
||||
ul ul { margin-top: 0px; }
|
||||
ol ul { margin-top: 0px; }
|
||||
ol ol { margin-top: 0px; }
|
||||
ul ol { margin-top: 0px; }
|
||||
|
||||
/* make the target distinct; helps when we're navigating to a function */
|
||||
a:target + * {
|
||||
background-color: #FF9;
|
||||
}
|
||||
|
||||
|
||||
/* styles for prettification of source */
|
||||
pre .comment { color: #bbccaa; }
|
||||
pre .constant { color: #a8660d; }
|
||||
pre .escape { color: #844631; }
|
||||
pre .keyword { color: #ffc090; font-weight: bold; }
|
||||
pre .library { color: #0e7c6b; }
|
||||
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
|
||||
pre .string { color: #8080ff; }
|
||||
pre .number { color: #f8660d; }
|
||||
pre .operator { color: #2239a8; font-weight: bold; }
|
||||
pre .preprocessor, pre .prepro { color: #a33243; }
|
||||
pre .global { color: #c040c0; }
|
||||
pre .user-keyword { color: #800080; }
|
||||
pre .prompt { color: #558817; }
|
||||
pre .url { color: #272fc2; text-decoration: underline; }
|
||||
]]
|
202
Documentation/compiler/ldoc/ldoc/html/ldoc_one_css.lua
Normal file
202
Documentation/compiler/ldoc/ldoc/html/ldoc_one_css.lua
Normal file
|
@ -0,0 +1,202 @@
|
|||
return require('ldoc.html._reset_css') .. [[
|
||||
|
||||
body {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
font-family: arial, helvetica, geneva, sans-serif;
|
||||
background-color: #ffffff; margin: 0px;
|
||||
}
|
||||
|
||||
code, tt { font-family: monospace; font-size: 1.1em; }
|
||||
|
||||
body, p, td, th { font-size: .95em; line-height: 1.2em;}
|
||||
|
||||
p, ul { margin: 10px 0 0 10px;}
|
||||
|
||||
strong { font-weight: bold;}
|
||||
|
||||
em { font-style: italic;}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
h2, h3, h4 { margin: 15px 0 10px 0; }
|
||||
h2 { font-size: 1.25em; }
|
||||
h3 { font-size: 1.15em; }
|
||||
h4 { font-size: 1.06em; }
|
||||
|
||||
a:link { font-weight: bold; color: #004080; text-decoration: none; }
|
||||
a:visited { font-weight: bold; color: #2808FF; text-decoration: none; }
|
||||
a:link:hover { text-decoration: underline; }
|
||||
|
||||
hr {
|
||||
color:#cccccc;
|
||||
background: #00007f;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
blockquote { margin-left: 3em; }
|
||||
|
||||
ul { list-style-type: disc; }
|
||||
|
||||
p.name {
|
||||
font-family: "Andale Mono", monospace;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: rgb(245, 245, 245);
|
||||
border: 1px solid #C0C0C0; /* silver */
|
||||
padding: 10px;
|
||||
margin: 10px 0 10px 0;
|
||||
overflow: auto;
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
pre.example {
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
table.index { border: 1px #00007f; }
|
||||
table.index td { text-align: left; vertical-align: top; }
|
||||
|
||||
#container {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
#product {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product big {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#main {
|
||||
border-left: 2px solid #cccccc;
|
||||
border-right: 2px solid #cccccc;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
float: top;
|
||||
vertical-align: top;
|
||||
background-color: #f5f5f5;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#navigation h2 {
|
||||
background-color:#e7e7e7;
|
||||
font-size:1.1em;
|
||||
color:#000000;
|
||||
text-align: left;
|
||||
padding:0.2em;
|
||||
border-top:1px solid #dddddd;
|
||||
border-bottom:1px solid #dddddd;
|
||||
}
|
||||
|
||||
#navigation ul
|
||||
{
|
||||
font-size:1em;
|
||||
list-style-type: none;
|
||||
margin: 1px 1px 10px 1px;
|
||||
}
|
||||
|
||||
#navigation li {
|
||||
text-indent: -1em;
|
||||
display: block;
|
||||
margin: 3px 0px 0px 22px;
|
||||
}
|
||||
|
||||
#navigation li li a {
|
||||
margin: 0px 3px 0px -1em;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#about {
|
||||
clear: both;
|
||||
padding: 5px;
|
||||
border-top: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font: 12pt "Times New Roman", "TimeNR", Times, serif;
|
||||
}
|
||||
a { font-weight: bold; color: #004080; text-decoration: underline; }
|
||||
|
||||
#main {
|
||||
background-color: #ffffff;
|
||||
border-left: 0px;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-left: 2%;
|
||||
margin-right: 2%;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
display: none;
|
||||
}
|
||||
pre.example {
|
||||
font-family: "Andale Mono", monospace;
|
||||
font-size: 10pt;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
table.module_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.module_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.module_list td.name { background-color: #f5f5f5; }
|
||||
table.module_list td.summary { width: 100%; }
|
||||
|
||||
|
||||
table.function_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.function_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.function_list td.name {
|
||||
background-color: #f5f5f5;
|
||||
white-space: normal; /* voids the "nowrap" in HTML */
|
||||
}
|
||||
table.function_list td.summary { width: 100%; }
|
||||
|
||||
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
|
||||
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
|
||||
dl.table h3, dl.function h3 {font-size: .95em;}
|
||||
|
||||
]]
|
||||
.. require('ldoc.html._code_css')
|
225
Documentation/compiler/ldoc/ldoc/html/ldoc_pale_css.lua
Normal file
225
Documentation/compiler/ldoc/ldoc/html/ldoc_pale_css.lua
Normal file
|
@ -0,0 +1,225 @@
|
|||
return require('ldoc.html._reset_css') .. [[
|
||||
|
||||
body {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
font-family: arial, helvetica, geneva, sans-serif;
|
||||
background-color: #ffffff; margin: 0px;
|
||||
}
|
||||
|
||||
code, tt { font-family: monospace; font-size: 1.1em; }
|
||||
span.parameter { font-family:monospace; }
|
||||
span.parameter:after { content:":"; }
|
||||
span.types:before { content:"("; }
|
||||
span.types:after { content:")"; }
|
||||
.type { font-weight: bold; font-style:italic }
|
||||
|
||||
body, p, td, th { font-size: .95em; line-height: 1.2em;}
|
||||
|
||||
p, ul { margin: 10px 0 0 0px;}
|
||||
|
||||
strong { font-weight: bold;}
|
||||
|
||||
em { font-style: italic;}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
h2, h3, h4 { margin: 15px 0 10px 0; }
|
||||
h2 { font-size: 1.25em; }
|
||||
h3 { font-size: 1.15em; }
|
||||
h4 { font-size: 1.06em; }
|
||||
|
||||
a:link { font-weight: bold; color: #004080; text-decoration: none; }
|
||||
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
|
||||
a:link:hover { text-decoration: underline; }
|
||||
|
||||
hr {
|
||||
color:#cccccc;
|
||||
background: #00007f;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
blockquote { margin-left: 3em; }
|
||||
|
||||
ul { list-style-type: disc; }
|
||||
|
||||
p.name {
|
||||
font-family: "Andale Mono", monospace;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: rgb(245, 245, 245);
|
||||
border: 1px solid #C0C0C0; /* silver */
|
||||
padding: 10px;
|
||||
margin: 10px 0 10px 0;
|
||||
overflow: auto;
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
pre.example {
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
table.index { border: 1px #00007f; }
|
||||
table.index td { text-align: left; vertical-align: top; }
|
||||
|
||||
#container {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product big {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#main {
|
||||
background-color:#FFFFFF; // #f0f0f0;
|
||||
//border-left: 2px solid #cccccc;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
float: left;
|
||||
width: 14em;
|
||||
vertical-align: top;
|
||||
background-color:#FFFFFF; // #f0f0f0;
|
||||
border-right: 2px solid #cccccc;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#navigation h2 {
|
||||
background-color:#FFFFFF;//:#e7e7e7;
|
||||
font-size:1.1em;
|
||||
color:#000000;
|
||||
text-align: left;
|
||||
padding:0.2em;
|
||||
//border-top:1px solid #dddddd;
|
||||
border-bottom:1px solid #dddddd;
|
||||
}
|
||||
|
||||
#navigation ul
|
||||
{
|
||||
font-size:1em;
|
||||
list-style-type: none;
|
||||
margin: 1px 1px 10px 1px;
|
||||
}
|
||||
|
||||
#navigation li {
|
||||
text-indent: -1em;
|
||||
display: block;
|
||||
margin: 3px 0px 0px 22px;
|
||||
}
|
||||
|
||||
#navigation li li a {
|
||||
margin: 0px 3px 0px -1em;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 14em;
|
||||
padding: 1em;
|
||||
width: 700px;
|
||||
border-left: 2px solid #cccccc;
|
||||
// border-right: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#about {
|
||||
clear: both;
|
||||
padding: 5px;
|
||||
border-top: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font: 12pt "Times New Roman", "TimeNR", Times, serif;
|
||||
}
|
||||
a { font-weight: bold; color: #004080; text-decoration: underline; }
|
||||
|
||||
#main {
|
||||
background-color: #ffffff;
|
||||
border-left: 0px;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin-left: 2%;
|
||||
margin-right: 2%;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 1em;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
display: none;
|
||||
}
|
||||
pre.example {
|
||||
font-family: "Andale Mono", monospace;
|
||||
font-size: 10pt;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
table.module_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.module_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; }
|
||||
table.module_list td.summary { width: 100%; }
|
||||
|
||||
table.function_list {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.function_list td {
|
||||
border-width: 1px;
|
||||
padding: 3px;
|
||||
border-style: solid;
|
||||
border-color: #cccccc;
|
||||
}
|
||||
table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; }
|
||||
table.function_list td.summary { width: 100%; }
|
||||
|
||||
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
|
||||
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
|
||||
dl.table h3, dl.function h3 {font-size: .95em;}
|
||||
|
||||
ul.nowrap {
|
||||
overflow:auto;
|
||||
whitespace:nowrap;
|
||||
}
|
||||
|
||||
/* stop sublists from having initial vertical space */
|
||||
ul ul { margin-top: 0px; }
|
||||
ol ul { margin-top: 0px; }
|
||||
ol ol { margin-top: 0px; }
|
||||
ul ol { margin-top: 0px; }
|
||||
|
||||
/* make the target distinct; helps when we're navigating to a function */
|
||||
a:target + * {
|
||||
background-color: #FF9;
|
||||
}
|
||||
|
||||
]]
|
||||
.. require('ldoc.html._code_css')
|
379
Documentation/compiler/ldoc/ldoc/lang.lua
Normal file
379
Documentation/compiler/ldoc/ldoc/lang.lua
Normal file
|
@ -0,0 +1,379 @@
|
|||
------------
|
||||
-- Language-dependent parsing of code.
|
||||
-- This encapsulates the different strategies needed for parsing C and Lua
|
||||
-- source code.
|
||||
|
||||
local class = require 'pl.class'
|
||||
local utils = require 'pl.utils'
|
||||
local List = require 'pl.List'
|
||||
local tools = require 'ldoc.tools'
|
||||
local lexer = require 'ldoc.lexer'
|
||||
local quit = utils.quit
|
||||
local tnext = lexer.skipws
|
||||
|
||||
|
||||
local Lang = class()
|
||||
|
||||
function Lang:trim_comment (s)
|
||||
return s:gsub(self.line_comment,'')
|
||||
end
|
||||
|
||||
function Lang:start_comment (v)
|
||||
local line = v:match (self.start_comment_)
|
||||
if line and self.end_comment_ and v:match (self.end_comment_) then
|
||||
return nil
|
||||
end
|
||||
local block = v:match(self.block_comment)
|
||||
return line or block, block
|
||||
end
|
||||
|
||||
function Lang:empty_comment (v)
|
||||
return v:match(self.empty_comment_)
|
||||
end
|
||||
|
||||
function Lang:grab_block_comment(v,tok)
|
||||
v = v:gsub(self.block_comment,'')
|
||||
return tools.grab_block_comment(v,tok,self.end_comment)
|
||||
end
|
||||
|
||||
function Lang:find_module(tok,t,v)
|
||||
return '...',t,v
|
||||
end
|
||||
|
||||
function Lang:item_follows(t,v)
|
||||
return false
|
||||
end
|
||||
|
||||
function Lang:finalize()
|
||||
self.empty_comment_ = self.start_comment_..'%s*$'
|
||||
end
|
||||
|
||||
function Lang:search_for_token (tok,type,value,t,v)
|
||||
while t and not (t == type and v == value) do
|
||||
if t == 'comment' and self:start_comment(v) then return nil,t,v end
|
||||
t,v = tnext(tok)
|
||||
end
|
||||
return t ~= nil,t,v
|
||||
end
|
||||
|
||||
function Lang:parse_extra (tags,tok)
|
||||
end
|
||||
|
||||
function Lang:is_module_modifier ()
|
||||
return false
|
||||
end
|
||||
|
||||
function Lang:parse_module_modifier (tags, tok)
|
||||
return nil, "@usage or @exports deduction not implemented for this language"
|
||||
end
|
||||
|
||||
|
||||
local Lua = class(Lang)
|
||||
|
||||
function Lua:_init()
|
||||
self.line_comment = '^%-%-+' -- used for stripping
|
||||
self.start_comment_ = '^%-%-%-+' -- used for doc comment line start
|
||||
self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments
|
||||
self.end_comment_ = '[^%-]%-%-+[^-]*\n$' ---- exclude --- this kind of comment ---
|
||||
self.method_call = ':'
|
||||
self:finalize()
|
||||
end
|
||||
|
||||
function Lua.lexer(fname)
|
||||
local f,e = io.open(fname)
|
||||
if not f then quit(e) end
|
||||
return lexer.lua(f,{}),f
|
||||
end
|
||||
|
||||
function Lua:grab_block_comment(v,tok)
|
||||
local equals = v:match('^%-%-%[(=*)%[')
|
||||
if not equals then return v end
|
||||
v = v:gsub(self.block_comment,'')
|
||||
return tools.grab_block_comment(v,tok,'%]'..equals..'%]')
|
||||
end
|
||||
|
||||
|
||||
-- luacheck: push ignore 312
|
||||
function Lua:parse_module_call(tok,t,v)
|
||||
t,v = tnext(tok)
|
||||
if t == '(' then t,v = tnext(tok) end
|
||||
if t == 'string' then -- explicit name, cool
|
||||
return v,t,v
|
||||
elseif t == '...' then -- we have to guess!
|
||||
return '...',t,v
|
||||
end
|
||||
end
|
||||
-- luacheck: pop
|
||||
|
||||
-- If a module name was not provided, then we look for an explicit module()
|
||||
-- call. However, we should not try too hard; if we hit a doc comment then
|
||||
-- we should go back and process it. Likewise, module(...) also means
|
||||
-- that we must infer the module name.
|
||||
function Lua:find_module(tok,t,v)
|
||||
local res
|
||||
res,t,v = self:search_for_token(tok,'iden','module',t,v)
|
||||
if not res then return nil,t,v end
|
||||
return self:parse_module_call(tok,t,v)
|
||||
end
|
||||
|
||||
local function parse_lua_parameters (tags,tok)
|
||||
tags.formal_args = tools.get_parameters(tok)
|
||||
tags:add('class','function')
|
||||
end
|
||||
|
||||
local function parse_lua_function_header (tags,tok)
|
||||
if not tags.name then
|
||||
tags:add('name',tools.get_fun_name(tok))
|
||||
end
|
||||
if not tags.name then return 'function has no name' end
|
||||
parse_lua_parameters(tags,tok)
|
||||
end
|
||||
|
||||
local function parse_lua_table (tags,tok)
|
||||
tags.formal_args = tools.get_parameters(tok,'}',function(s)
|
||||
return s == ',' or s == ';'
|
||||
end)
|
||||
end
|
||||
|
||||
--------------- function and variable inferrence -----------
|
||||
-- After a doc comment, there may be a local followed by:
|
||||
-- [1] (l)function: function NAME
|
||||
-- [2] (l)function: NAME = function
|
||||
-- [3] table: NAME = {
|
||||
-- [4] field: NAME = <anything else> (this is a module-level field)
|
||||
--
|
||||
-- Depending on the case successfully detected, returns a function which
|
||||
-- will be called later to fill in inferred item tags
|
||||
function Lua:item_follows(t,v,tok)
|
||||
local parser, case
|
||||
local is_local = t == 'keyword' and v == 'local'
|
||||
if is_local then t,v = tnext(tok) end
|
||||
if t == 'keyword' and v == 'function' then -- case [1]
|
||||
case = 1
|
||||
parser = parse_lua_function_header
|
||||
elseif t == 'iden' then
|
||||
local name,t,_ = tools.get_fun_name(tok,v)
|
||||
if t ~= '=' then return nil,"not 'name = function,table or value'" end
|
||||
t,v = tnext(tok)
|
||||
if t == 'keyword' and v == 'function' then -- case [2]
|
||||
tnext(tok) -- skip '('
|
||||
case = 2
|
||||
parser = function(tags,tok)
|
||||
tags:add('name',name)
|
||||
parse_lua_parameters(tags,tok)
|
||||
end
|
||||
elseif t == '{' then -- case [3]
|
||||
case = 3
|
||||
parser = function(tags,tok)
|
||||
tags:add('class','table')
|
||||
tags:add('name',name)
|
||||
parse_lua_table (tags,tok)
|
||||
end
|
||||
else -- case [4]
|
||||
case = 4
|
||||
parser = function(tags)
|
||||
tags:add('class','field')
|
||||
tags:add('name',name)
|
||||
end
|
||||
end
|
||||
elseif t == 'keyword' and v == 'return' then
|
||||
t, v = tnext(tok)
|
||||
if t == 'keyword' and v == 'function' then
|
||||
-- return function(a, b, c)
|
||||
tnext(tok) -- skip '('
|
||||
case = 2
|
||||
parser = parse_lua_parameters
|
||||
elseif t == '{' then
|
||||
-- return {...}
|
||||
case = 5
|
||||
parser = function(tags,tok)
|
||||
tags:add('class','table')
|
||||
parse_lua_table(tags,tok)
|
||||
end
|
||||
else
|
||||
return nil,'not returning function or table'
|
||||
end
|
||||
else
|
||||
return nil,"not 'name=value' or 'return value'"
|
||||
end
|
||||
return parser, is_local, case
|
||||
end
|
||||
|
||||
|
||||
-- we only call the function returned by the item_follows above if there
|
||||
-- is not already a name and a type.
|
||||
-- Otherwise, this is called. Currrently only tries to fill in the fields
|
||||
-- of a table from a table definition as identified above
|
||||
function Lua:parse_extra (tags,tok,case)
|
||||
if tags.class == 'table' and not tags.field and case == 3 then
|
||||
parse_lua_table(tags,tok)
|
||||
end
|
||||
end
|
||||
|
||||
-- For Lua, a --- @usage comment means that a long
|
||||
-- string containing the usage follows, which we
|
||||
-- use to update the module usage tag. Likewise, the @export
|
||||
-- tag alone in a doc comment refers to the following returned
|
||||
-- Lua table of functions
|
||||
|
||||
|
||||
function Lua:is_module_modifier (tags)
|
||||
return tags.summary == '' and (tags.usage or tags.export)
|
||||
end
|
||||
|
||||
-- Allow for private name convention.
|
||||
function Lua:is_private_var (name)
|
||||
return name:match '^_' or name:match '_$'
|
||||
end
|
||||
|
||||
function Lua:parse_module_modifier (tags, tok, F)
|
||||
if tags.usage then
|
||||
if tags.class ~= 'field' then return nil,"cannot deduce @usage" end
|
||||
local t1= tnext(tok)
|
||||
if t1 ~= '[' then return nil, t1..' '..': not a long string' end
|
||||
local _, v = tools.grab_block_comment('',tok,'%]%]')
|
||||
return true, v, 'usage'
|
||||
elseif tags.export then
|
||||
if tags.class ~= 'table' then return nil, "cannot deduce @export" end
|
||||
for f in tags.formal_args:iter() do
|
||||
if not self:is_private_var(f) then
|
||||
F:export_item(f)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- note a difference here: we scan C/C++ code in full-text mode, not line by line.
|
||||
-- This is because we can't detect multiline comments in line mode.
|
||||
-- Note: this applies to C/C++ code used to generate _Lua_ documentation!
|
||||
|
||||
local CC = class(Lang)
|
||||
|
||||
function CC:_init()
|
||||
self.line_comment = '^//+'
|
||||
self.start_comment_ = '^///+'
|
||||
self.block_comment = '^/%*%*+'
|
||||
self.method_call = ':'
|
||||
self:finalize()
|
||||
end
|
||||
|
||||
function CC.lexer(f)
|
||||
local err
|
||||
f,err = utils.readfile(f)
|
||||
if not f then quit(err) end
|
||||
return lexer.cpp(f,{},nil,true)
|
||||
end
|
||||
|
||||
function CC:grab_block_comment(v,tok)
|
||||
v = v:gsub(self.block_comment,''):gsub('\n%s*%*','\n')
|
||||
return 'comment',v:sub(1,-3)
|
||||
end
|
||||
|
||||
--- here the argument name is always last, and the type is composed of any tokens before
|
||||
function CC:extract_arg (tl,idx)
|
||||
idx = idx or 1
|
||||
local res = List()
|
||||
for i = idx,#tl-1 do
|
||||
res:append(tl[i][2])
|
||||
end
|
||||
local type = res:join ' '
|
||||
return tl[#tl][2], type
|
||||
end
|
||||
|
||||
function CC:item_follows (t,v,tok)
|
||||
if not self.extra.C then
|
||||
return false
|
||||
end
|
||||
if t == 'iden' or t == 'keyword' then --
|
||||
local _
|
||||
if v == self.extra.export then -- this is not part of the return type!
|
||||
_,v = tnext(tok)
|
||||
end
|
||||
-- types may have multiple tokens: example, const char *bonzo(...)
|
||||
local return_type, name = v
|
||||
_,v = tnext(tok)
|
||||
name = v
|
||||
t,v = tnext(tok)
|
||||
while t ~= '(' do
|
||||
return_type = return_type .. ' ' .. name
|
||||
name = v
|
||||
t,v = tnext(tok)
|
||||
end
|
||||
--print ('got',name,t,v,return_type)
|
||||
return function(tags,tok)
|
||||
if not tags.name then
|
||||
tags:add('name',name)
|
||||
end
|
||||
tags:add('class','function')
|
||||
if t == '(' then
|
||||
tags.formal_args,t,_ = tools.get_parameters(tok,')',',',self)
|
||||
if return_type ~= 'void' then
|
||||
tags.formal_args.return_type = return_type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local Moon = class(Lua)
|
||||
|
||||
function Moon:_init()
|
||||
self.line_comment = '^%-%-+' -- used for stripping
|
||||
self.start_comment_ = '^%s*%-%-%-+' -- used for doc comment line start
|
||||
self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments
|
||||
self.end_comment_ = '[^%-]%-%-+\n$' ---- exclude --- this kind of comment ---
|
||||
self.method_call = '\\'
|
||||
self:finalize()
|
||||
end
|
||||
|
||||
--- much like Lua, BUT auto-assign parameters start with @
|
||||
function Moon:extract_arg (tl,idx)
|
||||
idx = idx or 1
|
||||
local auto_assign = tl[idx][1] == '@'
|
||||
if auto_assign then idx = idx + 1 end
|
||||
local res = tl[idx][2]
|
||||
return res
|
||||
end
|
||||
|
||||
function Moon:item_follows (t,v,tok)
|
||||
if t == '.' then -- enclosed in with statement
|
||||
t,v = tnext(tok)
|
||||
end
|
||||
if t == 'iden' then
|
||||
local name,t,v = tools.get_fun_name(tok,v,'')
|
||||
if name == 'class' then
|
||||
local _
|
||||
name,_,_ = tools.get_fun_name(tok,v,'')
|
||||
-- class!
|
||||
return function(tags,tok)
|
||||
tags:add('class','type')
|
||||
tags:add('name',name)
|
||||
end
|
||||
elseif t == '=' or t == ':' then -- function/method
|
||||
local _
|
||||
t,_ = tnext(tok)
|
||||
return function(tags,tok)
|
||||
if not tags.name then
|
||||
tags:add('name',name)
|
||||
end
|
||||
if t == '(' then
|
||||
tags.formal_args,t,_ = tools.get_parameters(tok,')',',',self)
|
||||
else
|
||||
tags.formal_args = List()
|
||||
end
|
||||
t,_ = tnext(tok)
|
||||
tags:add('class','function')
|
||||
if t ~= '>' then
|
||||
tags.static = true
|
||||
end
|
||||
end
|
||||
else
|
||||
return nil, "expecting '=' or ':'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return { lua = Lua(), cc = CC(), moon = Moon() }
|
504
Documentation/compiler/ldoc/ldoc/lexer.lua
Normal file
504
Documentation/compiler/ldoc/ldoc/lexer.lua
Normal file
|
@ -0,0 +1,504 @@
|
|||
--- Lexical scanner for creating a sequence of tokens from text. <br>
|
||||
-- <p><code>lexer.scan(s)</code> returns an iterator over all tokens found in the
|
||||
-- string <code>s</code>. This iterator returns two values, a token type string
|
||||
-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the
|
||||
-- token.
|
||||
-- <p>
|
||||
-- Versions specialized for Lua and C are available; these also handle block comments
|
||||
-- and classify keywords as 'keyword' tokens. For example:
|
||||
-- <pre class=example>
|
||||
-- > s = 'for i=1,n do'
|
||||
-- > for t,v in lexer.lua(s) do print(t,v) end
|
||||
-- keyword for
|
||||
-- iden i
|
||||
-- = =
|
||||
-- number 1
|
||||
-- , ,
|
||||
-- iden n
|
||||
-- keyword do
|
||||
-- </pre>
|
||||
--
|
||||
-- Based on pl.lexer from Penlight
|
||||
|
||||
local strfind = string.find
|
||||
local strsub = string.sub
|
||||
local append = table.insert
|
||||
|
||||
local function assert_arg(idx,val,tp)
|
||||
if type(val) ~= tp then
|
||||
error("argument "..idx.." must be "..tp, 2)
|
||||
end
|
||||
end
|
||||
|
||||
local lexer = {}
|
||||
|
||||
local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+'
|
||||
local NUMBER2 = '^[%+%-]?%d+%.?%d*'
|
||||
local NUMBER3 = '^0x[%da-fA-F]+'
|
||||
local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+'
|
||||
local NUMBER5 = '^%d+%.?%d*'
|
||||
local IDEN = '^[%a_][%w_]*'
|
||||
local WSPACE = '^%s+'
|
||||
local STRING1 = [[^'.-[^\\]']]
|
||||
local STRING2 = [[^".-[^\\]"]]
|
||||
local STRING3 = "^((['\"])%2)" -- empty string
|
||||
local PREPRO = '^#.-[^\\]\n'
|
||||
|
||||
local plain_matches,lua_matches,cpp_matches,cpp_matches_no_string,lua_keyword,cpp_keyword
|
||||
|
||||
local function tdump(tok)
|
||||
return tok,tok
|
||||
end
|
||||
|
||||
local function ndump(tok,options)
|
||||
if options and options.number then
|
||||
tok = tonumber(tok)
|
||||
end
|
||||
return "number",tok
|
||||
end
|
||||
|
||||
-- regular strings, single or double quotes; usually we want them
|
||||
-- without the quotes
|
||||
local function sdump(tok,options)
|
||||
if options and options.string then
|
||||
tok = tok:sub(2,-2)
|
||||
end
|
||||
return "string",tok
|
||||
end
|
||||
|
||||
-- strings enclosed in back ticks
|
||||
local function bdump(tok,options)
|
||||
if options and options.string then
|
||||
tok = tok:sub(2,-2)
|
||||
end
|
||||
return "backtick",tok
|
||||
end
|
||||
|
||||
-- long Lua strings need extra work to get rid of the quotes
|
||||
local function sdump_l(tok,options)
|
||||
if options and options.string then
|
||||
tok = tok:sub(3,-3)
|
||||
end
|
||||
return "string",tok
|
||||
end
|
||||
|
||||
local function chdump(tok,options)
|
||||
if options and options.string then
|
||||
tok = tok:sub(2,-2)
|
||||
end
|
||||
return "char",tok
|
||||
end
|
||||
|
||||
local function cdump(tok)
|
||||
return 'comment',tok
|
||||
end
|
||||
|
||||
local function wsdump (tok)
|
||||
return "space",tok
|
||||
end
|
||||
|
||||
local function pdump (tok)
|
||||
return 'prepro',tok
|
||||
end
|
||||
|
||||
local function plain_vdump(tok)
|
||||
return "iden",tok
|
||||
end
|
||||
|
||||
local function lua_vdump(tok)
|
||||
if lua_keyword[tok] then
|
||||
return "keyword",tok
|
||||
else
|
||||
return "iden",tok
|
||||
end
|
||||
end
|
||||
|
||||
local function cpp_vdump(tok)
|
||||
if cpp_keyword[tok] then
|
||||
return "keyword",tok
|
||||
else
|
||||
return "iden",tok
|
||||
end
|
||||
end
|
||||
|
||||
local function count_lines(line, text)
|
||||
local index, limit = 1, #text
|
||||
while index <= limit do
|
||||
local start, stop = text:find('\r\n', index, true)
|
||||
if not start then
|
||||
start, stop = text:find('[\r\n\f]', index)
|
||||
if not start then break end
|
||||
end
|
||||
index = stop + 1
|
||||
line = line + 1
|
||||
end
|
||||
return line
|
||||
end
|
||||
|
||||
local multiline = { comment = true, space = true }
|
||||
|
||||
|
||||
--- create a plain token iterator from a string or file-like object.
|
||||
-- @param s the string
|
||||
-- @param matches an optional match table (set of pattern-action pairs)
|
||||
-- @param filter a table of token types to exclude, by default {space=true}
|
||||
-- @param options a table of options; by default, {number=true,string=true},
|
||||
-- which means convert numbers and strip string quotes.
|
||||
function lexer.scan (s,matches,filter,options)
|
||||
--assert_arg(1,s,'string')
|
||||
local file = type(s) ~= 'string' and s
|
||||
filter = filter or {space=true}
|
||||
options = options or {number=true,string=true}
|
||||
if filter then
|
||||
if filter.space then filter[wsdump] = true end
|
||||
if filter.comments then
|
||||
filter[cdump] = true
|
||||
end
|
||||
end
|
||||
if not matches then
|
||||
if not plain_matches then
|
||||
plain_matches = {
|
||||
{WSPACE,wsdump},
|
||||
{NUMBER3,ndump},
|
||||
{IDEN,plain_vdump},
|
||||
{NUMBER1,ndump},
|
||||
{NUMBER2,ndump},
|
||||
{STRING3,sdump},
|
||||
{STRING1,sdump},
|
||||
{STRING2,sdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
matches = plain_matches
|
||||
end
|
||||
local i1,i2,tok,pat,fun
|
||||
local line = 1
|
||||
if file then
|
||||
s = file:read()
|
||||
if not s then return nil end -- empty file
|
||||
if s:match '^\239\187' then -- UTF-8 BOM Abomination
|
||||
s = s:sub(4)
|
||||
end
|
||||
s = s ..'\n'
|
||||
end
|
||||
local sz = #s
|
||||
local idx = 1
|
||||
if sz == 0 then return nil end -- empty file
|
||||
|
||||
local res = {}
|
||||
local mt = {}
|
||||
mt.__index = mt
|
||||
setmetatable(res,mt)
|
||||
|
||||
function mt.lineno() return line end
|
||||
|
||||
function mt.getline()
|
||||
if idx < sz then
|
||||
tok = strsub(s,idx,-2)
|
||||
idx = sz + 1
|
||||
line = line + 1
|
||||
return tok
|
||||
else
|
||||
idx = sz + 1
|
||||
line = line + 1
|
||||
return file:read()
|
||||
end
|
||||
end
|
||||
|
||||
function mt.next (tok)
|
||||
local t,v = tok()
|
||||
while t == 'space' do
|
||||
t,v = tok()
|
||||
end
|
||||
return t,v
|
||||
end
|
||||
|
||||
function mt.__call ()
|
||||
if not s then return end
|
||||
while true do
|
||||
for _,m in ipairs(matches) do
|
||||
pat,fun = m[1],m[2]
|
||||
if fun == nil then error("no match for "..pat) end
|
||||
i1,i2 = strfind(s,pat,idx)
|
||||
if i1 then
|
||||
tok = strsub(s,i1,i2)
|
||||
idx = i2 + 1
|
||||
if not (filter and filter[fun]) then
|
||||
lexer.finished = idx > sz
|
||||
local t,v = fun(tok,options)
|
||||
if not file and multiline[t] then
|
||||
line = count_lines(line,v)
|
||||
end
|
||||
return t,v
|
||||
end
|
||||
end
|
||||
end
|
||||
if idx > sz then
|
||||
if file then
|
||||
line = line + 1
|
||||
s = file:read()
|
||||
if not s then return end
|
||||
s = s .. '\n'
|
||||
idx ,sz = 1,#s
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- get everything in a stream upto a newline.
|
||||
-- @param tok a token stream
|
||||
-- @return a string
|
||||
function lexer.getline (tok)
|
||||
return tok:getline()
|
||||
end
|
||||
|
||||
--- get current line number. <br>
|
||||
-- Only available if the input source is a file-like object.
|
||||
-- @param tok a token stream
|
||||
-- @return the line number and current column
|
||||
function lexer.lineno (tok)
|
||||
return tok:lineno()
|
||||
end
|
||||
|
||||
--- get the Lua keywords as a set-like table.
|
||||
-- So <code>res["and"]</code> etc would be <code>true</code>.
|
||||
-- @return a table
|
||||
function lexer.get_keywords ()
|
||||
if not lua_keyword then
|
||||
lua_keyword = {
|
||||
["and"] = true, ["break"] = true, ["do"] = true,
|
||||
["else"] = true, ["elseif"] = true, ["end"] = true,
|
||||
["false"] = true, ["for"] = true, ["function"] = true,
|
||||
["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true,
|
||||
["not"] = true, ["or"] = true, ["repeat"] = true,
|
||||
["return"] = true, ["then"] = true, ["true"] = true,
|
||||
["until"] = true, ["while"] = true
|
||||
}
|
||||
end
|
||||
return lua_keyword
|
||||
end
|
||||
|
||||
|
||||
--- create a Lua token iterator from a string or file-like object.
|
||||
-- Will return the token type and value.
|
||||
-- @param s the string
|
||||
-- @param filter a table of token types to exclude, by default {space=true,comments=true}
|
||||
-- @param options a table of options; by default, {number=true,string=true},
|
||||
-- which means convert numbers and strip string quotes.
|
||||
function lexer.lua(s,filter,options)
|
||||
filter = filter or {space=true,comments=true}
|
||||
lexer.get_keywords()
|
||||
if not lua_matches then
|
||||
lua_matches = {
|
||||
{WSPACE,wsdump},
|
||||
{NUMBER3,ndump},
|
||||
{IDEN,lua_vdump},
|
||||
{NUMBER4,ndump},
|
||||
{NUMBER5,ndump},
|
||||
{STRING3,sdump},
|
||||
{STRING1,sdump},
|
||||
{STRING2,sdump},
|
||||
{'^`[^`]+`',bdump},
|
||||
{'^%-%-%[(=*)%[.-%]%1%]',cdump},
|
||||
{'^%-%-.-\n',cdump},
|
||||
{'^%-%-.-$',cdump},
|
||||
{'^%[(=*)%[.-%]%1%]',sdump_l},
|
||||
{'^==',tdump},
|
||||
{'^~=',tdump},
|
||||
{'^<=',tdump},
|
||||
{'^>=',tdump},
|
||||
{'^%.%.%.',tdump},
|
||||
{'^%.%.',tdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
return lexer.scan(s,lua_matches,filter,options)
|
||||
end
|
||||
|
||||
--- create a C/C++ token iterator from a string or file-like object.
|
||||
-- Will return the token type type and value.
|
||||
-- @param s the string
|
||||
-- @param filter a table of token types to exclude, by default {space=true,comments=true}
|
||||
-- @param options a table of options; by default, {number=true,string=true},
|
||||
-- which means convert numbers and strip string quotes.
|
||||
function lexer.cpp(s,filter,options,no_string)
|
||||
filter = filter or {comments=true}
|
||||
if not cpp_keyword then
|
||||
cpp_keyword = {
|
||||
["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true,
|
||||
["else"] = true, ["continue"] = true, ["struct"] = true,
|
||||
["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true,
|
||||
["private"] = true, ["protected"] = true, ["goto"] = true,
|
||||
["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true,
|
||||
["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true,
|
||||
["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true,
|
||||
["double"] = true, ["while"] = true, ["new"] = true,
|
||||
["namespace"] = true, ["try"] = true, ["catch"] = true,
|
||||
["switch"] = true, ["case"] = true, ["extern"] = true,
|
||||
["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true,
|
||||
["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true,
|
||||
}
|
||||
end
|
||||
if not cpp_matches then
|
||||
cpp_matches = {
|
||||
{WSPACE,wsdump},
|
||||
{PREPRO,pdump},
|
||||
{NUMBER3,ndump},
|
||||
{IDEN,cpp_vdump},
|
||||
{NUMBER4,ndump},
|
||||
{NUMBER5,ndump},
|
||||
{STRING3,sdump},
|
||||
{STRING1,chdump},
|
||||
{STRING2,sdump},
|
||||
{'^//.-\n',cdump},
|
||||
{'^//.-$',cdump},
|
||||
{'^/%*.-%*/',cdump},
|
||||
{'^==',tdump},
|
||||
{'^!=',tdump},
|
||||
{'^<=',tdump},
|
||||
{'^>=',tdump},
|
||||
{'^->',tdump},
|
||||
{'^&&',tdump},
|
||||
{'^||',tdump},
|
||||
{'^%+%+',tdump},
|
||||
{'^%-%-',tdump},
|
||||
{'^%+=',tdump},
|
||||
{'^%-=',tdump},
|
||||
{'^%*=',tdump},
|
||||
{'^/=',tdump},
|
||||
{'^|=',tdump},
|
||||
{'^%^=',tdump},
|
||||
{'^::',tdump},
|
||||
{'^%.%.%.',tdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
if not cpp_matches_no_string then
|
||||
cpp_matches_no_string = {
|
||||
{WSPACE,wsdump},
|
||||
{PREPRO,pdump},
|
||||
{NUMBER3,ndump},
|
||||
{IDEN,cpp_vdump},
|
||||
{NUMBER4,ndump},
|
||||
{NUMBER5,ndump},
|
||||
{'^//.-\n',cdump},
|
||||
{'^/%*.-%*/',cdump},
|
||||
{'^==',tdump},
|
||||
{'^!=',tdump},
|
||||
{'^<=',tdump},
|
||||
{'^>=',tdump},
|
||||
{'^->',tdump},
|
||||
{'^&&',tdump},
|
||||
{'^||',tdump},
|
||||
{'^%+%+',tdump},
|
||||
{'^%-%-',tdump},
|
||||
{'^%+=',tdump},
|
||||
{'^%-=',tdump},
|
||||
{'^%*=',tdump},
|
||||
{'^/=',tdump},
|
||||
{'^|=',tdump},
|
||||
{'^%^=',tdump},
|
||||
{'^::',tdump},
|
||||
{'^%.%.%.',tdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
return lexer.scan(s,
|
||||
not no_string and cpp_matches or cpp_matches_no_string,
|
||||
filter,options)
|
||||
end
|
||||
|
||||
--- get a list of parameters separated by a delimiter from a stream.
|
||||
-- @param tok the token stream
|
||||
-- @param endtoken end of list (default ')'). Can be '\n'
|
||||
-- @param delim separator (default ',')
|
||||
-- @return a list of token lists.
|
||||
function lexer.get_separated_list(tok,endtoken,delim)
|
||||
endtoken = endtoken or ')'
|
||||
delim = delim or ','
|
||||
local function tappend (tl,t,val)
|
||||
val = val or t
|
||||
append(tl,{t,val})
|
||||
end
|
||||
local is_end
|
||||
if endtoken == '\n' then
|
||||
is_end = function(t,val)
|
||||
return t == 'space' and val:find '\n'
|
||||
end
|
||||
else
|
||||
is_end = function (t)
|
||||
return t == endtoken
|
||||
end
|
||||
end
|
||||
local is_delim
|
||||
if type(delim) == 'function' then
|
||||
is_delim = delim
|
||||
else
|
||||
is_delim = function(t)
|
||||
return t == delim
|
||||
end
|
||||
end
|
||||
local parm_values = {}
|
||||
local level = 1 -- used to count ( and )
|
||||
local tl = {}
|
||||
local token,value
|
||||
while true do
|
||||
token,value=tok()
|
||||
if not token then return nil,'EOS' end -- end of stream is an error!
|
||||
if is_end(token,value) and level == 1 then
|
||||
if next(tl) then
|
||||
append(parm_values,tl)
|
||||
end
|
||||
break
|
||||
elseif token == '(' then
|
||||
level = level + 1
|
||||
tappend(tl,'(')
|
||||
elseif token == ')' then
|
||||
level = level - 1
|
||||
if level == 0 then -- finished with parm list
|
||||
append(parm_values,tl)
|
||||
break
|
||||
else
|
||||
tappend(tl,')')
|
||||
end
|
||||
elseif level == 1 and is_delim(token) then
|
||||
append(parm_values,tl) -- a new parm
|
||||
tl = {}
|
||||
else
|
||||
tappend(tl,token,value)
|
||||
end
|
||||
end
|
||||
return parm_values,{token,value}
|
||||
end
|
||||
|
||||
--- get the next non-space token from the stream.
|
||||
-- @param tok the token stream.
|
||||
function lexer.skipws (tok)
|
||||
return tok:next()
|
||||
end
|
||||
|
||||
local skipws = lexer.skipws
|
||||
|
||||
--- get the next token, which must be of the expected type.
|
||||
-- Throws an error if this type does not match!
|
||||
-- @param tok the token stream
|
||||
-- @param expected_type the token type
|
||||
-- @param no_skip_ws whether we should skip whitespace
|
||||
function lexer.expecting (tok,expected_type,no_skip_ws)
|
||||
assert_arg(1,tok,'function')
|
||||
assert_arg(2,expected_type,'string')
|
||||
local t,v
|
||||
if no_skip_ws then
|
||||
t,v = tok()
|
||||
else
|
||||
t,v = skipws(tok)
|
||||
end
|
||||
if t ~= expected_type then error ("expecting "..expected_type,2) end
|
||||
return v
|
||||
end
|
||||
|
||||
return lexer
|
1357
Documentation/compiler/ldoc/ldoc/markdown.lua
Normal file
1357
Documentation/compiler/ldoc/ldoc/markdown.lua
Normal file
File diff suppressed because it is too large
Load diff
420
Documentation/compiler/ldoc/ldoc/markup.lua
Normal file
420
Documentation/compiler/ldoc/ldoc/markup.lua
Normal file
|
@ -0,0 +1,420 @@
|
|||
--------------
|
||||
-- Handling markup transformation.
|
||||
-- Currently just does Markdown, but this is intended to
|
||||
-- be the general module for managing other formats as well.
|
||||
|
||||
local doc = require 'ldoc.doc'
|
||||
local utils = require 'pl.utils'
|
||||
local stringx = require 'pl.stringx'
|
||||
local prettify = require 'ldoc.prettify'
|
||||
local concat = table.concat
|
||||
local markup = {}
|
||||
|
||||
local backtick_references
|
||||
|
||||
-- inline <references> use same lookup as @see
|
||||
local function resolve_inline_references (ldoc, txt, item, plain)
|
||||
local do_escape = not plain and not ldoc.dont_escape_underscore
|
||||
local res = (txt:gsub('@{([^}]-)}',function (name)
|
||||
if name:match '^\\' then return '@{'..name:sub(2)..'}' end
|
||||
local qname,label = utils.splitv(name,'%s*|')
|
||||
if not qname then
|
||||
qname = name
|
||||
end
|
||||
local ref, err
|
||||
local custom_ref, refname = utils.splitv(qname,':')
|
||||
if custom_ref and ldoc.custom_references then
|
||||
custom_ref = ldoc.custom_references[custom_ref]
|
||||
if custom_ref then
|
||||
ref,err = custom_ref(refname)
|
||||
end
|
||||
end
|
||||
if not ref then
|
||||
ref,err = markup.process_reference(qname)
|
||||
end
|
||||
if not ref then
|
||||
err = err .. ' ' .. qname
|
||||
if item and item.warning then item:warning(err)
|
||||
else
|
||||
io.stderr:write('nofile error: ',err,'\n')
|
||||
end
|
||||
return '???'
|
||||
end
|
||||
if not label then
|
||||
label = ref.label
|
||||
end
|
||||
if label and do_escape then -- a nastiness with markdown.lua and underscores
|
||||
label = label:gsub('_','\\_')
|
||||
end
|
||||
local html = ldoc.href(ref) or '#'
|
||||
label = ldoc.escape(label or qname)
|
||||
local res = ('<a href="%s">%s</a>'):format(html,label)
|
||||
return res
|
||||
end))
|
||||
if backtick_references then
|
||||
res = res:gsub('`([^`]+)`',function(name)
|
||||
local ref,_ = markup.process_reference(name)
|
||||
local label = name
|
||||
if name and do_escape then
|
||||
label = name:gsub('_', '\\_')
|
||||
end
|
||||
label = ldoc.escape(label)
|
||||
if ref then
|
||||
return ('<a href="%s">%s</a>'):format(ldoc.href(ref),label)
|
||||
else
|
||||
return '<code>'..label..'</code>'
|
||||
end
|
||||
end)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
-- for readme text, the idea here is to create module sections at ## so that
|
||||
-- they can appear in the contents list as a ToC.
|
||||
function markup.add_sections(F, txt)
|
||||
local sections, L, first = {}, 1, true
|
||||
local title_pat
|
||||
local lstrip = stringx.lstrip
|
||||
for line in stringx.lines(txt) do
|
||||
if first then
|
||||
local level,header = line:match '^(#+)%s*(.+)'
|
||||
if level then
|
||||
level = level .. '#'
|
||||
else
|
||||
level = '##'
|
||||
end
|
||||
title_pat = '^'..level..'([^#]%s*.+)'
|
||||
title_pat = lstrip(title_pat)
|
||||
first = false
|
||||
F.display_name = header
|
||||
end
|
||||
local title = line:match (title_pat)
|
||||
if title then
|
||||
--- Windows line endings are the cockroaches of text
|
||||
title = title:gsub('\r$','')
|
||||
-- Markdown allows trailing '#'...
|
||||
title = title:gsub('%s*#+$','')
|
||||
sections[L] = F:add_document_section(lstrip(title))
|
||||
end
|
||||
L = L + 1
|
||||
end
|
||||
F.sections = sections
|
||||
return txt
|
||||
end
|
||||
|
||||
local function indent_line (line)
|
||||
line = line:gsub('\t',' ') -- support for barbarians ;)
|
||||
local indent = #line:match '^%s*'
|
||||
return indent,line
|
||||
end
|
||||
|
||||
local function blank (line)
|
||||
return not line:find '%S'
|
||||
end
|
||||
|
||||
local global_context, local_context
|
||||
|
||||
-- before we pass Markdown documents to markdown/discount, we need to do three things:
|
||||
-- - resolve any @{refs} and (optionally) `refs`
|
||||
-- - any @lookup directives that set local context for ref lookup
|
||||
-- - insert any section ids which were generated by add_sections above
|
||||
-- - prettify any code blocks
|
||||
|
||||
local function process_multiline_markdown(ldoc, txt, F, filename, deflang)
|
||||
local res, L, append = {}, 0, table.insert
|
||||
local err_item = {
|
||||
warning = function (self,msg)
|
||||
io.stderr:write(filename..':'..L..': '..msg,'\n')
|
||||
end
|
||||
}
|
||||
local get = stringx.lines(txt)
|
||||
local getline = function()
|
||||
L = L + 1
|
||||
return get()
|
||||
end
|
||||
local function pretty_code (code, lang)
|
||||
code = concat(code,'\n')
|
||||
if code ~= '' then
|
||||
local _
|
||||
-- If we omit the following '\n', a '--' (or '//') comment on the
|
||||
-- last line won't be recognized.
|
||||
code, _ = prettify.code(lang,filename,code..'\n',L,false)
|
||||
code = resolve_inline_references(ldoc, code, err_item,true)
|
||||
append(res,'<pre>')
|
||||
append(res, code)
|
||||
append(res,'</pre>')
|
||||
else
|
||||
append(res,code)
|
||||
end
|
||||
end
|
||||
local indent,start_indent
|
||||
local_context = nil
|
||||
local line = getline()
|
||||
while line do
|
||||
local name = line:match '^@lookup%s+(%S+)'
|
||||
if name then
|
||||
local_context = name .. '.'
|
||||
line = getline()
|
||||
end
|
||||
local fence = line:match '^```(.*)'
|
||||
if fence then
|
||||
local plain = fence==''
|
||||
line = getline()
|
||||
local code = {}
|
||||
while not line:match '^```' do
|
||||
if not plain then
|
||||
append(code, line)
|
||||
else
|
||||
append(res, ' '..line)
|
||||
end
|
||||
line = getline()
|
||||
end
|
||||
pretty_code (code,fence)
|
||||
line = getline() -- skip fence
|
||||
if not line then break end
|
||||
end
|
||||
indent, line = indent_line(line)
|
||||
if indent >= 4 then -- indented code block
|
||||
local code = {}
|
||||
local plain
|
||||
while indent >= 4 or blank(line) do
|
||||
if not start_indent then
|
||||
start_indent = indent
|
||||
if line:match '^%s*@plain%s*$' then
|
||||
plain = true
|
||||
line = getline()
|
||||
end
|
||||
end
|
||||
if not plain then
|
||||
append(code,line:sub(start_indent + 1))
|
||||
else
|
||||
append(res,line)
|
||||
end
|
||||
line = getline()
|
||||
if line == nil then break end
|
||||
indent, line = indent_line(line)
|
||||
end
|
||||
start_indent = nil
|
||||
while #code > 1 and blank(code[#code]) do -- trim blank lines.
|
||||
table.remove(code)
|
||||
end
|
||||
pretty_code (code,deflang)
|
||||
else
|
||||
local section = F and F.sections[L]
|
||||
if section then
|
||||
append(res,('<a name="%s"></a>'):format(section))
|
||||
end
|
||||
line = resolve_inline_references(ldoc, line, err_item)
|
||||
append(res,line)
|
||||
line = getline()
|
||||
end
|
||||
end
|
||||
res = concat(res,'\n')
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
-- Handle markdown formatters
|
||||
-- Try to get the one the user has asked for, but if it's not available,
|
||||
-- try all the others we know about. If they don't work, fall back to text.
|
||||
|
||||
local function generic_formatter(format)
|
||||
local ok, f = pcall(require, format)
|
||||
return ok and f
|
||||
end
|
||||
|
||||
|
||||
local formatters =
|
||||
{
|
||||
markdown = function(format)
|
||||
local ok, markdown = pcall(require, 'markdown')
|
||||
if not ok then
|
||||
print('format: using built-in markdown')
|
||||
ok, markdown = pcall(require, 'ldoc.markdown')
|
||||
end
|
||||
return ok and markdown
|
||||
end,
|
||||
discount = function(format)
|
||||
local ok, markdown = pcall(require, 'discount')
|
||||
if ok then
|
||||
-- luacheck: push ignore 542
|
||||
if 'function' == type(markdown) then
|
||||
-- lua-discount by A.S. Bradbury, https://luarocks.org/modules/luarocks/lua-discount
|
||||
elseif 'table' == type(markdown) and ('function' == type(markdown.compile) or 'function' == type(markdown.to_html)) then
|
||||
-- discount by Craig Barnes, https://luarocks.org/modules/craigb/discount
|
||||
-- result of apt-get install lua-discount (links against libmarkdown2)
|
||||
local mysterious_debian_variant = markdown.to_html ~= nil
|
||||
markdown = markdown.compile or markdown.to_html
|
||||
return function(text)
|
||||
local result, errmsg = markdown(text)
|
||||
if result then
|
||||
if mysterious_debian_variant then
|
||||
return result
|
||||
else
|
||||
return result.body
|
||||
end
|
||||
else
|
||||
io.stderr:write('LDoc discount failed with error ',errmsg)
|
||||
os.exit(1)
|
||||
end
|
||||
end
|
||||
else
|
||||
ok = false
|
||||
end
|
||||
-- luacheck: pop
|
||||
end
|
||||
if not ok then
|
||||
print('format: using built-in markdown')
|
||||
ok, markdown = pcall(require, 'ldoc.markdown')
|
||||
end
|
||||
return ok and markdown
|
||||
end,
|
||||
lunamark = function(format)
|
||||
local ok, lunamark = pcall(require, format)
|
||||
if ok then
|
||||
local writer = lunamark.writer.html.new()
|
||||
local parse = lunamark.reader.markdown.new(writer,
|
||||
{ smart = true })
|
||||
return function(text) return parse(text) end
|
||||
end
|
||||
end,
|
||||
commonmark = function(format)
|
||||
local ok, cmark = pcall(require, 'cmark')
|
||||
if ok then
|
||||
return function(text)
|
||||
local doc = cmark.parse_document(text, string.len(text), cmark.OPT_DEFAULT)
|
||||
return cmark.render_html(doc, cmark.OPT_DEFAULT)
|
||||
end
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
local function get_formatter(format)
|
||||
local used_format = format
|
||||
local formatter = (formatters[format] or generic_formatter)(format)
|
||||
if not formatter then -- try another equivalent processor
|
||||
for name, f in pairs(formatters) do
|
||||
formatter = f(name)
|
||||
if formatter then
|
||||
print('format: '..format..' not found, using '..name)
|
||||
used_format = name
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return formatter, used_format
|
||||
end
|
||||
|
||||
local function text_processor(ldoc)
|
||||
return function(txt,item)
|
||||
if txt == nil then return '' end
|
||||
-- hack to separate paragraphs with blank lines
|
||||
txt = txt:gsub('\n\n','\n<p>')
|
||||
return resolve_inline_references(ldoc, txt, item, true)
|
||||
end
|
||||
end
|
||||
|
||||
local plain_processor
|
||||
|
||||
local function markdown_processor(ldoc, formatter)
|
||||
return function (txt,item,plain)
|
||||
if txt == nil then return '' end
|
||||
if plain then
|
||||
if not plain_processor then
|
||||
plain_processor = text_processor(ldoc)
|
||||
end
|
||||
return plain_processor(txt,item)
|
||||
end
|
||||
local is_file = utils.is_type(item,doc.File)
|
||||
local is_module = not is_file and item and doc.project_level(item.type)
|
||||
if is_file or is_module then
|
||||
local deflang = 'lua'
|
||||
if ldoc.parse_extra and ldoc.parse_extra.C then
|
||||
deflang = 'c'
|
||||
end
|
||||
if is_module then
|
||||
txt = process_multiline_markdown(ldoc, txt, nil, item.file.filename, deflang)
|
||||
else
|
||||
txt = process_multiline_markdown(ldoc, txt, item, item.filename, deflang)
|
||||
end
|
||||
else
|
||||
txt = resolve_inline_references(ldoc, txt, item)
|
||||
end
|
||||
txt = formatter(txt)
|
||||
-- We will add our own paragraph tags, if needed.
|
||||
return (txt:gsub('^%s*<p>(.+)</p>%s*$','%1'))
|
||||
end
|
||||
end
|
||||
|
||||
local function get_processor(ldoc, format)
|
||||
if format == 'plain' then return text_processor(ldoc) end
|
||||
|
||||
local formatter,actual_format = get_formatter(format)
|
||||
if formatter then
|
||||
markup.plain = false
|
||||
-- AFAIK only markdown.lua has underscore-in-identifier problem...
|
||||
if ldoc.dont_escape_underscore ~= nil then
|
||||
ldoc.dont_escape_underscore = actual_format ~= 'markdown'
|
||||
end
|
||||
return markdown_processor(ldoc, formatter)
|
||||
end
|
||||
|
||||
print('format: '..format..' not found, falling back to text')
|
||||
return text_processor(ldoc)
|
||||
end
|
||||
|
||||
|
||||
function markup.create (ldoc, format, pretty, user_keywords)
|
||||
local processor
|
||||
markup.plain = true
|
||||
if format == 'backtick' then
|
||||
ldoc.backtick_references = true
|
||||
format = 'plain'
|
||||
end
|
||||
backtick_references = ldoc.backtick_references
|
||||
global_context = ldoc.package and ldoc.package .. '.'
|
||||
prettify.set_prettifier(pretty)
|
||||
prettify.set_user_keywords(user_keywords)
|
||||
|
||||
markup.process_reference = function(name,istype)
|
||||
if local_context == 'none.' and not name:match '%.' then
|
||||
return nil,'not found'
|
||||
end
|
||||
local mod = ldoc.single or ldoc.module or ldoc.modules[1]
|
||||
local ref,err = mod:process_see_reference(name, ldoc.modules, istype)
|
||||
if ref then return ref end
|
||||
if global_context then
|
||||
local qname = global_context .. name
|
||||
ref = mod:process_see_reference(qname, ldoc.modules, istype)
|
||||
if ref then return ref end
|
||||
end
|
||||
if local_context then
|
||||
local qname = local_context .. name
|
||||
ref = mod:process_see_reference(qname, ldoc.modules, istype)
|
||||
if ref then return ref end
|
||||
end
|
||||
-- note that we'll return the original error!
|
||||
return ref,err
|
||||
end
|
||||
|
||||
markup.href = function(ref)
|
||||
return ldoc.href(ref)
|
||||
end
|
||||
|
||||
processor = get_processor(ldoc, format)
|
||||
if not markup.plain and backtick_references == nil then
|
||||
backtick_references = true
|
||||
end
|
||||
|
||||
markup.resolve_inline_references = function(txt, errfn)
|
||||
return resolve_inline_references(ldoc, txt, errfn, markup.plain)
|
||||
end
|
||||
markup.processor = processor
|
||||
prettify.resolve_inline_references = function(txt, errfn)
|
||||
return resolve_inline_references(ldoc, txt, errfn, true)
|
||||
end
|
||||
return processor
|
||||
end
|
||||
|
||||
return markup
|
430
Documentation/compiler/ldoc/ldoc/parse.lua
Normal file
430
Documentation/compiler/ldoc/ldoc/parse.lua
Normal file
|
@ -0,0 +1,430 @@
|
|||
-- parsing code for doc comments
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local List = require 'pl.List'
|
||||
-- local Map = require 'pl.Map'
|
||||
local stringio = require 'pl.stringio'
|
||||
local lexer = require 'ldoc.lexer'
|
||||
local tools = require 'ldoc.tools'
|
||||
local doc = require 'ldoc.doc'
|
||||
local Item,File = doc.Item,doc.File
|
||||
local unpack = utils.unpack
|
||||
|
||||
------ Parsing the Source --------------
|
||||
-- This uses the lexer from PL, but it should be possible to use Peter Odding's
|
||||
-- excellent Lpeg based lexer instead.
|
||||
|
||||
local parse = {}
|
||||
|
||||
local tnext, append = lexer.skipws, table.insert
|
||||
|
||||
-- a pattern particular to LuaDoc tag lines: the line must begin with @TAG,
|
||||
-- followed by the value, which may extend over several lines.
|
||||
local luadoc_tag = '^%s*@(%w+)'
|
||||
local luadoc_tag_value = luadoc_tag..'(.*)'
|
||||
local luadoc_tag_mod_and_value = luadoc_tag..'%[([^%]]*)%](.*)'
|
||||
|
||||
-- assumes that the doc comment consists of distinct tag lines
|
||||
local function parse_at_tags(text)
|
||||
local lines = stringio.lines(text)
|
||||
local preamble, line = tools.grab_while_not(lines,luadoc_tag)
|
||||
local tag_items = {}
|
||||
local follows
|
||||
while line do
|
||||
local tag, mod_string, rest = line :match(luadoc_tag_mod_and_value)
|
||||
if not tag then tag, rest = line :match (luadoc_tag_value) end
|
||||
local modifiers
|
||||
if mod_string then
|
||||
modifiers = { }
|
||||
for x in mod_string :gmatch "[^,]+" do
|
||||
local k, v = x :match "^([^=]+)=(.*)$"
|
||||
if not k then k, v = x, true end -- wuz x, x
|
||||
modifiers[k] = v
|
||||
end
|
||||
end
|
||||
-- follows: end of current tag
|
||||
-- line: beginning of next tag (for next iteration)
|
||||
follows, line = tools.grab_while_not(lines,luadoc_tag)
|
||||
append(tag_items,{tag, rest .. '\n' .. follows, modifiers})
|
||||
end
|
||||
return preamble,tag_items
|
||||
end
|
||||
|
||||
--local colon_tag = '%s*(%a+):%s'
|
||||
local colon_tag = '%s*(%S-):%s'
|
||||
local colon_tag_value = colon_tag..'(.*)'
|
||||
|
||||
local function parse_colon_tags (text)
|
||||
local lines = stringio.lines(text)
|
||||
local preamble, line = tools.grab_while_not(lines,colon_tag)
|
||||
local tag_items, follows = {}
|
||||
while line do
|
||||
local tag, rest = line:match(colon_tag_value)
|
||||
follows, line = tools.grab_while_not(lines,colon_tag)
|
||||
local value = rest .. '\n' .. follows
|
||||
if tag:match '^[%?!]' then
|
||||
tag = tag:gsub('^!','')
|
||||
value = tag .. ' ' .. value
|
||||
tag = 'tparam'
|
||||
end
|
||||
append(tag_items,{tag, value})
|
||||
end
|
||||
return preamble,tag_items
|
||||
end
|
||||
|
||||
-- Tags are stored as an ordered multi map from strings to strings
|
||||
-- If the same key is used, then the value becomes a list
|
||||
local Tags = {}
|
||||
Tags.__index = Tags
|
||||
|
||||
function Tags.new (t,name)
|
||||
local class
|
||||
if name then
|
||||
class = t
|
||||
t = {}
|
||||
end
|
||||
t._order = List()
|
||||
local tags = setmetatable(t,Tags)
|
||||
if name then
|
||||
tags:add('class',class)
|
||||
tags:add('name',name)
|
||||
end
|
||||
return tags
|
||||
end
|
||||
|
||||
function Tags:add (tag,value,modifiers)
|
||||
if modifiers then -- how modifiers are encoded
|
||||
value = {value,modifiers=modifiers}
|
||||
end
|
||||
local ovalue = self:get(tag)
|
||||
if ovalue then
|
||||
ovalue:append(value)
|
||||
value = ovalue
|
||||
end
|
||||
rawset(self,tag,value)
|
||||
if not ovalue then
|
||||
self._order:append(tag)
|
||||
end
|
||||
end
|
||||
|
||||
function Tags:get (tag)
|
||||
local ovalue = rawget(self,tag)
|
||||
if ovalue then -- previous value?
|
||||
if getmetatable(ovalue) ~= List then
|
||||
ovalue = List{ovalue}
|
||||
end
|
||||
return ovalue
|
||||
end
|
||||
end
|
||||
|
||||
function Tags:iter ()
|
||||
return self._order:iter()
|
||||
end
|
||||
|
||||
local function comment_contains_tags (comment,args)
|
||||
return (args.colon and comment:find ': ') or (not args.colon and comment:find '@')
|
||||
end
|
||||
|
||||
-- This takes the collected comment block, and uses the docstyle to
|
||||
-- extract tags and values. Assume that the summary ends in a period or a question
|
||||
-- mark, and everything else in the preamble is the description.
|
||||
-- If a tag appears more than once, then its value becomes a list of strings.
|
||||
-- Alias substitution and @TYPE NAME shortcutting is handled by Item.check_tag
|
||||
local function extract_tags (s,args)
|
||||
local preamble,tag_items
|
||||
if s:match '^%s*$' then return {} end
|
||||
if args.colon then --and s:match ':%s' and not s:match '@%a' then
|
||||
preamble,tag_items = parse_colon_tags(s)
|
||||
else
|
||||
preamble,tag_items = parse_at_tags(s)
|
||||
end
|
||||
local strip = tools.strip
|
||||
local summary, description = preamble:match('^(.-[%.?])(%s.+)')
|
||||
if not summary then
|
||||
-- perhaps the first sentence did not have a . or ? terminating it.
|
||||
-- Then try split at linefeed
|
||||
summary, description = preamble:match('^(.-\n\n)(.+)')
|
||||
if not summary then
|
||||
summary = preamble
|
||||
end
|
||||
end -- and strip(description) ?
|
||||
local tags = Tags.new{summary=summary and strip(summary) or '',description=description or ''}
|
||||
for _,item in ipairs(tag_items) do
|
||||
local tag, value, modifiers = Item.check_tag(tags,unpack(item))
|
||||
-- treat multiline values more gently..
|
||||
if not value:match '\n[^\n]+\n' then
|
||||
value = strip(value)
|
||||
end
|
||||
|
||||
tags:add(tag,value,modifiers)
|
||||
end
|
||||
return tags --Map(tags)
|
||||
end
|
||||
|
||||
|
||||
-- parses a Lua or C file, looking for ldoc comments. These are like LuaDoc comments;
|
||||
-- they start with multiple '-'. (Block commments are allowed)
|
||||
-- If they don't define a name tag, then by default
|
||||
-- it is assumed that a function definition follows. If it is the first comment
|
||||
-- encountered, then ldoc looks for a call to module() to find the name of the
|
||||
-- module if there isn't an explicit module name specified.
|
||||
|
||||
local function parse_file(fname, lang, package, args)
|
||||
local F = File(fname)
|
||||
local module_found, first_comment = false,true
|
||||
local current_item, module_item
|
||||
|
||||
F.args = args
|
||||
F.lang = lang
|
||||
F.base = package
|
||||
|
||||
local tok,f = lang.lexer(fname)
|
||||
if not tok then return nil end
|
||||
|
||||
local function lineno ()
|
||||
return tok:lineno()
|
||||
end
|
||||
|
||||
function F:warning (msg,kind,line)
|
||||
line = line or lineno()
|
||||
Item.had_warning = true
|
||||
io.stderr:write(fname..':'..line..': '..msg,'\n')
|
||||
end
|
||||
|
||||
function F:error (msg)
|
||||
self:warning(msg,'error')
|
||||
io.stderr:write('LDoc error\n')
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function add_module(tags,module_found,old_style)
|
||||
tags:add('name',module_found)
|
||||
tags:add('class','module')
|
||||
local item = F:new_item(tags,lineno())
|
||||
item.old_style = old_style
|
||||
module_item = item
|
||||
end
|
||||
|
||||
local mod
|
||||
local t,v = tnext(tok)
|
||||
-- with some coding styles first comment is standard boilerplate; option to ignore this.
|
||||
if args.boilerplate and t == 'comment' then
|
||||
-- hack to deal with boilerplate inside Lua block comments
|
||||
if v:match '%s*%-%-%[%[' then lang:grab_block_comment(v,tok) end
|
||||
t,v = tnext(tok)
|
||||
end
|
||||
if t == '#' then -- skip Lua shebang line, if present
|
||||
while t and t ~= 'comment' do t,v = tnext(tok) end
|
||||
if t == nil then
|
||||
F:warning('empty file')
|
||||
return nil
|
||||
end
|
||||
end
|
||||
if lang.parse_module_call and t ~= 'comment' then
|
||||
local prev_token
|
||||
while t do
|
||||
if prev_token ~= '.' and prev_token ~= ':' and t == 'iden' and v == 'module' then
|
||||
break
|
||||
end
|
||||
prev_token = t
|
||||
t, v = tnext(tok)
|
||||
end
|
||||
if not t then
|
||||
if not args.ignore then
|
||||
F:warning("no module() call found; no initial doc comment")
|
||||
end
|
||||
--return nil
|
||||
else
|
||||
mod,t,v = lang:parse_module_call(tok,t,v)
|
||||
if mod and mod ~= '...' then
|
||||
add_module(Tags.new{summary='(no description)'},mod,true)
|
||||
first_comment = false
|
||||
module_found = true
|
||||
end
|
||||
end
|
||||
end
|
||||
local ok, err = xpcall(function()
|
||||
while t do
|
||||
if t == 'comment' then
|
||||
local comment = {}
|
||||
local ldoc_comment,block = lang:start_comment(v)
|
||||
|
||||
if ldoc_comment and block then
|
||||
t,v = lang:grab_block_comment(v,tok)
|
||||
end
|
||||
|
||||
if lang:empty_comment(v) then -- ignore rest of empty start comments
|
||||
t,v = tok()
|
||||
if t == 'space' and not v:match '\n' then
|
||||
t,v = tok()
|
||||
end
|
||||
end
|
||||
|
||||
while t and t == 'comment' do
|
||||
v = lang:trim_comment(v)
|
||||
append(comment,v)
|
||||
t,v = tok()
|
||||
if t == 'space' and not v:match '\n' then
|
||||
t,v = tok()
|
||||
end
|
||||
end
|
||||
|
||||
if t == 'space' then t,v = tnext(tok) end
|
||||
|
||||
local item_follows, tags, is_local, case, parse_error
|
||||
if ldoc_comment then
|
||||
comment = table.concat(comment)
|
||||
if comment:match '^%s*$' then
|
||||
ldoc_comment = nil
|
||||
end
|
||||
end
|
||||
if ldoc_comment then
|
||||
if first_comment then
|
||||
first_comment = false
|
||||
else
|
||||
item_follows, is_local, case = lang:item_follows(t,v,tok)
|
||||
if not item_follows then
|
||||
parse_error = is_local
|
||||
is_local = false
|
||||
end
|
||||
end
|
||||
|
||||
if item_follows or comment_contains_tags(comment,args) then
|
||||
tags = extract_tags(comment,args)
|
||||
|
||||
-- explicitly named @module (which is recommended)
|
||||
if doc.project_level(tags.class) then
|
||||
module_found = tags.name
|
||||
-- might be a module returning a single function!
|
||||
if tags.param or tags['return'] then
|
||||
local parms, ret = tags.param, tags['return']
|
||||
local name = tags.name
|
||||
tags.param = nil
|
||||
tags['return'] = nil
|
||||
tags['class'] = nil
|
||||
tags['name'] = nil
|
||||
add_module(tags,name,false)
|
||||
tags = {
|
||||
summary = '',
|
||||
name = 'returns...',
|
||||
class = 'function',
|
||||
['return'] = ret,
|
||||
param = parms
|
||||
}
|
||||
end
|
||||
end
|
||||
doc.expand_annotation_item(tags,current_item)
|
||||
-- if the item has an explicit name or defined meaning
|
||||
-- then don't continue to do any code analysis!
|
||||
-- Watch out for the case where there are field or param tags
|
||||
-- but no class, since these will be fixed up later as module/class
|
||||
-- entities
|
||||
if (tags.field or tags.param) and not tags.class then
|
||||
parse_error = false
|
||||
end
|
||||
if tags.name then
|
||||
if not tags.class then
|
||||
F:warning("no type specified, assuming function: '"..tags.name.."'")
|
||||
tags:add('class','function')
|
||||
end
|
||||
item_follows, is_local, parse_error = false, false, false
|
||||
elseif args.no_args_infer then
|
||||
F:error("No name and type provided (no_args_infer)")
|
||||
elseif lang:is_module_modifier (tags) then
|
||||
if not item_follows then
|
||||
F:warning("@usage or @export followed by unknown code")
|
||||
break
|
||||
end
|
||||
item_follows(tags,tok)
|
||||
local res, value, tagname = lang:parse_module_modifier(tags,tok,F)
|
||||
if not res then F:warning(value); break
|
||||
else
|
||||
if tagname then
|
||||
module_item:set_tag(tagname,value)
|
||||
end
|
||||
-- don't continue to make an item!
|
||||
ldoc_comment = false
|
||||
end
|
||||
end
|
||||
end
|
||||
if parse_error then
|
||||
F:warning('definition cannot be parsed - '..parse_error)
|
||||
end
|
||||
end
|
||||
-- some hackery necessary to find the module() call
|
||||
if not module_found and ldoc_comment then
|
||||
local old_style
|
||||
module_found,t,v = lang:find_module(tok,t,v)
|
||||
-- right, we can add the module object ...
|
||||
old_style = module_found ~= nil
|
||||
if not module_found or module_found == '...' then
|
||||
-- we have to guess the module name
|
||||
module_found = tools.this_module_name(package,fname)
|
||||
end
|
||||
if not tags then tags = extract_tags(comment,args) end
|
||||
add_module(tags,module_found,old_style)
|
||||
tags = nil
|
||||
if not t then
|
||||
F:warning('contains no items','warning',1)
|
||||
break;
|
||||
end -- run out of file!
|
||||
-- if we did bump into a doc comment, then we can continue parsing it
|
||||
end
|
||||
|
||||
-- end of a block of document comments
|
||||
if ldoc_comment and tags then
|
||||
local line = lineno()
|
||||
if t ~= nil then
|
||||
if item_follows then -- parse the item definition
|
||||
local err = item_follows(tags,tok)
|
||||
if err then F:error(err) end
|
||||
elseif parse_error then
|
||||
F:warning('definition cannot be parsed - '..parse_error)
|
||||
else
|
||||
lang:parse_extra(tags,tok,case)
|
||||
end
|
||||
end
|
||||
if is_local or tags['local'] then
|
||||
tags:add('local',true)
|
||||
end
|
||||
-- support for standalone fields/properties of classes/modules
|
||||
if (tags.field or tags.param) and not tags.class then
|
||||
-- the hack is to take a subfield and pull out its name,
|
||||
-- (see Tag:add above) but let the subfield itself go through
|
||||
-- with any modifiers.
|
||||
local fp = tags.field or tags.param
|
||||
if type(fp) == 'table' then fp = fp[1] end
|
||||
fp = tools.extract_identifier(fp)
|
||||
tags:add('name',fp)
|
||||
tags:add('class','field')
|
||||
end
|
||||
if tags.name then
|
||||
current_item = F:new_item(tags,line)
|
||||
current_item.inferred = item_follows ~= nil
|
||||
if doc.project_level(tags.class) then
|
||||
if module_item then
|
||||
F:error("Module already declared!")
|
||||
end
|
||||
module_item = current_item
|
||||
end
|
||||
end
|
||||
if not t then break end
|
||||
end
|
||||
end
|
||||
if t ~= 'comment' then t,v = tok() end
|
||||
end
|
||||
end,debug.traceback)
|
||||
if not ok then return F, err end
|
||||
if f then f:close() end
|
||||
return F
|
||||
end
|
||||
|
||||
function parse.file(name,lang, args)
|
||||
local F,err = parse_file(name,lang,args.package,args)
|
||||
if err or not F then return F,err end
|
||||
local ok,err = xpcall(function() F:finish() end,debug.traceback)
|
||||
if not ok then return F,err end
|
||||
return F
|
||||
end
|
||||
|
||||
return parse
|
131
Documentation/compiler/ldoc/ldoc/prettify.lua
Normal file
131
Documentation/compiler/ldoc/ldoc/prettify.lua
Normal file
|
@ -0,0 +1,131 @@
|
|||
-- Making Lua source code look pretty.
|
||||
-- A simple scanner based prettifier, which scans comments for @{ref} and code
|
||||
-- for known modules and functions.
|
||||
-- A module reference to an example `test-fun.lua` would look like
|
||||
-- `@{example:test-fun}`.
|
||||
local List = require 'pl.List'
|
||||
local tablex = require 'pl.tablex'
|
||||
local globals = require 'ldoc.builtin.globals'
|
||||
local prettify = {}
|
||||
|
||||
local user_keywords = {}
|
||||
|
||||
local escaped_chars = {
|
||||
['&'] = '&',
|
||||
['<'] = '<',
|
||||
['>'] = '>',
|
||||
}
|
||||
local escape_pat = '[&<>]'
|
||||
|
||||
local function escape(str)
|
||||
return (str:gsub(escape_pat,escaped_chars))
|
||||
end
|
||||
|
||||
local function span(t,val)
|
||||
return ('<span class="%s">%s</span>'):format(t,val)
|
||||
end
|
||||
|
||||
local spans = {keyword=true,number=true,string=true,comment=true,global=true,backtick=true}
|
||||
|
||||
local cpp_lang = {C = true, c = true, cpp = true, cxx = true, h = true}
|
||||
|
||||
function prettify.lua (lang, fname, code, initial_lineno, pre, linenos)
|
||||
local res, lexer = List(), require 'ldoc.lexer'
|
||||
local tokenizer
|
||||
local ik = 1
|
||||
if not cpp_lang[lang] then
|
||||
tokenizer = lexer.lua
|
||||
else
|
||||
tokenizer = lexer.cpp
|
||||
end
|
||||
|
||||
if pre then
|
||||
res:append '<pre>\n'
|
||||
end
|
||||
initial_lineno = initial_lineno or 0
|
||||
|
||||
local tok = tokenizer(code,{},{})
|
||||
local error_reporter = {
|
||||
warning = function (self,msg)
|
||||
io.stderr:write(fname..':'..tok:lineno()+initial_lineno..': '..msg,'\n')
|
||||
end
|
||||
}
|
||||
local last_t, last_val
|
||||
local t,val = tok()
|
||||
if not t then return nil,"empty file" end
|
||||
while t do
|
||||
val = escape(val)
|
||||
if linenos and tok:lineno() == linenos[ik] then
|
||||
res:append('<a id="'..linenos[ik]..'"></a>')
|
||||
ik = ik + 1
|
||||
end
|
||||
if globals.functions[val] or globals.tables[val] then
|
||||
t = 'global'
|
||||
end
|
||||
if user_keywords[val] then
|
||||
res:append(span('user-keyword keyword-' .. val,val))
|
||||
elseif spans[t] then
|
||||
if t == 'comment' or t == 'backtick' then -- may contain @{ref} or `..`
|
||||
val = prettify.resolve_inline_references(val,error_reporter)
|
||||
end
|
||||
res:append(span(t,val))
|
||||
else
|
||||
res:append(val)
|
||||
end
|
||||
last_t, last_val = t,val
|
||||
t,val = tok()
|
||||
end
|
||||
if last_t == 'comment' then
|
||||
res[#res] = span('comment',last_val:gsub('\r*\n$',''))
|
||||
end
|
||||
local last = res[#res]
|
||||
if last:match '\n$' then
|
||||
res[#res] = last:gsub('\n+','')
|
||||
end
|
||||
if pre then
|
||||
res:append '</pre>\n'
|
||||
end
|
||||
return res:join ()
|
||||
end
|
||||
|
||||
local lxsh
|
||||
|
||||
local lxsh_highlighers = {bib=true,c=true,lua=true,sh=true}
|
||||
|
||||
function prettify.code (lang,fname,code,initial_lineno,pre)
|
||||
if not lxsh then
|
||||
return prettify.lua (lang,fname, code, initial_lineno, pre)
|
||||
else
|
||||
if not lxsh_highlighers[lang] then
|
||||
lang = 'lua'
|
||||
end
|
||||
code = lxsh.highlighters[lang](code, {
|
||||
formatter = lxsh.formatters.html,
|
||||
external = true
|
||||
})
|
||||
if not pre then
|
||||
code = code:gsub("^<pre.->(.-)%s*</pre>$", '%1')
|
||||
end
|
||||
return code
|
||||
end
|
||||
end
|
||||
|
||||
function prettify.set_prettifier (pretty)
|
||||
local ok
|
||||
if pretty == 'lxsh' then
|
||||
ok,lxsh = pcall(require,'lxsh')
|
||||
if not ok then
|
||||
print('pretty: '..pretty..' not found, using built-in Lua')
|
||||
lxsh = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function prettify.set_user_keywords(keywords)
|
||||
if keywords then
|
||||
user_keywords = tablex.makeset(keywords)
|
||||
end
|
||||
end
|
||||
|
||||
return prettify
|
||||
|
544
Documentation/compiler/ldoc/ldoc/tools.lua
Normal file
544
Documentation/compiler/ldoc/ldoc/tools.lua
Normal file
|
@ -0,0 +1,544 @@
|
|||
---------
|
||||
-- General utility functions for ldoc
|
||||
-- @module tools
|
||||
|
||||
local class = require 'pl.class'
|
||||
local List = require 'pl.List'
|
||||
local path = require 'pl.path'
|
||||
local utils = require 'pl.utils'
|
||||
local tablex = require 'pl.tablex'
|
||||
local stringx = require 'pl.stringx'
|
||||
local dir = require 'pl.dir'
|
||||
local tools = {}
|
||||
local M = tools
|
||||
local append = table.insert
|
||||
local lexer = require 'ldoc.lexer'
|
||||
local quit = utils.quit
|
||||
|
||||
-- at rendering time, can access the ldoc table from any module item,
|
||||
-- or the item itself if it's a module
|
||||
function M.item_ldoc (item)
|
||||
local mod = item and (item.module or item)
|
||||
return mod and mod.ldoc
|
||||
end
|
||||
|
||||
-- this constructs an iterator over a list of objects which returns only
|
||||
-- those objects where a field has a certain value. It's used to iterate
|
||||
-- only over functions or tables, etc. If the list of item has a module
|
||||
-- with a context, then use that to pre-sort the fltered items.
|
||||
-- (something rather similar exists in LuaDoc)
|
||||
function M.type_iterator (list,field,value)
|
||||
return function()
|
||||
local fls = list:filter(function(item)
|
||||
return item[field] == value
|
||||
end)
|
||||
local ldoc = M.item_ldoc(fls[1])
|
||||
if ldoc and ldoc.sort then
|
||||
fls:sort(function(ia,ib)
|
||||
return ia.name < ib.name
|
||||
end)
|
||||
end
|
||||
return fls:iter()
|
||||
end
|
||||
end
|
||||
|
||||
-- KindMap is used to iterate over a set of categories, called _kinds_,
|
||||
-- and the associated iterator over all items in that category.
|
||||
-- For instance, a module contains functions, tables, etc and we will
|
||||
-- want to iterate over these categories in a specified order:
|
||||
--
|
||||
-- for kind, items in module.kinds() do
|
||||
-- print('kind',kind)
|
||||
-- for item in items() do print(item.name) end
|
||||
-- end
|
||||
--
|
||||
-- The kind is typically used as a label or a Title, so for type 'function' the
|
||||
-- kind is 'Functions' and so on.
|
||||
|
||||
local KindMap = class()
|
||||
M.KindMap = KindMap
|
||||
|
||||
-- calling a KindMap returns an iterator. This returns the kind, the iterator
|
||||
-- over the items of that type, and the actual type tag value.
|
||||
function KindMap:__call ()
|
||||
local i = 1
|
||||
local klass = self.klass
|
||||
return function()
|
||||
local kind = klass.kinds[i]
|
||||
if not kind then return nil end -- no more kinds
|
||||
while not self[kind] do
|
||||
i = i + 1
|
||||
kind = klass.kinds[i]
|
||||
if not kind then return nil end
|
||||
end
|
||||
i = i + 1
|
||||
local type = klass.types_by_kind [kind].type
|
||||
return kind, self[kind], type
|
||||
end
|
||||
end
|
||||
|
||||
function KindMap:put_kind_first (kind)
|
||||
-- find this kind in our kind list
|
||||
local kinds = self.klass.kinds
|
||||
local idx = tablex.find(kinds,kind)
|
||||
-- and swop with the start!
|
||||
if idx then
|
||||
kinds[1],kinds[idx] = kinds[idx],kinds[1]
|
||||
end
|
||||
end
|
||||
|
||||
function KindMap:type_of (item)
|
||||
local klass = self.klass
|
||||
local kind = klass.types_by_tag[item.type]
|
||||
return klass.types_by_kind [kind]
|
||||
end
|
||||
|
||||
function KindMap:get_section_description (kind)
|
||||
return self.klass.descriptions[kind]
|
||||
end
|
||||
|
||||
function KindMap:get_item (kind)
|
||||
return self.klass.items_by_kind[kind]
|
||||
end
|
||||
|
||||
-- called for each new item. It does not actually create separate lists,
|
||||
-- (although that would not break the interface) but creates iterators
|
||||
-- for that item type if not already created.
|
||||
function KindMap:add (item,items,description)
|
||||
local group = item[self.fieldname] -- which wd be item's type or section
|
||||
local kname = self.klass.types_by_tag[group] -- the kind name
|
||||
if not self[kname] then
|
||||
self[kname] = M.type_iterator (items,self.fieldname,group)
|
||||
self.klass.descriptions[kname] = description
|
||||
end
|
||||
item.kind = kname:lower()
|
||||
end
|
||||
|
||||
-- KindMap has a 'class constructor' which is used to modify
|
||||
-- any new base class.
|
||||
function KindMap._class_init (klass)
|
||||
klass.kinds = {} -- list in correct order of kinds
|
||||
klass.types_by_tag = {} -- indexed by tag
|
||||
klass.types_by_kind = {} -- indexed by kind
|
||||
klass.descriptions = {} -- optional description for each kind
|
||||
klass.items_by_kind = {} -- some kinds are items
|
||||
end
|
||||
|
||||
|
||||
function KindMap.add_kind (klass,tag,kind,subnames,item)
|
||||
if not klass.types_by_kind[kind] then
|
||||
klass.types_by_tag[tag] = kind
|
||||
klass.types_by_kind[kind] = {type=tag,subnames=subnames}
|
||||
if item then
|
||||
klass.items_by_kind[kind] = item
|
||||
end
|
||||
append(klass.kinds,kind)
|
||||
end
|
||||
end
|
||||
|
||||
----- some useful utility functions ------
|
||||
|
||||
function M.module_basepath()
|
||||
local lpath = List.split(package.path,';')
|
||||
for p in lpath:iter() do
|
||||
local p = path.dirname(p)
|
||||
if path.isabs(p) then
|
||||
return p
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- split a qualified name into the module part and the name part,
|
||||
-- e.g 'pl.utils.split' becomes 'pl.utils' and 'split'. Also
|
||||
-- must understand colon notation!
|
||||
function M.split_dotted_name (s)
|
||||
local s1,s2 = s:match '^(.+)[%.:](.+)$'
|
||||
if s1 then -- we can split
|
||||
return s1,s2
|
||||
else
|
||||
return nil
|
||||
end
|
||||
--~ local s1,s2 = path.splitext(s)
|
||||
--~ if s2=='' then return nil
|
||||
--~ else return s1,s2:sub(2)
|
||||
--~ end
|
||||
end
|
||||
|
||||
-- grab lines from a line iterator `iter` until the line matches the pattern.
|
||||
-- Returns the joined lines and the line, which may be nil if we run out of
|
||||
-- lines.
|
||||
function M.grab_while_not(iter,pattern)
|
||||
local line = iter()
|
||||
local res = {}
|
||||
while line and not line:match(pattern) do
|
||||
append(res,line)
|
||||
line = iter()
|
||||
end
|
||||
res = table.concat(res,'\n')
|
||||
return res,line
|
||||
end
|
||||
|
||||
|
||||
function M.extract_identifier (value)
|
||||
return value:match('([%.:%-_%w]+)(.*)$')
|
||||
end
|
||||
|
||||
function M.identifier_list (ls)
|
||||
local ns = List()
|
||||
if type(ls) == 'string' then ls = List{ns} end
|
||||
for s in ls:iter() do
|
||||
if s:match ',' then
|
||||
ns:extend(List.split(s,'[,%s]+'))
|
||||
else
|
||||
ns:append(s)
|
||||
end
|
||||
end
|
||||
return ns
|
||||
end
|
||||
|
||||
function M.strip (s)
|
||||
return s:gsub('^%s+',''):gsub('%s+$','')
|
||||
end
|
||||
|
||||
-- Joins strings using a separator.
|
||||
--
|
||||
-- Empty strings and nil arguments are ignored:
|
||||
--
|
||||
-- assert(join('+', 'one', '', 'two', nil, 'three') == 'one+two+three')
|
||||
-- assert(join(' ', '', '') == '')
|
||||
--
|
||||
-- This is especially useful for the last case demonstrated above,
|
||||
-- where "conventional" solutions (".." or table.concat) would result
|
||||
-- in a spurious space.
|
||||
function M.join(sep, ...)
|
||||
local contents = {}
|
||||
for i = 1, select('#', ...) do
|
||||
local value = select(i, ...)
|
||||
if value and value ~= "" then
|
||||
contents[#contents + 1] = value
|
||||
end
|
||||
end
|
||||
return table.concat(contents, sep)
|
||||
end
|
||||
|
||||
function M.check_directory(d)
|
||||
if not path.isdir(d) then
|
||||
if not dir.makepath(d) then
|
||||
quit("Could not create "..d.." directory")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.check_file (f,original)
|
||||
if not path.exists(f)
|
||||
or not path.exists(original)
|
||||
or path.getmtime(original) > path.getmtime(f) then
|
||||
local text,err = utils.readfile(original)
|
||||
local _
|
||||
if text then
|
||||
_,err = utils.writefile(f,text)
|
||||
end
|
||||
if err then
|
||||
quit("Could not copy "..original.." to "..f)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.writefile(name,text)
|
||||
local f,err = io.open(name,"wb")
|
||||
--~ local ok,err = utils.writefile(name,text)
|
||||
if err then quit(err) end
|
||||
f:write(text)
|
||||
f:close()
|
||||
end
|
||||
|
||||
function M.name_of (lpath)
|
||||
local _
|
||||
lpath,_ = path.splitext(lpath)
|
||||
return lpath
|
||||
end
|
||||
|
||||
function M.this_module_name (basename,fname)
|
||||
if basename == '' then
|
||||
return M.name_of(fname)
|
||||
end
|
||||
basename = path.abspath(basename)
|
||||
if basename:sub(-1,-1) ~= path.sep then
|
||||
basename = basename..path.sep
|
||||
end
|
||||
local lpath,cnt = fname:gsub('^'..utils.escape(basename),'')
|
||||
--print('deduce',lpath,cnt,basename)
|
||||
if cnt ~= 1 then quit("module(...) name deduction failed: base "..basename.." "..fname) end
|
||||
lpath = lpath:gsub(path.sep,'.')
|
||||
return (M.name_of(lpath):gsub('%.init$',''))
|
||||
end
|
||||
|
||||
function M.find_existing_module (name, dname, searchfn)
|
||||
local fullpath,lua = searchfn(name)
|
||||
local mod = true
|
||||
if not fullpath then -- maybe it's a function reference?
|
||||
-- try again with the module part
|
||||
local mpath,fname = M.split_dotted_name(name)
|
||||
if mpath then
|
||||
fullpath,lua = searchfn(mpath)
|
||||
else
|
||||
fullpath = nil
|
||||
end
|
||||
if not fullpath then
|
||||
return nil, "module or function '"..dname.."' not found on module path"
|
||||
else
|
||||
mod = fname
|
||||
end
|
||||
end
|
||||
if not lua then return nil, "module '"..name.."' is a binary extension" end
|
||||
return fullpath, mod
|
||||
end
|
||||
|
||||
function M.lookup_existing_module_or_function (name, docpath)
|
||||
-- first look up on the Lua module path
|
||||
local on_docpath
|
||||
local fullpath, mod = M.find_existing_module(name,name,path.package_path)
|
||||
-- no go; but see if we can find it on the doc path
|
||||
if not fullpath then
|
||||
fullpath, mod = M.find_existing_module("ldoc.builtin." .. name,name,path.package_path)
|
||||
on_docpath = true
|
||||
--~ fullpath, mod = M.find_existing_module(name, function(name)
|
||||
--~ local fpath = package.searchpath(name,docpath)
|
||||
--~ return fpath,true -- result must always be 'lua'!
|
||||
--~ end)
|
||||
end
|
||||
return fullpath, mod, on_docpath -- `mod` can be the error message
|
||||
end
|
||||
|
||||
|
||||
--------- lexer tools -----
|
||||
|
||||
local tnext = lexer.skipws
|
||||
|
||||
local function type_of (tok) return tok and tok[1] or 'end' end
|
||||
local function value_of (tok) return tok[2] end
|
||||
|
||||
-- This parses Lua formal argument lists. It will return a list of argument
|
||||
-- names, which also has a comments field, which will contain any commments
|
||||
-- following the arguments. ldoc will use these in addition to explicit
|
||||
-- param tags.
|
||||
|
||||
function M.get_parameters (tok,endtoken,delim,lang)
|
||||
tok = M.space_skip_getter(tok)
|
||||
local args = List()
|
||||
args.comments = {}
|
||||
local ltl,tt = lexer.get_separated_list(tok,endtoken,delim)
|
||||
|
||||
if not ltl or not ltl[1] or #ltl[1] == 0 then return args end -- no arguments
|
||||
|
||||
local strip_comment, extract_arg
|
||||
|
||||
if lang then
|
||||
strip_comment = utils.bind1(lang.trim_comment,lang)
|
||||
extract_arg = utils.bind1(lang.extract_arg,lang)
|
||||
else
|
||||
strip_comment = function(text)
|
||||
return text:match("%s*%-%-+%s*(.*)")
|
||||
end
|
||||
extract_arg = function(tl,idx)
|
||||
idx = idx or 1
|
||||
local res = value_of(tl[idx])
|
||||
if res == '[' then -- we do allow array indices in tables now
|
||||
res = '['..value_of(tl[idx + 1])..']'
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
local function set_comment (idx,tok)
|
||||
local text = stringx.rstrip(value_of(tok))
|
||||
text = strip_comment(text)
|
||||
local arg = args[idx]
|
||||
local current_comment = args.comments[arg]
|
||||
if current_comment then
|
||||
text = current_comment .. " " .. text
|
||||
end
|
||||
args.comments[arg] = text
|
||||
end
|
||||
|
||||
local function add_arg (tl,idx)
|
||||
local name, type = extract_arg(tl,idx)
|
||||
args:append(name)
|
||||
if type then
|
||||
if not args.types then args.types = List() end
|
||||
args.types:append(type)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1,#ltl do
|
||||
local tl = ltl[i] -- token list for argument
|
||||
if #tl > 0 then
|
||||
local j = 1
|
||||
if type_of(tl[1]) == 'comment' then
|
||||
-- the comments for the i-1 th arg are in the i th arg...
|
||||
if i > 1 then
|
||||
while type_of(tl[j]) == 'comment' do
|
||||
set_comment(i-1,tl[j])
|
||||
j = j + 1
|
||||
end
|
||||
else -- first comment however is for the function return comment!
|
||||
args.return_comment = strip_comment(value_of(tl[i]))
|
||||
j = j + 1
|
||||
end
|
||||
if #tl > 1 then
|
||||
add_arg(tl,j)
|
||||
end
|
||||
else
|
||||
add_arg(tl,1)
|
||||
end
|
||||
if i == #ltl and #tl > 1 then
|
||||
while j <= #tl and type_of(tl[j]) ~= 'comment' do
|
||||
j = j + 1
|
||||
end
|
||||
if j > #tl then break end -- was no comments!
|
||||
while type_of(tl[j]) == 'comment' do
|
||||
set_comment(i,tl[j])
|
||||
j = j + 1
|
||||
end
|
||||
end
|
||||
else
|
||||
return nil,"empty argument"
|
||||
end
|
||||
end
|
||||
|
||||
-- we had argument comments
|
||||
-- but the last one may be outside the parens! (Geoff style)
|
||||
-- (only try this stunt if it's a function parameter list!)
|
||||
if (not endtoken or endtoken == ')') and (#args > 0 or next(args.comments)) then
|
||||
local n = #args
|
||||
local last_arg = args[n]
|
||||
if not args.comments[last_arg] then
|
||||
while true do
|
||||
tt = {tok()}
|
||||
if type_of(tt) == 'comment' then
|
||||
set_comment(n,tt)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- return what token we ended on as well - can be token _past_ ')'
|
||||
return args,tt[1],tt[2]
|
||||
end
|
||||
|
||||
-- parse a Lua identifier - contains names separated by . and (optionally) :.
|
||||
-- Set `colon` to be the secondary separator, '' for none.
|
||||
function M.get_fun_name (tok,first,colon)
|
||||
local res = {}
|
||||
local t,name,sep,_
|
||||
colon = colon or ':'
|
||||
if not first then
|
||||
t,name = tnext(tok)
|
||||
else
|
||||
t,name = 'iden',first
|
||||
end
|
||||
if t ~= 'iden' then return nil end
|
||||
t,sep = tnext(tok)
|
||||
while sep == '.' or sep == colon do
|
||||
append(res,name)
|
||||
append(res,sep)
|
||||
_,name = tnext(tok)
|
||||
t,sep = tnext(tok)
|
||||
end
|
||||
append(res,name)
|
||||
return table.concat(res),t,sep
|
||||
end
|
||||
|
||||
-- space-skipping version of token iterator
|
||||
function M.space_skip_getter(tok)
|
||||
return function ()
|
||||
local t,v = tok()
|
||||
while t and t == 'space' do
|
||||
t,v = tok()
|
||||
end
|
||||
return t,v
|
||||
end
|
||||
end
|
||||
|
||||
function M.quote (s)
|
||||
return "'"..s.."'"
|
||||
end
|
||||
|
||||
-- The PL Lua lexer does not do block comments
|
||||
-- when used in line-grabbing mode, so this function grabs each line
|
||||
-- until we meet the end of the comment
|
||||
function M.grab_block_comment (v,tok,patt)
|
||||
local res = {v}
|
||||
repeat
|
||||
v = lexer.getline(tok)
|
||||
if v:match (patt) then break end
|
||||
append(res,v)
|
||||
append(res,'\n')
|
||||
until false
|
||||
res = table.concat(res)
|
||||
--print(res)
|
||||
return 'comment',res
|
||||
end
|
||||
|
||||
local prel = path.normcase('/[^/]-/%.%.')
|
||||
|
||||
|
||||
function M.abspath (f)
|
||||
local count
|
||||
local res = path.normcase(path.abspath(f))
|
||||
while true do
|
||||
res,count = res:gsub(prel,'')
|
||||
if count == 0 then break end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function M.getallfiles(root,mask)
|
||||
local res = List(dir.getallfiles(root,mask))
|
||||
res:sort()
|
||||
return res
|
||||
end
|
||||
|
||||
function M.expand_file_list (list, mask)
|
||||
local exclude_list = list.exclude and M.files_from_list(list.exclude, mask)
|
||||
local files = List()
|
||||
local function process (f)
|
||||
f = M.abspath(f)
|
||||
if not exclude_list or exclude_list and exclude_list:index(f) == nil then
|
||||
files:append(f)
|
||||
end
|
||||
end
|
||||
for _,f in ipairs(list) do
|
||||
if path.isdir(f) then
|
||||
local dfiles = M.getallfiles(f,mask)
|
||||
for f in dfiles:iter() do
|
||||
process(f)
|
||||
end
|
||||
elseif path.isfile(f) then
|
||||
process(f)
|
||||
else
|
||||
quit("file or directory does not exist: "..M.quote(f))
|
||||
end
|
||||
end
|
||||
return files
|
||||
end
|
||||
|
||||
function M.process_file_list (list, mask, operation, ...)
|
||||
local files = M.expand_file_list(list,mask)
|
||||
for f in files:iter() do
|
||||
operation(f,...)
|
||||
end
|
||||
end
|
||||
|
||||
function M.files_from_list (list, mask)
|
||||
local excl = List()
|
||||
M.process_file_list (list, mask, function(f)
|
||||
excl:append(f)
|
||||
end)
|
||||
return excl
|
||||
end
|
||||
|
||||
|
||||
|
||||
return tools
|
BIN
Documentation/compiler/lfs.dll
Normal file
BIN
Documentation/compiler/lfs.dll
Normal file
Binary file not shown.
BIN
Documentation/compiler/lua.exe
Normal file
BIN
Documentation/compiler/lua.exe
Normal file
Binary file not shown.
675
Documentation/compiler/pl/Date.lua
Normal file
675
Documentation/compiler/pl/Date.lua
Normal file
|
@ -0,0 +1,675 @@
|
|||
--- Date and Date Format classes.
|
||||
-- See @{05-dates.md|the Guide}.
|
||||
--
|
||||
-- NOTE: the date module is deprecated! see
|
||||
-- https://github.com/lunarmodules/Penlight/issues/285
|
||||
--
|
||||
-- Dependencies: `pl.class`, `pl.stringx`, `pl.utils`
|
||||
-- @classmod pl.Date
|
||||
-- @pragma nostrip
|
||||
|
||||
local class = require 'pl.class'
|
||||
local os_time, os_date = os.time, os.date
|
||||
local stringx = require 'pl.stringx'
|
||||
local utils = require 'pl.utils'
|
||||
local assert_arg,assert_string = utils.assert_arg,utils.assert_string
|
||||
|
||||
|
||||
utils.raise_deprecation {
|
||||
source = "Penlight " .. utils._VERSION,
|
||||
message = "the 'Date' module is deprecated, see https://github.com/lunarmodules/Penlight/issues/285",
|
||||
version_removed = "2.0.0",
|
||||
version_deprecated = "1.9.2",
|
||||
}
|
||||
|
||||
|
||||
local Date = class()
|
||||
Date.Format = class()
|
||||
|
||||
--- Date constructor.
|
||||
-- @param t this can be either
|
||||
--
|
||||
-- * `nil` or empty - use current date and time
|
||||
-- * number - seconds since epoch (as returned by `os.time`). Resulting time is UTC
|
||||
-- * `Date` - make a copy of this date
|
||||
-- * table - table containing year, month, etc as for `os.time`. You may leave out year, month or day,
|
||||
-- in which case current values will be used.
|
||||
-- * year (will be followed by month, day etc)
|
||||
--
|
||||
-- @param ... true if Universal Coordinated Time, or two to five numbers: month,day,hour,min,sec
|
||||
-- @function Date
|
||||
function Date:_init(t,...)
|
||||
local time
|
||||
local nargs = select('#',...)
|
||||
if nargs > 2 then
|
||||
local extra = {...}
|
||||
local year = t
|
||||
t = {
|
||||
year = year,
|
||||
month = extra[1],
|
||||
day = extra[2],
|
||||
hour = extra[3],
|
||||
min = extra[4],
|
||||
sec = extra[5]
|
||||
}
|
||||
end
|
||||
if nargs == 1 then
|
||||
self.utc = select(1,...) == true
|
||||
end
|
||||
if t == nil or t == 'utc' then
|
||||
time = os_time()
|
||||
self.utc = t == 'utc'
|
||||
elseif type(t) == 'number' then
|
||||
time = t
|
||||
if self.utc == nil then self.utc = true end
|
||||
elseif type(t) == 'table' then
|
||||
if getmetatable(t) == Date then -- copy ctor
|
||||
time = t.time
|
||||
self.utc = t.utc
|
||||
else
|
||||
if not (t.year and t.month) then
|
||||
local lt = os_date('*t')
|
||||
if not t.year and not t.month and not t.day then
|
||||
t.year = lt.year
|
||||
t.month = lt.month
|
||||
t.day = lt.day
|
||||
else
|
||||
t.year = t.year or lt.year
|
||||
t.month = t.month or (t.day and lt.month or 1)
|
||||
t.day = t.day or 1
|
||||
end
|
||||
end
|
||||
t.day = t.day or 1
|
||||
time = os_time(t)
|
||||
end
|
||||
else
|
||||
error("bad type for Date constructor: "..type(t),2)
|
||||
end
|
||||
self:set(time)
|
||||
end
|
||||
|
||||
--- set the current time of this Date object.
|
||||
-- @int t seconds since epoch
|
||||
function Date:set(t)
|
||||
self.time = t
|
||||
if self.utc then
|
||||
self.tab = os_date('!*t',t)
|
||||
else
|
||||
self.tab = os_date('*t',t)
|
||||
end
|
||||
end
|
||||
|
||||
--- get the time zone offset from UTC.
|
||||
-- @int ts seconds ahead of UTC
|
||||
function Date.tzone (ts)
|
||||
if ts == nil then
|
||||
ts = os_time()
|
||||
elseif type(ts) == "table" then
|
||||
if getmetatable(ts) == Date then
|
||||
ts = ts.time
|
||||
else
|
||||
ts = Date(ts).time
|
||||
end
|
||||
end
|
||||
local utc = os_date('!*t',ts)
|
||||
local lcl = os_date('*t',ts)
|
||||
lcl.isdst = false
|
||||
return os.difftime(os_time(lcl), os_time(utc))
|
||||
end
|
||||
|
||||
--- convert this date to UTC.
|
||||
function Date:toUTC ()
|
||||
local ndate = Date(self)
|
||||
if not self.utc then
|
||||
ndate.utc = true
|
||||
ndate:set(ndate.time)
|
||||
end
|
||||
return ndate
|
||||
end
|
||||
|
||||
--- convert this UTC date to local.
|
||||
function Date:toLocal ()
|
||||
local ndate = Date(self)
|
||||
if self.utc then
|
||||
ndate.utc = false
|
||||
ndate:set(ndate.time)
|
||||
--~ ndate:add { sec = Date.tzone(self) }
|
||||
end
|
||||
return ndate
|
||||
end
|
||||
|
||||
--- set the year.
|
||||
-- @int y Four-digit year
|
||||
-- @class function
|
||||
-- @name Date:year
|
||||
|
||||
--- set the month.
|
||||
-- @int m month
|
||||
-- @class function
|
||||
-- @name Date:month
|
||||
|
||||
--- set the day.
|
||||
-- @int d day
|
||||
-- @class function
|
||||
-- @name Date:day
|
||||
|
||||
--- set the hour.
|
||||
-- @int h hour
|
||||
-- @class function
|
||||
-- @name Date:hour
|
||||
|
||||
--- set the minutes.
|
||||
-- @int min minutes
|
||||
-- @class function
|
||||
-- @name Date:min
|
||||
|
||||
--- set the seconds.
|
||||
-- @int sec seconds
|
||||
-- @class function
|
||||
-- @name Date:sec
|
||||
|
||||
--- set the day of year.
|
||||
-- @class function
|
||||
-- @int yday day of year
|
||||
-- @name Date:yday
|
||||
|
||||
--- get the year.
|
||||
-- @int y Four-digit year
|
||||
-- @class function
|
||||
-- @name Date:year
|
||||
|
||||
--- get the month.
|
||||
-- @class function
|
||||
-- @name Date:month
|
||||
|
||||
--- get the day.
|
||||
-- @class function
|
||||
-- @name Date:day
|
||||
|
||||
--- get the hour.
|
||||
-- @class function
|
||||
-- @name Date:hour
|
||||
|
||||
--- get the minutes.
|
||||
-- @class function
|
||||
-- @name Date:min
|
||||
|
||||
--- get the seconds.
|
||||
-- @class function
|
||||
-- @name Date:sec
|
||||
|
||||
--- get the day of year.
|
||||
-- @class function
|
||||
-- @name Date:yday
|
||||
|
||||
|
||||
for _,c in ipairs{'year','month','day','hour','min','sec','yday'} do
|
||||
Date[c] = function(self,val)
|
||||
if val then
|
||||
assert_arg(1,val,"number")
|
||||
self.tab[c] = val
|
||||
self:set(os_time(self.tab))
|
||||
return self
|
||||
else
|
||||
return self.tab[c]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- name of day of week.
|
||||
-- @bool full abbreviated if true, full otherwise.
|
||||
-- @ret string name
|
||||
function Date:weekday_name(full)
|
||||
return os_date(full and '%A' or '%a',self.time)
|
||||
end
|
||||
|
||||
--- name of month.
|
||||
-- @int full abbreviated if true, full otherwise.
|
||||
-- @ret string name
|
||||
function Date:month_name(full)
|
||||
return os_date(full and '%B' or '%b',self.time)
|
||||
end
|
||||
|
||||
--- is this day on a weekend?.
|
||||
function Date:is_weekend()
|
||||
return self.tab.wday == 1 or self.tab.wday == 7
|
||||
end
|
||||
|
||||
--- add to a date object.
|
||||
-- @param t a table containing one of the following keys and a value:
|
||||
-- one of `year`,`month`,`day`,`hour`,`min`,`sec`
|
||||
-- @return this date
|
||||
function Date:add(t)
|
||||
local old_dst = self.tab.isdst
|
||||
local key,val = next(t)
|
||||
self.tab[key] = self.tab[key] + val
|
||||
self:set(os_time(self.tab))
|
||||
if old_dst ~= self.tab.isdst then
|
||||
self.tab.hour = self.tab.hour - (old_dst and 1 or -1)
|
||||
self:set(os_time(self.tab))
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- last day of the month.
|
||||
-- @return int day
|
||||
function Date:last_day()
|
||||
local d = 28
|
||||
local m = self.tab.month
|
||||
while self.tab.month == m do
|
||||
d = d + 1
|
||||
self:add{day=1}
|
||||
end
|
||||
self:add{day=-1}
|
||||
return self
|
||||
end
|
||||
|
||||
--- difference between two Date objects.
|
||||
-- @tparam Date other Date object
|
||||
-- @treturn Date.Interval object
|
||||
function Date:diff(other)
|
||||
local dt = self.time - other.time
|
||||
if dt < 0 then error("date difference is negative!",2) end
|
||||
return Date.Interval(dt)
|
||||
end
|
||||
|
||||
--- long numerical ISO data format version of this date.
|
||||
function Date:__tostring()
|
||||
local fmt = '%Y-%m-%dT%H:%M:%S'
|
||||
if self.utc then
|
||||
fmt = "!"..fmt
|
||||
end
|
||||
local t = os_date(fmt,self.time)
|
||||
if self.utc then
|
||||
return t .. 'Z'
|
||||
else
|
||||
local offs = self:tzone()
|
||||
if offs == 0 then
|
||||
return t .. 'Z'
|
||||
end
|
||||
local sign = offs > 0 and '+' or '-'
|
||||
local h = math.ceil(offs/3600)
|
||||
local m = (offs % 3600)/60
|
||||
if m == 0 then
|
||||
return t .. ('%s%02d'):format(sign,h)
|
||||
else
|
||||
return t .. ('%s%02d:%02d'):format(sign,h,m)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- equality between Date objects.
|
||||
function Date:__eq(other)
|
||||
return self.time == other.time
|
||||
end
|
||||
|
||||
--- ordering between Date objects.
|
||||
function Date:__lt(other)
|
||||
return self.time < other.time
|
||||
end
|
||||
|
||||
--- difference between Date objects.
|
||||
-- @function Date:__sub
|
||||
Date.__sub = Date.diff
|
||||
|
||||
--- add a date and an interval.
|
||||
-- @param other either a `Date.Interval` object or a table such as
|
||||
-- passed to `Date:add`
|
||||
function Date:__add(other)
|
||||
local nd = Date(self)
|
||||
if Date.Interval:class_of(other) then
|
||||
other = {sec=other.time}
|
||||
end
|
||||
nd:add(other)
|
||||
return nd
|
||||
end
|
||||
|
||||
Date.Interval = class(Date)
|
||||
|
||||
---- Date.Interval constructor
|
||||
-- @int t an interval in seconds
|
||||
-- @function Date.Interval
|
||||
function Date.Interval:_init(t)
|
||||
self:set(t)
|
||||
end
|
||||
|
||||
function Date.Interval:set(t)
|
||||
self.time = t
|
||||
self.tab = os_date('!*t',self.time)
|
||||
end
|
||||
|
||||
local function ess(n)
|
||||
if n > 1 then return 's '
|
||||
else return ' '
|
||||
end
|
||||
end
|
||||
|
||||
--- If it's an interval then the format is '2 hours 29 sec' etc.
|
||||
function Date.Interval:__tostring()
|
||||
local t, res = self.tab, ''
|
||||
local y,m,d = t.year - 1970, t.month - 1, t.day - 1
|
||||
if y > 0 then res = res .. y .. ' year'..ess(y) end
|
||||
if m > 0 then res = res .. m .. ' month'..ess(m) end
|
||||
if d > 0 then res = res .. d .. ' day'..ess(d) end
|
||||
if y == 0 and m == 0 then
|
||||
local h = t.hour
|
||||
if h > 0 then res = res .. h .. ' hour'..ess(h) end
|
||||
if t.min > 0 then res = res .. t.min .. ' min ' end
|
||||
if t.sec > 0 then res = res .. t.sec .. ' sec ' end
|
||||
end
|
||||
if res == '' then res = 'zero' end
|
||||
return res
|
||||
end
|
||||
|
||||
------------ Date.Format class: parsing and renderinig dates ------------
|
||||
|
||||
-- short field names, explicit os.date names, and a mask for allowed field repeats
|
||||
local formats = {
|
||||
d = {'day',{true,true}},
|
||||
y = {'year',{false,true,false,true}},
|
||||
m = {'month',{true,true}},
|
||||
H = {'hour',{true,true}},
|
||||
M = {'min',{true,true}},
|
||||
S = {'sec',{true,true}},
|
||||
}
|
||||
|
||||
--- Date.Format constructor.
|
||||
-- @string fmt. A string where the following fields are significant:
|
||||
--
|
||||
-- * d day (either d or dd)
|
||||
-- * y year (either yy or yyy)
|
||||
-- * m month (either m or mm)
|
||||
-- * H hour (either H or HH)
|
||||
-- * M minute (either M or MM)
|
||||
-- * S second (either S or SS)
|
||||
--
|
||||
-- Alternatively, if fmt is nil then this returns a flexible date parser
|
||||
-- that tries various date/time schemes in turn:
|
||||
--
|
||||
-- * [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601), like `2010-05-10 12:35:23Z` or `2008-10-03T14:30+02`
|
||||
-- * times like 15:30 or 8.05pm (assumed to be today's date)
|
||||
-- * dates like 28/10/02 (European order!) or 5 Feb 2012
|
||||
-- * month name like march or Mar (case-insensitive, first 3 letters); here the
|
||||
-- day will be 1 and the year this current year
|
||||
--
|
||||
-- A date in format 3 can be optionally followed by a time in format 2.
|
||||
-- Please see test-date.lua in the tests folder for more examples.
|
||||
-- @usage df = Date.Format("yyyy-mm-dd HH:MM:SS")
|
||||
-- @class function
|
||||
-- @name Date.Format
|
||||
function Date.Format:_init(fmt)
|
||||
if not fmt then
|
||||
self.fmt = '%Y-%m-%d %H:%M:%S'
|
||||
self.outf = self.fmt
|
||||
self.plain = true
|
||||
return
|
||||
end
|
||||
local append = table.insert
|
||||
local D,PLUS,OPENP,CLOSEP = '\001','\002','\003','\004'
|
||||
local vars,used = {},{}
|
||||
local patt,outf = {},{}
|
||||
local i = 1
|
||||
while i < #fmt do
|
||||
local ch = fmt:sub(i,i)
|
||||
local df = formats[ch]
|
||||
if df then
|
||||
if used[ch] then error("field appeared twice: "..ch,4) end
|
||||
used[ch] = true
|
||||
-- this field may be repeated
|
||||
local _,inext = fmt:find(ch..'+',i+1)
|
||||
local cnt = not _ and 1 or inext-i+1
|
||||
if not df[2][cnt] then error("wrong number of fields: "..ch,4) end
|
||||
-- single chars mean 'accept more than one digit'
|
||||
local p = cnt==1 and (D..PLUS) or (D):rep(cnt)
|
||||
append(patt,OPENP..p..CLOSEP)
|
||||
append(vars,ch)
|
||||
if ch == 'y' then
|
||||
append(outf,cnt==2 and '%y' or '%Y')
|
||||
else
|
||||
append(outf,'%'..ch)
|
||||
end
|
||||
i = i + cnt
|
||||
else
|
||||
append(patt,ch)
|
||||
append(outf,ch)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
-- escape any magic characters
|
||||
fmt = utils.escape(table.concat(patt))
|
||||
-- fmt = table.concat(patt):gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1')
|
||||
-- replace markers with their magic equivalents
|
||||
fmt = fmt:gsub(D,'%%d'):gsub(PLUS,'+'):gsub(OPENP,'('):gsub(CLOSEP,')')
|
||||
self.fmt = fmt
|
||||
self.outf = table.concat(outf)
|
||||
self.vars = vars
|
||||
end
|
||||
|
||||
local parse_date
|
||||
|
||||
--- parse a string into a Date object.
|
||||
-- @string str a date string
|
||||
-- @return date object
|
||||
function Date.Format:parse(str)
|
||||
assert_string(1,str)
|
||||
if self.plain then
|
||||
return parse_date(str,self.us)
|
||||
end
|
||||
local res = {str:match(self.fmt)}
|
||||
if #res==0 then return nil, 'cannot parse '..str end
|
||||
local tab = {}
|
||||
for i,v in ipairs(self.vars) do
|
||||
local name = formats[v][1] -- e.g. 'y' becomes 'year'
|
||||
tab[name] = tonumber(res[i])
|
||||
end
|
||||
-- os.date() requires these fields; if not present, we assume
|
||||
-- that the time set is for the current day.
|
||||
if not (tab.year and tab.month and tab.day) then
|
||||
local today = Date()
|
||||
tab.year = tab.year or today:year()
|
||||
tab.month = tab.month or today:month()
|
||||
tab.day = tab.day or today:day()
|
||||
end
|
||||
local Y = tab.year
|
||||
if Y < 100 then -- classic Y2K pivot
|
||||
tab.year = Y + (Y < 35 and 2000 or 1999)
|
||||
elseif not Y then
|
||||
tab.year = 1970
|
||||
end
|
||||
return Date(tab)
|
||||
end
|
||||
|
||||
--- convert a Date object into a string.
|
||||
-- @param d a date object, or a time value as returned by @{os.time}
|
||||
-- @return string
|
||||
function Date.Format:tostring(d)
|
||||
local tm
|
||||
local fmt = self.outf
|
||||
if type(d) == 'number' then
|
||||
tm = d
|
||||
else
|
||||
tm = d.time
|
||||
if d.utc then
|
||||
fmt = '!'..fmt
|
||||
end
|
||||
end
|
||||
return os_date(fmt,tm)
|
||||
end
|
||||
|
||||
--- force US order in dates like 9/11/2001
|
||||
function Date.Format:US_order(yesno)
|
||||
self.us = yesno
|
||||
end
|
||||
|
||||
--local months = {jan=1,feb=2,mar=3,apr=4,may=5,jun=6,jul=7,aug=8,sep=9,oct=10,nov=11,dec=12}
|
||||
local months
|
||||
local parse_date_unsafe
|
||||
local function create_months()
|
||||
local ld, day1 = parse_date_unsafe '2000-12-31', {day=1}
|
||||
months = {}
|
||||
for i = 1,12 do
|
||||
ld = ld:last_day()
|
||||
ld:add(day1)
|
||||
local mon = ld:month_name():lower()
|
||||
months [mon] = i
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Allowed patterns:
|
||||
- [day] [monthname] [year] [time]
|
||||
- [day]/[month][/year] [time]
|
||||
|
||||
]]
|
||||
|
||||
local function looks_like_a_month(w)
|
||||
return w:match '^%a+,*$' ~= nil
|
||||
end
|
||||
local is_number = stringx.isdigit
|
||||
local function tonum(s,l1,l2,kind)
|
||||
kind = kind or ''
|
||||
local n = tonumber(s)
|
||||
if not n then error(("%snot a number: '%s'"):format(kind,s)) end
|
||||
if n < l1 or n > l2 then
|
||||
error(("%s out of range: %s is not between %d and %d"):format(kind,s,l1,l2))
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
local function parse_iso_end(p,ns,sec)
|
||||
-- may be fractional part of seconds
|
||||
local _,nfrac,secfrac = p:find('^%.%d+',ns+1)
|
||||
if secfrac then
|
||||
sec = sec .. secfrac
|
||||
p = p:sub(nfrac+1)
|
||||
else
|
||||
p = p:sub(ns+1)
|
||||
end
|
||||
-- ISO 8601 dates may end in Z (for UTC) or [+-][isotime]
|
||||
-- (we're working with the date as lower case, hence 'z')
|
||||
if p:match 'z$' then -- we're UTC!
|
||||
return sec, {h=0,m=0}
|
||||
end
|
||||
p = p:gsub(':','') -- turn 00:30 to 0030
|
||||
local _,_,sign,offs = p:find('^([%+%-])(%d+)')
|
||||
if not sign then return sec, nil end -- not UTC
|
||||
|
||||
if #offs == 2 then offs = offs .. '00' end -- 01 to 0100
|
||||
local tz = { h = tonumber(offs:sub(1,2)), m = tonumber(offs:sub(3,4)) }
|
||||
if sign == '-' then tz.h = -tz.h; tz.m = -tz.m end
|
||||
return sec, tz
|
||||
end
|
||||
|
||||
function parse_date_unsafe (s,US)
|
||||
s = s:gsub('T',' ') -- ISO 8601
|
||||
local parts = stringx.split(s:lower())
|
||||
local i,p = 1,parts[1]
|
||||
local function nextp() i = i + 1; p = parts[i] end
|
||||
local year,min,hour,sec,apm
|
||||
local tz
|
||||
local _,nxt,day, month = p:find '^(%d+)/(%d+)'
|
||||
if day then
|
||||
-- swop for US case
|
||||
if US then
|
||||
day, month = month, day
|
||||
end
|
||||
_,_,year = p:find('^/(%d+)',nxt+1)
|
||||
nextp()
|
||||
else -- ISO
|
||||
year,month,day = p:match('^(%d+)%-(%d+)%-(%d+)')
|
||||
if year then
|
||||
nextp()
|
||||
end
|
||||
end
|
||||
if p and not year and is_number(p) then -- has to be date
|
||||
if #p < 4 then
|
||||
day = p
|
||||
nextp()
|
||||
else -- unless it looks like a 24-hour time
|
||||
year = true
|
||||
end
|
||||
end
|
||||
if p and looks_like_a_month(p) then -- date followed by month
|
||||
p = p:sub(1,3)
|
||||
if not months then
|
||||
create_months()
|
||||
end
|
||||
local mon = months[p]
|
||||
if mon then
|
||||
month = mon
|
||||
else error("not a month: " .. p) end
|
||||
nextp()
|
||||
end
|
||||
if p and not year and is_number(p) then
|
||||
year = p
|
||||
nextp()
|
||||
end
|
||||
|
||||
if p then -- time is hh:mm[:ss], hhmm[ss] or H.M[am|pm]
|
||||
_,nxt,hour,min = p:find '^(%d+):(%d+)'
|
||||
local ns
|
||||
if nxt then -- are there seconds?
|
||||
_,ns,sec = p:find ('^:(%d+)',nxt+1)
|
||||
--if ns then
|
||||
sec,tz = parse_iso_end(p,ns or nxt,sec)
|
||||
--end
|
||||
else -- might be h.m
|
||||
_,ns,hour,min = p:find '^(%d+)%.(%d+)'
|
||||
if ns then
|
||||
apm = p:match '[ap]m$'
|
||||
else -- or hhmm[ss]
|
||||
local hourmin
|
||||
_,nxt,hourmin = p:find ('^(%d+)')
|
||||
if nxt then
|
||||
hour = hourmin:sub(1,2)
|
||||
min = hourmin:sub(3,4)
|
||||
sec = hourmin:sub(5,6)
|
||||
if #sec == 0 then sec = nil end
|
||||
sec,tz = parse_iso_end(p,nxt,sec)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local today
|
||||
if year == true then year = nil end
|
||||
if not (year and month and day) then
|
||||
today = Date()
|
||||
end
|
||||
day = day and tonum(day,1,31,'day') or (month and 1 or today:day())
|
||||
month = month and tonum(month,1,12,'month') or today:month()
|
||||
year = year and tonumber(year) or today:year()
|
||||
if year < 100 then -- two-digit year pivot around year < 2035
|
||||
year = year + (year < 35 and 2000 or 1900)
|
||||
end
|
||||
hour = hour and tonum(hour,0,apm and 12 or 24,'hour') or 12
|
||||
if apm == 'pm' then
|
||||
hour = hour + 12
|
||||
end
|
||||
min = min and tonum(min,0,59) or 0
|
||||
sec = sec and tonum(sec,0,60) or 0 --60 used to indicate leap second
|
||||
local res = Date {year = year, month = month, day = day, hour = hour, min = min, sec = sec}
|
||||
if tz then -- ISO 8601 UTC time
|
||||
local corrected = false
|
||||
if tz.h ~= 0 then res:add {hour = -tz.h}; corrected = true end
|
||||
if tz.m ~= 0 then res:add {min = -tz.m}; corrected = true end
|
||||
res.utc = true
|
||||
-- we're in UTC, so let's go local...
|
||||
if corrected then
|
||||
res = res:toLocal()
|
||||
end-- we're UTC!
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function parse_date (s)
|
||||
local ok, d = pcall(parse_date_unsafe,s)
|
||||
if not ok then -- error
|
||||
d = d:gsub('.-:%d+: ','')
|
||||
return nil, d
|
||||
else
|
||||
return d
|
||||
end
|
||||
end
|
||||
|
||||
return Date
|
||||
|
566
Documentation/compiler/pl/List.lua
Normal file
566
Documentation/compiler/pl/List.lua
Normal file
|
@ -0,0 +1,566 @@
|
|||
--- Python-style list class.
|
||||
--
|
||||
-- **Please Note**: methods that change the list will return the list.
|
||||
-- This is to allow for method chaining, but please note that `ls = ls:sort()`
|
||||
-- does not mean that a new copy of the list is made. In-place (mutable) methods
|
||||
-- are marked as returning 'the list' in this documentation.
|
||||
--
|
||||
-- See the Guide for further @{02-arrays.md.Python_style_Lists|discussion}
|
||||
--
|
||||
-- See <a href="http://www.python.org/doc/current/tut/tut.html">http://www.python.org/doc/current/tut/tut.html</a>, section 5.1
|
||||
--
|
||||
-- **Note**: The comments before some of the functions are from the Python docs
|
||||
-- and contain Python code.
|
||||
--
|
||||
-- Written for Lua version Nick Trout 4.0; Redone for Lua 5.1, Steve Donovan.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`
|
||||
-- @classmod pl.List
|
||||
-- @pragma nostrip
|
||||
|
||||
local tinsert,tremove,concat,tsort = table.insert,table.remove,table.concat,table.sort
|
||||
local setmetatable, getmetatable,type,tostring,string = setmetatable,getmetatable,type,tostring,string
|
||||
local tablex = require 'pl.tablex'
|
||||
local filter,imap,imap2,reduce,transform,tremovevalues = tablex.filter,tablex.imap,tablex.imap2,tablex.reduce,tablex.transform,tablex.removevalues
|
||||
local tsub = tablex.sub
|
||||
local utils = require 'pl.utils'
|
||||
local class = require 'pl.class'
|
||||
|
||||
local array_tostring,split,assert_arg,function_arg = utils.array_tostring,utils.split,utils.assert_arg,utils.function_arg
|
||||
local normalize_slice = tablex._normalize_slice
|
||||
|
||||
-- metatable for our list and map objects has already been defined..
|
||||
local Multimap = utils.stdmt.MultiMap
|
||||
local List = utils.stdmt.List
|
||||
|
||||
local iter
|
||||
|
||||
class(nil,nil,List)
|
||||
|
||||
-- we want the result to be _covariant_, i.e. t must have type of obj if possible
|
||||
local function makelist (t,obj)
|
||||
local klass = List
|
||||
if obj then
|
||||
klass = getmetatable(obj)
|
||||
end
|
||||
return setmetatable(t,klass)
|
||||
end
|
||||
|
||||
local function simple_table(t)
|
||||
return type(t) == 'table' and not getmetatable(t) and #t > 0
|
||||
end
|
||||
|
||||
function List._create (src)
|
||||
if simple_table(src) then return src end
|
||||
end
|
||||
|
||||
function List:_init (src)
|
||||
if self == src then return end -- existing table used as self!
|
||||
if src then
|
||||
for v in iter(src) do
|
||||
tinsert(self,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a new list. Can optionally pass a table;
|
||||
-- passing another instance of List will cause a copy to be created;
|
||||
-- this will return a plain table with an appropriate metatable.
|
||||
-- we pass anything which isn't a simple table to iterate() to work out
|
||||
-- an appropriate iterator
|
||||
-- @see List.iterate
|
||||
-- @param[opt] t An optional list-like table
|
||||
-- @return a new List
|
||||
-- @usage ls = List(); ls = List {1,2,3,4}
|
||||
-- @function List.new
|
||||
|
||||
List.new = List
|
||||
|
||||
--- Make a copy of an existing list.
|
||||
-- The difference from a plain 'copy constructor' is that this returns
|
||||
-- the actual List subtype.
|
||||
function List:clone()
|
||||
local ls = makelist({},self)
|
||||
ls:extend(self)
|
||||
return ls
|
||||
end
|
||||
|
||||
--- Add an item to the end of the list.
|
||||
-- @param i An item
|
||||
-- @return the list
|
||||
function List:append(i)
|
||||
tinsert(self,i)
|
||||
return self
|
||||
end
|
||||
|
||||
List.push = tinsert
|
||||
|
||||
--- Extend the list by appending all the items in the given list.
|
||||
-- equivalent to 'a[len(a):] = L'.
|
||||
-- @tparam List L Another List
|
||||
-- @return the list
|
||||
function List:extend(L)
|
||||
assert_arg(1,L,'table')
|
||||
for i = 1,#L do tinsert(self,L[i]) end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Insert an item at a given position. i is the index of the
|
||||
-- element before which to insert.
|
||||
-- @int i index of element before whichh to insert
|
||||
-- @param x A data item
|
||||
-- @return the list
|
||||
function List:insert(i, x)
|
||||
assert_arg(1,i,'number')
|
||||
tinsert(self,i,x)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Insert an item at the begining of the list.
|
||||
-- @param x a data item
|
||||
-- @return the list
|
||||
function List:put (x)
|
||||
return self:insert(1,x)
|
||||
end
|
||||
|
||||
--- Remove an element given its index.
|
||||
-- (equivalent of Python's del s[i])
|
||||
-- @int i the index
|
||||
-- @return the list
|
||||
function List:remove (i)
|
||||
assert_arg(1,i,'number')
|
||||
tremove(self,i)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove the first item from the list whose value is given.
|
||||
-- (This is called 'remove' in Python; renamed to avoid confusion
|
||||
-- with table.remove)
|
||||
-- Return nil if there is no such item.
|
||||
-- @param x A data value
|
||||
-- @return the list
|
||||
function List:remove_value(x)
|
||||
for i=1,#self do
|
||||
if self[i]==x then tremove(self,i) return self end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove the item at the given position in the list, and return it.
|
||||
-- If no index is specified, a:pop() returns the last item in the list.
|
||||
-- The item is also removed from the list.
|
||||
-- @int[opt] i An index
|
||||
-- @return the item
|
||||
function List:pop(i)
|
||||
if not i then i = #self end
|
||||
assert_arg(1,i,'number')
|
||||
return tremove(self,i)
|
||||
end
|
||||
|
||||
List.get = List.pop
|
||||
|
||||
--- Return the index in the list of the first item whose value is given.
|
||||
-- Return nil if there is no such item.
|
||||
-- @function List:index
|
||||
-- @param x A data value
|
||||
-- @int[opt=1] idx where to start search
|
||||
-- @return the index, or nil if not found.
|
||||
|
||||
local tfind = tablex.find
|
||||
List.index = tfind
|
||||
|
||||
--- Does this list contain the value?
|
||||
-- @param x A data value
|
||||
-- @return true or false
|
||||
function List:contains(x)
|
||||
return tfind(self,x) and true or false
|
||||
end
|
||||
|
||||
--- Return the number of times value appears in the list.
|
||||
-- @param x A data value
|
||||
-- @return number of times x appears
|
||||
function List:count(x)
|
||||
local cnt=0
|
||||
for i=1,#self do
|
||||
if self[i]==x then cnt=cnt+1 end
|
||||
end
|
||||
return cnt
|
||||
end
|
||||
|
||||
--- Sort the items of the list, in place.
|
||||
-- @func[opt='<'] cmp an optional comparison function
|
||||
-- @return the list
|
||||
function List:sort(cmp)
|
||||
if cmp then cmp = function_arg(1,cmp) end
|
||||
tsort(self,cmp)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return a sorted copy of this list.
|
||||
-- @func[opt='<'] cmp an optional comparison function
|
||||
-- @return a new list
|
||||
function List:sorted(cmp)
|
||||
return List(self):sort(cmp)
|
||||
end
|
||||
|
||||
--- Reverse the elements of the list, in place.
|
||||
-- @return the list
|
||||
function List:reverse()
|
||||
local t = self
|
||||
local n = #t
|
||||
for i = 1,n/2 do
|
||||
t[i],t[n] = t[n],t[i]
|
||||
n = n - 1
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Return the minimum and the maximum value of the list.
|
||||
-- @return minimum value
|
||||
-- @return maximum value
|
||||
function List:minmax()
|
||||
local vmin,vmax = 1e70,-1e70
|
||||
for i = 1,#self do
|
||||
local v = self[i]
|
||||
if v < vmin then vmin = v end
|
||||
if v > vmax then vmax = v end
|
||||
end
|
||||
return vmin,vmax
|
||||
end
|
||||
|
||||
--- Emulate list slicing. like 'list[first:last]' in Python.
|
||||
-- If first or last are negative then they are relative to the end of the list
|
||||
-- eg. slice(-2) gives last 2 entries in a list, and
|
||||
-- slice(-4,-2) gives from -4th to -2nd
|
||||
-- @param first An index
|
||||
-- @param last An index
|
||||
-- @return a new List
|
||||
function List:slice(first,last)
|
||||
return tsub(self,first,last)
|
||||
end
|
||||
|
||||
--- Empty the list.
|
||||
-- @return the list
|
||||
function List:clear()
|
||||
for i=1,#self do tremove(self) end
|
||||
return self
|
||||
end
|
||||
|
||||
local eps = 1.0e-10
|
||||
|
||||
--- Emulate Python's range(x) function.
|
||||
-- Include it in List table for tidiness
|
||||
-- @int start A number
|
||||
-- @int[opt] finish A number greater than start; if absent,
|
||||
-- then start is 1 and finish is start
|
||||
-- @int[opt=1] incr an increment (may be less than 1)
|
||||
-- @return a List from start .. finish
|
||||
-- @usage List.range(0,3) == List{0,1,2,3}
|
||||
-- @usage List.range(4) = List{1,2,3,4}
|
||||
-- @usage List.range(5,1,-1) == List{5,4,3,2,1}
|
||||
function List.range(start,finish,incr)
|
||||
if not finish then
|
||||
finish = start
|
||||
start = 1
|
||||
end
|
||||
if incr then
|
||||
assert_arg(3,incr,'number')
|
||||
if math.ceil(incr) ~= incr then finish = finish + eps end
|
||||
else
|
||||
incr = 1
|
||||
end
|
||||
assert_arg(1,start,'number')
|
||||
assert_arg(2,finish,'number')
|
||||
local t = List()
|
||||
for i=start,finish,incr do tinsert(t,i) end
|
||||
return t
|
||||
end
|
||||
|
||||
--- list:len() is the same as #list.
|
||||
function List:len()
|
||||
return #self
|
||||
end
|
||||
|
||||
-- Extended operations --
|
||||
|
||||
--- Remove a subrange of elements.
|
||||
-- equivalent to 'del s[i1:i2]' in Python.
|
||||
-- @int i1 start of range
|
||||
-- @int i2 end of range
|
||||
-- @return the list
|
||||
function List:chop(i1,i2)
|
||||
return tremovevalues(self,i1,i2)
|
||||
end
|
||||
|
||||
--- Insert a sublist into a list
|
||||
-- equivalent to 's[idx:idx] = list' in Python
|
||||
-- @int idx index
|
||||
-- @tparam List list list to insert
|
||||
-- @return the list
|
||||
-- @usage l = List{10,20}; l:splice(2,{21,22}); assert(l == List{10,21,22,20})
|
||||
function List:splice(idx,list)
|
||||
assert_arg(1,idx,'number')
|
||||
idx = idx - 1
|
||||
local i = 1
|
||||
for v in iter(list) do
|
||||
tinsert(self,i+idx,v)
|
||||
i = i + 1
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- General slice assignment s[i1:i2] = seq.
|
||||
-- @int i1 start index
|
||||
-- @int i2 end index
|
||||
-- @tparam List seq a list
|
||||
-- @return the list
|
||||
function List:slice_assign(i1,i2,seq)
|
||||
assert_arg(1,i1,'number')
|
||||
assert_arg(1,i2,'number')
|
||||
i1,i2 = normalize_slice(self,i1,i2)
|
||||
if i2 >= i1 then self:chop(i1,i2) end
|
||||
self:splice(i1,seq)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Concatenation operator.
|
||||
-- @within metamethods
|
||||
-- @tparam List L another List
|
||||
-- @return a new list consisting of the list with the elements of the new list appended
|
||||
function List:__concat(L)
|
||||
assert_arg(1,L,'table')
|
||||
local ls = self:clone()
|
||||
ls:extend(L)
|
||||
return ls
|
||||
end
|
||||
|
||||
--- Equality operator ==. True iff all elements of two lists are equal.
|
||||
-- @within metamethods
|
||||
-- @tparam List L another List
|
||||
-- @return true or false
|
||||
function List:__eq(L)
|
||||
if #self ~= #L then return false end
|
||||
for i = 1,#self do
|
||||
if self[i] ~= L[i] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Join the elements of a list using a delimiter.
|
||||
-- This method uses tostring on all elements.
|
||||
-- @string[opt=''] delim a delimiter string, can be empty.
|
||||
-- @return a string
|
||||
function List:join (delim)
|
||||
delim = delim or ''
|
||||
assert_arg(1,delim,'string')
|
||||
return concat(array_tostring(self),delim)
|
||||
end
|
||||
|
||||
--- Join a list of strings. <br>
|
||||
-- Uses `table.concat` directly.
|
||||
-- @function List:concat
|
||||
-- @string[opt=''] delim a delimiter
|
||||
-- @return a string
|
||||
List.concat = concat
|
||||
|
||||
local function tostring_q(val)
|
||||
local s = tostring(val)
|
||||
if type(val) == 'string' then
|
||||
s = '"'..s..'"'
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
--- How our list should be rendered as a string. Uses join().
|
||||
-- @within metamethods
|
||||
-- @see List:join
|
||||
function List:__tostring()
|
||||
return '{'..self:join(',',tostring_q)..'}'
|
||||
end
|
||||
|
||||
--- Call the function on each element of the list.
|
||||
-- @func fun a function or callable object
|
||||
-- @param ... optional values to pass to function
|
||||
function List:foreach (fun,...)
|
||||
fun = function_arg(1,fun)
|
||||
for i = 1,#self do
|
||||
fun(self[i],...)
|
||||
end
|
||||
end
|
||||
|
||||
local function lookup_fun (obj,name)
|
||||
local f = obj[name]
|
||||
if not f then error(type(obj).." does not have method "..name,3) end
|
||||
return f
|
||||
end
|
||||
|
||||
--- Call the named method on each element of the list.
|
||||
-- @string name the method name
|
||||
-- @param ... optional values to pass to function
|
||||
function List:foreachm (name,...)
|
||||
for i = 1,#self do
|
||||
local obj = self[i]
|
||||
local f = lookup_fun(obj,name)
|
||||
f(obj,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a list of all elements which match a function.
|
||||
-- @func fun a boolean function
|
||||
-- @param[opt] arg optional argument to be passed as second argument of the predicate
|
||||
-- @return a new filtered list.
|
||||
function List:filter (fun,arg)
|
||||
return makelist(filter(self,fun,arg),self)
|
||||
end
|
||||
|
||||
--- Split a string using a delimiter.
|
||||
-- @string s the string
|
||||
-- @string[opt] delim the delimiter (default spaces)
|
||||
-- @return a List of strings
|
||||
-- @see pl.utils.split
|
||||
function List.split (s,delim)
|
||||
assert_arg(1,s,'string')
|
||||
return makelist(split(s,delim))
|
||||
end
|
||||
|
||||
--- Apply a function to all elements.
|
||||
-- Any extra arguments will be passed to the function.
|
||||
-- @func fun a function of at least one argument
|
||||
-- @param ... arbitrary extra arguments.
|
||||
-- @return a new list: {f(x) for x in self}
|
||||
-- @usage List{'one','two'}:map(string.upper) == {'ONE','TWO'}
|
||||
-- @see pl.tablex.imap
|
||||
function List:map (fun,...)
|
||||
return makelist(imap(fun,self,...),self)
|
||||
end
|
||||
|
||||
--- Apply a function to all elements, in-place.
|
||||
-- Any extra arguments are passed to the function.
|
||||
-- @func fun A function that takes at least one argument
|
||||
-- @param ... arbitrary extra arguments.
|
||||
-- @return the list.
|
||||
function List:transform (fun,...)
|
||||
transform(fun,self,...)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Apply a function to elements of two lists.
|
||||
-- Any extra arguments will be passed to the function
|
||||
-- @func fun a function of at least two arguments
|
||||
-- @tparam List ls another list
|
||||
-- @param ... arbitrary extra arguments.
|
||||
-- @return a new list: {f(x,y) for x in self, for x in arg1}
|
||||
-- @see pl.tablex.imap2
|
||||
function List:map2 (fun,ls,...)
|
||||
return makelist(imap2(fun,self,ls,...),self)
|
||||
end
|
||||
|
||||
--- apply a named method to all elements.
|
||||
-- Any extra arguments will be passed to the method.
|
||||
-- @string name name of method
|
||||
-- @param ... extra arguments
|
||||
-- @return a new list of the results
|
||||
-- @see pl.seq.mapmethod
|
||||
function List:mapm (name,...)
|
||||
local res = {}
|
||||
for i = 1,#self do
|
||||
local val = self[i]
|
||||
local fn = lookup_fun(val,name)
|
||||
res[i] = fn(val,...)
|
||||
end
|
||||
return makelist(res,self)
|
||||
end
|
||||
|
||||
local function composite_call (method,f)
|
||||
return function(self,...)
|
||||
return self[method](self,f,...)
|
||||
end
|
||||
end
|
||||
|
||||
function List.default_map_with(T)
|
||||
return function(self,name)
|
||||
local m
|
||||
if T then
|
||||
local f = lookup_fun(T,name)
|
||||
m = composite_call('map',f)
|
||||
else
|
||||
m = composite_call('mapn',name)
|
||||
end
|
||||
getmetatable(self)[name] = m -- and cache..
|
||||
return m
|
||||
end
|
||||
end
|
||||
|
||||
List.default_map = List.default_map_with
|
||||
|
||||
--- 'reduce' a list using a binary function.
|
||||
-- @func fun a function of two arguments
|
||||
-- @return result of the function
|
||||
-- @see pl.tablex.reduce
|
||||
function List:reduce (fun)
|
||||
return reduce(fun,self)
|
||||
end
|
||||
|
||||
--- Partition a list using a classifier function.
|
||||
-- The function may return nil, but this will be converted to the string key '<nil>'.
|
||||
-- @func fun a function of at least one argument
|
||||
-- @param ... will also be passed to the function
|
||||
-- @treturn MultiMap a table where the keys are the returned values, and the values are Lists
|
||||
-- of values where the function returned that key.
|
||||
-- @see pl.MultiMap
|
||||
function List:partition (fun,...)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for i = 1,#self do
|
||||
local val = self[i]
|
||||
local klass = fun(val,...)
|
||||
if klass == nil then klass = '<nil>' end
|
||||
if not res[klass] then res[klass] = List() end
|
||||
res[klass]:append(val)
|
||||
end
|
||||
return setmetatable(res,Multimap)
|
||||
end
|
||||
|
||||
--- return an iterator over all values.
|
||||
function List:iter ()
|
||||
return iter(self)
|
||||
end
|
||||
|
||||
--- Create an iterator over a seqence.
|
||||
-- This captures the Python concept of 'sequence'.
|
||||
-- For tables, iterates over all values with integer indices.
|
||||
-- @param seq a sequence; a string (over characters), a table, a file object (over lines) or an iterator function
|
||||
-- @usage for x in iterate {1,10,22,55} do io.write(x,',') end ==> 1,10,22,55
|
||||
-- @usage for ch in iterate 'help' do do io.write(ch,' ') end ==> h e l p
|
||||
function List.iterate(seq)
|
||||
if type(seq) == 'string' then
|
||||
local idx = 0
|
||||
local n = #seq
|
||||
local sub = string.sub
|
||||
return function ()
|
||||
idx = idx + 1
|
||||
if idx > n then return nil
|
||||
else
|
||||
return sub(seq,idx,idx)
|
||||
end
|
||||
end
|
||||
elseif type(seq) == 'table' then
|
||||
local idx = 0
|
||||
local n = #seq
|
||||
return function()
|
||||
idx = idx + 1
|
||||
if idx > n then return nil
|
||||
else
|
||||
return seq[idx]
|
||||
end
|
||||
end
|
||||
elseif type(seq) == 'function' then
|
||||
return seq
|
||||
elseif type(seq) == 'userdata' and io.type(seq) == 'file' then
|
||||
return seq:lines()
|
||||
end
|
||||
end
|
||||
iter = List.iterate
|
||||
|
||||
return List
|
||||
|
120
Documentation/compiler/pl/Map.lua
Normal file
120
Documentation/compiler/pl/Map.lua
Normal file
|
@ -0,0 +1,120 @@
|
|||
--- A Map class.
|
||||
--
|
||||
-- > Map = require 'pl.Map'
|
||||
-- > m = Map{one=1,two=2}
|
||||
-- > m:update {three=3,four=4,two=20}
|
||||
-- > = m == M{one=1,two=20,three=3,four=4}
|
||||
-- true
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.class`, `pl.tablex`, `pl.pretty`
|
||||
-- @classmod pl.Map
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local stdmt = utils.stdmt
|
||||
local deepcompare = tablex.deepcompare
|
||||
|
||||
local pretty_write = require 'pl.pretty' . write
|
||||
local Map = stdmt.Map
|
||||
local Set = stdmt.Set
|
||||
|
||||
local class = require 'pl.class'
|
||||
|
||||
-- the Map class ---------------------
|
||||
class(nil,nil,Map)
|
||||
|
||||
function Map:_init (t)
|
||||
local mt = getmetatable(t)
|
||||
if mt == Set or mt == Map then
|
||||
self:update(t)
|
||||
else
|
||||
return t -- otherwise assumed to be a map-like table
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function makelist(t)
|
||||
return setmetatable(t, require('pl.List'))
|
||||
end
|
||||
|
||||
--- list of keys.
|
||||
Map.keys = tablex.keys
|
||||
|
||||
--- list of values.
|
||||
Map.values = tablex.values
|
||||
|
||||
--- return an iterator over all key-value pairs.
|
||||
function Map:iter ()
|
||||
return pairs(self)
|
||||
end
|
||||
|
||||
--- return a List of all key-value pairs, sorted by the keys.
|
||||
function Map:items()
|
||||
local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self))
|
||||
ls:sort(function(t1,t2) return t1[1] < t2[1] end)
|
||||
return ls
|
||||
end
|
||||
|
||||
--- set a value in the map if it doesn't exist yet.
|
||||
-- @param key the key
|
||||
-- @param default value to set
|
||||
-- @return the value stored in the map (existing value, or the new value)
|
||||
function Map:setdefault(key, default)
|
||||
local val = self[key]
|
||||
if val ~= nil then
|
||||
return val
|
||||
end
|
||||
self:set(key,default)
|
||||
return default
|
||||
end
|
||||
|
||||
--- size of map.
|
||||
-- note: this is a relatively expensive operation!
|
||||
-- @class function
|
||||
-- @name Map:len
|
||||
Map.len = tablex.size
|
||||
|
||||
--- put a value into the map.
|
||||
-- This will remove the key if the value is `nil`
|
||||
-- @param key the key
|
||||
-- @param val the value
|
||||
function Map:set (key,val)
|
||||
self[key] = val
|
||||
end
|
||||
|
||||
--- get a value from the map.
|
||||
-- @param key the key
|
||||
-- @return the value, or nil if not found.
|
||||
function Map:get (key)
|
||||
return rawget(self,key)
|
||||
end
|
||||
|
||||
local index_by = tablex.index_by
|
||||
|
||||
--- get a list of values indexed by a list of keys.
|
||||
-- @param keys a list-like table of keys
|
||||
-- @return a new list
|
||||
function Map:getvalues (keys)
|
||||
return makelist(index_by(self,keys))
|
||||
end
|
||||
|
||||
--- update the map using key/value pairs from another table.
|
||||
-- @tab table
|
||||
-- @function Map:update
|
||||
Map.update = tablex.update
|
||||
|
||||
--- equality between maps.
|
||||
-- @within metamethods
|
||||
-- @tparam Map m another map.
|
||||
function Map:__eq (m)
|
||||
-- note we explicitly ask deepcompare _not_ to use __eq!
|
||||
return deepcompare(self,m,true)
|
||||
end
|
||||
|
||||
--- string representation of a map.
|
||||
-- @within metamethods
|
||||
function Map:__tostring ()
|
||||
return pretty_write(self,'')
|
||||
end
|
||||
|
||||
return Map
|
54
Documentation/compiler/pl/MultiMap.lua
Normal file
54
Documentation/compiler/pl/MultiMap.lua
Normal file
|
@ -0,0 +1,54 @@
|
|||
--- MultiMap, a Map which has multiple values per key.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.class`, `pl.List`, `pl.Map`
|
||||
-- @classmod pl.MultiMap
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local class = require 'pl.class'
|
||||
local List = require 'pl.List'
|
||||
local Map = require 'pl.Map'
|
||||
|
||||
-- MultiMap is a standard MT
|
||||
local MultiMap = utils.stdmt.MultiMap
|
||||
|
||||
class(Map,nil,MultiMap)
|
||||
MultiMap._name = 'MultiMap'
|
||||
|
||||
function MultiMap:_init (t)
|
||||
if not t then return end
|
||||
self:update(t)
|
||||
end
|
||||
|
||||
--- update a MultiMap using a table.
|
||||
-- @param t either a Multimap or a map-like table.
|
||||
-- @return the map
|
||||
function MultiMap:update (t)
|
||||
utils.assert_arg(1,t,'table')
|
||||
if Map:class_of(t) then
|
||||
for k,v in pairs(t) do
|
||||
self[k] = List()
|
||||
self[k]:append(v)
|
||||
end
|
||||
else
|
||||
for k,v in pairs(t) do
|
||||
self[k] = List(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- add a new value to a key. Setting a nil value removes the key.
|
||||
-- @param key the key
|
||||
-- @param val the value
|
||||
-- @return the map
|
||||
function MultiMap:set (key,val)
|
||||
if val == nil then
|
||||
self[key] = nil
|
||||
else
|
||||
if not self[key] then
|
||||
self[key] = List()
|
||||
end
|
||||
self[key]:append(val)
|
||||
end
|
||||
end
|
||||
|
||||
return MultiMap
|
167
Documentation/compiler/pl/OrderedMap.lua
Normal file
167
Documentation/compiler/pl/OrderedMap.lua
Normal file
|
@ -0,0 +1,167 @@
|
|||
--- OrderedMap, a map which preserves ordering.
|
||||
--
|
||||
-- Derived from `pl.Map`.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.List`, `pl.Map`
|
||||
-- @classmod pl.OrderedMap
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local List = require 'pl.List'
|
||||
local index_by,tsort,concat = tablex.index_by,table.sort,table.concat
|
||||
|
||||
local class = require 'pl.class'
|
||||
local Map = require 'pl.Map'
|
||||
|
||||
local OrderedMap = class(Map)
|
||||
OrderedMap._name = 'OrderedMap'
|
||||
|
||||
local rawset = rawset
|
||||
|
||||
--- construct an OrderedMap.
|
||||
-- Will throw an error if the argument is bad.
|
||||
-- @param t optional initialization table, same as for @{OrderedMap:update}
|
||||
function OrderedMap:_init (t)
|
||||
rawset(self,'_keys',List())
|
||||
if t then
|
||||
local map,err = self:update(t)
|
||||
if not map then error(err,2) end
|
||||
end
|
||||
end
|
||||
|
||||
local assert_arg,raise = utils.assert_arg,utils.raise
|
||||
|
||||
--- update an OrderedMap using a table.
|
||||
-- If the table is itself an OrderedMap, then its entries will be appended.
|
||||
-- if it s a table of the form `{{key1=val1},{key2=val2},...}` these will be appended.
|
||||
--
|
||||
-- Otherwise, it is assumed to be a map-like table, and order of extra entries is arbitrary.
|
||||
-- @tab t a table.
|
||||
-- @return the map, or nil in case of error
|
||||
-- @return the error message
|
||||
function OrderedMap:update (t)
|
||||
assert_arg(1,t,'table')
|
||||
if OrderedMap:class_of(t) then
|
||||
for k,v in t:iter() do
|
||||
self:set(k,v)
|
||||
end
|
||||
elseif #t > 0 then -- an array must contain {key=val} tables
|
||||
if type(t[1]) == 'table' then
|
||||
for _,pair in ipairs(t) do
|
||||
local key,value = next(pair)
|
||||
if not key then return raise 'empty pair initialization table' end
|
||||
self:set(key,value)
|
||||
end
|
||||
else
|
||||
return raise 'cannot use an array to initialize an OrderedMap'
|
||||
end
|
||||
else
|
||||
for k,v in pairs(t) do
|
||||
self:set(k,v)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- set the key's value. This key will be appended at the end of the map.
|
||||
--
|
||||
-- If the value is nil, then the key is removed.
|
||||
-- @param key the key
|
||||
-- @param val the value
|
||||
-- @return the map
|
||||
function OrderedMap:set (key,val)
|
||||
if rawget(self, key) == nil and val ~= nil then -- new key
|
||||
self._keys:append(key) -- we keep in order
|
||||
rawset(self,key,val) -- don't want to provoke __newindex!
|
||||
else -- existing key-value pair
|
||||
if val == nil then
|
||||
self._keys:remove_value(key)
|
||||
rawset(self,key,nil)
|
||||
else
|
||||
self[key] = val
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
OrderedMap.__newindex = OrderedMap.set
|
||||
|
||||
--- insert a key/value pair before a given position.
|
||||
-- Note: if the map already contains the key, then this effectively
|
||||
-- moves the item to the new position by first removing at the old position.
|
||||
-- Has no effect if the key does not exist and val is nil
|
||||
-- @int pos a position starting at 1
|
||||
-- @param key the key
|
||||
-- @param val the value; if nil use the old value
|
||||
function OrderedMap:insert (pos,key,val)
|
||||
local oldval = self[key]
|
||||
val = val or oldval
|
||||
if oldval then
|
||||
self._keys:remove_value(key)
|
||||
end
|
||||
if val then
|
||||
self._keys:insert(pos,key)
|
||||
rawset(self,key,val)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- return the keys in order.
|
||||
-- (Not a copy!)
|
||||
-- @return List
|
||||
function OrderedMap:keys ()
|
||||
return self._keys
|
||||
end
|
||||
|
||||
--- return the values in order.
|
||||
-- this is relatively expensive.
|
||||
-- @return List
|
||||
function OrderedMap:values ()
|
||||
return List(index_by(self,self._keys))
|
||||
end
|
||||
|
||||
--- sort the keys.
|
||||
-- @func cmp a comparison function as for @{table.sort}
|
||||
-- @return the map
|
||||
function OrderedMap:sort (cmp)
|
||||
tsort(self._keys,cmp)
|
||||
return self
|
||||
end
|
||||
|
||||
--- iterate over key-value pairs in order.
|
||||
function OrderedMap:iter ()
|
||||
local i = 0
|
||||
local keys = self._keys
|
||||
local idx
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > #keys then return nil end
|
||||
idx = keys[i]
|
||||
return idx,self[idx]
|
||||
end
|
||||
end
|
||||
|
||||
--- iterate over an ordered map (5.2).
|
||||
-- @within metamethods
|
||||
-- @function OrderedMap:__pairs
|
||||
OrderedMap.__pairs = OrderedMap.iter
|
||||
|
||||
--- string representation of an ordered map.
|
||||
-- @within metamethods
|
||||
function OrderedMap:__tostring ()
|
||||
local res = {}
|
||||
for i,v in ipairs(self._keys) do
|
||||
local val = self[v]
|
||||
local vs = tostring(val)
|
||||
if type(val) ~= 'number' then
|
||||
vs = '"'..vs..'"'
|
||||
end
|
||||
res[i] = tostring(v)..'='..vs
|
||||
end
|
||||
return '{'..concat(res,',')..'}'
|
||||
end
|
||||
|
||||
return OrderedMap
|
||||
|
||||
|
||||
|
222
Documentation/compiler/pl/Set.lua
Normal file
222
Documentation/compiler/pl/Set.lua
Normal file
|
@ -0,0 +1,222 @@
|
|||
--- A Set class.
|
||||
--
|
||||
-- > Set = require 'pl.Set'
|
||||
-- > = Set{'one','two'} == Set{'two','one'}
|
||||
-- true
|
||||
-- > fruit = Set{'apple','banana','orange'}
|
||||
-- > = fruit['banana']
|
||||
-- true
|
||||
-- > = fruit['hazelnut']
|
||||
-- nil
|
||||
-- > colours = Set{'red','orange','green','blue'}
|
||||
-- > = fruit,colours
|
||||
-- [apple,orange,banana] [blue,green,orange,red]
|
||||
-- > = fruit+colours
|
||||
-- [blue,green,apple,red,orange,banana]
|
||||
-- [orange]
|
||||
-- > more_fruits = fruit + 'apricot'
|
||||
-- > = fruit*colours
|
||||
-- > = more_fruits, fruit
|
||||
-- [banana,apricot,apple,orange] [banana,apple,orange]
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.Map`, (`pl.List` if __tostring is used)
|
||||
-- @classmod pl.Set
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local array_tostring, concat = utils.array_tostring, table.concat
|
||||
local merge,difference = tablex.merge,tablex.difference
|
||||
local Map = require 'pl.Map'
|
||||
local class = require 'pl.class'
|
||||
local stdmt = utils.stdmt
|
||||
local Set = stdmt.Set
|
||||
|
||||
-- the Set class --------------------
|
||||
class(Map,nil,Set)
|
||||
|
||||
-- note: Set has _no_ methods!
|
||||
Set.__index = nil
|
||||
|
||||
local function makeset (t)
|
||||
return setmetatable(t,Set)
|
||||
end
|
||||
|
||||
--- create a set. <br>
|
||||
-- @param t may be a Set, Map or list-like table.
|
||||
-- @class function
|
||||
-- @name Set
|
||||
function Set:_init (t)
|
||||
t = t or {}
|
||||
local mt = getmetatable(t)
|
||||
if mt == Set or mt == Map then
|
||||
for k in pairs(t) do self[k] = true end
|
||||
else
|
||||
for _,v in ipairs(t) do self[v] = true end
|
||||
end
|
||||
end
|
||||
|
||||
--- string representation of a set.
|
||||
-- @within metamethods
|
||||
function Set:__tostring ()
|
||||
return '['..concat(array_tostring(Set.values(self)),',')..']'
|
||||
end
|
||||
|
||||
--- get a list of the values in a set.
|
||||
-- @param self a Set
|
||||
-- @function Set.values
|
||||
-- @return a list
|
||||
Set.values = Map.keys
|
||||
|
||||
--- map a function over the values of a set.
|
||||
-- @param self a Set
|
||||
-- @param fn a function
|
||||
-- @param ... extra arguments to pass to the function.
|
||||
-- @return a new set
|
||||
function Set.map (self,fn,...)
|
||||
fn = utils.function_arg(1,fn)
|
||||
local res = {}
|
||||
for k in pairs(self) do
|
||||
res[fn(k,...)] = true
|
||||
end
|
||||
return makeset(res)
|
||||
end
|
||||
|
||||
--- union of two sets (also +).
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return a new set
|
||||
function Set.union (self,set)
|
||||
return merge(self,set,true)
|
||||
end
|
||||
|
||||
--- modifies '+' operator to allow addition of non-Set elements
|
||||
--- Preserves +/- semantics - does not modify first argument.
|
||||
local function setadd(self,other)
|
||||
local mt = getmetatable(other)
|
||||
if mt == Set or mt == Map then
|
||||
return Set.union(self,other)
|
||||
else
|
||||
local new = Set(self)
|
||||
new[other] = true
|
||||
return new
|
||||
end
|
||||
end
|
||||
|
||||
--- union of sets.
|
||||
-- @within metamethods
|
||||
-- @function Set.__add
|
||||
|
||||
Set.__add = setadd
|
||||
|
||||
--- intersection of two sets (also *).
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return a new set
|
||||
-- @usage
|
||||
-- > s = Set{10,20,30}
|
||||
-- > t = Set{20,30,40}
|
||||
-- > = t
|
||||
-- [20,30,40]
|
||||
-- > = Set.intersection(s,t)
|
||||
-- [30,20]
|
||||
-- > = s*t
|
||||
-- [30,20]
|
||||
|
||||
function Set.intersection (self,set)
|
||||
return merge(self,set,false)
|
||||
end
|
||||
|
||||
--- intersection of sets.
|
||||
-- @within metamethods
|
||||
-- @function Set.__mul
|
||||
Set.__mul = Set.intersection
|
||||
|
||||
--- new set with elements in the set that are not in the other (also -).
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return a new set
|
||||
function Set.difference (self,set)
|
||||
return difference(self,set,false)
|
||||
end
|
||||
|
||||
--- modifies "-" operator to remove non-Set values from set.
|
||||
--- Preserves +/- semantics - does not modify first argument.
|
||||
local function setminus (self,other)
|
||||
local mt = getmetatable(other)
|
||||
if mt == Set or mt == Map then
|
||||
return Set.difference(self,other)
|
||||
else
|
||||
local new = Set(self)
|
||||
new[other] = nil
|
||||
return new
|
||||
end
|
||||
end
|
||||
|
||||
--- difference of sets.
|
||||
-- @within metamethods
|
||||
-- @function Set.__sub
|
||||
Set.__sub = setminus
|
||||
|
||||
-- a new set with elements in _either_ the set _or_ other but not both (also ^).
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return a new set
|
||||
function Set.symmetric_difference (self,set)
|
||||
return difference(self,set,true)
|
||||
end
|
||||
|
||||
--- symmetric difference of sets.
|
||||
-- @within metamethods
|
||||
-- @function Set.__pow
|
||||
Set.__pow = Set.symmetric_difference
|
||||
|
||||
--- is the first set a subset of the second (also <)?.
|
||||
-- @param self a Set
|
||||
-- @param set another set
|
||||
-- @return true or false
|
||||
function Set.issubset (self,set)
|
||||
for k in pairs(self) do
|
||||
if not set[k] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- first set subset of second?
|
||||
-- @within metamethods
|
||||
-- @function Set.__lt
|
||||
Set.__lt = Set.issubset
|
||||
|
||||
--- is the set empty?.
|
||||
-- @param self a Set
|
||||
-- @return true or false
|
||||
function Set.isempty (self)
|
||||
return next(self) == nil
|
||||
end
|
||||
|
||||
--- are the sets disjoint? (no elements in common).
|
||||
-- Uses naive definition, i.e. that intersection is empty
|
||||
-- @param s1 a Set
|
||||
-- @param s2 another set
|
||||
-- @return true or false
|
||||
function Set.isdisjoint (s1,s2)
|
||||
return Set.isempty(Set.intersection(s1,s2))
|
||||
end
|
||||
|
||||
--- size of this set (also # for 5.2).
|
||||
-- @param s a Set
|
||||
-- @return size
|
||||
-- @function Set.len
|
||||
Set.len = tablex.size
|
||||
|
||||
--- cardinality of set (5.2).
|
||||
-- @within metamethods
|
||||
-- @function Set.__len
|
||||
Set.__len = Set.len
|
||||
|
||||
--- equality between sets.
|
||||
-- @within metamethods
|
||||
function Set.__eq (s1,s2)
|
||||
return Set.issubset(s1,s2) and Set.issubset(s2,s1)
|
||||
end
|
||||
|
||||
return Set
|
309
Documentation/compiler/pl/app.lua
Normal file
309
Documentation/compiler/pl/app.lua
Normal file
|
@ -0,0 +1,309 @@
|
|||
--- Application support functions.
|
||||
-- See @{01-introduction.md.Application_Support|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.path`
|
||||
-- @module pl.app
|
||||
|
||||
local io,package,require = _G.io, _G.package, _G.require
|
||||
local utils = require 'pl.utils'
|
||||
local path = require 'pl.path'
|
||||
|
||||
local app = {}
|
||||
|
||||
--- return the name of the current script running.
|
||||
-- The name will be the name as passed on the command line
|
||||
-- @return string filename
|
||||
function app.script_name()
|
||||
if _G.arg and _G.arg[0] then
|
||||
return _G.arg[0]
|
||||
end
|
||||
return utils.raise("No script name found")
|
||||
end
|
||||
|
||||
--- prefixes the current script's path to the Lua module path.
|
||||
-- Applies to both the source and the binary module paths. It makes it easy for
|
||||
-- the main file of a multi-file program to access its modules in the same directory.
|
||||
-- `base` allows these modules to be put in a specified subdirectory, to allow for
|
||||
-- cleaner deployment and resolve potential conflicts between a script name and its
|
||||
-- library directory.
|
||||
--
|
||||
-- Note: the path is prefixed, so it is searched first when requiring modules.
|
||||
-- @string base optional base directory (absolute, or relative path).
|
||||
-- @bool nofollow always use the invocation's directory, even if the invoked file is a symlink
|
||||
-- @treturn string the current script's path with a trailing slash
|
||||
function app.require_here (base, nofollow)
|
||||
local p = app.script_name()
|
||||
if not path.isabs(p) then
|
||||
p = path.join(path.currentdir(),p)
|
||||
end
|
||||
if not nofollow then
|
||||
local t = path.link_attrib(p)
|
||||
if t and t.mode == 'link' then
|
||||
t = t.target
|
||||
if not path.isabs(t) then
|
||||
t = path.join(path.dirname(p), t)
|
||||
end
|
||||
p = t
|
||||
end
|
||||
end
|
||||
p = path.normpath(path.dirname(p))
|
||||
if p:sub(-1,-1) ~= path.sep then
|
||||
p = p..path.sep
|
||||
end
|
||||
if base then
|
||||
if path.is_windows then
|
||||
base = base:gsub('/','\\')
|
||||
end
|
||||
if path.isabs(base) then
|
||||
p = base .. path.sep
|
||||
else
|
||||
p = p..base..path.sep
|
||||
end
|
||||
end
|
||||
local so_ext = path.is_windows and 'dll' or 'so'
|
||||
local lsep = package.path:find '^;' and '' or ';'
|
||||
local csep = package.cpath:find '^;' and '' or ';'
|
||||
package.path = ('%s?.lua;%s?%sinit.lua%s%s'):format(p,p,path.sep,lsep,package.path)
|
||||
package.cpath = ('%s?.%s%s%s'):format(p,so_ext,csep,package.cpath)
|
||||
return p
|
||||
end
|
||||
|
||||
--- return a suitable path for files private to this application.
|
||||
-- These will look like '~/.SNAME/file', with '~' as with expanduser and
|
||||
-- SNAME is the name of the script without .lua extension.
|
||||
-- If the directory does not exist, it will be created.
|
||||
-- @string file a filename (w/out path)
|
||||
-- @return a full pathname, or nil
|
||||
-- @return cannot create directory error
|
||||
-- @usage
|
||||
-- -- when run from a script called 'testapp' (on Windows):
|
||||
-- local app = require 'pl.app'
|
||||
-- print(app.appfile 'test.txt')
|
||||
-- -- C:\Documents and Settings\steve\.testapp\test.txt
|
||||
function app.appfile(file)
|
||||
local sfullname, err = app.script_name()
|
||||
if not sfullname then return utils.raise(err) end
|
||||
local sname = path.basename(sfullname)
|
||||
local name = path.splitext(sname)
|
||||
local dir = path.join(path.expanduser('~'),'.'..name)
|
||||
if not path.isdir(dir) then
|
||||
local ret = path.mkdir(dir)
|
||||
if not ret then return utils.raise('cannot create '..dir) end
|
||||
end
|
||||
return path.join(dir,file)
|
||||
end
|
||||
|
||||
--- return string indicating operating system.
|
||||
-- @return 'Windows','OSX' or whatever uname returns (e.g. 'Linux')
|
||||
function app.platform()
|
||||
if path.is_windows then
|
||||
return 'Windows'
|
||||
else
|
||||
local f = io.popen('uname')
|
||||
local res = f:read()
|
||||
if res == 'Darwin' then res = 'OSX' end
|
||||
f:close()
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
--- return the full command-line used to invoke this script.
|
||||
-- It will not include the scriptname itself, see `app.script_name`.
|
||||
-- @return command-line
|
||||
-- @return name of Lua program used
|
||||
-- @usage
|
||||
-- -- execute: lua -lluacov -e 'print(_VERSION)' myscript.lua
|
||||
--
|
||||
-- -- myscript.lua
|
||||
-- print(require("pl.app").lua()) --> "lua -lluacov -e 'print(_VERSION)'", "lua"
|
||||
function app.lua()
|
||||
local args = _G.arg
|
||||
if not args then
|
||||
return utils.raise "not in a main program"
|
||||
end
|
||||
|
||||
local cmd = {}
|
||||
local i = -1
|
||||
while true do
|
||||
table.insert(cmd, 1, args[i])
|
||||
if not args[i-1] then
|
||||
return utils.quote_arg(cmd), args[i]
|
||||
end
|
||||
i = i - 1
|
||||
end
|
||||
end
|
||||
|
||||
--- parse command-line arguments into flags and parameters.
|
||||
-- Understands GNU-style command-line flags; short (`-f`) and long (`--flag`).
|
||||
--
|
||||
-- These may be given a value with either '=' or ':' (`-k:2`,`--alpha=3.2`,`-n2`),
|
||||
-- a number value can be given without a space. If the flag is marked
|
||||
-- as having a value, then a space-separated value is also accepted (`-i hello`),
|
||||
-- see the `flags_with_values` argument).
|
||||
--
|
||||
-- Multiple short args can be combined like so: ( `-abcd`).
|
||||
--
|
||||
-- When specifying the `flags_valid` parameter, its contents can also contain
|
||||
-- aliasses, to convert short/long flags to the same output name. See the
|
||||
-- example below.
|
||||
--
|
||||
-- Note: if a flag is repeated, the last value wins.
|
||||
-- @tparam {string} args an array of strings (default is the global `arg`)
|
||||
-- @tab flags_with_values any flags that take values, either list or hash
|
||||
-- table e.g. `{ out=true }` or `{ "out" }`.
|
||||
-- @tab flags_valid (optional) flags that are valid, either list or hashtable.
|
||||
-- If not given, everything
|
||||
-- will be accepted(everything in `flags_with_values` will automatically be allowed)
|
||||
-- @return a table of flags (flag=value pairs)
|
||||
-- @return an array of parameters
|
||||
-- @raise if args is nil, then the global `args` must be available!
|
||||
-- @usage
|
||||
-- -- Simple form:
|
||||
-- local flags, params = app.parse_args(nil,
|
||||
-- { "hello", "world" }, -- list of flags taking values
|
||||
-- { "l", "a", "b"}) -- list of allowed flags (value ones will be added)
|
||||
--
|
||||
-- -- More complex example using aliasses:
|
||||
-- local valid = {
|
||||
-- long = "l", -- if 'l' is specified, it is reported as 'long'
|
||||
-- new = { "n", "old" }, -- here both 'n' and 'old' will go into 'new'
|
||||
-- }
|
||||
-- local values = {
|
||||
-- "value", -- will automatically be added to the allowed set of flags
|
||||
-- "new", -- will mark 'n' and 'old' as requiring a value as well
|
||||
-- }
|
||||
-- local flags, params = app.parse_args(nil, values, valid)
|
||||
--
|
||||
-- -- command: myapp.lua -l --old:hello --value world param1 param2
|
||||
-- -- will yield:
|
||||
-- flags = {
|
||||
-- long = true, -- input from 'l'
|
||||
-- new = "hello", -- input from 'old'
|
||||
-- value = "world", -- allowed because it was in 'values', note: space separated!
|
||||
-- }
|
||||
-- params = {
|
||||
-- [1] = "param1"
|
||||
-- [2] = "param2"
|
||||
-- }
|
||||
function app.parse_args (args,flags_with_values, flags_valid)
|
||||
if not args then
|
||||
args = _G.arg
|
||||
if not args then utils.raise "Not in a main program: 'arg' not found" end
|
||||
end
|
||||
|
||||
local with_values = {}
|
||||
for k,v in pairs(flags_with_values or {}) do
|
||||
if type(k) == "number" then
|
||||
k = v
|
||||
end
|
||||
with_values[k] = true
|
||||
end
|
||||
|
||||
local valid
|
||||
if not flags_valid then
|
||||
-- if no allowed flags provided, we create a table that always returns
|
||||
-- the keyname, no matter what you look up
|
||||
valid = setmetatable({},{ __index = function(_, key) return key end })
|
||||
else
|
||||
valid = {}
|
||||
for k,aliasses in pairs(flags_valid) do
|
||||
if type(k) == "number" then -- array/list entry
|
||||
k = aliasses
|
||||
end
|
||||
if type(aliasses) == "string" then -- single alias
|
||||
aliasses = { aliasses }
|
||||
end
|
||||
if type(aliasses) == "table" then -- list of aliasses
|
||||
-- it's the alternate name, so add the proper mappings
|
||||
for i, alias in ipairs(aliasses) do
|
||||
valid[alias] = k
|
||||
end
|
||||
end
|
||||
valid[k] = k
|
||||
end
|
||||
do
|
||||
local new_with_values = {} -- needed to prevent "invalid key to 'next'" error
|
||||
for k,v in pairs(with_values) do
|
||||
if not valid[k] then
|
||||
valid[k] = k -- add the with_value entry as a valid one
|
||||
new_with_values[k] = true
|
||||
else
|
||||
new_with_values[valid[k]] = true --set, but by its alias
|
||||
end
|
||||
end
|
||||
with_values = new_with_values
|
||||
end
|
||||
end
|
||||
|
||||
-- now check that all flags with values are reported as such under all
|
||||
-- of their aliasses
|
||||
for k, main_alias in pairs(valid) do
|
||||
if with_values[main_alias] then
|
||||
with_values[k] = true
|
||||
end
|
||||
end
|
||||
|
||||
local _args = {}
|
||||
local flags = {}
|
||||
local i = 1
|
||||
while i <= #args do
|
||||
local a = args[i]
|
||||
local v = a:match('^-(.+)')
|
||||
local is_long
|
||||
if not v then
|
||||
-- we have a parameter
|
||||
_args[#_args+1] = a
|
||||
else
|
||||
-- it's a flag
|
||||
if v:find '^-' then
|
||||
is_long = true
|
||||
v = v:sub(2)
|
||||
end
|
||||
if with_values[v] then
|
||||
if i == #args or args[i+1]:find '^-' then
|
||||
return utils.raise ("no value for '"..v.."'")
|
||||
end
|
||||
flags[valid[v]] = args[i+1]
|
||||
i = i + 1
|
||||
else
|
||||
-- a value can also be indicated with = or :
|
||||
local var,val = utils.splitv (v,'[=:]', false, 2)
|
||||
var = var or v
|
||||
val = val or true
|
||||
if not is_long then
|
||||
if #var > 1 then
|
||||
if var:find '.%d+' then -- short flag, number value
|
||||
val = var:sub(2)
|
||||
var = var:sub(1,1)
|
||||
else -- multiple short flags
|
||||
for i = 1,#var do
|
||||
local f = var:sub(i,i)
|
||||
if not valid[f] then
|
||||
return utils.raise("unknown flag '"..f.."'")
|
||||
else
|
||||
f = valid[f]
|
||||
end
|
||||
flags[f] = true
|
||||
end
|
||||
val = nil -- prevents use of var as a flag below
|
||||
end
|
||||
else -- single short flag (can have value, defaults to true)
|
||||
val = val or true
|
||||
end
|
||||
end
|
||||
if val then
|
||||
if not valid[var] then
|
||||
return utils.raise("unknown flag '"..var.."'")
|
||||
else
|
||||
var = valid[var]
|
||||
end
|
||||
flags[var] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return flags,_args
|
||||
end
|
||||
|
||||
return app
|
585
Documentation/compiler/pl/array2d.lua
Normal file
585
Documentation/compiler/pl/array2d.lua
Normal file
|
@ -0,0 +1,585 @@
|
|||
--- Operations on two-dimensional arrays.
|
||||
-- See @{02-arrays.md.Operations_on_two_dimensional_tables|The Guide}
|
||||
--
|
||||
-- The size of the arrays is determined by using the length operator `#` hence
|
||||
-- the module is not `nil` safe, and the usual precautions apply.
|
||||
--
|
||||
-- Note: all functions taking `i1,j1,i2,j2` as arguments will normalize the
|
||||
-- arguments using `default_range`.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.types`
|
||||
-- @module pl.array2d
|
||||
|
||||
local tonumber,tostring,io,ipairs,string,table =
|
||||
_G.tonumber,_G.tostring,_G.io,_G.ipairs,_G.string,_G.table
|
||||
local setmetatable,getmetatable = setmetatable,getmetatable
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local types = require 'pl.types'
|
||||
local imap,tmap,reduce,keys,tmap2,tset,index_by = tablex.imap,tablex.map,tablex.reduce,tablex.keys,tablex.map2,tablex.set,tablex.index_by
|
||||
local remove = table.remove
|
||||
local splitv,fprintf,assert_arg = utils.splitv,utils.fprintf,utils.assert_arg
|
||||
local byte = string.byte
|
||||
local stdout = io.stdout
|
||||
local min = math.min
|
||||
|
||||
|
||||
local array2d = {}
|
||||
|
||||
local function obj (int,out)
|
||||
local mt = getmetatable(int)
|
||||
if mt then
|
||||
setmetatable(out,mt)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function makelist (res)
|
||||
return setmetatable(res, require('pl.List'))
|
||||
end
|
||||
|
||||
--- return the row and column size.
|
||||
-- Size is calculated using the Lua length operator #, so usual precautions
|
||||
-- regarding `nil` values apply.
|
||||
-- @array2d a a 2d array
|
||||
-- @treturn int number of rows (`#a`)
|
||||
-- @treturn int number of cols (`#a[1]`)
|
||||
function array2d.size (a)
|
||||
assert_arg(1,a,'table')
|
||||
return #a,#a[1]
|
||||
end
|
||||
|
||||
do
|
||||
local function index (t,k)
|
||||
return t[k]
|
||||
end
|
||||
|
||||
--- extract a column from the 2D array.
|
||||
-- @array2d a 2d array
|
||||
-- @param j column index
|
||||
-- @return 1d array
|
||||
function array2d.column (a,j)
|
||||
assert_arg(1,a,'table')
|
||||
return makelist(imap(index,a,j))
|
||||
end
|
||||
end
|
||||
local column = array2d.column
|
||||
|
||||
--- extract a row from the 2D array.
|
||||
-- Added in line with `column`, for read-only purposes directly
|
||||
-- accessing a[i] is more performant.
|
||||
-- @array2d a 2d array
|
||||
-- @param i row index
|
||||
-- @return 1d array (copy of the row)
|
||||
function array2d.row(a,i)
|
||||
assert_arg(1,a,'table')
|
||||
local row = a[i]
|
||||
local r = {}
|
||||
for n,v in ipairs(row) do
|
||||
r[n] = v
|
||||
end
|
||||
return makelist(r)
|
||||
end
|
||||
|
||||
--- map a function over a 2D array
|
||||
-- @func f a function of at least one argument
|
||||
-- @array2d a 2d array
|
||||
-- @param arg an optional extra argument to be passed to the function.
|
||||
-- @return 2d array
|
||||
function array2d.map (f,a,arg)
|
||||
assert_arg(2,a,'table')
|
||||
f = utils.function_arg(1,f)
|
||||
return obj(a,imap(function(row) return imap(f,row,arg) end, a))
|
||||
end
|
||||
|
||||
--- reduce the rows using a function.
|
||||
-- @func f a binary function
|
||||
-- @array2d a 2d array
|
||||
-- @return 1d array
|
||||
-- @see pl.tablex.reduce
|
||||
function array2d.reduce_rows (f,a)
|
||||
assert_arg(1,a,'table')
|
||||
return tmap(function(row) return reduce(f,row) end, a)
|
||||
end
|
||||
|
||||
--- reduce the columns using a function.
|
||||
-- @func f a binary function
|
||||
-- @array2d a 2d array
|
||||
-- @return 1d array
|
||||
-- @see pl.tablex.reduce
|
||||
function array2d.reduce_cols (f,a)
|
||||
assert_arg(1,a,'table')
|
||||
return tmap(function(c) return reduce(f,column(a,c)) end, keys(a[1]))
|
||||
end
|
||||
|
||||
--- reduce a 2D array into a scalar, using two operations.
|
||||
-- @func opc operation to reduce the final result
|
||||
-- @func opr operation to reduce the rows
|
||||
-- @param a 2D array
|
||||
function array2d.reduce2 (opc,opr,a)
|
||||
assert_arg(3,a,'table')
|
||||
local tmp = array2d.reduce_rows(opr,a)
|
||||
return reduce(opc,tmp)
|
||||
end
|
||||
|
||||
--- map a function over two arrays.
|
||||
-- They can be both or either 2D arrays
|
||||
-- @func f function of at least two arguments
|
||||
-- @int ad order of first array (`1` if `a` is a list/array, `2` if it is a 2d array)
|
||||
-- @int bd order of second array (`1` if `b` is a list/array, `2` if it is a 2d array)
|
||||
-- @tab a 1d or 2d array
|
||||
-- @tab b 1d or 2d array
|
||||
-- @param arg optional extra argument to pass to function
|
||||
-- @return 2D array, unless both arrays are 1D
|
||||
function array2d.map2 (f,ad,bd,a,b,arg)
|
||||
assert_arg(1,a,'table')
|
||||
assert_arg(2,b,'table')
|
||||
f = utils.function_arg(1,f)
|
||||
if ad == 1 and bd == 2 then
|
||||
return imap(function(row)
|
||||
return tmap2(f,a,row,arg)
|
||||
end, b)
|
||||
elseif ad == 2 and bd == 1 then
|
||||
return imap(function(row)
|
||||
return tmap2(f,row,b,arg)
|
||||
end, a)
|
||||
elseif ad == 1 and bd == 1 then
|
||||
return tmap2(f,a,b)
|
||||
elseif ad == 2 and bd == 2 then
|
||||
return tmap2(function(rowa,rowb)
|
||||
return tmap2(f,rowa,rowb,arg)
|
||||
end, a,b)
|
||||
end
|
||||
end
|
||||
|
||||
--- cartesian product of two 1d arrays.
|
||||
-- @func f a function of 2 arguments
|
||||
-- @array t1 a 1d table
|
||||
-- @array t2 a 1d table
|
||||
-- @return 2d table
|
||||
-- @usage product('..',{1,2},{'a','b'}) == {{'1a','2a'},{'1b','2b'}}
|
||||
function array2d.product (f,t1,t2)
|
||||
f = utils.function_arg(1,f)
|
||||
assert_arg(2,t1,'table')
|
||||
assert_arg(3,t2,'table')
|
||||
local res = {}
|
||||
for i,v in ipairs(t2) do
|
||||
res[i] = tmap(f,t1,v)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- flatten a 2D array.
|
||||
-- (this goes over columns first.)
|
||||
-- @array2d t 2d table
|
||||
-- @return a 1d table
|
||||
-- @usage flatten {{1,2},{3,4},{5,6}} == {1,2,3,4,5,6}
|
||||
function array2d.flatten (t)
|
||||
local res = {}
|
||||
local k = 1
|
||||
local rows, cols = array2d.size(t)
|
||||
for r = 1, rows do
|
||||
local row = t[r]
|
||||
for c = 1, cols do
|
||||
res[k] = row[c]
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- reshape a 2D array. Reshape the aray by specifying a new nr of rows.
|
||||
-- @array2d t 2d array
|
||||
-- @int nrows new number of rows
|
||||
-- @bool co use column-order (Fortran-style) (default false)
|
||||
-- @return a new 2d array
|
||||
function array2d.reshape (t,nrows,co)
|
||||
local nr,nc = array2d.size(t)
|
||||
local ncols = nr*nc / nrows
|
||||
local res = {}
|
||||
local ir,ic = 1,1
|
||||
for i = 1,nrows do
|
||||
local row = {}
|
||||
for j = 1,ncols do
|
||||
row[j] = t[ir][ic]
|
||||
if not co then
|
||||
ic = ic + 1
|
||||
if ic > nc then
|
||||
ir = ir + 1
|
||||
ic = 1
|
||||
end
|
||||
else
|
||||
ir = ir + 1
|
||||
if ir > nr then
|
||||
ic = ic + 1
|
||||
ir = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
res[i] = row
|
||||
end
|
||||
return obj(t,res)
|
||||
end
|
||||
|
||||
--- transpose a 2D array.
|
||||
-- @array2d t 2d array
|
||||
-- @return a new 2d array
|
||||
function array2d.transpose(t)
|
||||
assert_arg(1,t,'table')
|
||||
local _, c = array2d.size(t)
|
||||
return array2d.reshape(t,c,true)
|
||||
end
|
||||
|
||||
--- swap two rows of an array.
|
||||
-- @array2d t a 2d array
|
||||
-- @int i1 a row index
|
||||
-- @int i2 a row index
|
||||
-- @return t (same, modified 2d array)
|
||||
function array2d.swap_rows (t,i1,i2)
|
||||
assert_arg(1,t,'table')
|
||||
t[i1],t[i2] = t[i2],t[i1]
|
||||
return t
|
||||
end
|
||||
|
||||
--- swap two columns of an array.
|
||||
-- @array2d t a 2d array
|
||||
-- @int j1 a column index
|
||||
-- @int j2 a column index
|
||||
-- @return t (same, modified 2d array)
|
||||
function array2d.swap_cols (t,j1,j2)
|
||||
assert_arg(1,t,'table')
|
||||
for _, row in ipairs(t) do
|
||||
row[j1],row[j2] = row[j2],row[j1]
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
--- extract the specified rows.
|
||||
-- @array2d t 2d array
|
||||
-- @tparam {int} ridx a table of row indices
|
||||
-- @return a new 2d array with the extracted rows
|
||||
function array2d.extract_rows (t,ridx)
|
||||
return obj(t,index_by(t,ridx))
|
||||
end
|
||||
|
||||
--- extract the specified columns.
|
||||
-- @array2d t 2d array
|
||||
-- @tparam {int} cidx a table of column indices
|
||||
-- @return a new 2d array with the extracted colums
|
||||
function array2d.extract_cols (t,cidx)
|
||||
assert_arg(1,t,'table')
|
||||
local res = {}
|
||||
for i = 1,#t do
|
||||
res[i] = index_by(t[i],cidx)
|
||||
end
|
||||
return obj(t,res)
|
||||
end
|
||||
|
||||
--- remove a row from an array.
|
||||
-- @function array2d.remove_row
|
||||
-- @array2d t a 2d array
|
||||
-- @int i a row index
|
||||
array2d.remove_row = remove
|
||||
|
||||
--- remove a column from an array.
|
||||
-- @array2d t a 2d array
|
||||
-- @int j a column index
|
||||
function array2d.remove_col (t,j)
|
||||
assert_arg(1,t,'table')
|
||||
for i = 1,#t do
|
||||
remove(t[i],j)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local function _parse (s)
|
||||
local r, c = s:match 'R(%d+)C(%d+)'
|
||||
if r then
|
||||
r,c = tonumber(r),tonumber(c)
|
||||
return r,c
|
||||
end
|
||||
c,r = s:match '(%a+)(%d+)'
|
||||
if c then
|
||||
local cv = 0
|
||||
for i = 1, #c do
|
||||
cv = cv * 26 + byte(c:sub(i,i)) - byte 'A' + 1
|
||||
end
|
||||
return tonumber(r), cv
|
||||
end
|
||||
error('bad cell specifier: '..s)
|
||||
end
|
||||
|
||||
--- parse a spreadsheet range or cell.
|
||||
-- The range/cell can be specified either as 'A1:B2' or 'R1C1:R2C2' or for
|
||||
-- single cells as 'A1' or 'R1C1'.
|
||||
-- @string s a range (case insensitive).
|
||||
-- @treturn int start row
|
||||
-- @treturn int start col
|
||||
-- @treturn int end row (or `nil` if the range was a single cell)
|
||||
-- @treturn int end col (or `nil` if the range was a single cell)
|
||||
function array2d.parse_range (s)
|
||||
assert_arg(1,s,'string')
|
||||
s = s:upper()
|
||||
if s:find ':' then
|
||||
local start,finish = splitv(s,':')
|
||||
local i1,j1 = _parse(start)
|
||||
local i2,j2 = _parse(finish)
|
||||
return i1,j1,i2,j2
|
||||
else -- single value
|
||||
local i,j = _parse(s)
|
||||
return i,j
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- get a slice of a 2D array.
|
||||
-- Same as `slice`.
|
||||
-- @see slice
|
||||
function array2d.range (...)
|
||||
return array2d.slice(...)
|
||||
end
|
||||
|
||||
local default_range do
|
||||
local function norm_value(v, max)
|
||||
if not v then return v end
|
||||
if v < 0 then
|
||||
v = max + v + 1
|
||||
end
|
||||
if v < 1 then v = 1 end
|
||||
if v > max then v = max end
|
||||
return v
|
||||
end
|
||||
|
||||
--- normalizes coordinates to valid positive entries and defaults.
|
||||
-- Negative indices will be counted from the end, too low, or too high
|
||||
-- will be limited by the array sizes.
|
||||
-- @array2d t a 2D array
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
-- @return i1, j1, i2, j2
|
||||
function array2d.default_range (t,i1,j1,i2,j2)
|
||||
if (type(i1) == 'string') and not (j1 or i2 or j2) then
|
||||
i1, j1, i2, j2 = array2d.parse_range(i1)
|
||||
end
|
||||
local nr, nc = array2d.size(t)
|
||||
i1 = norm_value(i1 or 1, nr)
|
||||
j1 = norm_value(j1 or 1, nc)
|
||||
i2 = norm_value(i2 or nr, nr)
|
||||
j2 = norm_value(j2 or nc, nc)
|
||||
return i1,j1,i2,j2
|
||||
end
|
||||
default_range = array2d.default_range
|
||||
end
|
||||
|
||||
--- get a slice of a 2D array. Note that if the specified range has
|
||||
-- a 1D result, the rank of the result will be 1.
|
||||
-- @array2d t a 2D array
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
-- @return an array, 2D in general but 1D in special cases.
|
||||
function array2d.slice (t,i1,j1,i2,j2)
|
||||
assert_arg(1,t,'table')
|
||||
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
|
||||
local res = {}
|
||||
for i = i1,i2 do
|
||||
local val
|
||||
local row = t[i]
|
||||
if j1 == j2 then
|
||||
val = row[j1]
|
||||
else
|
||||
val = {}
|
||||
for j = j1,j2 do
|
||||
val[#val+1] = row[j]
|
||||
end
|
||||
end
|
||||
res[#res+1] = val
|
||||
end
|
||||
if i1 == i2 then res = res[1] end
|
||||
return obj(t,res)
|
||||
end
|
||||
|
||||
--- set a specified range of an array to a value.
|
||||
-- @array2d t a 2D array
|
||||
-- @param value the value (may be a function, called as `val(i,j)`)
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
-- @see tablex.set
|
||||
function array2d.set (t,value,i1,j1,i2,j2)
|
||||
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
|
||||
local i = i1
|
||||
if types.is_callable(value) then
|
||||
local old_f = value
|
||||
value = function(j)
|
||||
return old_f(i,j)
|
||||
end
|
||||
end
|
||||
while i <= i2 do
|
||||
tset(t[i],value,j1,j2)
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
--- write a 2D array to a file.
|
||||
-- @array2d t a 2D array
|
||||
-- @param f a file object (default stdout)
|
||||
-- @string fmt a format string (default is just to use tostring)
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
function array2d.write (t,f,fmt,i1,j1,i2,j2)
|
||||
assert_arg(1,t,'table')
|
||||
f = f or stdout
|
||||
local rowop
|
||||
if fmt then
|
||||
rowop = function(row,j) fprintf(f,fmt,row[j]) end
|
||||
else
|
||||
rowop = function(row,j) f:write(tostring(row[j]),' ') end
|
||||
end
|
||||
local function newline()
|
||||
f:write '\n'
|
||||
end
|
||||
array2d.forall(t,rowop,newline,i1,j1,i2,j2)
|
||||
end
|
||||
|
||||
--- perform an operation for all values in a 2D array.
|
||||
-- @array2d t 2D array
|
||||
-- @func row_op function to call on each value; `row_op(row,j)`
|
||||
-- @func end_row_op function to call at end of each row; `end_row_op(i)`
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
function array2d.forall (t,row_op,end_row_op,i1,j1,i2,j2)
|
||||
assert_arg(1,t,'table')
|
||||
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
|
||||
for i = i1,i2 do
|
||||
local row = t[i]
|
||||
for j = j1,j2 do
|
||||
row_op(row,j)
|
||||
end
|
||||
if end_row_op then end_row_op(i) end
|
||||
end
|
||||
end
|
||||
|
||||
---- move a block from the destination to the source.
|
||||
-- @array2d dest a 2D array
|
||||
-- @int di start row in dest
|
||||
-- @int dj start col in dest
|
||||
-- @array2d src a 2D array
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
function array2d.move (dest,di,dj,src,i1,j1,i2,j2)
|
||||
assert_arg(1,dest,'table')
|
||||
assert_arg(4,src,'table')
|
||||
i1,j1,i2,j2 = default_range(src,i1,j1,i2,j2)
|
||||
local nr,nc = array2d.size(dest)
|
||||
i2, j2 = min(nr,i2), min(nc,j2)
|
||||
--i1, j1 = max(1,i1), max(1,j1)
|
||||
dj = dj - 1
|
||||
for i = i1,i2 do
|
||||
local drow, srow = dest[i+di-1], src[i]
|
||||
for j = j1,j2 do
|
||||
drow[j+dj] = srow[j]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- iterate over all elements in a 2D array, with optional indices.
|
||||
-- @array2d a 2D array
|
||||
-- @bool indices with indices (default false)
|
||||
-- @tparam[opt=1] int|string i1 start row or spreadsheet range passed to `parse_range`
|
||||
-- @tparam[opt=1] int j1 start col
|
||||
-- @tparam[opt=N] int i2 end row
|
||||
-- @tparam[opt=M] int j2 end col
|
||||
-- @see parse_range
|
||||
-- @return either `value` or `i,j,value` depending on the value of `indices`
|
||||
function array2d.iter(a,indices,i1,j1,i2,j2)
|
||||
assert_arg(1,a,'table')
|
||||
i1,j1,i2,j2 = default_range(a,i1,j1,i2,j2)
|
||||
local i,j = i1,j1-1
|
||||
local row = a[i]
|
||||
return function()
|
||||
j = j + 1
|
||||
if j > j2 then
|
||||
j = j1
|
||||
i = i + 1
|
||||
row = a[i]
|
||||
if i > i2 then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
if indices then
|
||||
return i,j,row[j]
|
||||
else
|
||||
return row[j]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- iterate over all columns.
|
||||
-- @array2d a a 2D array
|
||||
-- @return column, column-index
|
||||
function array2d.columns(a)
|
||||
assert_arg(1,a,'table')
|
||||
local n = #a[1]
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > n then return nil end
|
||||
return column(a,i), i
|
||||
end
|
||||
end
|
||||
|
||||
--- iterate over all rows.
|
||||
-- Returns a copy of the row, for read-only purposes directly iterating
|
||||
-- is more performant; `ipairs(a)`
|
||||
-- @array2d a a 2D array
|
||||
-- @return row, row-index
|
||||
function array2d.rows(a)
|
||||
assert_arg(1,a,'table')
|
||||
local n = #a
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > n then return nil end
|
||||
return array2d.row(a,i), i
|
||||
end
|
||||
end
|
||||
|
||||
--- new array of specified dimensions
|
||||
-- @int rows number of rows
|
||||
-- @int cols number of cols
|
||||
-- @param val initial value; if it's a function then use `val(i,j)`
|
||||
-- @return new 2d array
|
||||
function array2d.new(rows,cols,val)
|
||||
local res = {}
|
||||
local fun = types.is_callable(val)
|
||||
for i = 1,rows do
|
||||
local row = {}
|
||||
if fun then
|
||||
for j = 1,cols do row[j] = val(i,j) end
|
||||
else
|
||||
for j = 1,cols do row[j] = val end
|
||||
end
|
||||
res[i] = row
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
return array2d
|
265
Documentation/compiler/pl/class.lua
Normal file
265
Documentation/compiler/pl/class.lua
Normal file
|
@ -0,0 +1,265 @@
|
|||
--- Provides a reuseable and convenient framework for creating classes in Lua.
|
||||
-- Two possible notations:
|
||||
--
|
||||
-- B = class(A)
|
||||
-- class.B(A)
|
||||
--
|
||||
-- The latter form creates a named class within the current environment. Note
|
||||
-- that this implicitly brings in `pl.utils` as a dependency.
|
||||
--
|
||||
-- See the Guide for further @{01-introduction.md.Simplifying_Object_Oriented_Programming_in_Lua|discussion}
|
||||
-- @module pl.class
|
||||
|
||||
local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type =
|
||||
_G.error, _G.getmetatable, _G.io, _G.pairs, _G.rawget, _G.rawset, _G.setmetatable, _G.tostring, _G.type
|
||||
local compat
|
||||
|
||||
-- this trickery is necessary to prevent the inheritance of 'super' and
|
||||
-- the resulting recursive call problems.
|
||||
local function call_ctor (c,obj,...)
|
||||
local init = rawget(c,'_init')
|
||||
local parent_with_init = rawget(c,'_parent_with_init')
|
||||
|
||||
if parent_with_init then
|
||||
if not init then -- inheriting an init
|
||||
init = rawget(parent_with_init, '_init')
|
||||
parent_with_init = rawget(parent_with_init, '_parent_with_init')
|
||||
end
|
||||
if parent_with_init then -- super() points to one above whereever _init came from
|
||||
rawset(obj,'super',function(obj,...)
|
||||
call_ctor(parent_with_init,obj,...)
|
||||
end)
|
||||
end
|
||||
else
|
||||
-- Without this, calling super() where none exists will sometimes loop and stack overflow
|
||||
rawset(obj,'super',nil)
|
||||
end
|
||||
|
||||
local res = init(obj,...)
|
||||
if parent_with_init then -- If this execution of call_ctor set a super, unset it
|
||||
rawset(obj,'super',nil)
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
--- initializes an __instance__ upon creation.
|
||||
-- @function class:_init
|
||||
-- @param ... parameters passed to the constructor
|
||||
-- @usage local Cat = class()
|
||||
-- function Cat:_init(name)
|
||||
-- --self:super(name) -- call the ancestor initializer if needed
|
||||
-- self.name = name
|
||||
-- end
|
||||
--
|
||||
-- local pussycat = Cat("pussycat")
|
||||
-- print(pussycat.name) --> pussycat
|
||||
|
||||
--- checks whether an __instance__ is derived from some class.
|
||||
-- Works the other way around as `class_of`. It has two ways of using;
|
||||
-- 1) call with a class to check against, 2) call without params.
|
||||
-- @function instance:is_a
|
||||
-- @param some_class class to check against, or `nil` to return the class
|
||||
-- @return `true` if `instance` is derived from `some_class`, or if `some_class == nil` then
|
||||
-- it returns the class table of the instance
|
||||
-- @usage local pussycat = Lion() -- assuming Lion derives from Cat
|
||||
-- if pussycat:is_a(Cat) then
|
||||
-- -- it's true, it is a Lion, but also a Cat
|
||||
-- end
|
||||
--
|
||||
-- if pussycat:is_a() == Lion then
|
||||
-- -- It's true
|
||||
-- end
|
||||
local function is_a(self,klass)
|
||||
if klass == nil then
|
||||
-- no class provided, so return the class this instance is derived from
|
||||
return getmetatable(self)
|
||||
end
|
||||
local m = getmetatable(self)
|
||||
if not m then return false end --*can't be an object!
|
||||
while m do
|
||||
if m == klass then return true end
|
||||
m = rawget(m,'_base')
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- checks whether an __instance__ is derived from some class.
|
||||
-- Works the other way around as `is_a`.
|
||||
-- @function some_class:class_of
|
||||
-- @param some_instance instance to check against
|
||||
-- @return `true` if `some_instance` is derived from `some_class`
|
||||
-- @usage local pussycat = Lion() -- assuming Lion derives from Cat
|
||||
-- if Cat:class_of(pussycat) then
|
||||
-- -- it's true
|
||||
-- end
|
||||
local function class_of(klass,obj)
|
||||
if type(klass) ~= 'table' or not rawget(klass,'is_a') then return false end
|
||||
return klass.is_a(obj,klass)
|
||||
end
|
||||
|
||||
--- cast an object to another class.
|
||||
-- It is not clever (or safe!) so use carefully.
|
||||
-- @param some_instance the object to be changed
|
||||
-- @function some_class:cast
|
||||
local function cast (klass, obj)
|
||||
return setmetatable(obj,klass)
|
||||
end
|
||||
|
||||
|
||||
local function _class_tostring (obj)
|
||||
local mt = obj._class
|
||||
local name = rawget(mt,'_name')
|
||||
setmetatable(obj,nil)
|
||||
local str = tostring(obj)
|
||||
setmetatable(obj,mt)
|
||||
if name then str = name ..str:gsub('table','') end
|
||||
return str
|
||||
end
|
||||
|
||||
local function tupdate(td,ts,dont_override)
|
||||
for k,v in pairs(ts) do
|
||||
if not dont_override or td[k] == nil then
|
||||
td[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function _class(base,c_arg,c)
|
||||
-- the class `c` will be the metatable for all its objects,
|
||||
-- and they will look up their methods in it.
|
||||
local mt = {} -- a metatable for the class to support __call and _handler
|
||||
-- can define class by passing it a plain table of methods
|
||||
local plain = type(base) == 'table' and not getmetatable(base)
|
||||
if plain then
|
||||
c = base
|
||||
base = c._base
|
||||
else
|
||||
c = c or {}
|
||||
end
|
||||
|
||||
if type(base) == 'table' then
|
||||
-- our new class is a shallow copy of the base class!
|
||||
-- but be careful not to wipe out any methods we have been given at this point!
|
||||
tupdate(c,base,plain)
|
||||
c._base = base
|
||||
-- inherit the 'not found' handler, if present
|
||||
if rawget(c,'_handler') then mt.__index = c._handler end
|
||||
elseif base ~= nil then
|
||||
error("must derive from a table type",3)
|
||||
end
|
||||
|
||||
c.__index = c
|
||||
setmetatable(c,mt)
|
||||
if not plain then
|
||||
if base and rawget(base,'_init') then c._parent_with_init = base end -- For super and inherited init
|
||||
c._init = nil
|
||||
end
|
||||
|
||||
if base and rawget(base,'_class_init') then
|
||||
base._class_init(c,c_arg)
|
||||
end
|
||||
|
||||
-- expose a ctor which can be called by <classname>(<args>)
|
||||
mt.__call = function(class_tbl,...)
|
||||
local obj
|
||||
if rawget(c,'_create') then obj = c._create(...) end
|
||||
if not obj then obj = {} end
|
||||
setmetatable(obj,c)
|
||||
|
||||
if rawget(c,'_init') or rawget(c,'_parent_with_init') then -- constructor exists
|
||||
local res = call_ctor(c,obj,...)
|
||||
if res then -- _if_ a ctor returns a value, it becomes the object...
|
||||
obj = res
|
||||
setmetatable(obj,c)
|
||||
end
|
||||
end
|
||||
|
||||
if base and rawget(base,'_post_init') then
|
||||
base._post_init(obj)
|
||||
end
|
||||
|
||||
return obj
|
||||
end
|
||||
-- Call Class.catch to set a handler for methods/properties not found in the class!
|
||||
c.catch = function(self, handler)
|
||||
if type(self) == "function" then
|
||||
-- called using . instead of :
|
||||
handler = self
|
||||
end
|
||||
c._handler = handler
|
||||
mt.__index = handler
|
||||
end
|
||||
c.is_a = is_a
|
||||
c.class_of = class_of
|
||||
c.cast = cast
|
||||
c._class = c
|
||||
|
||||
if not rawget(c,'__tostring') then
|
||||
c.__tostring = _class_tostring
|
||||
end
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
--- create a new class, derived from a given base class.
|
||||
-- Supporting two class creation syntaxes:
|
||||
-- either `Name = class(base)` or `class.Name(base)`.
|
||||
-- The first form returns the class directly and does not set its `_name`.
|
||||
-- The second form creates a variable `Name` in the current environment set
|
||||
-- to the class, and also sets `_name`.
|
||||
-- @function class
|
||||
-- @param base optional base class
|
||||
-- @param c_arg optional parameter to class constructor
|
||||
-- @param c optional table to be used as class
|
||||
local class
|
||||
class = setmetatable({},{
|
||||
__call = function(fun,...)
|
||||
return _class(...)
|
||||
end,
|
||||
__index = function(tbl,key)
|
||||
if key == 'class' then
|
||||
io.stderr:write('require("pl.class").class is deprecated. Use require("pl.class")\n')
|
||||
return class
|
||||
end
|
||||
compat = compat or require 'pl.compat'
|
||||
local env = compat.getfenv(2)
|
||||
return function(...)
|
||||
local c = _class(...)
|
||||
c._name = key
|
||||
rawset(env,key,c)
|
||||
return c
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
class.properties = class()
|
||||
|
||||
function class.properties._class_init(klass)
|
||||
klass.__index = function(t,key)
|
||||
-- normal class lookup!
|
||||
local v = klass[key]
|
||||
if v then return v end
|
||||
-- is it a getter?
|
||||
v = rawget(klass,'get_'..key)
|
||||
if v then
|
||||
return v(t)
|
||||
end
|
||||
-- is it a field?
|
||||
return rawget(t,'_'..key)
|
||||
end
|
||||
klass.__newindex = function (t,key,value)
|
||||
-- if there's a setter, use that, otherwise directly set table
|
||||
local p = 'set_'..key
|
||||
local setter = klass[p]
|
||||
if setter then
|
||||
setter(t,value)
|
||||
else
|
||||
rawset(t,key,value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return class
|
||||
|
252
Documentation/compiler/pl/compat.lua
Normal file
252
Documentation/compiler/pl/compat.lua
Normal file
|
@ -0,0 +1,252 @@
|
|||
----------------
|
||||
--- Lua 5.1/5.2/5.3 compatibility.
|
||||
-- Injects `table.pack`, `table.unpack`, and `package.searchpath` in the global
|
||||
-- environment, to make sure they are available for Lua 5.1 and LuaJIT.
|
||||
--
|
||||
-- All other functions are exported as usual in the returned module table.
|
||||
--
|
||||
-- NOTE: everything in this module is also available in `pl.utils`.
|
||||
-- @module pl.compat
|
||||
local compat = {}
|
||||
|
||||
--- boolean flag this is Lua 5.1 (or LuaJIT).
|
||||
-- @field lua51
|
||||
compat.lua51 = _VERSION == 'Lua 5.1'
|
||||
|
||||
--- boolean flag this is LuaJIT.
|
||||
-- @field jit
|
||||
compat.jit = (tostring(assert):match('builtin') ~= nil)
|
||||
|
||||
--- boolean flag this is LuaJIT with 5.2 compatibility compiled in.
|
||||
-- @field jit52
|
||||
if compat.jit then
|
||||
-- 'goto' is a keyword when 52 compatibility is enabled in LuaJit
|
||||
compat.jit52 = not loadstring("local goto = 1")
|
||||
end
|
||||
|
||||
--- the directory separator character for the current platform.
|
||||
-- @field dir_separator
|
||||
compat.dir_separator = _G.package.config:sub(1,1)
|
||||
|
||||
--- boolean flag this is a Windows platform.
|
||||
-- @field is_windows
|
||||
compat.is_windows = compat.dir_separator == '\\'
|
||||
|
||||
--- execute a shell command, in a compatible and platform independent way.
|
||||
-- This is a compatibility function that returns the same for Lua 5.1 and
|
||||
-- Lua 5.2+.
|
||||
--
|
||||
-- NOTE: Windows systems can use signed 32bit integer exitcodes. Posix systems
|
||||
-- only use exitcodes 0-255, anything else is undefined.
|
||||
--
|
||||
-- NOTE2: In Lua 5.2 and 5.3 a Windows exitcode of -1 would not properly be
|
||||
-- returned, this function will return it properly for all versions.
|
||||
-- @param cmd a shell command
|
||||
-- @return true if successful
|
||||
-- @return actual return code
|
||||
function compat.execute(cmd)
|
||||
local res1,res2,res3 = os.execute(cmd)
|
||||
if res2 == "No error" and res3 == 0 and compat.is_windows then
|
||||
-- os.execute bug in Lua 5.2/5.3 not reporting -1 properly on Windows
|
||||
-- this was fixed in 5.4
|
||||
res3 = -1
|
||||
end
|
||||
if compat.lua51 and not compat.jit52 then
|
||||
if compat.is_windows then
|
||||
return res1==0,res1
|
||||
else
|
||||
res1 = res1 > 255 and res1 / 256 or res1
|
||||
return res1==0,res1
|
||||
end
|
||||
else
|
||||
if compat.is_windows then
|
||||
return res3==0,res3
|
||||
else
|
||||
return not not res1,res3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
----------------
|
||||
-- Load Lua code as a text or binary chunk (in a Lua 5.2 compatible way).
|
||||
-- @param ld code string or loader
|
||||
-- @param[opt] source name of chunk for errors
|
||||
-- @param[opt] mode 'b', 't' or 'bt'
|
||||
-- @param[opt] env environment to load the chunk in
|
||||
-- @function compat.load
|
||||
|
||||
---------------
|
||||
-- Get environment of a function (in a Lua 5.1 compatible way).
|
||||
-- Not 100% compatible, so with Lua 5.2 it may return nil for a function with no
|
||||
-- global references!
|
||||
-- Based on code by [Sergey Rozhenko](http://lua-users.org/lists/lua-l/2010-06/msg00313.html)
|
||||
-- @param f a function or a call stack reference
|
||||
-- @function compat.getfenv
|
||||
|
||||
---------------
|
||||
-- Set environment of a function (in a Lua 5.1 compatible way).
|
||||
-- @param f a function or a call stack reference
|
||||
-- @param env a table that becomes the new environment of `f`
|
||||
-- @function compat.setfenv
|
||||
|
||||
if compat.lua51 then -- define Lua 5.2 style load()
|
||||
if not compat.jit then -- but LuaJIT's load _is_ compatible
|
||||
local lua51_load = load
|
||||
function compat.load(str,src,mode,env)
|
||||
local chunk,err
|
||||
if type(str) == 'string' then
|
||||
if str:byte(1) == 27 and not (mode or 'bt'):find 'b' then
|
||||
return nil,"attempt to load a binary chunk"
|
||||
end
|
||||
chunk,err = loadstring(str,src)
|
||||
else
|
||||
chunk,err = lua51_load(str,src)
|
||||
end
|
||||
if chunk and env then setfenv(chunk,env) end
|
||||
return chunk,err
|
||||
end
|
||||
else
|
||||
compat.load = load
|
||||
end
|
||||
compat.setfenv, compat.getfenv = setfenv, getfenv
|
||||
else
|
||||
compat.load = load
|
||||
-- setfenv/getfenv replacements for Lua 5.2
|
||||
-- by Sergey Rozhenko
|
||||
-- http://lua-users.org/lists/lua-l/2010-06/msg00313.html
|
||||
-- Roberto Ierusalimschy notes that it is possible for getfenv to return nil
|
||||
-- in the case of a function with no globals:
|
||||
-- http://lua-users.org/lists/lua-l/2010-06/msg00315.html
|
||||
function compat.setfenv(f, t)
|
||||
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
|
||||
local name
|
||||
local up = 0
|
||||
repeat
|
||||
up = up + 1
|
||||
name = debug.getupvalue(f, up)
|
||||
until name == '_ENV' or name == nil
|
||||
if name then
|
||||
debug.upvaluejoin(f, up, function() return name end, 1) -- use unique upvalue
|
||||
debug.setupvalue(f, up, t)
|
||||
end
|
||||
if f ~= 0 then return f end
|
||||
end
|
||||
|
||||
function compat.getfenv(f)
|
||||
local f = f or 0
|
||||
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
|
||||
local name, val
|
||||
local up = 0
|
||||
repeat
|
||||
up = up + 1
|
||||
name, val = debug.getupvalue(f, up)
|
||||
until name == '_ENV' or name == nil
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Global exported functions (for Lua 5.1 & LuaJIT)
|
||||
-- @section lua52
|
||||
|
||||
--- pack an argument list into a table.
|
||||
-- @param ... any arguments
|
||||
-- @return a table with field n set to the length
|
||||
-- @function table.pack
|
||||
if not table.pack then
|
||||
function table.pack (...) -- luacheck: ignore
|
||||
return {n=select('#',...); ...}
|
||||
end
|
||||
end
|
||||
|
||||
--- unpack a table and return the elements.
|
||||
--
|
||||
-- NOTE: this version does NOT honor the n field, and hence it is not nil-safe.
|
||||
-- See `utils.unpack` for a version that is nil-safe.
|
||||
-- @param t table to unpack
|
||||
-- @param[opt] i index from which to start unpacking, defaults to 1
|
||||
-- @param[opt] j index of the last element to unpack, defaults to #t
|
||||
-- @return multiple return values from the table
|
||||
-- @function table.unpack
|
||||
-- @see utils.unpack
|
||||
if not table.unpack then
|
||||
table.unpack = unpack -- luacheck: ignore
|
||||
end
|
||||
|
||||
--- return the full path where a file name would be matched.
|
||||
-- This function was introduced in Lua 5.2, so this compatibility version
|
||||
-- will be injected in Lua 5.1 engines.
|
||||
-- @string name file name, possibly dotted
|
||||
-- @string path a path-template in the same form as package.path or package.cpath
|
||||
-- @string[opt] sep template separate character to be replaced by path separator. Default: "."
|
||||
-- @string[opt] rep the path separator to use, defaults to system separator. Default; "/" on Unixes, "\" on Windows.
|
||||
-- @see path.package_path
|
||||
-- @function package.searchpath
|
||||
-- @return on success: path of the file
|
||||
-- @return on failure: nil, error string listing paths tried
|
||||
if not package.searchpath then
|
||||
function package.searchpath (name,path,sep,rep) -- luacheck: ignore
|
||||
if type(name) ~= "string" then
|
||||
error(("bad argument #1 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
|
||||
end
|
||||
if type(path) ~= "string" then
|
||||
error(("bad argument #2 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
|
||||
end
|
||||
if sep ~= nil and type(sep) ~= "string" then
|
||||
error(("bad argument #3 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
|
||||
end
|
||||
if rep ~= nil and type(rep) ~= "string" then
|
||||
error(("bad argument #4 to 'searchpath' (string expected, got %s)"):format(type(path)), 2)
|
||||
end
|
||||
sep = sep or "."
|
||||
rep = rep or compat.dir_separator
|
||||
do
|
||||
local s, e = name:find(sep, nil, true)
|
||||
while s do
|
||||
name = name:sub(1, s-1) .. rep .. name:sub(e+1, -1)
|
||||
s, e = name:find(sep, s + #rep + 1, true)
|
||||
end
|
||||
end
|
||||
local tried = {}
|
||||
for m in path:gmatch('[^;]+') do
|
||||
local nm = m:gsub('?', name)
|
||||
tried[#tried+1] = nm
|
||||
local f = io.open(nm,'r')
|
||||
if f then f:close(); return nm end
|
||||
end
|
||||
return nil, "\tno file '" .. table.concat(tried, "'\n\tno file '") .. "'"
|
||||
end
|
||||
end
|
||||
|
||||
--- Global exported functions (for Lua < 5.4)
|
||||
-- @section lua54
|
||||
|
||||
--- raise a warning message.
|
||||
-- This functions mimics the `warn` function added in Lua 5.4.
|
||||
-- @function warn
|
||||
-- @param ... any arguments
|
||||
if not rawget(_G, "warn") then
|
||||
local enabled = false
|
||||
local function warn(arg1, ...)
|
||||
if type(arg1) == "string" and arg1:sub(1, 1) == "@" then
|
||||
-- control message
|
||||
if arg1 == "@on" then
|
||||
enabled = true
|
||||
return
|
||||
end
|
||||
if arg1 == "@off" then
|
||||
enabled = false
|
||||
return
|
||||
end
|
||||
return -- ignore unknown control messages
|
||||
end
|
||||
if enabled then
|
||||
io.stderr:write("Lua warning: ", arg1, ...)
|
||||
io.stderr:write("\n")
|
||||
end
|
||||
end
|
||||
-- use rawset to bypass OpenResty's protection of global scope
|
||||
rawset(_G, "warn", warn)
|
||||
end
|
||||
|
||||
return compat
|
285
Documentation/compiler/pl/comprehension.lua
Normal file
285
Documentation/compiler/pl/comprehension.lua
Normal file
|
@ -0,0 +1,285 @@
|
|||
--- List comprehensions implemented in Lua.
|
||||
--
|
||||
-- See the [wiki page](http://lua-users.org/wiki/ListComprehensions)
|
||||
--
|
||||
-- local C= require 'pl.comprehension' . new()
|
||||
--
|
||||
-- C ('x for x=1,10') ()
|
||||
-- ==> {1,2,3,4,5,6,7,8,9,10}
|
||||
-- C 'x^2 for x=1,4' ()
|
||||
-- ==> {1,4,9,16}
|
||||
-- C '{x,x^2} for x=1,4' ()
|
||||
-- ==> {{1,1},{2,4},{3,9},{4,16}}
|
||||
-- C '2*x for x' {1,2,3}
|
||||
-- ==> {2,4,6}
|
||||
-- dbl = C '2*x for x'
|
||||
-- dbl {10,20,30}
|
||||
-- ==> {20,40,60}
|
||||
-- C 'x for x if x % 2 == 0' {1,2,3,4,5}
|
||||
-- ==> {2,4}
|
||||
-- C '{x,y} for x = 1,2 for y = 1,2' ()
|
||||
-- ==> {{1,1},{1,2},{2,1},{2,2}}
|
||||
-- C '{x,y} for x for y' ({1,2},{10,20})
|
||||
-- ==> {{1,10},{1,20},{2,10},{2,20}}
|
||||
-- assert(C 'sum(x^2 for x)' {2,3,4} == 2^2+3^2+4^2)
|
||||
--
|
||||
-- (c) 2008 David Manura. Licensed under the same terms as Lua (MIT license).
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.luabalanced`
|
||||
--
|
||||
-- See @{07-functional.md.List_Comprehensions|the Guide}
|
||||
-- @module pl.comprehension
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
|
||||
local status,lb = pcall(require, "pl.luabalanced")
|
||||
if not status then
|
||||
lb = require 'luabalanced'
|
||||
end
|
||||
|
||||
local math_max = math.max
|
||||
local table_concat = table.concat
|
||||
|
||||
-- fold operations
|
||||
-- http://en.wikipedia.org/wiki/Fold_(higher-order_function)
|
||||
local ops = {
|
||||
list = {init=' {} ', accum=' __result[#__result+1] = (%s) '},
|
||||
table = {init=' {} ', accum=' local __k, __v = %s __result[__k] = __v '},
|
||||
sum = {init=' 0 ', accum=' __result = __result + (%s) '},
|
||||
min = {init=' nil ', accum=' local __tmp = %s ' ..
|
||||
' if __result then if __tmp < __result then ' ..
|
||||
'__result = __tmp end else __result = __tmp end '},
|
||||
max = {init=' nil ', accum=' local __tmp = %s ' ..
|
||||
' if __result then if __tmp > __result then ' ..
|
||||
'__result = __tmp end else __result = __tmp end '},
|
||||
}
|
||||
|
||||
|
||||
-- Parses comprehension string expr.
|
||||
-- Returns output expression list <out> string, array of for types
|
||||
-- ('=', 'in' or nil) <fortypes>, array of input variable name
|
||||
-- strings <invarlists>, array of input variable value strings
|
||||
-- <invallists>, array of predicate expression strings <preds>,
|
||||
-- operation name string <opname>, and number of placeholder
|
||||
-- parameters <max_param>.
|
||||
--
|
||||
-- The is equivalent to the mathematical set-builder notation:
|
||||
--
|
||||
-- <opname> { <out> | <invarlist> in <invallist> , <preds> }
|
||||
--
|
||||
-- @usage "x^2 for x" -- array values
|
||||
-- @usage "x^2 for x=1,10,2" -- numeric for
|
||||
-- @usage "k^v for k,v in pairs(_1)" -- iterator for
|
||||
-- @usage "(x+y)^2 for x for y if x > y" -- nested
|
||||
--
|
||||
local function parse_comprehension(expr)
|
||||
local pos = 1
|
||||
|
||||
-- extract opname (if exists)
|
||||
local opname
|
||||
local tok, post = expr:match('^%s*([%a_][%w_]*)%s*%(()', pos)
|
||||
local pose = #expr + 1
|
||||
if tok then
|
||||
local tok2, posb = lb.match_bracketed(expr, post-1)
|
||||
assert(tok2, 'syntax error')
|
||||
if expr:match('^%s*$', posb) then
|
||||
opname = tok
|
||||
pose = posb - 1
|
||||
pos = post
|
||||
end
|
||||
end
|
||||
opname = opname or "list"
|
||||
|
||||
-- extract out expression list
|
||||
local out; out, pos = lb.match_explist(expr, pos)
|
||||
assert(out, "syntax error: missing expression list")
|
||||
out = table_concat(out, ', ')
|
||||
|
||||
-- extract "for" clauses
|
||||
local fortypes = {}
|
||||
local invarlists = {}
|
||||
local invallists = {}
|
||||
while 1 do
|
||||
local post = expr:match('^%s*for%s+()', pos)
|
||||
if not post then break end
|
||||
pos = post
|
||||
|
||||
-- extract input vars
|
||||
local iv; iv, pos = lb.match_namelist(expr, pos)
|
||||
assert(#iv > 0, 'syntax error: zero variables')
|
||||
for _,ident in ipairs(iv) do
|
||||
assert(not ident:match'^__',
|
||||
"identifier " .. ident .. " may not contain __ prefix")
|
||||
end
|
||||
invarlists[#invarlists+1] = iv
|
||||
|
||||
-- extract '=' or 'in' (optional)
|
||||
local fortype, post = expr:match('^(=)%s*()', pos)
|
||||
if not fortype then fortype, post = expr:match('^(in)%s+()', pos) end
|
||||
if fortype then
|
||||
pos = post
|
||||
-- extract input value range
|
||||
local il; il, pos = lb.match_explist(expr, pos)
|
||||
assert(#il > 0, 'syntax error: zero expressions')
|
||||
assert(fortype ~= '=' or #il == 2 or #il == 3,
|
||||
'syntax error: numeric for requires 2 or three expressions')
|
||||
fortypes[#invarlists] = fortype
|
||||
invallists[#invarlists] = il
|
||||
else
|
||||
fortypes[#invarlists] = false
|
||||
invallists[#invarlists] = false
|
||||
end
|
||||
end
|
||||
assert(#invarlists > 0, 'syntax error: missing "for" clause')
|
||||
|
||||
-- extract "if" clauses
|
||||
local preds = {}
|
||||
while 1 do
|
||||
local post = expr:match('^%s*if%s+()', pos)
|
||||
if not post then break end
|
||||
pos = post
|
||||
local pred; pred, pos = lb.match_expression(expr, pos)
|
||||
assert(pred, 'syntax error: predicated expression not found')
|
||||
preds[#preds+1] = pred
|
||||
end
|
||||
|
||||
-- extract number of parameter variables (name matching "_%d+")
|
||||
local stmp = ''; lb.gsub(expr, function(u, sin) -- strip comments/strings
|
||||
if u == 'e' then stmp = stmp .. ' ' .. sin .. ' ' end
|
||||
end)
|
||||
local max_param = 0; stmp:gsub('[%a_][%w_]*', function(s)
|
||||
local s = s:match('^_(%d+)$')
|
||||
if s then max_param = math_max(max_param, tonumber(s)) end
|
||||
end)
|
||||
|
||||
if pos ~= pose then
|
||||
assert(false, "syntax error: unrecognized " .. expr:sub(pos))
|
||||
end
|
||||
|
||||
--DEBUG:
|
||||
--print('----\n', string.format("%q", expr), string.format("%q", out), opname)
|
||||
--for k,v in ipairs(invarlists) do print(k,v, invallists[k]) end
|
||||
--for k,v in ipairs(preds) do print(k,v) end
|
||||
|
||||
return out, fortypes, invarlists, invallists, preds, opname, max_param
|
||||
end
|
||||
|
||||
|
||||
-- Create Lua code string representing comprehension.
|
||||
-- Arguments are in the form returned by parse_comprehension.
|
||||
local function code_comprehension(
|
||||
out, fortypes, invarlists, invallists, preds, opname, max_param
|
||||
)
|
||||
local op = assert(ops[opname])
|
||||
local code = op.accum:gsub('%%s', out)
|
||||
|
||||
for i=#preds,1,-1 do local pred = preds[i]
|
||||
code = ' if ' .. pred .. ' then ' .. code .. ' end '
|
||||
end
|
||||
for i=#invarlists,1,-1 do
|
||||
if not fortypes[i] then
|
||||
local arrayname = '__in' .. i
|
||||
local idx = '__idx' .. i
|
||||
code =
|
||||
' for ' .. idx .. ' = 1, #' .. arrayname .. ' do ' ..
|
||||
' local ' .. invarlists[i][1] .. ' = ' .. arrayname .. '['..idx..'] ' ..
|
||||
code .. ' end '
|
||||
else
|
||||
code =
|
||||
' for ' ..
|
||||
table_concat(invarlists[i], ', ') ..
|
||||
' ' .. fortypes[i] .. ' ' ..
|
||||
table_concat(invallists[i], ', ') ..
|
||||
' do ' .. code .. ' end '
|
||||
end
|
||||
end
|
||||
code = ' local __result = ( ' .. op.init .. ' ) ' .. code
|
||||
return code
|
||||
end
|
||||
|
||||
|
||||
-- Convert code string represented by code_comprehension
|
||||
-- into Lua function. Also must pass ninputs = #invarlists,
|
||||
-- max_param, and invallists (from parse_comprehension).
|
||||
-- Uses environment env.
|
||||
local function wrap_comprehension(code, ninputs, max_param, invallists, env)
|
||||
assert(ninputs > 0)
|
||||
local ts = {}
|
||||
for i=1,max_param do
|
||||
ts[#ts+1] = '_' .. i
|
||||
end
|
||||
for i=1,ninputs do
|
||||
if not invallists[i] then
|
||||
local name = '__in' .. i
|
||||
ts[#ts+1] = name
|
||||
end
|
||||
end
|
||||
if #ts > 0 then
|
||||
code = ' local ' .. table_concat(ts, ', ') .. ' = ... ' .. code
|
||||
end
|
||||
code = code .. ' return __result '
|
||||
--print('DEBUG:', code)
|
||||
local f, err = utils.load(code,'tmp','t',env)
|
||||
if not f then assert(false, err .. ' with generated code ' .. code) end
|
||||
return f
|
||||
end
|
||||
|
||||
|
||||
-- Build Lua function from comprehension string.
|
||||
-- Uses environment env.
|
||||
local function build_comprehension(expr, env)
|
||||
local out, fortypes, invarlists, invallists, preds, opname, max_param
|
||||
= parse_comprehension(expr)
|
||||
local code = code_comprehension(
|
||||
out, fortypes, invarlists, invallists, preds, opname, max_param)
|
||||
local f = wrap_comprehension(code, #invarlists, max_param, invallists, env)
|
||||
return f
|
||||
end
|
||||
|
||||
|
||||
-- Creates new comprehension cache.
|
||||
-- Any list comprehension function created are set to the environment
|
||||
-- env (defaults to caller of new).
|
||||
local function new(env)
|
||||
-- Note: using a single global comprehension cache would have had
|
||||
-- security implications (e.g. retrieving cached functions created
|
||||
-- in other environments).
|
||||
-- The cache lookup function could have instead been written to retrieve
|
||||
-- the caller's environment, lookup up the cache private to that
|
||||
-- environment, and then looked up the function in that cache.
|
||||
-- That would avoid the need for this <new> call to
|
||||
-- explicitly manage caches; however, that might also have an undue
|
||||
-- performance penalty.
|
||||
|
||||
if not env then
|
||||
env = utils.getfenv(2)
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
local cache = setmetatable({}, mt)
|
||||
|
||||
-- Index operator builds, caches, and returns Lua function
|
||||
-- corresponding to comprehension expression string.
|
||||
--
|
||||
-- Example: f = comprehension['x^2 for x']
|
||||
--
|
||||
function mt:__index(expr)
|
||||
local f = build_comprehension(expr, env)
|
||||
self[expr] = f -- cache
|
||||
return f
|
||||
end
|
||||
|
||||
-- Convenience syntax.
|
||||
-- Allows comprehension 'x^2 for x' instead of comprehension['x^2 for x'].
|
||||
mt.__call = mt.__index
|
||||
|
||||
cache.new = new
|
||||
|
||||
return cache
|
||||
end
|
||||
|
||||
|
||||
local comprehension = {}
|
||||
comprehension.new = new
|
||||
|
||||
return comprehension
|
207
Documentation/compiler/pl/config.lua
Normal file
207
Documentation/compiler/pl/config.lua
Normal file
|
@ -0,0 +1,207 @@
|
|||
--- Reads configuration files into a Lua table.
|
||||
-- Understands INI files, classic Unix config files, and simple
|
||||
-- delimited columns of values. See @{06-data.md.Reading_Configuration_Files|the Guide}
|
||||
--
|
||||
-- # test.config
|
||||
-- # Read timeout in seconds
|
||||
-- read.timeout=10
|
||||
-- # Write timeout in seconds
|
||||
-- write.timeout=5
|
||||
-- #acceptable ports
|
||||
-- ports = 1002,1003,1004
|
||||
--
|
||||
-- -- readconfig.lua
|
||||
-- local config = require 'config'
|
||||
-- local t = config.read 'test.config'
|
||||
-- print(pretty.write(t))
|
||||
--
|
||||
-- ### output #####
|
||||
-- {
|
||||
-- ports = {
|
||||
-- 1002,
|
||||
-- 1003,
|
||||
-- 1004
|
||||
-- },
|
||||
-- write_timeout = 5,
|
||||
-- read_timeout = 10
|
||||
-- }
|
||||
--
|
||||
-- @module pl.config
|
||||
|
||||
local type,tonumber,ipairs,io, table = _G.type,_G.tonumber,_G.ipairs,_G.io,_G.table
|
||||
|
||||
local function split(s,re)
|
||||
local res = {}
|
||||
local t_insert = table.insert
|
||||
re = '[^'..re..']+'
|
||||
for k in s:gmatch(re) do t_insert(res,k) end
|
||||
return res
|
||||
end
|
||||
|
||||
local function strip(s)
|
||||
return s:gsub('^%s+',''):gsub('%s+$','')
|
||||
end
|
||||
|
||||
local function strip_quotes (s)
|
||||
return s:gsub("['\"](.*)['\"]",'%1')
|
||||
end
|
||||
|
||||
local config = {}
|
||||
|
||||
--- like io.lines(), but allows for lines to be continued with '\'.
|
||||
-- @param file a file-like object (anything where read() returns the next line) or a filename.
|
||||
-- Defaults to stardard input.
|
||||
-- @return an iterator over the lines, or nil
|
||||
-- @return error 'not a file-like object' or 'file is nil'
|
||||
function config.lines(file)
|
||||
local f,openf,err
|
||||
local line = ''
|
||||
if type(file) == 'string' then
|
||||
f,err = io.open(file,'r')
|
||||
if not f then return nil,err end
|
||||
openf = true
|
||||
else
|
||||
f = file or io.stdin
|
||||
if not file.read then return nil, 'not a file-like object' end
|
||||
end
|
||||
if not f then return nil, 'file is nil' end
|
||||
return function()
|
||||
local l = f:read()
|
||||
while l do
|
||||
-- only for non-blank lines that don't begin with either ';' or '#'
|
||||
if l:match '%S' and not l:match '^%s*[;#]' then
|
||||
-- does the line end with '\'?
|
||||
local i = l:find '\\%s*$'
|
||||
if i then -- if so,
|
||||
line = line..l:sub(1,i-1)
|
||||
elseif line == '' then
|
||||
return l
|
||||
else
|
||||
l = line..l
|
||||
line = ''
|
||||
return l
|
||||
end
|
||||
end
|
||||
l = f:read()
|
||||
end
|
||||
if openf then f:close() end
|
||||
end
|
||||
end
|
||||
|
||||
--- read a configuration file into a table
|
||||
-- @param file either a file-like object or a string, which must be a filename
|
||||
-- @tab[opt] cnfg a configuration table that may contain these fields:
|
||||
--
|
||||
-- * `smart` try to deduce what kind of config file we have (default false)
|
||||
-- * `variabilize` make names into valid Lua identifiers (default true)
|
||||
-- * `convert_numbers` try to convert values into numbers (default true)
|
||||
-- * `trim_space` ensure that there is no starting or trailing whitespace with values (default true)
|
||||
-- * `trim_quotes` remove quotes from strings (default false)
|
||||
-- * `list_delim` delimiter to use when separating columns (default ',')
|
||||
-- * `keysep` separator between key and value pairs (default '=')
|
||||
--
|
||||
-- @return a table containing items, or `nil`
|
||||
-- @return error message (same as @{config.lines}
|
||||
function config.read(file,cnfg)
|
||||
local auto
|
||||
|
||||
local iter,err = config.lines(file)
|
||||
if not iter then return nil,err end
|
||||
local line = iter()
|
||||
cnfg = cnfg or {}
|
||||
if cnfg.smart then
|
||||
auto = true
|
||||
if line:match '^[^=]+=' then
|
||||
cnfg.keysep = '='
|
||||
elseif line:match '^[^:]+:' then
|
||||
cnfg.keysep = ':'
|
||||
cnfg.list_delim = ':'
|
||||
elseif line:match '^%S+%s+' then
|
||||
cnfg.keysep = ' '
|
||||
-- more than two columns assume that it's a space-delimited list
|
||||
-- cf /etc/fstab with /etc/ssh/ssh_config
|
||||
if line:match '^%S+%s+%S+%s+%S+' then
|
||||
cnfg.list_delim = ' '
|
||||
end
|
||||
cnfg.variabilize = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function check_cnfg (var,def)
|
||||
local val = cnfg[var]
|
||||
if val == nil then return def else return val end
|
||||
end
|
||||
|
||||
local initial_digits = '^[%d%+%-]'
|
||||
local t = {}
|
||||
local top_t = t
|
||||
local variabilize = check_cnfg ('variabilize',true)
|
||||
local list_delim = check_cnfg('list_delim',',')
|
||||
local convert_numbers = check_cnfg('convert_numbers',true)
|
||||
local convert_boolean = check_cnfg('convert_boolean',false)
|
||||
local trim_space = check_cnfg('trim_space',true)
|
||||
local trim_quotes = check_cnfg('trim_quotes',false)
|
||||
local ignore_assign = check_cnfg('ignore_assign',false)
|
||||
local keysep = check_cnfg('keysep','=')
|
||||
local keypat = keysep == ' ' and '%s+' or '%s*'..keysep..'%s*'
|
||||
if list_delim == ' ' then list_delim = '%s+' end
|
||||
|
||||
local function process_name(key)
|
||||
if variabilize then
|
||||
key = key:gsub('[^%w]','_')
|
||||
end
|
||||
return key
|
||||
end
|
||||
|
||||
local function process_value(value)
|
||||
if list_delim and value:find(list_delim) then
|
||||
value = split(value,list_delim)
|
||||
for i,v in ipairs(value) do
|
||||
value[i] = process_value(v)
|
||||
end
|
||||
elseif convert_numbers and value:find(initial_digits) then
|
||||
local val = tonumber(value)
|
||||
if not val and value:match ' kB$' then
|
||||
value = value:gsub(' kB','')
|
||||
val = tonumber(value)
|
||||
end
|
||||
if val then value = val end
|
||||
elseif convert_boolean and value == 'true' then
|
||||
return true
|
||||
elseif convert_boolean and value == 'false' then
|
||||
return false
|
||||
end
|
||||
if type(value) == 'string' then
|
||||
if trim_space then value = strip(value) end
|
||||
if not trim_quotes and auto and value:match '^"' then
|
||||
trim_quotes = true
|
||||
end
|
||||
if trim_quotes then value = strip_quotes(value) end
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
while line do
|
||||
if line:find('^%[') then -- section!
|
||||
local section = process_name(line:match('%[([^%]]+)%]'))
|
||||
t = top_t
|
||||
t[section] = {}
|
||||
t = t[section]
|
||||
else
|
||||
line = line:gsub('^%s*','')
|
||||
local i1,i2 = line:find(keypat)
|
||||
if i1 and not ignore_assign then -- key,value assignment
|
||||
local key = process_name(line:sub(1,i1-1))
|
||||
local value = process_value(line:sub(i2+1))
|
||||
t[key] = value
|
||||
else -- a plain list of values...
|
||||
t[#t+1] = process_value(line)
|
||||
end
|
||||
end
|
||||
line = iter()
|
||||
end
|
||||
return top_t
|
||||
end
|
||||
|
||||
return config
|
654
Documentation/compiler/pl/data.lua
Normal file
654
Documentation/compiler/pl/data.lua
Normal file
|
@ -0,0 +1,654 @@
|
|||
--- Reading and querying simple tabular data.
|
||||
--
|
||||
-- data.read 'test.txt'
|
||||
-- ==> {{10,20},{2,5},{40,50},fieldnames={'x','y'},delim=','}
|
||||
--
|
||||
-- Provides a way of creating basic SQL-like queries.
|
||||
--
|
||||
-- require 'pl'
|
||||
-- local d = data.read('xyz.txt')
|
||||
-- local q = d:select('x,y,z where x > 3 and z < 2 sort by y')
|
||||
-- for x,y,z in q do
|
||||
-- print(x,y,z)
|
||||
-- end
|
||||
--
|
||||
-- See @{06-data.md.Reading_Columnar_Data|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.array2d` (fallback methods)
|
||||
-- @module pl.data
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local _DEBUG = rawget(_G,'_DEBUG')
|
||||
|
||||
local patterns,function_arg,usplit,array_tostring = utils.patterns,utils.function_arg,utils.split,utils.array_tostring
|
||||
local append,concat = table.insert,table.concat
|
||||
local gsub = string.gsub
|
||||
local io = io
|
||||
local _G,print,type,tonumber,ipairs,setmetatable = _G,print,type,tonumber,ipairs,setmetatable
|
||||
|
||||
|
||||
local data = {}
|
||||
|
||||
local parse_select
|
||||
|
||||
local function rstrip(s)
|
||||
return (s:gsub('%s+$',''))
|
||||
end
|
||||
|
||||
local function strip (s)
|
||||
return (rstrip(s):gsub('^%s*',''))
|
||||
end
|
||||
|
||||
-- This gives `l` the standard List metatable,
|
||||
-- pulling in the List module.
|
||||
local function makelist(l)
|
||||
return setmetatable(l, require('pl.List'))
|
||||
end
|
||||
|
||||
local function map(fun,t)
|
||||
local res = {}
|
||||
for i = 1,#t do
|
||||
res[i] = fun(t[i])
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function split(line,delim,csv,n)
|
||||
local massage
|
||||
-- CSV fields may be double-quoted and may contain commas!
|
||||
if csv and line:match '"' then
|
||||
line = line:gsub('"([^"]+)"',function(str)
|
||||
local s,cnt = str:gsub(',','\001')
|
||||
if cnt > 0 then massage = true end
|
||||
return s
|
||||
end)
|
||||
if massage then
|
||||
massage = function(s) return (s:gsub('\001',',')) end
|
||||
end
|
||||
end
|
||||
local res = (usplit(line,delim,false,n))
|
||||
if csv then
|
||||
-- restore CSV commas-in-fields
|
||||
if massage then res = map(massage,res) end
|
||||
-- in CSV mode trailiing commas are significant!
|
||||
if line:match ',$' then append(res,'') end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
local function find(t,v)
|
||||
for i = 1,#t do
|
||||
if v == t[i] then return i end
|
||||
end
|
||||
end
|
||||
|
||||
local DataMT = {
|
||||
column_by_name = function(self,name)
|
||||
if type(name) == 'number' then
|
||||
name = '$'..name
|
||||
end
|
||||
local arr = {}
|
||||
for res in data.query(self,name) do
|
||||
append(arr,res)
|
||||
end
|
||||
return makelist(arr)
|
||||
end,
|
||||
|
||||
copy_select = function(self,condn)
|
||||
condn = parse_select(condn,self)
|
||||
local iter = data.query(self,condn)
|
||||
local res = {}
|
||||
local row = makelist{iter()}
|
||||
while #row > 0 do
|
||||
append(res,row)
|
||||
row = makelist{iter()}
|
||||
end
|
||||
res.delim = self.delim
|
||||
return data.new(res,split(condn.fields,','))
|
||||
end,
|
||||
|
||||
column_names = function(self)
|
||||
return self.fieldnames
|
||||
end,
|
||||
}
|
||||
|
||||
local array2d
|
||||
|
||||
DataMT.__index = function(self,name)
|
||||
local f = DataMT[name]
|
||||
if f then return f end
|
||||
if not array2d then
|
||||
array2d = require 'pl.array2d'
|
||||
end
|
||||
return array2d[name]
|
||||
end
|
||||
|
||||
--- return a particular column as a list of values (method).
|
||||
-- @param name either name of column, or numerical index.
|
||||
-- @function Data.column_by_name
|
||||
|
||||
--- return a query iterator on this data (method).
|
||||
-- @string condn the query expression
|
||||
-- @function Data.select
|
||||
-- @see data.query
|
||||
|
||||
--- return a row iterator on this data (method).
|
||||
-- @string condn the query expression
|
||||
-- @function Data.select_row
|
||||
|
||||
--- return a new data object based on this query (method).
|
||||
-- @string condn the query expression
|
||||
-- @function Data.copy_select
|
||||
|
||||
--- return the field names of this data object (method).
|
||||
-- @function Data.column_names
|
||||
|
||||
--- write out a row (method).
|
||||
-- @param f file-like object
|
||||
-- @function Data.write_row
|
||||
|
||||
--- write data out to file (method).
|
||||
-- @param f file-like object
|
||||
-- @function Data.write
|
||||
|
||||
|
||||
-- [guessing delimiter] We check for comma, tab and spaces in that order.
|
||||
-- [issue] any other delimiters to be checked?
|
||||
local delims = {',', '\t', ' ', ';'}
|
||||
|
||||
local function guess_delim (line)
|
||||
if line=='' then return ' ' end
|
||||
for _,delim in ipairs(delims) do
|
||||
if line:find(delim) then
|
||||
return delim == ' ' and '%s+' or delim
|
||||
end
|
||||
end
|
||||
return ' '
|
||||
end
|
||||
|
||||
-- [file parameter] If it's a string, we try open as a filename. If nil, then
|
||||
-- either stdin or stdout depending on the mode. Otherwise, check if this is
|
||||
-- a file-like object (implements read or write depending)
|
||||
local function open_file (f,mode)
|
||||
local opened, err
|
||||
local reading = mode == 'r'
|
||||
if type(f) == 'string' then
|
||||
if f == 'stdin' then
|
||||
f = io.stdin
|
||||
elseif f == 'stdout' then
|
||||
f = io.stdout
|
||||
else
|
||||
f,err = io.open(f,mode)
|
||||
if not f then return nil,err end
|
||||
opened = true
|
||||
end
|
||||
end
|
||||
if f and ((reading and not f.read) or (not reading and not f.write)) then
|
||||
return nil, "not a file-like object"
|
||||
end
|
||||
return f,nil,opened
|
||||
end
|
||||
|
||||
--- read a delimited file in a Lua table.
|
||||
-- By default, attempts to treat first line as separated list of fieldnames.
|
||||
-- @param file a filename or a file-like object
|
||||
-- @tab cnfg parsing options
|
||||
-- @string cnfg.delim a string pattern to split fields
|
||||
-- @array cnfg.fieldnames (i.e. don't read from first line)
|
||||
-- @bool cnfg.no_convert (default is to try conversion on first data line)
|
||||
-- @tab cnfg.convert table of custom conversion functions with column keys
|
||||
-- @int cnfg.numfields indices of columns known to be numbers
|
||||
-- @bool cnfg.last_field_collect only split as many fields as fieldnames.
|
||||
-- @int cnfg.thousands_dot thousands separator in Excel CSV is '.'
|
||||
-- @bool cnfg.csv fields may be double-quoted and contain commas;
|
||||
-- Also, empty fields are considered to be equivalent to zero.
|
||||
-- @return `data` object, or `nil`
|
||||
-- @return error message. May be a file error, 'not a file-like object'
|
||||
-- or a conversion error
|
||||
function data.read(file,cnfg)
|
||||
local count,line
|
||||
local D = {}
|
||||
if not cnfg then cnfg = {} end
|
||||
local f,err,opened = open_file(file,'r')
|
||||
if not f then return nil, err end
|
||||
local thousands_dot = cnfg.thousands_dot
|
||||
local csv = cnfg.csv
|
||||
if csv then cnfg.delim = ',' end
|
||||
|
||||
-- note that using dot as the thousands separator (@thousands_dot)
|
||||
-- requires a special conversion function! For CSV, _empty fields_ are
|
||||
-- considered to default to numerial zeroes.
|
||||
local tonumber = tonumber
|
||||
local function try_number(x)
|
||||
if thousands_dot then x = x:gsub('%.(...)','%1') end
|
||||
if csv and x == '' then x = '0' end
|
||||
local v = tonumber(x)
|
||||
if v == nil then return nil,"not a number" end
|
||||
return v
|
||||
end
|
||||
|
||||
count = 1
|
||||
line = f:read()
|
||||
if not line then return nil, "empty file" end
|
||||
|
||||
-- first question: what is the delimiter?
|
||||
D.delim = cnfg.delim and cnfg.delim or guess_delim(line)
|
||||
local delim = D.delim
|
||||
|
||||
local conversion
|
||||
local numfields = {}
|
||||
local function append_conversion (idx,conv)
|
||||
conversion = conversion or {}
|
||||
append(numfields,idx)
|
||||
append(conversion,conv)
|
||||
end
|
||||
if cnfg.numfields then
|
||||
for _,n in ipairs(cnfg.numfields) do append_conversion(n,try_number) end
|
||||
end
|
||||
|
||||
-- some space-delimited data starts with a space. This should not be a column,
|
||||
-- although it certainly would be for comma-separated, etc.
|
||||
local stripper
|
||||
if delim == '%s+' and line:find(delim) == 1 then
|
||||
stripper = function(s) return s:gsub('^%s+','') end
|
||||
line = stripper(line)
|
||||
end
|
||||
-- first line will usually be field names. Unless fieldnames are specified,
|
||||
-- we check if it contains purely numerical values for the case of reading
|
||||
-- plain data files.
|
||||
if not cnfg.fieldnames then
|
||||
local fields,nums
|
||||
fields = split(line,delim,csv)
|
||||
if not cnfg.convert then
|
||||
nums = map(tonumber,fields)
|
||||
if #nums == #fields then -- they're ALL numbers!
|
||||
append(D,nums) -- add the first converted row
|
||||
-- and specify conversions for subsequent rows
|
||||
for i = 1,#nums do append_conversion(i,try_number) end
|
||||
else -- we'll try to check numbers just now..
|
||||
nums = nil
|
||||
end
|
||||
else -- [explicit column conversions] (any deduced number conversions will be added)
|
||||
for idx,conv in pairs(cnfg.convert) do append_conversion(idx,conv) end
|
||||
end
|
||||
if nums == nil then
|
||||
cnfg.fieldnames = fields
|
||||
end
|
||||
line = f:read()
|
||||
count = count + 1
|
||||
if stripper then line = stripper(line) end
|
||||
elseif type(cnfg.fieldnames) == 'string' then
|
||||
cnfg.fieldnames = split(cnfg.fieldnames,delim,csv)
|
||||
end
|
||||
local nfields
|
||||
-- at this point, the column headers have been read in. If the first
|
||||
-- row consisted of numbers, it has already been added to the dataset.
|
||||
if cnfg.fieldnames then
|
||||
D.fieldnames = cnfg.fieldnames
|
||||
-- [collecting end field] If @last_field_collect then we'll
|
||||
-- only split as many fields as there are fieldnames
|
||||
if cnfg.last_field_collect then
|
||||
nfields = #D.fieldnames
|
||||
end
|
||||
-- [implicit column conversion] unless @no_convert, we need the numerical field indices
|
||||
-- of the first data row. These can also be specified explicitly by @numfields.
|
||||
if not cnfg.no_convert then
|
||||
local fields = split(line,D.delim,csv,nfields)
|
||||
for i = 1,#fields do
|
||||
if not find(numfields,i) and try_number(fields[i]) then
|
||||
append_conversion(i,try_number)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- keep going until finished
|
||||
while line do
|
||||
if not line:find ('^%s*$') then -- [blank lines] ignore them!
|
||||
if stripper then line = stripper(line) end
|
||||
local fields = split(line,delim,csv,nfields)
|
||||
if conversion then -- there were field conversions...
|
||||
for k = 1,#numfields do
|
||||
local i,conv = numfields[k],conversion[k]
|
||||
local val,err = conv(fields[i])
|
||||
if val == nil then
|
||||
return nil, err..": "..fields[i].." at line "..count
|
||||
else
|
||||
fields[i] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
append(D,fields)
|
||||
end
|
||||
line = f:read()
|
||||
count = count + 1
|
||||
end
|
||||
if opened then f:close() end
|
||||
if delim == '%s+' then D.delim = ' ' end
|
||||
if not D.fieldnames then D.fieldnames = {} end
|
||||
return data.new(D)
|
||||
end
|
||||
|
||||
local function write_row (data,f,row,delim)
|
||||
data.temp = array_tostring(row,data.temp)
|
||||
f:write(concat(data.temp,delim),'\n')
|
||||
end
|
||||
|
||||
function DataMT:write_row(f,row)
|
||||
write_row(self,f,row,self.delim)
|
||||
end
|
||||
|
||||
--- write 2D data to a file.
|
||||
-- Does not assume that the data has actually been
|
||||
-- generated with `new` or `read`.
|
||||
-- @param data 2D array
|
||||
-- @param file filename or file-like object
|
||||
-- @tparam[opt] {string} fieldnames list of fields (optional)
|
||||
-- @string[opt='\t'] delim delimiter (default tab)
|
||||
-- @return true or nil, error
|
||||
function data.write (data,file,fieldnames,delim)
|
||||
local f,err,opened = open_file(file,'w')
|
||||
if not f then return nil, err end
|
||||
if not fieldnames then
|
||||
fieldnames = data.fieldnames
|
||||
end
|
||||
delim = delim or '\t'
|
||||
if fieldnames and #fieldnames > 0 then
|
||||
f:write(concat(fieldnames,delim),'\n')
|
||||
end
|
||||
for i = 1,#data do
|
||||
write_row(data,f,data[i],delim)
|
||||
end
|
||||
if opened then f:close() end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function DataMT:write(file)
|
||||
data.write(self,file,self.fieldnames,self.delim)
|
||||
end
|
||||
|
||||
local function massage_fieldnames (fields,copy)
|
||||
-- fieldnames must be valid Lua identifiers; ignore any surrounding padding
|
||||
-- but keep the original fieldnames...
|
||||
for i = 1,#fields do
|
||||
local f = strip(fields[i])
|
||||
copy[i] = f
|
||||
fields[i] = f:gsub('%W','_')
|
||||
end
|
||||
end
|
||||
|
||||
--- create a new dataset from a table of rows.
|
||||
-- Can specify the fieldnames, else the table must have a field called
|
||||
-- 'fieldnames', which is either a string of delimiter-separated names,
|
||||
-- or a table of names. <br>
|
||||
-- If the table does not have a field called 'delim', then an attempt will be
|
||||
-- made to guess it from the fieldnames string, defaults otherwise to tab.
|
||||
-- @param d the table.
|
||||
-- @tparam[opt] {string} fieldnames optional fieldnames
|
||||
-- @return the table.
|
||||
function data.new (d,fieldnames)
|
||||
d.fieldnames = d.fieldnames or fieldnames or ''
|
||||
if not d.delim and type(d.fieldnames) == 'string' then
|
||||
d.delim = guess_delim(d.fieldnames)
|
||||
d.fieldnames = split(d.fieldnames,d.delim)
|
||||
end
|
||||
d.fieldnames = makelist(d.fieldnames)
|
||||
d.original_fieldnames = {}
|
||||
massage_fieldnames(d.fieldnames,d.original_fieldnames)
|
||||
setmetatable(d,DataMT)
|
||||
-- a query with just the fieldname will return a sequence
|
||||
-- of values, which seq.copy turns into a table.
|
||||
return d
|
||||
end
|
||||
|
||||
local sorted_query = [[
|
||||
return function (t)
|
||||
local i = 0
|
||||
local v
|
||||
local ls = {}
|
||||
for i,v in ipairs(t) do
|
||||
if CONDITION then
|
||||
ls[#ls+1] = v
|
||||
end
|
||||
end
|
||||
table.sort(ls,function(v1,v2)
|
||||
return SORT_EXPR
|
||||
end)
|
||||
local n = #ls
|
||||
return function()
|
||||
i = i + 1
|
||||
v = ls[i]
|
||||
if i > n then return end
|
||||
return FIELDLIST
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
-- question: is this optimized case actually worth the extra code?
|
||||
local simple_query = [[
|
||||
return function (t)
|
||||
local n = #t
|
||||
local i = 0
|
||||
local v
|
||||
return function()
|
||||
repeat
|
||||
i = i + 1
|
||||
v = t[i]
|
||||
until i > n or CONDITION
|
||||
if i > n then return end
|
||||
return FIELDLIST
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
local function is_string (s)
|
||||
return type(s) == 'string'
|
||||
end
|
||||
|
||||
local field_error
|
||||
|
||||
local function fieldnames_as_string (data)
|
||||
return concat(data.fieldnames,',')
|
||||
end
|
||||
|
||||
local function massage_fields(data,f)
|
||||
local idx
|
||||
if f:find '^%d+$' then
|
||||
idx = tonumber(f)
|
||||
else
|
||||
idx = find(data.fieldnames,f)
|
||||
end
|
||||
if idx then
|
||||
return 'v['..idx..']'
|
||||
else
|
||||
field_error = f..' not found in '..fieldnames_as_string(data)
|
||||
return f
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function process_select (data,parms)
|
||||
--- preparing fields ----
|
||||
field_error = nil
|
||||
local fields = parms.fields
|
||||
local numfields = fields:find '%$' or #data.fieldnames == 0
|
||||
if fields:find '^%s*%*%s*' then
|
||||
if not numfields then
|
||||
fields = fieldnames_as_string(data)
|
||||
else
|
||||
local ncol = #data[1]
|
||||
fields = {}
|
||||
for i = 1,ncol do append(fields,'$'..i) end
|
||||
fields = concat(fields,',')
|
||||
end
|
||||
end
|
||||
local idpat = patterns.IDEN
|
||||
if numfields then
|
||||
idpat = '%$(%d+)'
|
||||
else
|
||||
-- massage field names to replace non-identifier chars
|
||||
fields = rstrip(fields):gsub('[^,%w]','_')
|
||||
end
|
||||
local massage_fields = utils.bind1(massage_fields,data)
|
||||
local ret = gsub(fields,idpat,massage_fields)
|
||||
if field_error then return nil,field_error end
|
||||
parms.fields = fields
|
||||
parms.proc_fields = ret
|
||||
parms.where = parms.where or 'true'
|
||||
if is_string(parms.where) then
|
||||
parms.where = gsub(parms.where,idpat,massage_fields)
|
||||
field_error = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
parse_select = function(s,data)
|
||||
local endp
|
||||
local parms = {}
|
||||
local w1,w2 = s:find('where ')
|
||||
local s1,s2 = s:find('sort by ')
|
||||
if w1 then -- where clause!
|
||||
endp = (s1 or 0)-1
|
||||
parms.where = s:sub(w2+1,endp)
|
||||
end
|
||||
if s1 then -- sort by clause (must be last!)
|
||||
parms.sort_by = s:sub(s2+1)
|
||||
end
|
||||
endp = (w1 or s1 or 0)-1
|
||||
parms.fields = s:sub(1,endp)
|
||||
local status,err = process_select(data,parms)
|
||||
if not status then return nil,err
|
||||
else return parms end
|
||||
end
|
||||
|
||||
--- create a query iterator from a select string.
|
||||
-- Select string has this format: <br>
|
||||
-- FIELDLIST [ where LUA-CONDN [ sort by FIELD] ]<br>
|
||||
-- FIELDLIST is a comma-separated list of valid fields, or '*'. <br> <br>
|
||||
-- The condition can also be a table, with fields 'fields' (comma-sep string or
|
||||
-- table), 'sort_by' (string) and 'where' (Lua expression string or function)
|
||||
-- @param data table produced by read
|
||||
-- @param condn select string or table
|
||||
-- @param context a list of tables to be searched when resolving functions
|
||||
-- @param return_row if true, wrap the results in a row table
|
||||
-- @return an iterator over the specified fields, or nil
|
||||
-- @return an error message
|
||||
function data.query(data,condn,context,return_row)
|
||||
local err
|
||||
if is_string(condn) then
|
||||
condn,err = parse_select(condn,data)
|
||||
if not condn then return nil,err end
|
||||
elseif type(condn) == 'table' then
|
||||
if type(condn.fields) == 'table' then
|
||||
condn.fields = concat(condn.fields,',')
|
||||
end
|
||||
if not condn.proc_fields then
|
||||
local status,err = process_select(data,condn)
|
||||
if not status then return nil,err end
|
||||
end
|
||||
else
|
||||
return nil, "condition must be a string or a table"
|
||||
end
|
||||
local query
|
||||
if condn.sort_by then -- use sorted_query
|
||||
query = sorted_query
|
||||
else
|
||||
query = simple_query
|
||||
end
|
||||
local fields = condn.proc_fields or condn.fields
|
||||
if return_row then
|
||||
fields = '{'..fields..'}'
|
||||
end
|
||||
query = query:gsub('FIELDLIST',fields)
|
||||
if is_string(condn.where) then
|
||||
query = query:gsub('CONDITION',condn.where)
|
||||
condn.where = nil
|
||||
else
|
||||
query = query:gsub('CONDITION','_condn(v)')
|
||||
condn.where = function_arg(0,condn.where,'condition.where must be callable')
|
||||
end
|
||||
if condn.sort_by then
|
||||
local expr,sort_var,sort_dir
|
||||
local sort_by = condn.sort_by
|
||||
local i1,i2 = sort_by:find('%s+')
|
||||
if i1 then
|
||||
sort_var,sort_dir = sort_by:sub(1,i1-1),sort_by:sub(i2+1)
|
||||
else
|
||||
sort_var = sort_by
|
||||
sort_dir = 'asc'
|
||||
end
|
||||
if sort_var:match '^%$' then sort_var = sort_var:sub(2) end
|
||||
sort_var = massage_fields(data,sort_var)
|
||||
if field_error then return nil,field_error end
|
||||
if sort_dir == 'asc' then
|
||||
sort_dir = '<'
|
||||
else
|
||||
sort_dir = '>'
|
||||
end
|
||||
expr = ('%s %s %s'):format(sort_var:gsub('v','v1'),sort_dir,sort_var:gsub('v','v2'))
|
||||
query = query:gsub('SORT_EXPR',expr)
|
||||
end
|
||||
if condn.where then
|
||||
query = 'return function(_condn) '..query..' end'
|
||||
end
|
||||
if _DEBUG then print(query) end
|
||||
|
||||
local fn,err = utils.load(query,'tmp')
|
||||
if not fn then return nil,err end
|
||||
fn = fn() -- get the function
|
||||
if condn.where then
|
||||
fn = fn(condn.where)
|
||||
end
|
||||
local qfun = fn(data)
|
||||
if context then
|
||||
-- [specifying context for condition] @context is a list of tables which are
|
||||
-- 'injected'into the condition's custom context
|
||||
append(context,_G)
|
||||
local lookup = {}
|
||||
utils.setfenv(qfun,lookup)
|
||||
setmetatable(lookup,{
|
||||
__index = function(tbl,key)
|
||||
-- _G.print(tbl,key)
|
||||
for k,t in ipairs(context) do
|
||||
if t[key] then return t[key] end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
return qfun
|
||||
end
|
||||
|
||||
|
||||
DataMT.select = data.query
|
||||
DataMT.select_row = function(d,condn,context)
|
||||
return data.query(d,condn,context,true)
|
||||
end
|
||||
|
||||
--- Filter input using a query.
|
||||
-- @string Q a query string
|
||||
-- @param infile filename or file-like object
|
||||
-- @param outfile filename or file-like object
|
||||
-- @bool dont_fail true if you want to return an error, not just fail
|
||||
function data.filter (Q,infile,outfile,dont_fail)
|
||||
local d = data.read(infile or 'stdin')
|
||||
local out = open_file(outfile or 'stdout')
|
||||
local iter,err = d:select(Q)
|
||||
local delim = d.delim
|
||||
if not iter then
|
||||
err = 'error: '..err
|
||||
if dont_fail then
|
||||
return nil,err
|
||||
else
|
||||
utils.quit(1,err)
|
||||
end
|
||||
end
|
||||
while true do
|
||||
local res = {iter()}
|
||||
if #res == 0 then break end
|
||||
out:write(concat(res,delim),'\n')
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
|
527
Documentation/compiler/pl/dir.lua
Normal file
527
Documentation/compiler/pl/dir.lua
Normal file
|
@ -0,0 +1,527 @@
|
|||
--- Listing files in directories and creating/removing directory paths.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.path`
|
||||
--
|
||||
-- Soft Dependencies: `alien`, `ffi` (either are used on Windows for copying/moving files)
|
||||
-- @module pl.dir
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local path = require 'pl.path'
|
||||
local is_windows = path.is_windows
|
||||
local ldir = path.dir
|
||||
local mkdir = path.mkdir
|
||||
local rmdir = path.rmdir
|
||||
local sub = string.sub
|
||||
local os,pcall,ipairs,pairs,require,setmetatable = os,pcall,ipairs,pairs,require,setmetatable
|
||||
local remove = os.remove
|
||||
local append = table.insert
|
||||
local assert_arg,assert_string,raise = utils.assert_arg,utils.assert_string,utils.raise
|
||||
|
||||
local exists, isdir = path.exists, path.isdir
|
||||
local sep = path.sep
|
||||
|
||||
local dir = {}
|
||||
|
||||
local function makelist(l)
|
||||
return setmetatable(l, require('pl.List'))
|
||||
end
|
||||
|
||||
local function assert_dir (n,val)
|
||||
assert_arg(n,val,'string',path.isdir,'not a directory',4)
|
||||
end
|
||||
|
||||
local function filemask(mask)
|
||||
mask = utils.escape(path.normcase(mask))
|
||||
return '^'..mask:gsub('%%%*','.*'):gsub('%%%?','.')..'$'
|
||||
end
|
||||
|
||||
--- Test whether a file name matches a shell pattern.
|
||||
-- Both parameters are case-normalized if operating system is
|
||||
-- case-insensitive.
|
||||
-- @string filename A file name.
|
||||
-- @string pattern A shell pattern. The only special characters are
|
||||
-- `'*'` and `'?'`: `'*'` matches any sequence of characters and
|
||||
-- `'?'` matches any single character.
|
||||
-- @treturn bool
|
||||
-- @raise dir and mask must be strings
|
||||
function dir.fnmatch(filename,pattern)
|
||||
assert_string(1,filename)
|
||||
assert_string(2,pattern)
|
||||
return path.normcase(filename):find(filemask(pattern)) ~= nil
|
||||
end
|
||||
|
||||
--- Return a list of all file names within an array which match a pattern.
|
||||
-- @tab filenames An array containing file names.
|
||||
-- @string pattern A shell pattern (see `fnmatch`).
|
||||
-- @treturn List(string) List of matching file names.
|
||||
-- @raise dir and mask must be strings
|
||||
function dir.filter(filenames,pattern)
|
||||
assert_arg(1,filenames,'table')
|
||||
assert_string(2,pattern)
|
||||
local res = {}
|
||||
local mask = filemask(pattern)
|
||||
for i,f in ipairs(filenames) do
|
||||
if path.normcase(f):find(mask) then append(res,f) end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
local function _listfiles(dirname,filemode,match)
|
||||
local res = {}
|
||||
local check = utils.choose(filemode,path.isfile,path.isdir)
|
||||
if not dirname then dirname = '.' end
|
||||
for f in ldir(dirname) do
|
||||
if f ~= '.' and f ~= '..' then
|
||||
local p = path.join(dirname,f)
|
||||
if check(p) and (not match or match(f)) then
|
||||
append(res,p)
|
||||
end
|
||||
end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- return a list of all files in a directory which match a shell pattern.
|
||||
-- @string[opt='.'] dirname A directory.
|
||||
-- @string[opt] mask A shell pattern (see `fnmatch`). If not given, all files are returned.
|
||||
-- @treturn {string} list of files
|
||||
-- @raise dirname and mask must be strings
|
||||
function dir.getfiles(dirname,mask)
|
||||
dirname = dirname or '.'
|
||||
assert_dir(1,dirname)
|
||||
if mask then assert_string(2,mask) end
|
||||
local match
|
||||
if mask then
|
||||
mask = filemask(mask)
|
||||
match = function(f)
|
||||
return path.normcase(f):find(mask)
|
||||
end
|
||||
end
|
||||
return _listfiles(dirname,true,match)
|
||||
end
|
||||
|
||||
--- return a list of all subdirectories of the directory.
|
||||
-- @string[opt='.'] dirname A directory.
|
||||
-- @treturn {string} a list of directories
|
||||
-- @raise dir must be a valid directory
|
||||
function dir.getdirectories(dirname)
|
||||
dirname = dirname or '.'
|
||||
assert_dir(1,dirname)
|
||||
return _listfiles(dirname,false)
|
||||
end
|
||||
|
||||
local alien,ffi,ffi_checked,CopyFile,MoveFile,GetLastError,win32_errors,cmd_tmpfile
|
||||
|
||||
local function execute_command(cmd,parms)
|
||||
if not cmd_tmpfile then cmd_tmpfile = path.tmpname () end
|
||||
local err = path.is_windows and ' > ' or ' 2> '
|
||||
cmd = cmd..' '..parms..err..utils.quote_arg(cmd_tmpfile)
|
||||
local ret = utils.execute(cmd)
|
||||
if not ret then
|
||||
local err = (utils.readfile(cmd_tmpfile):gsub('\n(.*)',''))
|
||||
remove(cmd_tmpfile)
|
||||
return false,err
|
||||
else
|
||||
remove(cmd_tmpfile)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function find_ffi_copyfile ()
|
||||
if not ffi_checked then
|
||||
ffi_checked = true
|
||||
local res
|
||||
res,alien = pcall(require,'alien')
|
||||
if not res then
|
||||
alien = nil
|
||||
res, ffi = pcall(require,'ffi')
|
||||
end
|
||||
if not res then
|
||||
ffi = nil
|
||||
return
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
if alien then
|
||||
-- register the Win32 CopyFile and MoveFile functions
|
||||
local kernel = alien.load('kernel32.dll')
|
||||
CopyFile = kernel.CopyFileA
|
||||
CopyFile:types{'string','string','int',ret='int',abi='stdcall'}
|
||||
MoveFile = kernel.MoveFileA
|
||||
MoveFile:types{'string','string',ret='int',abi='stdcall'}
|
||||
GetLastError = kernel.GetLastError
|
||||
GetLastError:types{ret ='int', abi='stdcall'}
|
||||
elseif ffi then
|
||||
ffi.cdef [[
|
||||
int CopyFileA(const char *src, const char *dest, int iovr);
|
||||
int MoveFileA(const char *src, const char *dest);
|
||||
int GetLastError();
|
||||
]]
|
||||
CopyFile = ffi.C.CopyFileA
|
||||
MoveFile = ffi.C.MoveFileA
|
||||
GetLastError = ffi.C.GetLastError
|
||||
end
|
||||
win32_errors = {
|
||||
ERROR_FILE_NOT_FOUND = 2,
|
||||
ERROR_PATH_NOT_FOUND = 3,
|
||||
ERROR_ACCESS_DENIED = 5,
|
||||
ERROR_WRITE_PROTECT = 19,
|
||||
ERROR_BAD_UNIT = 20,
|
||||
ERROR_NOT_READY = 21,
|
||||
ERROR_WRITE_FAULT = 29,
|
||||
ERROR_READ_FAULT = 30,
|
||||
ERROR_SHARING_VIOLATION = 32,
|
||||
ERROR_LOCK_VIOLATION = 33,
|
||||
ERROR_HANDLE_DISK_FULL = 39,
|
||||
ERROR_BAD_NETPATH = 53,
|
||||
ERROR_NETWORK_BUSY = 54,
|
||||
ERROR_DEV_NOT_EXIST = 55,
|
||||
ERROR_FILE_EXISTS = 80,
|
||||
ERROR_OPEN_FAILED = 110,
|
||||
ERROR_INVALID_NAME = 123,
|
||||
ERROR_BAD_PATHNAME = 161,
|
||||
ERROR_ALREADY_EXISTS = 183,
|
||||
}
|
||||
end
|
||||
|
||||
local function two_arguments (f1,f2)
|
||||
return utils.quote_arg(f1)..' '..utils.quote_arg(f2)
|
||||
end
|
||||
|
||||
local function file_op (is_copy,src,dest,flag)
|
||||
if flag == 1 and path.exists(dest) then
|
||||
return false,"cannot overwrite destination"
|
||||
end
|
||||
if is_windows then
|
||||
-- if we haven't tried to load Alien/LuaJIT FFI before, then do so
|
||||
find_ffi_copyfile()
|
||||
-- fallback if there's no Alien, just use DOS commands *shudder*
|
||||
-- 'rename' involves a copy and then deleting the source.
|
||||
if not CopyFile then
|
||||
if path.is_windows then
|
||||
src = src:gsub("/","\\")
|
||||
dest = dest:gsub("/","\\")
|
||||
end
|
||||
local res, err = execute_command('copy',two_arguments(src,dest))
|
||||
if not res then return false,err end
|
||||
if not is_copy then
|
||||
return execute_command('del',utils.quote_arg(src))
|
||||
end
|
||||
return true
|
||||
else
|
||||
if path.isdir(dest) then
|
||||
dest = path.join(dest,path.basename(src))
|
||||
end
|
||||
local ret
|
||||
if is_copy then ret = CopyFile(src,dest,flag)
|
||||
else ret = MoveFile(src,dest) end
|
||||
if ret == 0 then
|
||||
local err = GetLastError()
|
||||
for name,value in pairs(win32_errors) do
|
||||
if value == err then return false,name end
|
||||
end
|
||||
return false,"Error #"..err
|
||||
else return true
|
||||
end
|
||||
end
|
||||
else -- for Unix, just use cp for now
|
||||
return execute_command(is_copy and 'cp' or 'mv',
|
||||
two_arguments(src,dest))
|
||||
end
|
||||
end
|
||||
|
||||
--- copy a file.
|
||||
-- @string src source file
|
||||
-- @string dest destination file or directory
|
||||
-- @bool flag true if you want to force the copy (default)
|
||||
-- @treturn bool operation succeeded
|
||||
-- @raise src and dest must be strings
|
||||
function dir.copyfile (src,dest,flag)
|
||||
assert_string(1,src)
|
||||
assert_string(2,dest)
|
||||
flag = flag==nil or flag
|
||||
return file_op(true,src,dest,flag and 0 or 1)
|
||||
end
|
||||
|
||||
--- move a file.
|
||||
-- @string src source file
|
||||
-- @string dest destination file or directory
|
||||
-- @treturn bool operation succeeded
|
||||
-- @raise src and dest must be strings
|
||||
function dir.movefile (src,dest)
|
||||
assert_string(1,src)
|
||||
assert_string(2,dest)
|
||||
return file_op(false,src,dest,0)
|
||||
end
|
||||
|
||||
local function _dirfiles(dirname,attrib)
|
||||
local dirs = {}
|
||||
local files = {}
|
||||
for f in ldir(dirname) do
|
||||
if f ~= '.' and f ~= '..' then
|
||||
local p = path.join(dirname,f)
|
||||
local mode = attrib(p,'mode')
|
||||
if mode=='directory' then
|
||||
append(dirs,f)
|
||||
else
|
||||
append(files,f)
|
||||
end
|
||||
end
|
||||
end
|
||||
return makelist(dirs), makelist(files)
|
||||
end
|
||||
|
||||
|
||||
--- return an iterator which walks through a directory tree starting at root.
|
||||
-- The iterator returns (root,dirs,files)
|
||||
-- Note that dirs and files are lists of names (i.e. you must say path.join(root,d)
|
||||
-- to get the actual full path)
|
||||
-- If bottom_up is false (or not present), then the entries at the current level are returned
|
||||
-- before we go deeper. This means that you can modify the returned list of directories before
|
||||
-- continuing.
|
||||
-- This is a clone of os.walk from the Python libraries.
|
||||
-- @string root A starting directory
|
||||
-- @bool bottom_up False if we start listing entries immediately.
|
||||
-- @bool follow_links follow symbolic links
|
||||
-- @return an iterator returning root,dirs,files
|
||||
-- @raise root must be a directory
|
||||
function dir.walk(root,bottom_up,follow_links)
|
||||
assert_dir(1,root)
|
||||
local attrib
|
||||
if path.is_windows or not follow_links then
|
||||
attrib = path.attrib
|
||||
else
|
||||
attrib = path.link_attrib
|
||||
end
|
||||
|
||||
local to_scan = { root }
|
||||
local to_return = {}
|
||||
local iter = function()
|
||||
while #to_scan > 0 do
|
||||
local current_root = table.remove(to_scan)
|
||||
local dirs,files = _dirfiles(current_root, attrib)
|
||||
for _, d in ipairs(dirs) do
|
||||
table.insert(to_scan, current_root..path.sep..d)
|
||||
end
|
||||
if not bottom_up then
|
||||
return current_root, dirs, files
|
||||
else
|
||||
table.insert(to_return, { current_root, dirs, files })
|
||||
end
|
||||
end
|
||||
if #to_return > 0 then
|
||||
return utils.unpack(table.remove(to_return))
|
||||
end
|
||||
end
|
||||
|
||||
return iter
|
||||
end
|
||||
|
||||
--- remove a whole directory tree.
|
||||
-- Symlinks in the tree will be deleted without following them.
|
||||
-- @string fullpath A directory path (must be an actual directory, not a symlink)
|
||||
-- @return true or nil
|
||||
-- @return error if failed
|
||||
-- @raise fullpath must be a string
|
||||
function dir.rmtree(fullpath)
|
||||
assert_dir(1,fullpath)
|
||||
if path.islink(fullpath) then return false,'will not follow symlink' end
|
||||
for root,dirs,files in dir.walk(fullpath,true) do
|
||||
if path.islink(root) then
|
||||
-- sub dir is a link, remove link, do not follow
|
||||
if is_windows then
|
||||
-- Windows requires using "rmdir". Deleting the link like a file
|
||||
-- will instead delete all files from the target directory!!
|
||||
local res, err = rmdir(root)
|
||||
if not res then return nil,err .. ": " .. root end
|
||||
else
|
||||
local res, err = remove(root)
|
||||
if not res then return nil,err .. ": " .. root end
|
||||
end
|
||||
else
|
||||
for i,f in ipairs(files) do
|
||||
local res, err = remove(path.join(root,f))
|
||||
if not res then return nil,err .. ": " .. path.join(root,f) end
|
||||
end
|
||||
local res, err = rmdir(root)
|
||||
if not res then return nil,err .. ": " .. root end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
local dirpat
|
||||
if path.is_windows then
|
||||
dirpat = '(.+)\\[^\\]+$'
|
||||
else
|
||||
dirpat = '(.+)/[^/]+$'
|
||||
end
|
||||
|
||||
local _makepath
|
||||
function _makepath(p)
|
||||
-- windows root drive case
|
||||
if p:find '^%a:[\\]*$' then
|
||||
return true
|
||||
end
|
||||
if not path.isdir(p) then
|
||||
local subp = p:match(dirpat)
|
||||
if subp then
|
||||
local ok, err = _makepath(subp)
|
||||
if not ok then return nil, err end
|
||||
end
|
||||
return mkdir(p)
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- create a directory path.
|
||||
-- This will create subdirectories as necessary!
|
||||
-- @string p A directory path
|
||||
-- @return true on success, nil + errormsg on failure
|
||||
-- @raise failure to create
|
||||
function dir.makepath (p)
|
||||
assert_string(1,p)
|
||||
if path.is_windows then
|
||||
p = p:gsub("/", "\\")
|
||||
end
|
||||
return _makepath(path.abspath(p))
|
||||
end
|
||||
end
|
||||
|
||||
--- clone a directory tree. Will always try to create a new directory structure
|
||||
-- if necessary.
|
||||
-- @string path1 the base path of the source tree
|
||||
-- @string path2 the new base path for the destination
|
||||
-- @func file_fun an optional function to apply on all files
|
||||
-- @bool verbose an optional boolean to control the verbosity of the output.
|
||||
-- It can also be a logging function that behaves like print()
|
||||
-- @return true, or nil
|
||||
-- @return error message, or list of failed directory creations
|
||||
-- @return list of failed file operations
|
||||
-- @raise path1 and path2 must be strings
|
||||
-- @usage clonetree('.','../backup',copyfile)
|
||||
function dir.clonetree (path1,path2,file_fun,verbose)
|
||||
assert_string(1,path1)
|
||||
assert_string(2,path2)
|
||||
if verbose == true then verbose = print end
|
||||
local abspath,normcase,isdir,join = path.abspath,path.normcase,path.isdir,path.join
|
||||
local faildirs,failfiles = {},{}
|
||||
if not isdir(path1) then return raise 'source is not a valid directory' end
|
||||
path1 = abspath(normcase(path1))
|
||||
path2 = abspath(normcase(path2))
|
||||
if verbose then verbose('normalized:',path1,path2) end
|
||||
-- particularly NB that the new path isn't fully contained in the old path
|
||||
if path1 == path2 then return raise "paths are the same" end
|
||||
local _,i2 = path2:find(path1,1,true)
|
||||
if i2 == #path1 and path2:sub(i2+1,i2+1) == path.sep then
|
||||
return raise 'destination is a subdirectory of the source'
|
||||
end
|
||||
local cp = path.common_prefix (path1,path2)
|
||||
local idx = #cp
|
||||
if idx == 0 then -- no common path, but watch out for Windows paths!
|
||||
if path1:sub(2,2) == ':' then idx = 3 end
|
||||
end
|
||||
for root,dirs,files in dir.walk(path1) do
|
||||
local opath = path2..root:sub(idx)
|
||||
if verbose then verbose('paths:',opath,root) end
|
||||
if not isdir(opath) then
|
||||
local ret = dir.makepath(opath)
|
||||
if not ret then append(faildirs,opath) end
|
||||
if verbose then verbose('creating:',opath,ret) end
|
||||
end
|
||||
if file_fun then
|
||||
for i,f in ipairs(files) do
|
||||
local p1 = join(root,f)
|
||||
local p2 = join(opath,f)
|
||||
local ret = file_fun(p1,p2)
|
||||
if not ret then append(failfiles,p2) end
|
||||
if verbose then
|
||||
verbose('files:',p1,p2,ret)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true,faildirs,failfiles
|
||||
end
|
||||
|
||||
|
||||
-- each entry of the stack is an array with three items:
|
||||
-- 1. the name of the directory
|
||||
-- 2. the lfs iterator function
|
||||
-- 3. the lfs iterator userdata
|
||||
local function treeiter(iterstack)
|
||||
local diriter = iterstack[#iterstack]
|
||||
if not diriter then
|
||||
return -- done
|
||||
end
|
||||
|
||||
local dirname = diriter[1]
|
||||
local entry = diriter[2](diriter[3])
|
||||
if not entry then
|
||||
table.remove(iterstack)
|
||||
return treeiter(iterstack) -- tail-call to try next
|
||||
end
|
||||
|
||||
if entry ~= "." and entry ~= ".." then
|
||||
entry = dirname .. sep .. entry
|
||||
if exists(entry) then -- Just in case a symlink is broken.
|
||||
local is_dir = isdir(entry)
|
||||
if is_dir then
|
||||
table.insert(iterstack, { entry, ldir(entry) })
|
||||
end
|
||||
return entry, is_dir
|
||||
end
|
||||
end
|
||||
|
||||
return treeiter(iterstack) -- tail-call to try next
|
||||
end
|
||||
|
||||
|
||||
--- return an iterator over all entries in a directory tree
|
||||
-- @string d a directory
|
||||
-- @return an iterator giving pathname and mode (true for dir, false otherwise)
|
||||
-- @raise d must be a non-empty string
|
||||
function dir.dirtree( d )
|
||||
assert( d and d ~= "", "directory parameter is missing or empty" )
|
||||
|
||||
local last = sub ( d, -1 )
|
||||
if last == sep or last == '/' then
|
||||
d = sub( d, 1, -2 )
|
||||
end
|
||||
|
||||
local iterstack = { {d, ldir(d)} }
|
||||
|
||||
return treeiter, iterstack
|
||||
end
|
||||
|
||||
|
||||
--- Recursively returns all the file starting at 'path'. It can optionally take a shell pattern and
|
||||
-- only returns files that match 'shell_pattern'. If a pattern is given it will do a case insensitive search.
|
||||
-- @string[opt='.'] start_path A directory.
|
||||
-- @string[opt='*'] shell_pattern A shell pattern (see `fnmatch`).
|
||||
-- @treturn List(string) containing all the files found recursively starting at 'path' and filtered by 'shell_pattern'.
|
||||
-- @raise start_path must be a directory
|
||||
function dir.getallfiles( start_path, shell_pattern )
|
||||
start_path = start_path or '.'
|
||||
assert_dir(1,start_path)
|
||||
shell_pattern = shell_pattern or "*"
|
||||
|
||||
local files = {}
|
||||
local normcase = path.normcase
|
||||
for filename, mode in dir.dirtree( start_path ) do
|
||||
if not mode then
|
||||
local mask = filemask( shell_pattern )
|
||||
if normcase(filename):find( mask ) then
|
||||
files[#files + 1] = filename
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return makelist(files)
|
||||
end
|
||||
|
||||
return dir
|
55
Documentation/compiler/pl/file.lua
Normal file
55
Documentation/compiler/pl/file.lua
Normal file
|
@ -0,0 +1,55 @@
|
|||
--- File manipulation functions: reading, writing, moving and copying.
|
||||
--
|
||||
-- This module wraps a number of functions from other modules into a
|
||||
-- file related module for convenience.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.dir`, `pl.path`
|
||||
-- @module pl.file
|
||||
local os = os
|
||||
local utils = require 'pl.utils'
|
||||
local dir = require 'pl.dir'
|
||||
local path = require 'pl.path'
|
||||
|
||||
local file = {}
|
||||
|
||||
--- return the contents of a file as a string.
|
||||
-- This function is a copy of `utils.readfile`.
|
||||
-- @function file.read
|
||||
file.read = utils.readfile
|
||||
|
||||
--- write a string to a file.
|
||||
-- This function is a copy of `utils.writefile`.
|
||||
-- @function file.write
|
||||
file.write = utils.writefile
|
||||
|
||||
--- copy a file.
|
||||
-- This function is a copy of `dir.copyfile`.
|
||||
-- @function file.copy
|
||||
file.copy = dir.copyfile
|
||||
|
||||
--- move a file.
|
||||
-- This function is a copy of `dir.movefile`.
|
||||
-- @function file.move
|
||||
file.move = dir.movefile
|
||||
|
||||
--- Return the time of last access as the number of seconds since the epoch.
|
||||
-- This function is a copy of `path.getatime`.
|
||||
-- @function file.access_time
|
||||
file.access_time = path.getatime
|
||||
|
||||
---Return when the file was created.
|
||||
-- This function is a copy of `path.getctime`.
|
||||
-- @function file.creation_time
|
||||
file.creation_time = path.getctime
|
||||
|
||||
--- Return the time of last modification.
|
||||
-- This function is a copy of `path.getmtime`.
|
||||
-- @function file.modified_time
|
||||
file.modified_time = path.getmtime
|
||||
|
||||
--- Delete a file.
|
||||
-- This function is a copy of `os.remove`.
|
||||
-- @function file.delete
|
||||
file.delete = os.remove
|
||||
|
||||
return file
|
393
Documentation/compiler/pl/func.lua
Normal file
393
Documentation/compiler/pl/func.lua
Normal file
|
@ -0,0 +1,393 @@
|
|||
--- Functional helpers like composition, binding and placeholder expressions.
|
||||
-- Placeholder expressions are useful for short anonymous functions, and were
|
||||
-- inspired by the Boost Lambda library.
|
||||
--
|
||||
-- > utils.import 'pl.func'
|
||||
-- > ls = List{10,20,30}
|
||||
-- > = ls:map(_1+1)
|
||||
-- {11,21,31}
|
||||
--
|
||||
-- They can also be used to _bind_ particular arguments of a function.
|
||||
--
|
||||
-- > p = bind(print,'start>',_0)
|
||||
-- > p(10,20,30)
|
||||
-- > start> 10 20 30
|
||||
--
|
||||
-- See @{07-functional.md.Creating_Functions_from_Functions|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`
|
||||
-- @module pl.func
|
||||
local type,setmetatable,getmetatable,rawset = type,setmetatable,getmetatable,rawset
|
||||
local concat,append = table.concat,table.insert
|
||||
local tostring = tostring
|
||||
local utils = require 'pl.utils'
|
||||
local pairs,rawget,unpack,pack = pairs,rawget,utils.unpack,utils.pack
|
||||
local tablex = require 'pl.tablex'
|
||||
local map = tablex.map
|
||||
local _DEBUG = rawget(_G,'_DEBUG')
|
||||
local assert_arg = utils.assert_arg
|
||||
|
||||
local func = {}
|
||||
|
||||
-- metatable for Placeholder Expressions (PE)
|
||||
local _PEMT = {}
|
||||
|
||||
local function P (t)
|
||||
setmetatable(t,_PEMT)
|
||||
return t
|
||||
end
|
||||
|
||||
func.PE = P
|
||||
|
||||
local function isPE (obj)
|
||||
return getmetatable(obj) == _PEMT
|
||||
end
|
||||
|
||||
func.isPE = isPE
|
||||
|
||||
-- construct a placeholder variable (e.g _1 and _2)
|
||||
local function PH (idx)
|
||||
return P {op='X',repr='_'..idx, index=idx}
|
||||
end
|
||||
|
||||
-- construct a constant placeholder variable (e.g _C1 and _C2)
|
||||
local function CPH (idx)
|
||||
return P {op='X',repr='_C'..idx, index=idx}
|
||||
end
|
||||
|
||||
func._1,func._2,func._3,func._4,func._5 = PH(1),PH(2),PH(3),PH(4),PH(5)
|
||||
func._0 = P{op='X',repr='...',index=0}
|
||||
|
||||
function func.Var (name)
|
||||
local ls = utils.split(name,'[%s,]+')
|
||||
local res = {}
|
||||
for i = 1, #ls do
|
||||
append(res,P{op='X',repr=ls[i],index=0})
|
||||
end
|
||||
return unpack(res)
|
||||
end
|
||||
|
||||
function func._ (value)
|
||||
return P{op='X',repr=value,index='wrap'}
|
||||
end
|
||||
|
||||
local repr
|
||||
|
||||
func.Nil = func.Var 'nil'
|
||||
|
||||
function _PEMT.__index(obj,key)
|
||||
return P{op='[]',obj,key}
|
||||
end
|
||||
|
||||
function _PEMT.__call(fun,...)
|
||||
return P{op='()',fun,...}
|
||||
end
|
||||
|
||||
function _PEMT.__tostring (e)
|
||||
return repr(e)
|
||||
end
|
||||
|
||||
function _PEMT.__unm(arg)
|
||||
return P{op='unm',arg}
|
||||
end
|
||||
|
||||
function func.Not (arg)
|
||||
return P{op='not',arg}
|
||||
end
|
||||
|
||||
function func.Len (arg)
|
||||
return P{op='#',arg}
|
||||
end
|
||||
|
||||
|
||||
local function binreg(context,t)
|
||||
for name,op in pairs(t) do
|
||||
rawset(context,name,function(x,y)
|
||||
return P{op=op,x,y}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local function import_name (name,fun,context)
|
||||
rawset(context,name,function(...)
|
||||
return P{op='()',fun,...}
|
||||
end)
|
||||
end
|
||||
|
||||
local imported_functions = {}
|
||||
|
||||
local function is_global_table (n)
|
||||
return type(_G[n]) == 'table'
|
||||
end
|
||||
|
||||
--- wrap a table of functions. This makes them available for use in
|
||||
-- placeholder expressions.
|
||||
-- @string tname a table name
|
||||
-- @tab context context to put results, defaults to environment of caller
|
||||
function func.import(tname,context)
|
||||
assert_arg(1,tname,'string',is_global_table,'arg# 1: not a name of a global table')
|
||||
local t = _G[tname]
|
||||
context = context or _G
|
||||
for name,fun in pairs(t) do
|
||||
import_name(name,fun,context)
|
||||
imported_functions[fun] = name
|
||||
end
|
||||
end
|
||||
|
||||
--- register a function for use in placeholder expressions.
|
||||
-- @func fun a function
|
||||
-- @string[opt] name an optional name
|
||||
-- @return a placeholder functiond
|
||||
function func.register (fun,name)
|
||||
assert_arg(1,fun,'function')
|
||||
if name then
|
||||
assert_arg(2,name,'string')
|
||||
imported_functions[fun] = name
|
||||
end
|
||||
return function(...)
|
||||
return P{op='()',fun,...}
|
||||
end
|
||||
end
|
||||
|
||||
function func.lookup_imported_name (fun)
|
||||
return imported_functions[fun]
|
||||
end
|
||||
|
||||
local function _arg(...) return ... end
|
||||
|
||||
function func.Args (...)
|
||||
return P{op='()',_arg,...}
|
||||
end
|
||||
|
||||
-- binary operators with their precedences (see Lua manual)
|
||||
-- precedences might be incremented by one before use depending on
|
||||
-- left- or right-associativity, space them out
|
||||
local binary_operators = {
|
||||
['or'] = 0,
|
||||
['and'] = 2,
|
||||
['=='] = 4, ['~='] = 4, ['<'] = 4, ['>'] = 4, ['<='] = 4, ['>='] = 4,
|
||||
['..'] = 6,
|
||||
['+'] = 8, ['-'] = 8,
|
||||
['*'] = 10, ['/'] = 10, ['%'] = 10,
|
||||
['^'] = 14
|
||||
}
|
||||
|
||||
-- unary operators with their precedences
|
||||
local unary_operators = {
|
||||
['not'] = 12, ['#'] = 12, ['unm'] = 12
|
||||
}
|
||||
|
||||
-- comparisons (as prefix functions)
|
||||
binreg (func,{And='and',Or='or',Eq='==',Lt='<',Gt='>',Le='<=',Ge='>='})
|
||||
|
||||
-- standard binary operators (as metamethods)
|
||||
binreg (_PEMT,{__add='+',__sub='-',__mul='*',__div='/',__mod='%',__pow='^',__concat='..'})
|
||||
|
||||
binreg (_PEMT,{__eq='=='})
|
||||
|
||||
--- all elements of a table except the first.
|
||||
-- @tab ls a list-like table.
|
||||
function func.tail (ls)
|
||||
assert_arg(1,ls,'table')
|
||||
local res = {}
|
||||
for i = 2,#ls do
|
||||
append(res,ls[i])
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- create a string representation of a placeholder expression.
|
||||
-- @param e a placeholder expression
|
||||
-- @param lastpred not used
|
||||
function repr (e,lastpred)
|
||||
local tail = func.tail
|
||||
if isPE(e) then
|
||||
local pred = binary_operators[e.op] or unary_operators[e.op]
|
||||
if pred then
|
||||
-- binary or unary operator
|
||||
local s
|
||||
if binary_operators[e.op] then
|
||||
local left_pred = pred
|
||||
local right_pred = pred
|
||||
if e.op == '..' or e.op == '^' then
|
||||
left_pred = left_pred + 1
|
||||
else
|
||||
right_pred = right_pred + 1
|
||||
end
|
||||
local left_arg = repr(e[1], left_pred)
|
||||
local right_arg = repr(e[2], right_pred)
|
||||
s = left_arg..' '..e.op..' '..right_arg
|
||||
else
|
||||
local op = e.op == 'unm' and '-' or e.op
|
||||
s = op..' '..repr(e[1], pred)
|
||||
end
|
||||
if lastpred and lastpred > pred then
|
||||
s = '('..s..')'
|
||||
end
|
||||
return s
|
||||
else -- either postfix, or a placeholder
|
||||
local ls = map(repr,e)
|
||||
if e.op == '[]' then
|
||||
return ls[1]..'['..ls[2]..']'
|
||||
elseif e.op == '()' then
|
||||
local fn
|
||||
if ls[1] ~= nil then -- was _args, undeclared!
|
||||
fn = ls[1]
|
||||
else
|
||||
fn = ''
|
||||
end
|
||||
return fn..'('..concat(tail(ls),',')..')'
|
||||
else
|
||||
return e.repr
|
||||
end
|
||||
end
|
||||
elseif type(e) == 'string' then
|
||||
return '"'..e..'"'
|
||||
elseif type(e) == 'function' then
|
||||
local name = func.lookup_imported_name(e)
|
||||
if name then return name else return tostring(e) end
|
||||
else
|
||||
return tostring(e) --should not really get here!
|
||||
end
|
||||
end
|
||||
func.repr = repr
|
||||
|
||||
-- collect all the non-PE values in this PE into vlist, and replace each occurence
|
||||
-- with a constant PH (_C1, etc). Return the maximum placeholder index found.
|
||||
local collect_values
|
||||
function collect_values (e,vlist)
|
||||
if isPE(e) then
|
||||
if e.op ~= 'X' then
|
||||
local m = 0
|
||||
for i = 1,#e do
|
||||
local subx = e[i]
|
||||
local pe = isPE(subx)
|
||||
if pe then
|
||||
if subx.op == 'X' and subx.index == 'wrap' then
|
||||
subx = subx.repr
|
||||
pe = false
|
||||
else
|
||||
m = math.max(m,collect_values(subx,vlist))
|
||||
end
|
||||
end
|
||||
if not pe then
|
||||
append(vlist,subx)
|
||||
e[i] = CPH(#vlist)
|
||||
end
|
||||
end
|
||||
return m
|
||||
else -- was a placeholder, it has an index...
|
||||
return e.index
|
||||
end
|
||||
else -- plain value has no placeholder dependence
|
||||
return 0
|
||||
end
|
||||
end
|
||||
func.collect_values = collect_values
|
||||
|
||||
--- instantiate a PE into an actual function. First we find the largest placeholder used,
|
||||
-- e.g. _2; from this a list of the formal parameters can be build. Then we collect and replace
|
||||
-- any non-PE values from the PE, and build up a constant binding list.
|
||||
-- Finally, the expression can be compiled, and e.__PE_function is set.
|
||||
-- @param e a placeholder expression
|
||||
-- @return a function
|
||||
function func.instantiate (e)
|
||||
local consts,values,parms = {},{},{}
|
||||
local rep, err, fun
|
||||
local n = func.collect_values(e,values)
|
||||
for i = 1,#values do
|
||||
append(consts,'_C'..i)
|
||||
if _DEBUG then print(i,values[i]) end
|
||||
end
|
||||
for i =1,n do
|
||||
append(parms,'_'..i)
|
||||
end
|
||||
consts = concat(consts,',')
|
||||
parms = concat(parms,',')
|
||||
rep = repr(e)
|
||||
local fstr = ('return function(%s) return function(%s) return %s end end'):format(consts,parms,rep)
|
||||
if _DEBUG then print(fstr) end
|
||||
fun,err = utils.load(fstr,'fun')
|
||||
if not fun then return nil,err end
|
||||
fun = fun() -- get wrapper
|
||||
fun = fun(unpack(values)) -- call wrapper (values could be empty)
|
||||
e.__PE_function = fun
|
||||
return fun
|
||||
end
|
||||
|
||||
--- instantiate a PE unless it has already been done.
|
||||
-- @param e a placeholder expression
|
||||
-- @return the function
|
||||
function func.I(e)
|
||||
if rawget(e,'__PE_function') then
|
||||
return e.__PE_function
|
||||
else return func.instantiate(e)
|
||||
end
|
||||
end
|
||||
|
||||
utils.add_function_factory(_PEMT,func.I)
|
||||
|
||||
--- bind the first parameter of the function to a value.
|
||||
-- @function func.bind1
|
||||
-- @func fn a function of one or more arguments
|
||||
-- @param p a value
|
||||
-- @return a function of one less argument
|
||||
-- @usage (bind1(math.max,10))(20) == math.max(10,20)
|
||||
func.bind1 = utils.bind1
|
||||
func.curry = func.bind1
|
||||
|
||||
--- create a function which chains two functions.
|
||||
-- @func f a function of at least one argument
|
||||
-- @func g a function of at least one argument
|
||||
-- @return a function
|
||||
-- @usage printf = compose(io.write,string.format)
|
||||
function func.compose (f,g)
|
||||
return function(...) return f(g(...)) end
|
||||
end
|
||||
|
||||
--- bind the arguments of a function to given values.
|
||||
-- `bind(fn,v,_2)` is equivalent to `bind1(fn,v)`.
|
||||
-- @func fn a function of at least one argument
|
||||
-- @param ... values or placeholder variables
|
||||
-- @return a function
|
||||
-- @usage (bind(f,_1,a))(b) == f(a,b)
|
||||
-- @usage (bind(f,_2,_1))(a,b) == f(b,a)
|
||||
function func.bind(fn,...)
|
||||
local args = pack(...)
|
||||
local holders,parms,bvalues,values = {},{},{'fn'},{}
|
||||
local nv,maxplace,varargs = 1,0,false
|
||||
for i = 1,args.n do
|
||||
local a = args[i]
|
||||
if isPE(a) and a.op == 'X' then
|
||||
append(holders,a.repr)
|
||||
maxplace = math.max(maxplace,a.index)
|
||||
if a.index == 0 then varargs = true end
|
||||
else
|
||||
local v = '_v'..nv
|
||||
append(bvalues,v)
|
||||
append(holders,v)
|
||||
append(values,a)
|
||||
nv = nv + 1
|
||||
end
|
||||
end
|
||||
for np = 1,maxplace do
|
||||
append(parms,'_'..np)
|
||||
end
|
||||
if varargs then append(parms,'...') end
|
||||
bvalues = concat(bvalues,',')
|
||||
parms = concat(parms,',')
|
||||
holders = concat(holders,',')
|
||||
local fstr = ([[
|
||||
return function (%s)
|
||||
return function(%s) return fn(%s) end
|
||||
end
|
||||
]]):format(bvalues,parms,holders)
|
||||
if _DEBUG then print(fstr) end
|
||||
local res = utils.load(fstr)
|
||||
res = res()
|
||||
return res(fn,unpack(values))
|
||||
end
|
||||
|
||||
return func
|
||||
|
||||
|
91
Documentation/compiler/pl/import_into.lua
Normal file
91
Documentation/compiler/pl/import_into.lua
Normal file
|
@ -0,0 +1,91 @@
|
|||
--------------
|
||||
-- PL loader, for loading all PL libraries, only on demand.
|
||||
-- Whenever a module is implicitly accesssed, the table will have the module automatically injected.
|
||||
-- (e.g. `_ENV.tablex`)
|
||||
-- then that module is dynamically loaded. The submodules are all brought into
|
||||
-- the table that is provided as the argument, or returned in a new table.
|
||||
-- If a table is provided, that table's metatable is clobbered, but the values are not.
|
||||
-- This module returns a single function, which is passed the environment.
|
||||
-- If this is `true`, then return a 'shadow table' as the module
|
||||
-- See @{01-introduction.md.To_Inject_or_not_to_Inject_|the Guide}
|
||||
|
||||
-- @module pl.import_into
|
||||
|
||||
return function(env)
|
||||
local mod
|
||||
if env == true then
|
||||
mod = {}
|
||||
env = {}
|
||||
end
|
||||
local env = env or {}
|
||||
|
||||
local modules = {
|
||||
utils = true,path=true,dir=true,tablex=true,stringio=true,sip=true,
|
||||
input=true,seq=true,lexer=true,stringx=true,
|
||||
config=true,pretty=true,data=true,func=true,text=true,
|
||||
operator=true,lapp=true,array2d=true,
|
||||
comprehension=true,xml=true,types=true,
|
||||
test = true, app = true, file = true, class = true,
|
||||
luabalanced = true, permute = true, template = true,
|
||||
url = true, compat = true,
|
||||
-- classes --
|
||||
List = true, Map = true, Set = true,
|
||||
OrderedMap = true, MultiMap = true, Date = true,
|
||||
}
|
||||
rawset(env,'utils',require 'pl.utils')
|
||||
|
||||
for name,klass in pairs(env.utils.stdmt) do
|
||||
klass.__index = function(t,key)
|
||||
return require ('pl.'..name)[key]
|
||||
end;
|
||||
end
|
||||
|
||||
-- ensure that we play nice with libraries that also attach a metatable
|
||||
-- to the global table; always forward to a custom __index if we don't
|
||||
-- match
|
||||
|
||||
local _hook,_prev_index
|
||||
local gmt = {}
|
||||
local prevenvmt = getmetatable(env)
|
||||
if prevenvmt then
|
||||
_prev_index = prevenvmt.__index
|
||||
if prevenvmt.__newindex then
|
||||
gmt.__newindex = prevenvmt.__newindex
|
||||
end
|
||||
end
|
||||
|
||||
function gmt.hook(handler)
|
||||
_hook = handler
|
||||
end
|
||||
|
||||
function gmt.__index(t,name)
|
||||
local found = modules[name]
|
||||
-- either true, or the name of the module containing this class.
|
||||
-- either way, we load the required module and make it globally available.
|
||||
if found then
|
||||
-- e..g pretty.dump causes pl.pretty to become available as 'pretty'
|
||||
rawset(env,name,require('pl.'..name))
|
||||
return env[name]
|
||||
else
|
||||
local res
|
||||
if _hook then
|
||||
res = _hook(t,name)
|
||||
if res then return res end
|
||||
end
|
||||
if _prev_index then
|
||||
return _prev_index(t,name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if mod then
|
||||
function gmt.__newindex(t,name,value)
|
||||
mod[name] = value
|
||||
rawset(t,name,value)
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(env,gmt)
|
||||
|
||||
return env,mod or env
|
||||
end
|
11
Documentation/compiler/pl/init.lua
Normal file
11
Documentation/compiler/pl/init.lua
Normal file
|
@ -0,0 +1,11 @@
|
|||
--------------
|
||||
-- Entry point for loading all PL libraries only on demand, into the global space.
|
||||
-- Requiring 'pl' means that whenever a module is implicitly accesssed
|
||||
-- (e.g. `utils.split`)
|
||||
-- then that module is dynamically loaded. The submodules are all brought into
|
||||
-- the global space.
|
||||
--Updated to use @{pl.import_into}
|
||||
-- @module pl
|
||||
require'pl.import_into'(_G)
|
||||
|
||||
if rawget(_G,'PENLIGHT_STRICT') then require 'pl.strict' end
|
171
Documentation/compiler/pl/input.lua
Normal file
171
Documentation/compiler/pl/input.lua
Normal file
|
@ -0,0 +1,171 @@
|
|||
--- Iterators for extracting words or numbers from an input source.
|
||||
--
|
||||
-- require 'pl'
|
||||
-- local total,n = seq.sum(input.numbers())
|
||||
-- print('average',total/n)
|
||||
--
|
||||
-- _source_ is defined as a string or a file-like object (i.e. has a read() method which returns the next line)
|
||||
--
|
||||
-- See @{06-data.md.Reading_Unstructured_Text_Data|here}
|
||||
--
|
||||
-- Dependencies: `pl.utils`
|
||||
-- @module pl.input
|
||||
local strfind = string.find
|
||||
local strsub = string.sub
|
||||
local strmatch = string.match
|
||||
local utils = require 'pl.utils'
|
||||
local unpack = utils.unpack
|
||||
local pairs,type,tonumber = pairs,type,tonumber
|
||||
local patterns = utils.patterns
|
||||
local io = io
|
||||
|
||||
local input = {}
|
||||
|
||||
--- create an iterator over all tokens.
|
||||
-- based on allwords from PiL, 7.1
|
||||
-- @func getter any function that returns a line of text
|
||||
-- @string pattern
|
||||
-- @string[opt] fn Optionally can pass a function to process each token as it's found.
|
||||
-- @return an iterator
|
||||
function input.alltokens (getter,pattern,fn)
|
||||
local line = getter() -- current line
|
||||
local pos = 1 -- current position in the line
|
||||
return function () -- iterator function
|
||||
while line do -- repeat while there are lines
|
||||
local s, e = strfind(line, pattern, pos)
|
||||
if s then -- found a word?
|
||||
pos = e + 1 -- next position is after this token
|
||||
local res = strsub(line, s, e) -- return the token
|
||||
if fn then res = fn(res) end
|
||||
return res
|
||||
else
|
||||
line = getter() -- token not found; try next line
|
||||
pos = 1 -- restart from first position
|
||||
end
|
||||
end
|
||||
return nil -- no more lines: end of traversal
|
||||
end
|
||||
end
|
||||
local alltokens = input.alltokens
|
||||
|
||||
-- question: shd this _split_ a string containing line feeds?
|
||||
|
||||
--- create a function which grabs the next value from a source. If the source is a string, then the getter
|
||||
-- will return the string and thereafter return nil. If not specified then the source is assumed to be stdin.
|
||||
-- @param f a string or a file-like object (i.e. has a read() method which returns the next line)
|
||||
-- @return a getter function
|
||||
function input.create_getter(f)
|
||||
if f then
|
||||
if type(f) == 'string' then
|
||||
local ls = utils.split(f,'\n')
|
||||
local i,n = 0,#ls
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > n then return nil end
|
||||
return ls[i]
|
||||
end
|
||||
else
|
||||
-- anything that supports the read() method!
|
||||
if not f.read then error('not a file-like object') end
|
||||
return function() return f:read() end
|
||||
end
|
||||
else
|
||||
return io.read -- i.e. just read from stdin
|
||||
end
|
||||
end
|
||||
|
||||
--- generate a sequence of numbers from a source.
|
||||
-- @param f A source
|
||||
-- @return An iterator
|
||||
function input.numbers(f)
|
||||
return alltokens(input.create_getter(f),
|
||||
'('..patterns.FLOAT..')',tonumber)
|
||||
end
|
||||
|
||||
--- generate a sequence of words from a source.
|
||||
-- @param f A source
|
||||
-- @return An iterator
|
||||
function input.words(f)
|
||||
return alltokens(input.create_getter(f),"%w+")
|
||||
end
|
||||
|
||||
local function apply_tonumber (no_fail,...)
|
||||
local args = {...}
|
||||
for i = 1,#args do
|
||||
local n = tonumber(args[i])
|
||||
if n == nil then
|
||||
if not no_fail then return nil,args[i] end
|
||||
else
|
||||
args[i] = n
|
||||
end
|
||||
end
|
||||
return args
|
||||
end
|
||||
|
||||
--- parse an input source into fields.
|
||||
-- By default, will fail if it cannot convert a field to a number.
|
||||
-- @param ids a list of field indices, or a maximum field index
|
||||
-- @string delim delimiter to parse fields (default space)
|
||||
-- @param f a source @see create_getter
|
||||
-- @tab opts option table, `{no_fail=true}`
|
||||
-- @return an iterator with the field values
|
||||
-- @usage for x,y in fields {2,3} do print(x,y) end -- 2nd and 3rd fields from stdin
|
||||
function input.fields (ids,delim,f,opts)
|
||||
local sep
|
||||
local s
|
||||
local getter = input.create_getter(f)
|
||||
local no_fail = opts and opts.no_fail
|
||||
local no_convert = opts and opts.no_convert
|
||||
if not delim or delim == ' ' then
|
||||
delim = '%s'
|
||||
sep = '%s+'
|
||||
s = '%s*'
|
||||
else
|
||||
sep = delim
|
||||
s = ''
|
||||
end
|
||||
local max_id = 0
|
||||
if type(ids) == 'table' then
|
||||
for i,id in pairs(ids) do
|
||||
if id > max_id then max_id = id end
|
||||
end
|
||||
else
|
||||
max_id = ids
|
||||
ids = {}
|
||||
for i = 1,max_id do ids[#ids+1] = i end
|
||||
end
|
||||
local pat = '[^'..delim..']*'
|
||||
local k = 1
|
||||
for i = 1,max_id do
|
||||
if ids[k] == i then
|
||||
k = k + 1
|
||||
s = s..'('..pat..')'
|
||||
else
|
||||
s = s..pat
|
||||
end
|
||||
if i < max_id then
|
||||
s = s..sep
|
||||
end
|
||||
end
|
||||
local linecount = 1
|
||||
return function()
|
||||
local line,results,err
|
||||
repeat
|
||||
line = getter()
|
||||
linecount = linecount + 1
|
||||
if not line then return nil end
|
||||
if no_convert then
|
||||
results = {strmatch(line,s)}
|
||||
else
|
||||
results,err = apply_tonumber(no_fail,strmatch(line,s))
|
||||
if not results then
|
||||
utils.quit("line "..(linecount-1)..": cannot convert '"..err.."' to number")
|
||||
end
|
||||
end
|
||||
until #results > 0
|
||||
return unpack(results)
|
||||
end
|
||||
end
|
||||
|
||||
return input
|
||||
|
458
Documentation/compiler/pl/lapp.lua
Normal file
458
Documentation/compiler/pl/lapp.lua
Normal file
|
@ -0,0 +1,458 @@
|
|||
--- Simple command-line parsing using human-readable specification.
|
||||
-- Supports GNU-style parameters.
|
||||
--
|
||||
-- lapp = require 'pl.lapp'
|
||||
-- local args = lapp [[
|
||||
-- Does some calculations
|
||||
-- -o,--offset (default 0.0) Offset to add to scaled number
|
||||
-- -s,--scale (number) Scaling factor
|
||||
-- <number> (number) Number to be scaled
|
||||
-- ]]
|
||||
--
|
||||
-- print(args.offset + args.scale * args.number)
|
||||
--
|
||||
-- Lines beginning with `'-'` are flags; there may be a short and a long name;
|
||||
-- lines beginning with `'<var>'` are arguments. Anything in parens after
|
||||
-- the flag/argument is either a default, a type name or a range constraint.
|
||||
--
|
||||
-- See @{08-additional.md.Command_line_Programs_with_Lapp|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.sip`
|
||||
-- @module pl.lapp
|
||||
|
||||
local status,sip = pcall(require,'pl.sip')
|
||||
if not status then
|
||||
sip = require 'sip'
|
||||
end
|
||||
local match = sip.match_at_start
|
||||
local append,tinsert = table.insert,table.insert
|
||||
|
||||
sip.custom_pattern('X','(%a[%w_%-]*)')
|
||||
|
||||
local function lines(s) return s:gmatch('([^\n]*)\n') end
|
||||
local function lstrip(str) return str:gsub('^%s+','') end
|
||||
local function strip(str) return lstrip(str):gsub('%s+$','') end
|
||||
local function at(s,k) return s:sub(k,k) end
|
||||
|
||||
local lapp = {}
|
||||
|
||||
local open_files,parms,aliases,parmlist,usage,script
|
||||
|
||||
lapp.callback = false -- keep Strict happy
|
||||
|
||||
local filetypes = {
|
||||
stdin = {io.stdin,'file-in'}, stdout = {io.stdout,'file-out'},
|
||||
stderr = {io.stderr,'file-out'}
|
||||
}
|
||||
|
||||
--- controls whether to dump usage on error.
|
||||
-- Defaults to true
|
||||
lapp.show_usage_error = true
|
||||
|
||||
--- quit this script immediately.
|
||||
-- @string msg optional message
|
||||
-- @bool no_usage suppress 'usage' display
|
||||
function lapp.quit(msg,no_usage)
|
||||
if no_usage == 'throw' then
|
||||
error(msg)
|
||||
end
|
||||
if msg then
|
||||
io.stderr:write(msg..'\n\n')
|
||||
end
|
||||
if not no_usage then
|
||||
io.stderr:write(usage)
|
||||
end
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
--- print an error to stderr and quit.
|
||||
-- @string msg a message
|
||||
-- @bool no_usage suppress 'usage' display
|
||||
function lapp.error(msg,no_usage)
|
||||
if not lapp.show_usage_error then
|
||||
no_usage = true
|
||||
elseif lapp.show_usage_error == 'throw' then
|
||||
no_usage = 'throw'
|
||||
end
|
||||
lapp.quit(script..': '..msg,no_usage)
|
||||
end
|
||||
|
||||
--- open a file.
|
||||
-- This will quit on error, and keep a list of file objects for later cleanup.
|
||||
-- @string file filename
|
||||
-- @string[opt] opt same as second parameter of `io.open`
|
||||
function lapp.open (file,opt)
|
||||
local val,err = io.open(file,opt)
|
||||
if not val then lapp.error(err,true) end
|
||||
append(open_files,val)
|
||||
return val
|
||||
end
|
||||
|
||||
--- quit if the condition is false.
|
||||
-- @bool condn a condition
|
||||
-- @string msg message text
|
||||
function lapp.assert(condn,msg)
|
||||
if not condn then
|
||||
lapp.error(msg)
|
||||
end
|
||||
end
|
||||
|
||||
local function range_check(x,min,max,parm)
|
||||
lapp.assert(min <= x and max >= x,parm..' out of range')
|
||||
end
|
||||
|
||||
local function xtonumber(s)
|
||||
local val = tonumber(s)
|
||||
if not val then lapp.error("unable to convert to number: "..s) end
|
||||
return val
|
||||
end
|
||||
|
||||
local types = {}
|
||||
|
||||
local builtin_types = {string=true,number=true,['file-in']='file',['file-out']='file',boolean=true}
|
||||
|
||||
local function convert_parameter(ps,val)
|
||||
if ps.converter then
|
||||
val = ps.converter(val)
|
||||
end
|
||||
if ps.type == 'number' then
|
||||
val = xtonumber(val)
|
||||
elseif builtin_types[ps.type] == 'file' then
|
||||
val = lapp.open(val,(ps.type == 'file-in' and 'r') or 'w' )
|
||||
elseif ps.type == 'boolean' then
|
||||
return val
|
||||
end
|
||||
if ps.constraint then
|
||||
ps.constraint(val)
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
--- add a new type to Lapp. These appear in parens after the value like
|
||||
-- a range constraint, e.g. '<ival> (integer) Process PID'
|
||||
-- @string name name of type
|
||||
-- @param converter either a function to convert values, or a Lua type name.
|
||||
-- @func[opt] constraint optional function to verify values, should use lapp.error
|
||||
-- if failed.
|
||||
function lapp.add_type (name,converter,constraint)
|
||||
types[name] = {converter=converter,constraint=constraint}
|
||||
end
|
||||
|
||||
local function force_short(short)
|
||||
lapp.assert(#short==1,short..": short parameters should be one character")
|
||||
end
|
||||
|
||||
-- deducing type of variable from default value;
|
||||
local function process_default (sval,vtype)
|
||||
local val, success
|
||||
if not vtype or vtype == 'number' then
|
||||
val = tonumber(sval)
|
||||
end
|
||||
if val then -- we have a number!
|
||||
return val,'number'
|
||||
elseif filetypes[sval] then
|
||||
local ft = filetypes[sval]
|
||||
return ft[1],ft[2]
|
||||
else
|
||||
if sval == 'true' and not vtype then
|
||||
return true, 'boolean'
|
||||
end
|
||||
if sval:match '^["\']' then sval = sval:sub(2,-2) end
|
||||
|
||||
local ps = types[vtype] or {}
|
||||
ps.type = vtype
|
||||
|
||||
local show_usage_error = lapp.show_usage_error
|
||||
lapp.show_usage_error = "throw"
|
||||
success, val = pcall(convert_parameter, ps, sval)
|
||||
lapp.show_usage_error = show_usage_error
|
||||
if success then
|
||||
return val, vtype or 'string'
|
||||
end
|
||||
|
||||
return sval,vtype or 'string'
|
||||
end
|
||||
end
|
||||
|
||||
--- process a Lapp options string.
|
||||
-- Usually called as `lapp()`.
|
||||
-- @string str the options text
|
||||
-- @tparam {string} args a table of arguments (default is `_G.arg`)
|
||||
-- @return a table with parameter-value pairs
|
||||
function lapp.process_options_string(str,args)
|
||||
local results = {}
|
||||
local varargs
|
||||
local arg = args or _G.arg
|
||||
open_files = {}
|
||||
parms = {}
|
||||
aliases = {}
|
||||
parmlist = {}
|
||||
|
||||
local function check_varargs(s)
|
||||
local res,cnt = s:gsub('^%.%.%.%s*','')
|
||||
return res, (cnt > 0)
|
||||
end
|
||||
|
||||
local function set_result(ps,parm,val)
|
||||
parm = type(parm) == "string" and parm:gsub("%W", "_") or parm -- so foo-bar becomes foo_bar in Lua
|
||||
if not ps.varargs then
|
||||
results[parm] = val
|
||||
else
|
||||
if not results[parm] then
|
||||
results[parm] = { val }
|
||||
else
|
||||
append(results[parm],val)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
usage = str
|
||||
|
||||
for _,a in ipairs(arg) do
|
||||
if a == "-h" or a == "--help" then
|
||||
return lapp.quit()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for line in lines(str) do
|
||||
local res = {}
|
||||
local optparm,defval,vtype,constraint,rest
|
||||
line = lstrip(line)
|
||||
local function check(str)
|
||||
return match(str,line,res)
|
||||
end
|
||||
|
||||
-- flags: either '-<short>', '-<short>,--<long>' or '--<long>'
|
||||
if check '-$v{short}, --$o{long} $' or check '-$v{short} $' or check '--$o{long} $' then
|
||||
if res.long then
|
||||
optparm = res.long:gsub('[^%w%-]','_') -- I'm not sure the $o pattern will let anything else through?
|
||||
if #res.rest == 1 then optparm = optparm .. res.rest end
|
||||
if res.short then aliases[res.short] = optparm end
|
||||
else
|
||||
optparm = res.short
|
||||
end
|
||||
if res.short and not lapp.slack then force_short(res.short) end
|
||||
res.rest, varargs = check_varargs(res.rest)
|
||||
elseif check '$<{name} $' then -- is it <parameter_name>?
|
||||
-- so <input file...> becomes input_file ...
|
||||
optparm,rest = res.name:match '([^%.]+)(.*)'
|
||||
-- follow lua legal variable names
|
||||
optparm = optparm:sub(1,1):gsub('%A','_') .. optparm:sub(2):gsub('%W', '_')
|
||||
varargs = rest == '...'
|
||||
append(parmlist,optparm)
|
||||
end
|
||||
-- this is not a pure doc line and specifies the flag/parameter type
|
||||
if res.rest then
|
||||
line = res.rest
|
||||
res = {}
|
||||
local optional
|
||||
local defval_str
|
||||
-- do we have ([optional] [<type>] [default <val>])?
|
||||
if match('$({def} $',line,res) or match('$({def}',line,res) then
|
||||
local typespec = strip(res.def)
|
||||
local ftype, rest = typespec:match('^(%S+)(.*)$')
|
||||
rest = strip(rest)
|
||||
if ftype == 'optional' then
|
||||
ftype, rest = rest:match('^(%S+)(.*)$')
|
||||
rest = strip(rest)
|
||||
optional = true
|
||||
end
|
||||
local default
|
||||
if ftype == 'default' then
|
||||
default = true
|
||||
if rest == '' then lapp.error("value must follow default") end
|
||||
else -- a type specification
|
||||
if match('$f{min}..$f{max}',ftype,res) then
|
||||
-- a numerical range like 1..10
|
||||
local min,max = res.min,res.max
|
||||
vtype = 'number'
|
||||
constraint = function(x)
|
||||
range_check(x,min,max,optparm)
|
||||
end
|
||||
elseif not ftype:match '|' then -- plain type
|
||||
vtype = ftype
|
||||
else
|
||||
-- 'enum' type is a string which must belong to
|
||||
-- one of several distinct values
|
||||
local enums = ftype
|
||||
local enump = '|' .. enums .. '|'
|
||||
vtype = 'string'
|
||||
constraint = function(s)
|
||||
lapp.assert(enump:find('|'..s..'|', 1, true),
|
||||
"value '"..s.."' not in "..enums
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
res.rest = rest
|
||||
typespec = res.rest
|
||||
-- optional 'default value' clause. Type is inferred as
|
||||
-- 'string' or 'number' if there's no explicit type
|
||||
if default or match('default $r{rest}',typespec,res) then
|
||||
defval_str = res.rest
|
||||
defval,vtype = process_default(res.rest,vtype)
|
||||
end
|
||||
else -- must be a plain flag, no extra parameter required
|
||||
defval = false
|
||||
vtype = 'boolean'
|
||||
end
|
||||
local ps = {
|
||||
type = vtype,
|
||||
defval = defval,
|
||||
defval_str = defval_str,
|
||||
required = defval == nil and not optional,
|
||||
comment = res.rest or optparm,
|
||||
constraint = constraint,
|
||||
varargs = varargs
|
||||
}
|
||||
varargs = nil
|
||||
if types[vtype] then
|
||||
local converter = types[vtype].converter
|
||||
if type(converter) == 'string' then
|
||||
ps.type = converter
|
||||
else
|
||||
ps.converter = converter
|
||||
end
|
||||
ps.constraint = types[vtype].constraint
|
||||
elseif not builtin_types[vtype] and vtype then
|
||||
lapp.error(vtype.." is unknown type")
|
||||
end
|
||||
parms[optparm] = ps
|
||||
end
|
||||
end
|
||||
-- cool, we have our parms, let's parse the command line args
|
||||
local iparm = 1
|
||||
local iextra = 1
|
||||
local i = 1
|
||||
local parm,ps,val
|
||||
local end_of_flags = false
|
||||
|
||||
local function check_parm (parm)
|
||||
local eqi = parm:find '[=:]'
|
||||
if eqi then
|
||||
tinsert(arg,i+1,parm:sub(eqi+1))
|
||||
parm = parm:sub(1,eqi-1)
|
||||
end
|
||||
return parm,eqi
|
||||
end
|
||||
|
||||
local function is_flag (parm)
|
||||
return parms[aliases[parm] or parm]
|
||||
end
|
||||
|
||||
while i <= #arg do
|
||||
local theArg = arg[i]
|
||||
local res = {}
|
||||
-- after '--' we don't parse args and they end up in
|
||||
-- the array part of the result (args[1] etc)
|
||||
if theArg == '--' then
|
||||
end_of_flags = true
|
||||
iparm = #parmlist + 1
|
||||
i = i + 1
|
||||
theArg = arg[i]
|
||||
if not theArg then
|
||||
break
|
||||
end
|
||||
end
|
||||
-- look for a flag, -<short flags> or --<long flag>
|
||||
if not end_of_flags and (match('--$S{long}',theArg,res) or match('-$S{short}',theArg,res)) then
|
||||
if res.long then -- long option
|
||||
parm = check_parm(res.long)
|
||||
elseif #res.short == 1 or is_flag(res.short) then
|
||||
parm = res.short
|
||||
else
|
||||
local parmstr,eq = check_parm(res.short)
|
||||
if not eq then
|
||||
parm = at(parmstr,1)
|
||||
local flag = is_flag(parm)
|
||||
if flag and flag.type ~= 'boolean' then
|
||||
--if isdigit(at(parmstr,2)) then
|
||||
-- a short option followed by a digit is an exception (for AW;))
|
||||
-- push ahead into the arg array
|
||||
tinsert(arg,i+1,parmstr:sub(2))
|
||||
else
|
||||
-- push multiple flags into the arg array!
|
||||
for k = 2,#parmstr do
|
||||
tinsert(arg,i+k-1,'-'..at(parmstr,k))
|
||||
end
|
||||
end
|
||||
else
|
||||
parm = parmstr
|
||||
end
|
||||
end
|
||||
if aliases[parm] then parm = aliases[parm] end
|
||||
if not parms[parm] and (parm == 'h' or parm == 'help') then
|
||||
lapp.quit()
|
||||
end
|
||||
else -- a parameter
|
||||
parm = parmlist[iparm]
|
||||
if not parm then
|
||||
-- extra unnamed parameters are indexed starting at 1
|
||||
parm = iextra
|
||||
ps = { type = 'string' }
|
||||
parms[parm] = ps
|
||||
iextra = iextra + 1
|
||||
else
|
||||
ps = parms[parm]
|
||||
end
|
||||
if not ps.varargs then
|
||||
iparm = iparm + 1
|
||||
end
|
||||
val = theArg
|
||||
end
|
||||
ps = parms[parm]
|
||||
if not ps then lapp.error("unrecognized parameter: "..parm) end
|
||||
if ps.type ~= 'boolean' then -- we need a value! This should follow
|
||||
if not val then
|
||||
i = i + 1
|
||||
val = arg[i]
|
||||
theArg = val
|
||||
end
|
||||
lapp.assert(val,parm.." was expecting a value")
|
||||
else -- toggle boolean flags (usually false -> true)
|
||||
val = not ps.defval
|
||||
end
|
||||
ps.used = true
|
||||
val = convert_parameter(ps,val)
|
||||
set_result(ps,parm,val)
|
||||
if builtin_types[ps.type] == 'file' then
|
||||
set_result(ps,parm..'_name',theArg)
|
||||
end
|
||||
if lapp.callback then
|
||||
lapp.callback(parm,theArg,res)
|
||||
end
|
||||
i = i + 1
|
||||
val = nil
|
||||
end
|
||||
-- check unused parms, set defaults and check if any required parameters were missed
|
||||
for parm,ps in pairs(parms) do
|
||||
if not ps.used then
|
||||
if ps.required then lapp.error("missing required parameter: "..parm) end
|
||||
set_result(ps,parm,ps.defval)
|
||||
if builtin_types[ps.type] == "file" then
|
||||
set_result(ps, parm .. "_name", ps.defval_str)
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
if arg then
|
||||
script = arg[0]
|
||||
script = script or rawget(_G,"LAPP_SCRIPT") or "unknown"
|
||||
-- strip dir and extension to get current script name
|
||||
script = script:gsub('.+[\\/]',''):gsub('%.%a+$','')
|
||||
else
|
||||
script = "inter"
|
||||
end
|
||||
|
||||
|
||||
setmetatable(lapp, {
|
||||
__call = function(tbl,str,args) return lapp.process_options_string(str,args) end,
|
||||
})
|
||||
|
||||
|
||||
return lapp
|
||||
|
||||
|
515
Documentation/compiler/pl/lexer.lua
Normal file
515
Documentation/compiler/pl/lexer.lua
Normal file
|
@ -0,0 +1,515 @@
|
|||
--- Lexical scanner for creating a sequence of tokens from text.
|
||||
-- `lexer.scan(s)` returns an iterator over all tokens found in the
|
||||
-- string `s`. This iterator returns two values, a token type string
|
||||
-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the
|
||||
-- token.
|
||||
--
|
||||
-- Versions specialized for Lua and C are available; these also handle block comments
|
||||
-- and classify keywords as 'keyword' tokens. For example:
|
||||
--
|
||||
-- > s = 'for i=1,n do'
|
||||
-- > for t,v in lexer.lua(s) do print(t,v) end
|
||||
-- keyword for
|
||||
-- iden i
|
||||
-- = =
|
||||
-- number 1
|
||||
-- , ,
|
||||
-- iden n
|
||||
-- keyword do
|
||||
--
|
||||
-- See the Guide for further @{06-data.md.Lexical_Scanning|discussion}
|
||||
-- @module pl.lexer
|
||||
|
||||
local strfind = string.find
|
||||
local strsub = string.sub
|
||||
local append = table.insert
|
||||
|
||||
|
||||
local function assert_arg(idx,val,tp)
|
||||
if type(val) ~= tp then
|
||||
error("argument "..idx.." must be "..tp, 2)
|
||||
end
|
||||
end
|
||||
|
||||
local lexer = {}
|
||||
|
||||
local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+'
|
||||
local NUMBER1a = '^[%+%-]?%d*%.%d+[eE][%+%-]?%d+'
|
||||
local NUMBER2 = '^[%+%-]?%d+%.?%d*'
|
||||
local NUMBER2a = '^[%+%-]?%d*%.%d+'
|
||||
local NUMBER3 = '^0x[%da-fA-F]+'
|
||||
local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+'
|
||||
local NUMBER4a = '^%d*%.%d+[eE][%+%-]?%d+'
|
||||
local NUMBER5 = '^%d+%.?%d*'
|
||||
local NUMBER5a = '^%d*%.%d+'
|
||||
local IDEN = '^[%a_][%w_]*'
|
||||
local WSPACE = '^%s+'
|
||||
local STRING1 = "^(['\"])%1" -- empty string
|
||||
local STRING2 = [[^(['"])(\*)%2%1]]
|
||||
local STRING3 = [[^(['"]).-[^\](\*)%2%1]]
|
||||
local CHAR1 = "^''"
|
||||
local CHAR2 = [[^'(\*)%1']]
|
||||
local CHAR3 = [[^'.-[^\](\*)%1']]
|
||||
local PREPRO = '^#.-[^\\]\n'
|
||||
|
||||
local plain_matches,lua_matches,cpp_matches,lua_keyword,cpp_keyword
|
||||
|
||||
local function tdump(tok)
|
||||
return tok,tok
|
||||
end
|
||||
|
||||
local function ndump(tok,options)
|
||||
if options and options.number then
|
||||
tok = tonumber(tok)
|
||||
end
|
||||
return "number",tok
|
||||
end
|
||||
|
||||
-- regular strings, single or double quotes; usually we want them
|
||||
-- without the quotes
|
||||
local function sdump(tok,options)
|
||||
if options and options.string then
|
||||
tok = tok:sub(2,-2)
|
||||
end
|
||||
return "string",tok
|
||||
end
|
||||
|
||||
-- long Lua strings need extra work to get rid of the quotes
|
||||
local function sdump_l(tok,options,findres)
|
||||
if options and options.string then
|
||||
local quotelen = 3
|
||||
if findres[3] then
|
||||
quotelen = quotelen + findres[3]:len()
|
||||
end
|
||||
tok = tok:sub(quotelen, -quotelen)
|
||||
if tok:sub(1, 1) == "\n" then
|
||||
tok = tok:sub(2)
|
||||
end
|
||||
end
|
||||
return "string",tok
|
||||
end
|
||||
|
||||
local function chdump(tok,options)
|
||||
if options and options.string then
|
||||
tok = tok:sub(2,-2)
|
||||
end
|
||||
return "char",tok
|
||||
end
|
||||
|
||||
local function cdump(tok)
|
||||
return "comment",tok
|
||||
end
|
||||
|
||||
local function wsdump (tok)
|
||||
return "space",tok
|
||||
end
|
||||
|
||||
local function pdump (tok)
|
||||
return "prepro",tok
|
||||
end
|
||||
|
||||
local function plain_vdump(tok)
|
||||
return "iden",tok
|
||||
end
|
||||
|
||||
local function lua_vdump(tok)
|
||||
if lua_keyword[tok] then
|
||||
return "keyword",tok
|
||||
else
|
||||
return "iden",tok
|
||||
end
|
||||
end
|
||||
|
||||
local function cpp_vdump(tok)
|
||||
if cpp_keyword[tok] then
|
||||
return "keyword",tok
|
||||
else
|
||||
return "iden",tok
|
||||
end
|
||||
end
|
||||
|
||||
--- create a plain token iterator from a string or file-like object.
|
||||
-- @tparam string|file s a string or a file-like object with `:read()` method returning lines.
|
||||
-- @tab matches an optional match table - array of token descriptions.
|
||||
-- A token is described by a `{pattern, action}` pair, where `pattern` should match
|
||||
-- token body and `action` is a function called when a token of described type is found.
|
||||
-- @tab[opt] filter a table of token types to exclude, by default `{space=true}`
|
||||
-- @tab[opt] options a table of options; by default, `{number=true,string=true}`,
|
||||
-- which means convert numbers and strip string quotes.
|
||||
function lexer.scan(s,matches,filter,options)
|
||||
local file = type(s) ~= 'string' and s
|
||||
filter = filter or {space=true}
|
||||
options = options or {number=true,string=true}
|
||||
if filter then
|
||||
if filter.space then filter[wsdump] = true end
|
||||
if filter.comments then
|
||||
filter[cdump] = true
|
||||
end
|
||||
end
|
||||
if not matches then
|
||||
if not plain_matches then
|
||||
plain_matches = {
|
||||
{WSPACE,wsdump},
|
||||
{NUMBER3,ndump},
|
||||
{IDEN,plain_vdump},
|
||||
{NUMBER1,ndump},
|
||||
{NUMBER1a,ndump},
|
||||
{NUMBER2,ndump},
|
||||
{NUMBER2a,ndump},
|
||||
{STRING1,sdump},
|
||||
{STRING2,sdump},
|
||||
{STRING3,sdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
matches = plain_matches
|
||||
end
|
||||
|
||||
local line_nr = 0
|
||||
local next_line = file and file:read()
|
||||
local sz = file and 0 or #s
|
||||
local idx = 1
|
||||
|
||||
local tlist_i
|
||||
local tlist
|
||||
|
||||
local first_hit = true
|
||||
|
||||
local function iter(res)
|
||||
local tp = type(res)
|
||||
|
||||
if tlist then -- returning the inserted token list
|
||||
local cur = tlist[tlist_i]
|
||||
if cur then
|
||||
tlist_i = tlist_i + 1
|
||||
return cur[1], cur[2]
|
||||
else
|
||||
tlist = nil
|
||||
end
|
||||
end
|
||||
|
||||
if tp == 'string' then -- search up to some special pattern
|
||||
local i1,i2 = strfind(s,res,idx)
|
||||
if i1 then
|
||||
local tok = strsub(s,i1,i2)
|
||||
idx = i2 + 1
|
||||
return '', tok
|
||||
else
|
||||
idx = sz + 1
|
||||
return '', ''
|
||||
end
|
||||
|
||||
elseif tp == 'table' then -- insert a token list
|
||||
tlist_i = 1
|
||||
tlist = res
|
||||
return '', ''
|
||||
|
||||
elseif tp ~= 'nil' then -- return position
|
||||
return line_nr, idx
|
||||
|
||||
else -- look for next token
|
||||
if first_hit then
|
||||
if not file then line_nr = 1 end
|
||||
first_hit = false
|
||||
end
|
||||
|
||||
if idx > sz then
|
||||
if file then
|
||||
if not next_line then
|
||||
return -- past the end of file, done
|
||||
end
|
||||
s = next_line
|
||||
line_nr = line_nr + 1
|
||||
next_line = file:read()
|
||||
if next_line then
|
||||
s = s .. '\n'
|
||||
end
|
||||
idx, sz = 1, #s
|
||||
else
|
||||
return -- past the end of input, done
|
||||
end
|
||||
end
|
||||
|
||||
for _,m in ipairs(matches) do
|
||||
local pat = m[1]
|
||||
local fun = m[2]
|
||||
local findres = {strfind(s,pat,idx)}
|
||||
local i1, i2 = findres[1], findres[2]
|
||||
if i1 then
|
||||
local tok = strsub(s,i1,i2)
|
||||
idx = i2 + 1
|
||||
local ret1, ret2
|
||||
if not (filter and filter[fun]) then
|
||||
lexer.finished = idx > sz
|
||||
ret1, ret2 = fun(tok, options, findres)
|
||||
end
|
||||
if not file and tok:find("\n") then
|
||||
-- Update line number.
|
||||
local _, newlines = tok:gsub("\n", {})
|
||||
line_nr = line_nr + newlines
|
||||
end
|
||||
if ret1 then
|
||||
return ret1, ret2 -- found a match
|
||||
else
|
||||
return iter() -- tail-call to try again
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return iter
|
||||
end
|
||||
|
||||
local function isstring (s)
|
||||
return type(s) == 'string'
|
||||
end
|
||||
|
||||
--- insert tokens into a stream.
|
||||
-- @param tok a token stream
|
||||
-- @param a1 a string is the type, a table is a token list and
|
||||
-- a function is assumed to be a token-like iterator (returns type & value)
|
||||
-- @string a2 a string is the value
|
||||
function lexer.insert (tok,a1,a2)
|
||||
if not a1 then return end
|
||||
local ts
|
||||
if isstring(a1) and isstring(a2) then
|
||||
ts = {{a1,a2}}
|
||||
elseif type(a1) == 'function' then
|
||||
ts = {}
|
||||
for t,v in a1() do
|
||||
append(ts,{t,v})
|
||||
end
|
||||
else
|
||||
ts = a1
|
||||
end
|
||||
tok(ts)
|
||||
end
|
||||
|
||||
--- get everything in a stream upto a newline.
|
||||
-- @param tok a token stream
|
||||
-- @return a string
|
||||
function lexer.getline (tok)
|
||||
local _,v = tok('.-\n')
|
||||
return v
|
||||
end
|
||||
|
||||
--- get current line number.
|
||||
-- @param tok a token stream
|
||||
-- @return the line number.
|
||||
-- if the input source is a file-like object,
|
||||
-- also return the column.
|
||||
function lexer.lineno (tok)
|
||||
return tok(0)
|
||||
end
|
||||
|
||||
--- get the rest of the stream.
|
||||
-- @param tok a token stream
|
||||
-- @return a string
|
||||
function lexer.getrest (tok)
|
||||
local _,v = tok('.+')
|
||||
return v
|
||||
end
|
||||
|
||||
--- get the Lua keywords as a set-like table.
|
||||
-- So `res["and"]` etc would be `true`.
|
||||
-- @return a table
|
||||
function lexer.get_keywords ()
|
||||
if not lua_keyword then
|
||||
lua_keyword = {
|
||||
["and"] = true, ["break"] = true, ["do"] = true,
|
||||
["else"] = true, ["elseif"] = true, ["end"] = true,
|
||||
["false"] = true, ["for"] = true, ["function"] = true,
|
||||
["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true,
|
||||
["not"] = true, ["or"] = true, ["repeat"] = true,
|
||||
["return"] = true, ["then"] = true, ["true"] = true,
|
||||
["until"] = true, ["while"] = true
|
||||
}
|
||||
end
|
||||
return lua_keyword
|
||||
end
|
||||
|
||||
--- create a Lua token iterator from a string or file-like object.
|
||||
-- Will return the token type and value.
|
||||
-- @string s the string
|
||||
-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}`
|
||||
-- @tab[opt] options a table of options; by default, `{number=true,string=true}`,
|
||||
-- which means convert numbers and strip string quotes.
|
||||
function lexer.lua(s,filter,options)
|
||||
filter = filter or {space=true,comments=true}
|
||||
lexer.get_keywords()
|
||||
if not lua_matches then
|
||||
lua_matches = {
|
||||
{WSPACE,wsdump},
|
||||
{NUMBER3,ndump},
|
||||
{IDEN,lua_vdump},
|
||||
{NUMBER4,ndump},
|
||||
{NUMBER4a,ndump},
|
||||
{NUMBER5,ndump},
|
||||
{NUMBER5a,ndump},
|
||||
{STRING1,sdump},
|
||||
{STRING2,sdump},
|
||||
{STRING3,sdump},
|
||||
{'^%-%-%[(=*)%[.-%]%1%]',cdump},
|
||||
{'^%-%-.-\n',cdump},
|
||||
{'^%[(=*)%[.-%]%1%]',sdump_l},
|
||||
{'^==',tdump},
|
||||
{'^~=',tdump},
|
||||
{'^<=',tdump},
|
||||
{'^>=',tdump},
|
||||
{'^%.%.%.',tdump},
|
||||
{'^%.%.',tdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
return lexer.scan(s,lua_matches,filter,options)
|
||||
end
|
||||
|
||||
--- create a C/C++ token iterator from a string or file-like object.
|
||||
-- Will return the token type type and value.
|
||||
-- @string s the string
|
||||
-- @tab[opt] filter a table of token types to exclude, by default `{space=true,comments=true}`
|
||||
-- @tab[opt] options a table of options; by default, `{number=true,string=true}`,
|
||||
-- which means convert numbers and strip string quotes.
|
||||
function lexer.cpp(s,filter,options)
|
||||
filter = filter or {space=true,comments=true}
|
||||
if not cpp_keyword then
|
||||
cpp_keyword = {
|
||||
["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true,
|
||||
["else"] = true, ["continue"] = true, ["struct"] = true,
|
||||
["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true,
|
||||
["private"] = true, ["protected"] = true, ["goto"] = true,
|
||||
["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true,
|
||||
["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true,
|
||||
["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true,
|
||||
["double"] = true, ["while"] = true, ["new"] = true,
|
||||
["namespace"] = true, ["try"] = true, ["catch"] = true,
|
||||
["switch"] = true, ["case"] = true, ["extern"] = true,
|
||||
["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true,
|
||||
["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true,
|
||||
}
|
||||
end
|
||||
if not cpp_matches then
|
||||
cpp_matches = {
|
||||
{WSPACE,wsdump},
|
||||
{PREPRO,pdump},
|
||||
{NUMBER3,ndump},
|
||||
{IDEN,cpp_vdump},
|
||||
{NUMBER4,ndump},
|
||||
{NUMBER4a,ndump},
|
||||
{NUMBER5,ndump},
|
||||
{NUMBER5a,ndump},
|
||||
{CHAR1,chdump},
|
||||
{CHAR2,chdump},
|
||||
{CHAR3,chdump},
|
||||
{STRING1,sdump},
|
||||
{STRING2,sdump},
|
||||
{STRING3,sdump},
|
||||
{'^//.-\n',cdump},
|
||||
{'^/%*.-%*/',cdump},
|
||||
{'^==',tdump},
|
||||
{'^!=',tdump},
|
||||
{'^<=',tdump},
|
||||
{'^>=',tdump},
|
||||
{'^->',tdump},
|
||||
{'^&&',tdump},
|
||||
{'^||',tdump},
|
||||
{'^%+%+',tdump},
|
||||
{'^%-%-',tdump},
|
||||
{'^%+=',tdump},
|
||||
{'^%-=',tdump},
|
||||
{'^%*=',tdump},
|
||||
{'^/=',tdump},
|
||||
{'^|=',tdump},
|
||||
{'^%^=',tdump},
|
||||
{'^::',tdump},
|
||||
{'^.',tdump}
|
||||
}
|
||||
end
|
||||
return lexer.scan(s,cpp_matches,filter,options)
|
||||
end
|
||||
|
||||
--- get a list of parameters separated by a delimiter from a stream.
|
||||
-- @param tok the token stream
|
||||
-- @string[opt=')'] endtoken end of list. Can be '\n'
|
||||
-- @string[opt=','] delim separator
|
||||
-- @return a list of token lists.
|
||||
function lexer.get_separated_list(tok,endtoken,delim)
|
||||
endtoken = endtoken or ')'
|
||||
delim = delim or ','
|
||||
local parm_values = {}
|
||||
local level = 1 -- used to count ( and )
|
||||
local tl = {}
|
||||
local function tappend (tl,t,val)
|
||||
val = val or t
|
||||
append(tl,{t,val})
|
||||
end
|
||||
local is_end
|
||||
if endtoken == '\n' then
|
||||
is_end = function(t,val)
|
||||
return t == 'space' and val:find '\n'
|
||||
end
|
||||
else
|
||||
is_end = function (t)
|
||||
return t == endtoken
|
||||
end
|
||||
end
|
||||
local token,value
|
||||
while true do
|
||||
token,value=tok()
|
||||
if not token then return nil,'EOS' end -- end of stream is an error!
|
||||
if is_end(token,value) and level == 1 then
|
||||
append(parm_values,tl)
|
||||
break
|
||||
elseif token == '(' then
|
||||
level = level + 1
|
||||
tappend(tl,'(')
|
||||
elseif token == ')' then
|
||||
level = level - 1
|
||||
if level == 0 then -- finished with parm list
|
||||
append(parm_values,tl)
|
||||
break
|
||||
else
|
||||
tappend(tl,')')
|
||||
end
|
||||
elseif token == delim and level == 1 then
|
||||
append(parm_values,tl) -- a new parm
|
||||
tl = {}
|
||||
else
|
||||
tappend(tl,token,value)
|
||||
end
|
||||
end
|
||||
return parm_values,{token,value}
|
||||
end
|
||||
|
||||
--- get the next non-space token from the stream.
|
||||
-- @param tok the token stream.
|
||||
function lexer.skipws (tok)
|
||||
local t,v = tok()
|
||||
while t == 'space' do
|
||||
t,v = tok()
|
||||
end
|
||||
return t,v
|
||||
end
|
||||
|
||||
local skipws = lexer.skipws
|
||||
|
||||
--- get the next token, which must be of the expected type.
|
||||
-- Throws an error if this type does not match!
|
||||
-- @param tok the token stream
|
||||
-- @string expected_type the token type
|
||||
-- @bool no_skip_ws whether we should skip whitespace
|
||||
function lexer.expecting (tok,expected_type,no_skip_ws)
|
||||
assert_arg(1,tok,'function')
|
||||
assert_arg(2,expected_type,'string')
|
||||
local t,v
|
||||
if no_skip_ws then
|
||||
t,v = tok()
|
||||
else
|
||||
t,v = skipws(tok)
|
||||
end
|
||||
if t ~= expected_type then error ("expecting "..expected_type,2) end
|
||||
return v
|
||||
end
|
||||
|
||||
return lexer
|
264
Documentation/compiler/pl/luabalanced.lua
Normal file
264
Documentation/compiler/pl/luabalanced.lua
Normal file
|
@ -0,0 +1,264 @@
|
|||
--- Extract delimited Lua sequences from strings.
|
||||
-- Inspired by Damian Conway's Text::Balanced in Perl. <br/>
|
||||
-- <ul>
|
||||
-- <li>[1] <a href="http://lua-users.org/wiki/LuaBalanced">Lua Wiki Page</a></li>
|
||||
-- <li>[2] http://search.cpan.org/dist/Text-Balanced/lib/Text/Balanced.pm</li>
|
||||
-- </ul> <br/>
|
||||
-- <pre class=example>
|
||||
-- local lb = require "pl.luabalanced"
|
||||
-- --Extract Lua expression starting at position 4.
|
||||
-- print(lb.match_expression("if x^2 + x > 5 then print(x) end", 4))
|
||||
-- --> x^2 + x > 5 16
|
||||
-- --Extract Lua string starting at (default) position 1.
|
||||
-- print(lb.match_string([["test\"123" .. "more"]]))
|
||||
-- --> "test\"123" 12
|
||||
-- </pre>
|
||||
-- (c) 2008, David Manura, Licensed under the same terms as Lua (MIT license).
|
||||
-- @class module
|
||||
-- @name pl.luabalanced
|
||||
|
||||
local M = {}
|
||||
|
||||
local assert = assert
|
||||
|
||||
-- map opening brace <-> closing brace.
|
||||
local ends = { ['('] = ')', ['{'] = '}', ['['] = ']' }
|
||||
local begins = {}; for k,v in pairs(ends) do begins[v] = k end
|
||||
|
||||
|
||||
-- Match Lua string in string <s> starting at position <pos>.
|
||||
-- Returns <string>, <posnew>, where <string> is the matched
|
||||
-- string (or nil on no match) and <posnew> is the character
|
||||
-- following the match (or <pos> on no match).
|
||||
-- Supports all Lua string syntax: "...", '...', [[...]], [=[...]=], etc.
|
||||
local function match_string(s, pos)
|
||||
pos = pos or 1
|
||||
local posa = pos
|
||||
local c = s:sub(pos,pos)
|
||||
if c == '"' or c == "'" then
|
||||
pos = pos + 1
|
||||
while 1 do
|
||||
pos = assert(s:find("[" .. c .. "\\]", pos), 'syntax error')
|
||||
if s:sub(pos,pos) == c then
|
||||
local part = s:sub(posa, pos)
|
||||
return part, pos + 1
|
||||
else
|
||||
pos = pos + 2
|
||||
end
|
||||
end
|
||||
else
|
||||
local sc = s:match("^%[(=*)%[", pos)
|
||||
if sc then
|
||||
local _; _, pos = s:find("%]" .. sc .. "%]", pos)
|
||||
assert(pos)
|
||||
local part = s:sub(posa, pos)
|
||||
return part, pos + 1
|
||||
else
|
||||
return nil, pos
|
||||
end
|
||||
end
|
||||
end
|
||||
M.match_string = match_string
|
||||
|
||||
|
||||
-- Match bracketed Lua expression, e.g. "(...)", "{...}", "[...]", "[[...]]",
|
||||
-- [=[...]=], etc.
|
||||
-- Function interface is similar to match_string.
|
||||
local function match_bracketed(s, pos)
|
||||
pos = pos or 1
|
||||
local posa = pos
|
||||
local ca = s:sub(pos,pos)
|
||||
if not ends[ca] then
|
||||
return nil, pos
|
||||
end
|
||||
local stack = {}
|
||||
while 1 do
|
||||
pos = s:find('[%(%{%[%)%}%]\"\']', pos)
|
||||
assert(pos, 'syntax error: unbalanced')
|
||||
local c = s:sub(pos,pos)
|
||||
if c == '"' or c == "'" then
|
||||
local part; part, pos = match_string(s, pos)
|
||||
assert(part)
|
||||
elseif ends[c] then -- open
|
||||
local mid, posb
|
||||
if c == '[' then mid, posb = s:match('^%[(=*)%[()', pos) end
|
||||
if mid then
|
||||
pos = s:match('%]' .. mid .. '%]()', posb)
|
||||
assert(pos, 'syntax error: long string not terminated')
|
||||
if #stack == 0 then
|
||||
local part = s:sub(posa, pos-1)
|
||||
return part, pos
|
||||
end
|
||||
else
|
||||
stack[#stack+1] = c
|
||||
pos = pos + 1
|
||||
end
|
||||
else -- close
|
||||
assert(stack[#stack] == assert(begins[c]), 'syntax error: unbalanced')
|
||||
stack[#stack] = nil
|
||||
if #stack == 0 then
|
||||
local part = s:sub(posa, pos)
|
||||
return part, pos+1
|
||||
end
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
M.match_bracketed = match_bracketed
|
||||
|
||||
|
||||
-- Match Lua comment, e.g. "--...\n", "--[[...]]", "--[=[...]=]", etc.
|
||||
-- Function interface is similar to match_string.
|
||||
local function match_comment(s, pos)
|
||||
pos = pos or 1
|
||||
if s:sub(pos, pos+1) ~= '--' then
|
||||
return nil, pos
|
||||
end
|
||||
pos = pos + 2
|
||||
local partt, post = match_string(s, pos)
|
||||
if partt then
|
||||
return '--' .. partt, post
|
||||
end
|
||||
local part; part, pos = s:match('^([^\n]*\n?)()', pos)
|
||||
return '--' .. part, pos
|
||||
end
|
||||
|
||||
|
||||
-- Match Lua expression, e.g. "a + b * c[e]".
|
||||
-- Function interface is similar to match_string.
|
||||
local wordop = {['and']=true, ['or']=true, ['not']=true}
|
||||
local is_compare = {['>']=true, ['<']=true, ['~']=true}
|
||||
local function match_expression(s, pos)
|
||||
pos = pos or 1
|
||||
local _
|
||||
local posa = pos
|
||||
local lastident
|
||||
local poscs, posce
|
||||
while pos do
|
||||
local c = s:sub(pos,pos)
|
||||
if c == '"' or c == "'" or c == '[' and s:find('^[=%[]', pos+1) then
|
||||
local part; part, pos = match_string(s, pos)
|
||||
assert(part, 'syntax error')
|
||||
elseif c == '-' and s:sub(pos+1,pos+1) == '-' then
|
||||
-- note: handle adjacent comments in loop to properly support
|
||||
-- backtracing (poscs/posce).
|
||||
poscs = pos
|
||||
while s:sub(pos,pos+1) == '--' do
|
||||
local part; part, pos = match_comment(s, pos)
|
||||
assert(part)
|
||||
pos = s:match('^%s*()', pos)
|
||||
posce = pos
|
||||
end
|
||||
elseif c == '(' or c == '{' or c == '[' then
|
||||
_, pos = match_bracketed(s, pos)
|
||||
elseif c == '=' and s:sub(pos+1,pos+1) == '=' then
|
||||
pos = pos + 2 -- skip over two-char op containing '='
|
||||
elseif c == '=' and is_compare[s:sub(pos-1,pos-1)] then
|
||||
pos = pos + 1 -- skip over two-char op containing '='
|
||||
elseif c:match'^[%)%}%];,=]' then
|
||||
local part = s:sub(posa, pos-1)
|
||||
return part, pos
|
||||
elseif c:match'^[%w_]' then
|
||||
local newident,newpos = s:match('^([%w_]+)()', pos)
|
||||
if pos ~= posa and not wordop[newident] then -- non-first ident
|
||||
local pose = ((posce == pos) and poscs or pos) - 1
|
||||
while s:match('^%s', pose) do pose = pose - 1 end
|
||||
local ce = s:sub(pose,pose)
|
||||
if ce:match'[%)%}\'\"%]]' or
|
||||
ce:match'[%w_]' and not wordop[lastident]
|
||||
then
|
||||
local part = s:sub(posa, pos-1)
|
||||
return part, pos
|
||||
end
|
||||
end
|
||||
lastident, pos = newident, newpos
|
||||
else
|
||||
pos = pos + 1
|
||||
end
|
||||
pos = s:find('[%(%{%[%)%}%]\"\';,=%w_%-]', pos)
|
||||
end
|
||||
local part = s:sub(posa, #s)
|
||||
return part, #s+1
|
||||
end
|
||||
M.match_expression = match_expression
|
||||
|
||||
|
||||
-- Match name list (zero or more names). E.g. "a,b,c"
|
||||
-- Function interface is similar to match_string,
|
||||
-- but returns array as match.
|
||||
local function match_namelist(s, pos)
|
||||
pos = pos or 1
|
||||
local list = {}
|
||||
while 1 do
|
||||
local c = #list == 0 and '^' or '^%s*,%s*'
|
||||
local item, post = s:match(c .. '([%a_][%w_]*)%s*()', pos)
|
||||
if item then pos = post else break end
|
||||
list[#list+1] = item
|
||||
end
|
||||
return list, pos
|
||||
end
|
||||
M.match_namelist = match_namelist
|
||||
|
||||
|
||||
-- Match expression list (zero or more expressions). E.g. "a+b,b*c".
|
||||
-- Function interface is similar to match_string,
|
||||
-- but returns array as match.
|
||||
local function match_explist(s, pos)
|
||||
pos = pos or 1
|
||||
local list = {}
|
||||
while 1 do
|
||||
if #list ~= 0 then
|
||||
local post = s:match('^%s*,%s*()', pos)
|
||||
if post then pos = post else break end
|
||||
end
|
||||
local item; item, pos = match_expression(s, pos)
|
||||
assert(item, 'syntax error')
|
||||
list[#list+1] = item
|
||||
end
|
||||
return list, pos
|
||||
end
|
||||
M.match_explist = match_explist
|
||||
|
||||
|
||||
-- Replace snippets of code in Lua code string <s>
|
||||
-- using replacement function f(u,sin) --> sout.
|
||||
-- <u> is the type of snippet ('c' = comment, 's' = string,
|
||||
-- 'e' = any other code).
|
||||
-- Snippet is replaced with <sout> (unless <sout> is nil or false, in
|
||||
-- which case the original snippet is kept)
|
||||
-- This is somewhat analogous to string.gsub .
|
||||
local function gsub(s, f)
|
||||
local pos = 1
|
||||
local posa = 1
|
||||
local sret = ''
|
||||
while 1 do
|
||||
pos = s:find('[%-\'\"%[]', pos)
|
||||
if not pos then break end
|
||||
if s:match('^%-%-', pos) then
|
||||
local exp = s:sub(posa, pos-1)
|
||||
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
|
||||
local comment; comment, pos = match_comment(s, pos)
|
||||
sret = sret .. (f('c', assert(comment)) or comment)
|
||||
posa = pos
|
||||
else
|
||||
local posb = s:find('^[\'\"%[]', pos)
|
||||
local str
|
||||
if posb then str, pos = match_string(s, posb) end
|
||||
if str then
|
||||
local exp = s:sub(posa, posb-1)
|
||||
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
|
||||
sret = sret .. (f('s', str) or str)
|
||||
posa = pos
|
||||
else
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
local exp = s:sub(posa)
|
||||
if #exp > 0 then sret = sret .. (f('e', exp) or exp) end
|
||||
return sret
|
||||
end
|
||||
M.gsub = gsub
|
||||
|
||||
|
||||
return M
|
209
Documentation/compiler/pl/operator.lua
Normal file
209
Documentation/compiler/pl/operator.lua
Normal file
|
@ -0,0 +1,209 @@
|
|||
--- Lua operators available as functions.
|
||||
--
|
||||
-- (similar to the Python module of the same name)
|
||||
--
|
||||
-- There is a module field `optable` which maps the operator strings
|
||||
-- onto these functions, e.g. `operator.optable['()']==operator.call`
|
||||
--
|
||||
-- Operator strings like '>' and '{}' can be passed to most Penlight functions
|
||||
-- expecting a function argument.
|
||||
--
|
||||
-- @module pl.operator
|
||||
|
||||
local strfind = string.find
|
||||
|
||||
local operator = {}
|
||||
|
||||
--- apply function to some arguments **()**
|
||||
-- @param fn a function or callable object
|
||||
-- @param ... arguments
|
||||
function operator.call(fn,...)
|
||||
return fn(...)
|
||||
end
|
||||
|
||||
--- get the indexed value from a table **[]**
|
||||
-- @param t a table or any indexable object
|
||||
-- @param k the key
|
||||
function operator.index(t,k)
|
||||
return t[k]
|
||||
end
|
||||
|
||||
--- returns true if arguments are equal **==**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.eq(a,b)
|
||||
return a==b
|
||||
end
|
||||
|
||||
--- returns true if arguments are not equal **~=**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.neq(a,b)
|
||||
return a~=b
|
||||
end
|
||||
|
||||
--- returns true if a is less than b **<**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.lt(a,b)
|
||||
return a < b
|
||||
end
|
||||
|
||||
--- returns true if a is less or equal to b **<=**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.le(a,b)
|
||||
return a <= b
|
||||
end
|
||||
|
||||
--- returns true if a is greater than b **>**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.gt(a,b)
|
||||
return a > b
|
||||
end
|
||||
|
||||
--- returns true if a is greater or equal to b **>=**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.ge(a,b)
|
||||
return a >= b
|
||||
end
|
||||
|
||||
--- returns length of string or table **#**
|
||||
-- @param a a string or a table
|
||||
function operator.len(a)
|
||||
return #a
|
||||
end
|
||||
|
||||
--- add two values **+**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.add(a,b)
|
||||
return a+b
|
||||
end
|
||||
|
||||
--- subtract b from a **-**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.sub(a,b)
|
||||
return a-b
|
||||
end
|
||||
|
||||
--- multiply two values __*__
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.mul(a,b)
|
||||
return a*b
|
||||
end
|
||||
|
||||
--- divide first value by second **/**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.div(a,b)
|
||||
return a/b
|
||||
end
|
||||
|
||||
--- raise first to the power of second **^**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.pow(a,b)
|
||||
return a^b
|
||||
end
|
||||
|
||||
--- modulo; remainder of a divided by b **%**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.mod(a,b)
|
||||
return a%b
|
||||
end
|
||||
|
||||
--- concatenate two values (either strings or `__concat` defined) **..**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.concat(a,b)
|
||||
return a..b
|
||||
end
|
||||
|
||||
--- return the negative of a value **-**
|
||||
-- @param a value
|
||||
function operator.unm(a)
|
||||
return -a
|
||||
end
|
||||
|
||||
--- false if value evaluates as true **not**
|
||||
-- @param a value
|
||||
function operator.lnot(a)
|
||||
return not a
|
||||
end
|
||||
|
||||
--- true if both values evaluate as true **and**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.land(a,b)
|
||||
return a and b
|
||||
end
|
||||
|
||||
--- true if either value evaluate as true **or**
|
||||
-- @param a value
|
||||
-- @param b value
|
||||
function operator.lor(a,b)
|
||||
return a or b
|
||||
end
|
||||
|
||||
--- make a table from the arguments **{}**
|
||||
-- @param ... non-nil arguments
|
||||
-- @return a table
|
||||
function operator.table (...)
|
||||
return {...}
|
||||
end
|
||||
|
||||
--- match two strings **~**.
|
||||
-- uses @{string.find}
|
||||
function operator.match (a,b)
|
||||
return strfind(a,b)~=nil
|
||||
end
|
||||
|
||||
--- the null operation.
|
||||
-- @param ... arguments
|
||||
-- @return the arguments
|
||||
function operator.nop (...)
|
||||
return ...
|
||||
end
|
||||
|
||||
---- Map from operator symbol to function.
|
||||
-- Most of these map directly from operators;
|
||||
-- But note these extras
|
||||
--
|
||||
-- * __'()'__ `call`
|
||||
-- * __'[]'__ `index`
|
||||
-- * __'{}'__ `table`
|
||||
-- * __'~'__ `match`
|
||||
--
|
||||
-- @table optable
|
||||
-- @field operator
|
||||
operator.optable = {
|
||||
['+']=operator.add,
|
||||
['-']=operator.sub,
|
||||
['*']=operator.mul,
|
||||
['/']=operator.div,
|
||||
['%']=operator.mod,
|
||||
['^']=operator.pow,
|
||||
['..']=operator.concat,
|
||||
['()']=operator.call,
|
||||
['[]']=operator.index,
|
||||
['<']=operator.lt,
|
||||
['<=']=operator.le,
|
||||
['>']=operator.gt,
|
||||
['>=']=operator.ge,
|
||||
['==']=operator.eq,
|
||||
['~=']=operator.neq,
|
||||
['#']=operator.len,
|
||||
['and']=operator.land,
|
||||
['or']=operator.lor,
|
||||
['{}']=operator.table,
|
||||
['~']=operator.match,
|
||||
['']=operator.nop,
|
||||
}
|
||||
|
||||
return operator
|
572
Documentation/compiler/pl/path.lua
Normal file
572
Documentation/compiler/pl/path.lua
Normal file
|
@ -0,0 +1,572 @@
|
|||
--- Path manipulation and file queries.
|
||||
--
|
||||
-- This is modelled after Python's os.path library (10.1); see @{04-paths.md|the Guide}.
|
||||
--
|
||||
-- NOTE: the functions assume the paths being dealt with to originate
|
||||
-- from the OS the application is running on. Windows drive letters are not
|
||||
-- to be used when running on a Unix system for example. The one exception
|
||||
-- is Windows paths to allow both forward and backward slashes (since Lua
|
||||
-- also accepts those)
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `lfs`
|
||||
-- @module pl.path
|
||||
|
||||
-- imports and locals
|
||||
local _G = _G
|
||||
local sub = string.sub
|
||||
local getenv = os.getenv
|
||||
local tmpnam = os.tmpname
|
||||
local package = package
|
||||
local append, concat, remove = table.insert, table.concat, table.remove
|
||||
local utils = require 'pl.utils'
|
||||
local assert_string,raise = utils.assert_string,utils.raise
|
||||
|
||||
local res,lfs = _G.pcall(_G.require,'lfs')
|
||||
if not res then
|
||||
error("pl.path requires LuaFileSystem")
|
||||
end
|
||||
|
||||
local attrib = lfs.attributes
|
||||
local currentdir = lfs.currentdir
|
||||
local link_attrib = lfs.symlinkattributes
|
||||
|
||||
local path = {}
|
||||
|
||||
local function err_func(name, param, err, code)
|
||||
local ret = ("%s failed"):format(tostring(name))
|
||||
if param ~= nil then
|
||||
ret = ret .. (" for '%s'"):format(tostring(param))
|
||||
end
|
||||
ret = ret .. (": %s"):format(tostring(err))
|
||||
if code ~= nil then
|
||||
ret = ret .. (" (code %s)"):format(tostring(code))
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Lua iterator over the entries of a given directory.
|
||||
-- Implicit link to [`luafilesystem.dir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function dir
|
||||
path.dir = lfs.dir
|
||||
|
||||
--- Creates a directory.
|
||||
-- Implicit link to [`luafilesystem.mkdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function mkdir
|
||||
path.mkdir = function(d)
|
||||
local ok, err, code = lfs.mkdir(d)
|
||||
if not ok then
|
||||
return ok, err_func("mkdir", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Removes a directory.
|
||||
-- Implicit link to [`luafilesystem.rmdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function rmdir
|
||||
path.rmdir = function(d)
|
||||
local ok, err, code = lfs.rmdir(d)
|
||||
if not ok then
|
||||
return ok, err_func("rmdir", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Gets attributes.
|
||||
-- Implicit link to [`luafilesystem.attributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function attrib
|
||||
path.attrib = function(d, r)
|
||||
local ok, err, code = attrib(d, r)
|
||||
if not ok then
|
||||
return ok, err_func("attrib", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Get the working directory.
|
||||
-- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function currentdir
|
||||
path.currentdir = function()
|
||||
local ok, err, code = currentdir()
|
||||
if not ok then
|
||||
return ok, err_func("currentdir", nil, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Gets symlink attributes.
|
||||
-- Implicit link to [`luafilesystem.symlinkattributes`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function link_attrib
|
||||
path.link_attrib = function(d, r)
|
||||
local ok, err, code = link_attrib(d, r)
|
||||
if not ok then
|
||||
return ok, err_func("link_attrib", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- Changes the working directory.
|
||||
-- On Windows, if a drive is specified, it also changes the current drive. If
|
||||
-- only specifying the drive, it will only switch drive, but not modify the path.
|
||||
-- Implicit link to [`luafilesystem.chdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
|
||||
-- @function chdir
|
||||
path.chdir = function(d)
|
||||
local ok, err, code = lfs.chdir(d)
|
||||
if not ok then
|
||||
return ok, err_func("chdir", d, err, code), code
|
||||
end
|
||||
return ok, err, code
|
||||
end
|
||||
|
||||
--- is this a directory?
|
||||
-- @string P A file path
|
||||
function path.isdir(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'mode') == 'directory'
|
||||
end
|
||||
|
||||
--- is this a file?
|
||||
-- @string P A file path
|
||||
function path.isfile(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'mode') == 'file'
|
||||
end
|
||||
|
||||
-- is this a symbolic link?
|
||||
-- @string P A file path
|
||||
function path.islink(P)
|
||||
assert_string(1,P)
|
||||
if link_attrib then
|
||||
return link_attrib(P,'mode')=='link'
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- return size of a file.
|
||||
-- @string P A file path
|
||||
function path.getsize(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'size')
|
||||
end
|
||||
|
||||
--- does a path exist?
|
||||
-- @string P A file path
|
||||
-- @return the file path if it exists (either as file, directory, socket, etc), nil otherwise
|
||||
function path.exists(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'mode') ~= nil and P
|
||||
end
|
||||
|
||||
--- Return the time of last access as the number of seconds since the epoch.
|
||||
-- @string P A file path
|
||||
function path.getatime(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'access')
|
||||
end
|
||||
|
||||
--- Return the time of last modification as the number of seconds since the epoch.
|
||||
-- @string P A file path
|
||||
function path.getmtime(P)
|
||||
assert_string(1,P)
|
||||
return attrib(P,'modification')
|
||||
end
|
||||
|
||||
---Return the system's ctime as the number of seconds since the epoch.
|
||||
-- @string P A file path
|
||||
function path.getctime(P)
|
||||
assert_string(1,P)
|
||||
return path.attrib(P,'change')
|
||||
end
|
||||
|
||||
|
||||
local function at(s,i)
|
||||
return sub(s,i,i)
|
||||
end
|
||||
|
||||
path.is_windows = utils.is_windows
|
||||
|
||||
local sep, other_sep, seps
|
||||
-- constant sep is the directory separator for this platform.
|
||||
-- constant dirsep is the separator in the PATH environment variable
|
||||
if path.is_windows then
|
||||
path.sep = '\\'; other_sep = '/'
|
||||
path.dirsep = ';'
|
||||
seps = { ['/'] = true, ['\\'] = true }
|
||||
else
|
||||
path.sep = '/'
|
||||
path.dirsep = ':'
|
||||
seps = { ['/'] = true }
|
||||
end
|
||||
sep = path.sep
|
||||
|
||||
--- are we running Windows?
|
||||
-- @class field
|
||||
-- @name path.is_windows
|
||||
|
||||
--- path separator for this platform.
|
||||
-- @class field
|
||||
-- @name path.sep
|
||||
|
||||
--- separator for PATH for this platform
|
||||
-- @class field
|
||||
-- @name path.dirsep
|
||||
|
||||
--- given a path, return the directory part and a file part.
|
||||
-- if there's no directory part, the first value will be empty
|
||||
-- @string P A file path
|
||||
-- @return directory part
|
||||
-- @return file part
|
||||
-- @usage
|
||||
-- local dir, file = path.splitpath("some/dir/myfile.txt")
|
||||
-- assert(dir == "some/dir")
|
||||
-- assert(file == "myfile.txt")
|
||||
--
|
||||
-- local dir, file = path.splitpath("some/dir/")
|
||||
-- assert(dir == "some/dir")
|
||||
-- assert(file == "")
|
||||
--
|
||||
-- local dir, file = path.splitpath("some_dir")
|
||||
-- assert(dir == "")
|
||||
-- assert(file == "some_dir")
|
||||
function path.splitpath(P)
|
||||
assert_string(1,P)
|
||||
local i = #P
|
||||
local ch = at(P,i)
|
||||
while i > 0 and ch ~= sep and ch ~= other_sep do
|
||||
i = i - 1
|
||||
ch = at(P,i)
|
||||
end
|
||||
if i == 0 then
|
||||
return '',P
|
||||
else
|
||||
return sub(P,1,i-1), sub(P,i+1)
|
||||
end
|
||||
end
|
||||
|
||||
--- return an absolute path.
|
||||
-- @string P A file path
|
||||
-- @string[opt] pwd optional start path to use (default is current dir)
|
||||
function path.abspath(P,pwd)
|
||||
assert_string(1,P)
|
||||
if pwd then assert_string(2,pwd) end
|
||||
local use_pwd = pwd ~= nil
|
||||
if not use_pwd and not currentdir() then return P end
|
||||
P = P:gsub('[\\/]$','')
|
||||
pwd = pwd or currentdir()
|
||||
if not path.isabs(P) then
|
||||
P = path.join(pwd,P)
|
||||
elseif path.is_windows and not use_pwd and at(P,2) ~= ':' and at(P,2) ~= '\\' then
|
||||
P = pwd:sub(1,2)..P -- attach current drive to path like '\\fred.txt'
|
||||
end
|
||||
return path.normpath(P)
|
||||
end
|
||||
|
||||
--- given a path, return the root part and the extension part.
|
||||
-- if there's no extension part, the second value will be empty
|
||||
-- @string P A file path
|
||||
-- @treturn string root part (everything upto the "."", maybe empty)
|
||||
-- @treturn string extension part (including the ".", maybe empty)
|
||||
-- @usage
|
||||
-- local file_path, ext = path.splitext("/bonzo/dog_stuff/cat.txt")
|
||||
-- assert(file_path == "/bonzo/dog_stuff/cat")
|
||||
-- assert(ext == ".txt")
|
||||
--
|
||||
-- local file_path, ext = path.splitext("")
|
||||
-- assert(file_path == "")
|
||||
-- assert(ext == "")
|
||||
function path.splitext(P)
|
||||
assert_string(1,P)
|
||||
local i = #P
|
||||
local ch = at(P,i)
|
||||
while i > 0 and ch ~= '.' do
|
||||
if seps[ch] then
|
||||
return P,''
|
||||
end
|
||||
i = i - 1
|
||||
ch = at(P,i)
|
||||
end
|
||||
if i == 0 then
|
||||
return P,''
|
||||
else
|
||||
return sub(P,1,i-1),sub(P,i)
|
||||
end
|
||||
end
|
||||
|
||||
--- return the directory part of a path
|
||||
-- @string P A file path
|
||||
-- @treturn string everything before the last dir-separator
|
||||
-- @see splitpath
|
||||
-- @usage
|
||||
-- path.dirname("/some/path/file.txt") -- "/some/path"
|
||||
-- path.dirname("file.txt") -- "" (empty string)
|
||||
function path.dirname(P)
|
||||
assert_string(1,P)
|
||||
local p1 = path.splitpath(P)
|
||||
return p1
|
||||
end
|
||||
|
||||
--- return the file part of a path
|
||||
-- @string P A file path
|
||||
-- @treturn string
|
||||
-- @see splitpath
|
||||
-- @usage
|
||||
-- path.basename("/some/path/file.txt") -- "file.txt"
|
||||
-- path.basename("/some/path/file/") -- "" (empty string)
|
||||
function path.basename(P)
|
||||
assert_string(1,P)
|
||||
local _,p2 = path.splitpath(P)
|
||||
return p2
|
||||
end
|
||||
|
||||
--- get the extension part of a path.
|
||||
-- @string P A file path
|
||||
-- @treturn string
|
||||
-- @see splitext
|
||||
-- @usage
|
||||
-- path.extension("/some/path/file.txt") -- ".txt"
|
||||
-- path.extension("/some/path/file_txt") -- "" (empty string)
|
||||
function path.extension(P)
|
||||
assert_string(1,P)
|
||||
local _,p2 = path.splitext(P)
|
||||
return p2
|
||||
end
|
||||
|
||||
--- is this an absolute path?
|
||||
-- @string P A file path
|
||||
-- @usage
|
||||
-- path.isabs("hello/path") -- false
|
||||
-- path.isabs("/hello/path") -- true
|
||||
-- -- Windows;
|
||||
-- path.isabs("hello\path") -- false
|
||||
-- path.isabs("\hello\path") -- true
|
||||
-- path.isabs("C:\hello\path") -- true
|
||||
-- path.isabs("C:hello\path") -- false
|
||||
function path.isabs(P)
|
||||
assert_string(1,P)
|
||||
if path.is_windows and at(P,2) == ":" then
|
||||
return seps[at(P,3)] ~= nil
|
||||
end
|
||||
return seps[at(P,1)] ~= nil
|
||||
end
|
||||
|
||||
--- return the path resulting from combining the individual paths.
|
||||
-- if the second (or later) path is absolute, we return the last absolute path (joined with any non-absolute paths following).
|
||||
-- empty elements (except the last) will be ignored.
|
||||
-- @string p1 A file path
|
||||
-- @string p2 A file path
|
||||
-- @string ... more file paths
|
||||
-- @treturn string the combined path
|
||||
-- @usage
|
||||
-- path.join("/first","second","third") -- "/first/second/third"
|
||||
-- path.join("first","second/third") -- "first/second/third"
|
||||
-- path.join("/first","/second","third") -- "/second/third"
|
||||
function path.join(p1,p2,...)
|
||||
assert_string(1,p1)
|
||||
assert_string(2,p2)
|
||||
if select('#',...) > 0 then
|
||||
local p = path.join(p1,p2)
|
||||
local args = {...}
|
||||
for i = 1,#args do
|
||||
assert_string(i,args[i])
|
||||
p = path.join(p,args[i])
|
||||
end
|
||||
return p
|
||||
end
|
||||
if path.isabs(p2) then return p2 end
|
||||
local endc = at(p1,#p1)
|
||||
if endc ~= path.sep and endc ~= other_sep and endc ~= "" then
|
||||
p1 = p1..path.sep
|
||||
end
|
||||
return p1..p2
|
||||
end
|
||||
|
||||
--- normalize the case of a pathname. On Unix, this returns the path unchanged,
|
||||
-- for Windows it converts;
|
||||
--
|
||||
-- * the path to lowercase
|
||||
-- * forward slashes to backward slashes
|
||||
-- @string P A file path
|
||||
-- @usage path.normcase("/Some/Path/File.txt")
|
||||
-- -- Windows: "\some\path\file.txt"
|
||||
-- -- Others : "/Some/Path/File.txt"
|
||||
function path.normcase(P)
|
||||
assert_string(1,P)
|
||||
if path.is_windows then
|
||||
return P:gsub('/','\\'):lower()
|
||||
else
|
||||
return P
|
||||
end
|
||||
end
|
||||
|
||||
--- normalize a path name.
|
||||
-- `A//B`, `A/./B`, and `A/foo/../B` all become `A/B`.
|
||||
--
|
||||
-- An empty path results in '.'.
|
||||
-- @string P a file path
|
||||
function path.normpath(P)
|
||||
assert_string(1,P)
|
||||
-- Split path into anchor and relative path.
|
||||
local anchor = ''
|
||||
if path.is_windows then
|
||||
if P:match '^\\\\' then -- UNC
|
||||
anchor = '\\\\'
|
||||
P = P:sub(3)
|
||||
elseif seps[at(P, 1)] then
|
||||
anchor = '\\'
|
||||
P = P:sub(2)
|
||||
elseif at(P, 2) == ':' then
|
||||
anchor = P:sub(1, 2)
|
||||
P = P:sub(3)
|
||||
if seps[at(P, 1)] then
|
||||
anchor = anchor..'\\'
|
||||
P = P:sub(2)
|
||||
end
|
||||
end
|
||||
P = P:gsub('/','\\')
|
||||
else
|
||||
-- According to POSIX, in path start '//' and '/' are distinct,
|
||||
-- but '///+' is equivalent to '/'.
|
||||
if P:match '^//' and at(P, 3) ~= '/' then
|
||||
anchor = '//'
|
||||
P = P:sub(3)
|
||||
elseif at(P, 1) == '/' then
|
||||
anchor = '/'
|
||||
P = P:match '^/*(.*)$'
|
||||
end
|
||||
end
|
||||
local parts = {}
|
||||
for part in P:gmatch('[^'..sep..']+') do
|
||||
if part == '..' then
|
||||
if #parts ~= 0 and parts[#parts] ~= '..' then
|
||||
remove(parts)
|
||||
else
|
||||
append(parts, part)
|
||||
end
|
||||
elseif part ~= '.' then
|
||||
append(parts, part)
|
||||
end
|
||||
end
|
||||
P = anchor..concat(parts, sep)
|
||||
if P == '' then P = '.' end
|
||||
return P
|
||||
end
|
||||
|
||||
--- relative path from current directory or optional start point
|
||||
-- @string P a path
|
||||
-- @string[opt] start optional start point (default current directory)
|
||||
function path.relpath (P,start)
|
||||
assert_string(1,P)
|
||||
if start then assert_string(2,start) end
|
||||
local split,min,append = utils.split, math.min, table.insert
|
||||
P = path.abspath(P,start)
|
||||
start = start or currentdir()
|
||||
local compare
|
||||
if path.is_windows then
|
||||
P = P:gsub("/","\\")
|
||||
start = start:gsub("/","\\")
|
||||
compare = function(v) return v:lower() end
|
||||
else
|
||||
compare = function(v) return v end
|
||||
end
|
||||
local startl, Pl = split(start,sep), split(P,sep)
|
||||
local n = min(#startl,#Pl)
|
||||
if path.is_windows and n > 0 and at(Pl[1],2) == ':' and Pl[1] ~= startl[1] then
|
||||
return P
|
||||
end
|
||||
local k = n+1 -- default value if this loop doesn't bail out!
|
||||
for i = 1,n do
|
||||
if compare(startl[i]) ~= compare(Pl[i]) then
|
||||
k = i
|
||||
break
|
||||
end
|
||||
end
|
||||
local rell = {}
|
||||
for i = 1, #startl-k+1 do rell[i] = '..' end
|
||||
if k <= #Pl then
|
||||
for i = k,#Pl do append(rell,Pl[i]) end
|
||||
end
|
||||
return table.concat(rell,sep)
|
||||
end
|
||||
|
||||
|
||||
--- Replace a starting '~' with the user's home directory.
|
||||
-- In windows, if HOME isn't set, then USERPROFILE is used in preference to
|
||||
-- HOMEDRIVE HOMEPATH. This is guaranteed to be writeable on all versions of Windows.
|
||||
-- @string P A file path
|
||||
function path.expanduser(P)
|
||||
assert_string(1,P)
|
||||
if at(P,1) == '~' then
|
||||
local home = getenv('HOME')
|
||||
if not home then -- has to be Windows
|
||||
home = getenv 'USERPROFILE' or (getenv 'HOMEDRIVE' .. getenv 'HOMEPATH')
|
||||
end
|
||||
return home..sub(P,2)
|
||||
else
|
||||
return P
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Return a suitable full path to a new temporary file name.
|
||||
-- unlike os.tmpname(), it always gives you a writeable path (uses TEMP environment variable on Windows)
|
||||
function path.tmpname ()
|
||||
local res = tmpnam()
|
||||
-- On Windows if Lua is compiled using MSVC14 os.tmpname
|
||||
-- already returns an absolute path within TEMP env variable directory,
|
||||
-- no need to prepend it.
|
||||
if path.is_windows and not res:find(':') then
|
||||
res = getenv('TEMP')..res
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- return the largest common prefix path of two paths.
|
||||
-- @string path1 a file path
|
||||
-- @string path2 a file path
|
||||
-- @return the common prefix (Windows: separators will be normalized, casing will be original)
|
||||
function path.common_prefix (path1,path2)
|
||||
assert_string(1,path1)
|
||||
assert_string(2,path2)
|
||||
-- get them in order!
|
||||
if #path1 > #path2 then path2,path1 = path1,path2 end
|
||||
local compare
|
||||
if path.is_windows then
|
||||
path1 = path1:gsub("/", "\\")
|
||||
path2 = path2:gsub("/", "\\")
|
||||
compare = function(v) return v:lower() end
|
||||
else
|
||||
compare = function(v) return v end
|
||||
end
|
||||
for i = 1,#path1 do
|
||||
if compare(at(path1,i)) ~= compare(at(path2,i)) then
|
||||
local cp = path1:sub(1,i-1)
|
||||
if at(path1,i-1) ~= sep then
|
||||
cp = path.dirname(cp)
|
||||
end
|
||||
return cp
|
||||
end
|
||||
end
|
||||
if at(path2,#path1+1) ~= sep then path1 = path.dirname(path1) end
|
||||
return path1
|
||||
--return ''
|
||||
end
|
||||
|
||||
--- return the full path where a particular Lua module would be found.
|
||||
-- Both package.path and package.cpath is searched, so the result may
|
||||
-- either be a Lua file or a shared library.
|
||||
-- @string mod name of the module
|
||||
-- @return on success: path of module, lua or binary
|
||||
-- @return on error: nil, error string listing paths tried
|
||||
function path.package_path(mod)
|
||||
assert_string(1,mod)
|
||||
local res, err1, err2
|
||||
res, err1 = package.searchpath(mod,package.path)
|
||||
if res then return res,true end
|
||||
res, err2 = package.searchpath(mod,package.cpath)
|
||||
if res then return res,false end
|
||||
return raise ('cannot find module on path\n' .. err1 .. "\n" .. err2)
|
||||
end
|
||||
|
||||
|
||||
---- finis -----
|
||||
return path
|
196
Documentation/compiler/pl/permute.lua
Normal file
196
Documentation/compiler/pl/permute.lua
Normal file
|
@ -0,0 +1,196 @@
|
|||
--- Permutation operations.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`
|
||||
-- @module pl.permute
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local copy = tablex.deepcopy
|
||||
local append = table.insert
|
||||
local assert_arg = utils.assert_arg
|
||||
|
||||
|
||||
local permute = {}
|
||||
|
||||
|
||||
--- an iterator over all order-permutations of the elements of a list.
|
||||
-- Please note that the same list is returned each time, so do not keep references!
|
||||
-- @param a list-like table
|
||||
-- @return an iterator which provides the next permutation as a list
|
||||
function permute.order_iter(a)
|
||||
assert_arg(1,a,'table')
|
||||
|
||||
local t = #a
|
||||
local stack = { 1 }
|
||||
local function iter()
|
||||
local h = #stack
|
||||
local n = t - h + 1
|
||||
|
||||
local i = stack[h]
|
||||
if i > t then
|
||||
return
|
||||
end
|
||||
|
||||
if n == 0 then
|
||||
table.remove(stack)
|
||||
h = h - 1
|
||||
|
||||
stack[h] = stack[h] + 1
|
||||
return a
|
||||
|
||||
elseif i <= n then
|
||||
|
||||
-- put i-th element as the last one
|
||||
a[n], a[i] = a[i], a[n]
|
||||
|
||||
-- generate all permutations of the other elements
|
||||
table.insert(stack, 1)
|
||||
|
||||
else
|
||||
|
||||
table.remove(stack)
|
||||
h = h - 1
|
||||
|
||||
n = n + 1
|
||||
i = stack[h]
|
||||
|
||||
-- restore i-th element
|
||||
a[n], a[i] = a[i], a[n]
|
||||
|
||||
stack[h] = stack[h] + 1
|
||||
end
|
||||
return iter() -- tail-call
|
||||
end
|
||||
|
||||
return iter
|
||||
end
|
||||
|
||||
|
||||
--- construct a table containing all the order-permutations of a list.
|
||||
-- @param a list-like table
|
||||
-- @return a table of tables
|
||||
-- @usage permute.order_table {1,2,3} --> {{2,3,1},{3,2,1},{3,1,2},{1,3,2},{2,1,3},{1,2,3}}
|
||||
function permute.order_table (a)
|
||||
assert_arg(1,a,'table')
|
||||
local res = {}
|
||||
for t in permute.iter(a) do
|
||||
append(res,copy(t))
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- an iterator over all permutations of the elements of the given lists.
|
||||
-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided (see `utils.pack`)
|
||||
-- @return an iterator which provides the next permutation as return values in the same order as the provided lists, preceeded by an index
|
||||
-- @usage
|
||||
-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety
|
||||
-- local bools = utils.pack(true, false)
|
||||
-- local iter = permute.list_iter(strs, bools)
|
||||
--
|
||||
-- print(iter()) --> 1, one, true
|
||||
-- print(iter()) --> 2, nil, true
|
||||
-- print(iter()) --> 3, three, true
|
||||
-- print(iter()) --> 4, one, false
|
||||
-- print(iter()) --> 5, nil, false
|
||||
-- print(iter()) --> 6, three, false
|
||||
function permute.list_iter(...)
|
||||
local elements = {...}
|
||||
local pointers = {}
|
||||
local sizes = {}
|
||||
local size = #elements
|
||||
for i, list in ipairs(elements) do
|
||||
assert_arg(i,list,'table')
|
||||
pointers[i] = 1
|
||||
sizes[i] = list.n or #list
|
||||
end
|
||||
local count = 0
|
||||
|
||||
return function()
|
||||
if pointers[size] > sizes[size] then return end -- we're done
|
||||
count = count + 1
|
||||
local r = { n = #elements }
|
||||
local cascade_up = true
|
||||
for i = 1, size do
|
||||
r[i] = elements[i][pointers[i]]
|
||||
if cascade_up then
|
||||
pointers[i] = pointers[i] + 1
|
||||
if pointers[i] <= sizes[i] then
|
||||
-- this list is not done yet, stop cascade
|
||||
cascade_up = false
|
||||
else
|
||||
-- this list is done
|
||||
if i ~= size then
|
||||
-- reset pointer
|
||||
pointers[i] = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return count, utils.unpack(r)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- construct a table containing all the permutations of a set of lists.
|
||||
-- @param ... list-like tables, they are nil-safe if a length-field `n` is provided
|
||||
-- @return a list of lists, the sub-lists have an 'n' field for nil-safety
|
||||
-- @usage
|
||||
-- local strs = utils.pack("one", nil, "three") -- adds an 'n' field for nil-safety
|
||||
-- local bools = utils.pack(true, false)
|
||||
-- local results = permute.list_table(strs, bools)
|
||||
-- -- results = {
|
||||
-- -- { "one, true, n = 2 }
|
||||
-- -- { nil, true, n = 2 },
|
||||
-- -- { "three, true, n = 2 },
|
||||
-- -- { "one, false, n = 2 },
|
||||
-- -- { nil, false, n = 2 },
|
||||
-- -- { "three", false, n = 2 },
|
||||
-- -- }
|
||||
function permute.list_table(...)
|
||||
local iter = permute.list_iter(...)
|
||||
local results = {}
|
||||
local i = 1
|
||||
while true do
|
||||
local values = utils.pack(iter())
|
||||
if values[1] == nil then return results end
|
||||
for i = 1, values.n do values[i] = values[i+1] end
|
||||
values.n = values.n - 1
|
||||
results[i] = values
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- backward compat, to be deprecated
|
||||
|
||||
--- deprecated.
|
||||
-- @param ...
|
||||
-- @see permute.order_iter
|
||||
function permute.iter(...)
|
||||
utils.raise_deprecation {
|
||||
source = "Penlight " .. utils._VERSION,
|
||||
message = "function 'iter' was renamed to 'order_iter'",
|
||||
version_removed = "2.0.0",
|
||||
deprecated_after = "1.9.2",
|
||||
}
|
||||
|
||||
return permute.order_iter(...)
|
||||
end
|
||||
|
||||
--- deprecated.
|
||||
-- @param ...
|
||||
-- @see permute.order_iter
|
||||
function permute.table(...)
|
||||
utils.raise_deprecation {
|
||||
source = "Penlight " .. utils._VERSION,
|
||||
message = "function 'table' was renamed to 'order_table'",
|
||||
version_removed = "2.0.0",
|
||||
deprecated_after = "1.9.2",
|
||||
}
|
||||
|
||||
return permute.order_table(...)
|
||||
end
|
||||
|
||||
return permute
|
438
Documentation/compiler/pl/pretty.lua
Normal file
438
Documentation/compiler/pl/pretty.lua
Normal file
|
@ -0,0 +1,438 @@
|
|||
--- Pretty-printing Lua tables.
|
||||
-- Also provides a sandboxed Lua table reader and
|
||||
-- a function to present large numbers in human-friendly format.
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.lexer`, `pl.stringx`, `debug`
|
||||
-- @module pl.pretty
|
||||
|
||||
local append = table.insert
|
||||
local concat = table.concat
|
||||
local mfloor, mhuge = math.floor, math.huge
|
||||
local mtype = math.type
|
||||
local utils = require 'pl.utils'
|
||||
local lexer = require 'pl.lexer'
|
||||
local debug = require 'debug'
|
||||
local quote_string = require'pl.stringx'.quote_string
|
||||
local assert_arg = utils.assert_arg
|
||||
|
||||
local original_tostring = tostring
|
||||
|
||||
-- Patch tostring to format numbers with better precision
|
||||
-- and to produce cross-platform results for
|
||||
-- infinite values and NaN.
|
||||
local function tostring(value)
|
||||
if type(value) ~= "number" then
|
||||
return original_tostring(value)
|
||||
elseif value ~= value then
|
||||
return "NaN"
|
||||
elseif value == mhuge then
|
||||
return "Inf"
|
||||
elseif value == -mhuge then
|
||||
return "-Inf"
|
||||
elseif (_VERSION ~= "Lua 5.3" or mtype(value) == "integer") and mfloor(value) == value then
|
||||
return ("%d"):format(value)
|
||||
else
|
||||
local res = ("%.14g"):format(value)
|
||||
if _VERSION == "Lua 5.3" and mtype(value) == "float" and not res:find("%.") then
|
||||
-- Number is internally a float but looks like an integer.
|
||||
-- Insert ".0" after first run of digits.
|
||||
res = res:gsub("%d+", "%0.0", 1)
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
local pretty = {}
|
||||
|
||||
local function save_global_env()
|
||||
local env = {}
|
||||
env.hook, env.mask, env.count = debug.gethook()
|
||||
|
||||
-- env.hook is "external hook" if is a C hook function
|
||||
if env.hook~="external hook" then
|
||||
debug.sethook()
|
||||
end
|
||||
|
||||
env.string_mt = getmetatable("")
|
||||
debug.setmetatable("", nil)
|
||||
return env
|
||||
end
|
||||
|
||||
local function restore_global_env(env)
|
||||
if env then
|
||||
debug.setmetatable("", env.string_mt)
|
||||
if env.hook~="external hook" then
|
||||
debug.sethook(env.hook, env.mask, env.count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Read a string representation of a Lua table.
|
||||
-- This function loads and runs the string as Lua code, but bails out
|
||||
-- if it contains a function definition.
|
||||
-- Loaded string is executed in an empty environment.
|
||||
-- @string s string to read in `{...}` format, possibly with some whitespace
|
||||
-- before or after the curly braces. A single line comment may be present
|
||||
-- at the beginning.
|
||||
-- @return a table in case of success.
|
||||
-- If loading the string failed, return `nil` and error message.
|
||||
-- If executing loaded string failed, return `nil` and the error it raised.
|
||||
function pretty.read(s)
|
||||
assert_arg(1,s,'string')
|
||||
if s:find '^%s*%-%-' then -- may start with a comment..
|
||||
s = s:gsub('%-%-.-\n','')
|
||||
end
|
||||
if not s:find '^%s*{' then return nil,"not a Lua table" end
|
||||
if s:find '[^\'"%w_]function[^\'"%w_]' then
|
||||
local tok = lexer.lua(s)
|
||||
for t,v in tok do
|
||||
if t == 'keyword' and v == 'function' then
|
||||
return nil,"cannot have functions in table definition"
|
||||
end
|
||||
end
|
||||
end
|
||||
s = 'return '..s
|
||||
local chunk,err = utils.load(s,'tbl','t',{})
|
||||
if not chunk then return nil,err end
|
||||
local global_env = save_global_env()
|
||||
local ok,ret = pcall(chunk)
|
||||
restore_global_env(global_env)
|
||||
if ok then return ret
|
||||
else
|
||||
return nil,ret
|
||||
end
|
||||
end
|
||||
|
||||
--- Read a Lua chunk.
|
||||
-- @string s Lua code.
|
||||
-- @tab[opt] env environment used to run the code, empty by default.
|
||||
-- @bool[opt] paranoid abort loading if any looping constructs a found in the code
|
||||
-- and disable string methods.
|
||||
-- @return the environment in case of success or `nil` and syntax or runtime error
|
||||
-- if something went wrong.
|
||||
function pretty.load (s, env, paranoid)
|
||||
env = env or {}
|
||||
if paranoid then
|
||||
local tok = lexer.lua(s)
|
||||
for t,v in tok do
|
||||
if t == 'keyword'
|
||||
and (v == 'for' or v == 'repeat' or v == 'function' or v == 'goto')
|
||||
then
|
||||
return nil,"looping not allowed"
|
||||
end
|
||||
end
|
||||
end
|
||||
local chunk,err = utils.load(s,'tbl','t',env)
|
||||
if not chunk then return nil,err end
|
||||
local global_env = paranoid and save_global_env()
|
||||
local ok,err = pcall(chunk)
|
||||
restore_global_env(global_env)
|
||||
if not ok then return nil,err end
|
||||
return env
|
||||
end
|
||||
|
||||
local function quote_if_necessary (v)
|
||||
if not v then return ''
|
||||
else
|
||||
--AAS
|
||||
if v:find ' ' then v = quote_string(v) end
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local keywords
|
||||
|
||||
local function is_identifier (s)
|
||||
return type(s) == 'string' and s:find('^[%a_][%w_]*$') and not keywords[s]
|
||||
end
|
||||
|
||||
local function quote (s)
|
||||
if type(s) == 'table' then
|
||||
return pretty.write(s,'')
|
||||
else
|
||||
--AAS
|
||||
return quote_string(s)-- ('%q'):format(tostring(s))
|
||||
end
|
||||
end
|
||||
|
||||
local function index (numkey,key)
|
||||
--AAS
|
||||
if not numkey then
|
||||
key = quote(key)
|
||||
key = key:find("^%[") and (" " .. key .. " ") or key
|
||||
end
|
||||
return '['..key..']'
|
||||
end
|
||||
|
||||
|
||||
--- Create a string representation of a Lua table.
|
||||
-- This function never fails, but may complain by returning an
|
||||
-- extra value. Normally puts out one item per line, using
|
||||
-- the provided indent; set the second parameter to an empty string
|
||||
-- if you want output on one line.
|
||||
--
|
||||
-- *NOTE:* this is NOT a serialization function, not a full blown
|
||||
-- debug function. Checkout out respectively the
|
||||
-- [serpent](https://github.com/pkulchenko/serpent)
|
||||
-- or [inspect](https://github.com/kikito/inspect.lua)
|
||||
-- Lua modules for that if you need them.
|
||||
-- @tab tbl Table to serialize to a string.
|
||||
-- @string[opt] space The indent to use.
|
||||
-- Defaults to two spaces; pass an empty string for no indentation.
|
||||
-- @bool[opt] not_clever Pass `true` for plain output, e.g `{['key']=1}`.
|
||||
-- Defaults to `false`.
|
||||
-- @return a string
|
||||
-- @return an optional error message
|
||||
function pretty.write (tbl,space,not_clever)
|
||||
if type(tbl) ~= 'table' then
|
||||
local res = tostring(tbl)
|
||||
if type(tbl) == 'string' then return quote(tbl) end
|
||||
return res, 'not a table'
|
||||
end
|
||||
if not keywords then
|
||||
keywords = lexer.get_keywords()
|
||||
end
|
||||
local set = ' = '
|
||||
if space == '' then set = '=' end
|
||||
space = space or ' '
|
||||
local lines = {}
|
||||
local line = ''
|
||||
local tables = {}
|
||||
|
||||
|
||||
local function put(s)
|
||||
if #s > 0 then
|
||||
line = line..s
|
||||
end
|
||||
end
|
||||
|
||||
local function putln (s)
|
||||
if #line > 0 then
|
||||
line = line..s
|
||||
append(lines,line)
|
||||
line = ''
|
||||
else
|
||||
append(lines,s)
|
||||
end
|
||||
end
|
||||
|
||||
local function eat_last_comma ()
|
||||
local n = #lines
|
||||
local lastch = lines[n]:sub(-1,-1)
|
||||
if lastch == ',' then
|
||||
lines[n] = lines[n]:sub(1,-2)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- safe versions for iterators since 5.3+ honors metamethods that can throw
|
||||
-- errors
|
||||
local ipairs = function(t)
|
||||
local i = 0
|
||||
local ok, v
|
||||
local getter = function() return t[i] end
|
||||
return function()
|
||||
i = i + 1
|
||||
ok, v = pcall(getter)
|
||||
if v == nil or not ok then return end
|
||||
return i, t[i]
|
||||
end
|
||||
end
|
||||
local pairs = function(t)
|
||||
local k, v, ok
|
||||
local getter = function() return next(t, k) end
|
||||
return function()
|
||||
ok, k, v = pcall(getter)
|
||||
if not ok then return end
|
||||
return k, v
|
||||
end
|
||||
end
|
||||
|
||||
local writeit
|
||||
writeit = function (t,oldindent,indent)
|
||||
local tp = type(t)
|
||||
if tp ~= 'string' and tp ~= 'table' then
|
||||
putln(quote_if_necessary(tostring(t))..',')
|
||||
elseif tp == 'string' then
|
||||
-- if t:find('\n') then
|
||||
-- putln('[[\n'..t..']],')
|
||||
-- else
|
||||
-- putln(quote(t)..',')
|
||||
-- end
|
||||
--AAS
|
||||
putln(quote_string(t) ..",")
|
||||
elseif tp == 'table' then
|
||||
if tables[t] then
|
||||
putln('<cycle>,')
|
||||
return
|
||||
end
|
||||
tables[t] = true
|
||||
local newindent = indent..space
|
||||
putln('{')
|
||||
local used = {}
|
||||
if not not_clever then
|
||||
for i,val in ipairs(t) do
|
||||
put(indent)
|
||||
writeit(val,indent,newindent)
|
||||
used[i] = true
|
||||
end
|
||||
end
|
||||
local ordered_keys = {}
|
||||
for k,v in pairs(t) do
|
||||
if type(k) ~= 'number' then
|
||||
ordered_keys[#ordered_keys + 1] = k
|
||||
end
|
||||
end
|
||||
table.sort(ordered_keys, function (a, b)
|
||||
if type(a) == type(b) then
|
||||
return tostring(a) < tostring(b)
|
||||
else
|
||||
return type(a) < type(b)
|
||||
end
|
||||
end)
|
||||
local function write_entry (key, val)
|
||||
local tkey = type(key)
|
||||
local numkey = tkey == 'number'
|
||||
if not_clever then
|
||||
key = tostring(key)
|
||||
put(indent..index(numkey,key)..set)
|
||||
writeit(val,indent,newindent)
|
||||
else
|
||||
if not numkey or not used[key] then -- non-array indices
|
||||
if tkey ~= 'string' then
|
||||
key = tostring(key)
|
||||
end
|
||||
if numkey or not is_identifier(key) then
|
||||
key = index(numkey,key)
|
||||
end
|
||||
put(indent..key..set)
|
||||
writeit(val,indent,newindent)
|
||||
end
|
||||
end
|
||||
end
|
||||
for i = 1, #ordered_keys do
|
||||
local key = ordered_keys[i]
|
||||
local val = t[key]
|
||||
write_entry(key, val)
|
||||
end
|
||||
for key,val in pairs(t) do
|
||||
if type(key) == 'number' then
|
||||
write_entry(key, val)
|
||||
end
|
||||
end
|
||||
tables[t] = nil
|
||||
eat_last_comma()
|
||||
putln(oldindent..'},')
|
||||
else
|
||||
putln(tostring(t)..',')
|
||||
end
|
||||
end
|
||||
writeit(tbl,'',space)
|
||||
eat_last_comma()
|
||||
return concat(lines,#space > 0 and '\n' or '')
|
||||
end
|
||||
|
||||
--- Dump a Lua table out to a file or stdout.
|
||||
-- @tab t The table to write to a file or stdout.
|
||||
-- @string[opt] filename File name to write too. Defaults to writing
|
||||
-- to stdout.
|
||||
function pretty.dump (t, filename)
|
||||
if not filename then
|
||||
print(pretty.write(t))
|
||||
return true
|
||||
else
|
||||
return utils.writefile(filename, pretty.write(t))
|
||||
end
|
||||
end
|
||||
|
||||
--- Dump a series of arguments to stdout for debug purposes.
|
||||
-- This function is attached to the module table `__call` method, to make it
|
||||
-- extra easy to access. So the full:
|
||||
--
|
||||
-- print(require("pl.pretty").write({...}))
|
||||
--
|
||||
-- Can be shortened to:
|
||||
--
|
||||
-- require"pl.pretty" (...)
|
||||
--
|
||||
-- Any `nil` entries will be printed as `"<nil>"` to make them explicit.
|
||||
-- @param ... the parameters to dump to stdout.
|
||||
-- @usage
|
||||
-- -- example debug output
|
||||
-- require"pl.pretty" ("hello", nil, "world", { bye = "world", true} )
|
||||
--
|
||||
-- -- output:
|
||||
-- {
|
||||
-- ["arg 1"] = "hello",
|
||||
-- ["arg 2"] = "<nil>",
|
||||
-- ["arg 3"] = "world",
|
||||
-- ["arg 4"] = {
|
||||
-- true,
|
||||
-- bye = "world"
|
||||
-- }
|
||||
-- }
|
||||
function pretty.debug(...)
|
||||
local n = select("#", ...)
|
||||
local t = { ... }
|
||||
for i = 1, n do
|
||||
local value = t[i]
|
||||
if value == nil then
|
||||
value = "<nil>"
|
||||
end
|
||||
t[i] = nil
|
||||
t["arg " .. i] = value
|
||||
end
|
||||
|
||||
print(pretty.write(t))
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local memp,nump = {'B','KiB','MiB','GiB'},{'','K','M','B'}
|
||||
|
||||
local function comma (val)
|
||||
local thou = math.floor(val/1000)
|
||||
if thou > 0 then return comma(thou)..','.. tostring(val % 1000)
|
||||
else return tostring(val) end
|
||||
end
|
||||
|
||||
--- Format large numbers nicely for human consumption.
|
||||
-- @number num a number.
|
||||
-- @string[opt] kind one of `'M'` (memory in `KiB`, `MiB`, etc.),
|
||||
-- `'N'` (postfixes are `'K'`, `'M'` and `'B'`),
|
||||
-- or `'T'` (use commas as thousands separator), `'N'` by default.
|
||||
-- @int[opt] prec number of digits to use for `'M'` and `'N'`, `1` by default.
|
||||
function pretty.number (num,kind,prec)
|
||||
local fmt = '%.'..(prec or 1)..'f%s'
|
||||
if kind == 'T' then
|
||||
return comma(num)
|
||||
else
|
||||
local postfixes, fact
|
||||
if kind == 'M' then
|
||||
fact = 1024
|
||||
postfixes = memp
|
||||
else
|
||||
fact = 1000
|
||||
postfixes = nump
|
||||
end
|
||||
local div = fact
|
||||
local k = 1
|
||||
while num >= div and k <= #postfixes do
|
||||
div = div * fact
|
||||
k = k + 1
|
||||
end
|
||||
div = div / fact
|
||||
if k > #postfixes then k = k - 1; div = div/fact end
|
||||
if k > 1 then
|
||||
return fmt:format(num/div,postfixes[k] or 'duh')
|
||||
else
|
||||
return num..postfixes[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return setmetatable(pretty, {
|
||||
__call = function(self, ...)
|
||||
return self.debug(...)
|
||||
end
|
||||
})
|
544
Documentation/compiler/pl/seq.lua
Normal file
544
Documentation/compiler/pl/seq.lua
Normal file
|
@ -0,0 +1,544 @@
|
|||
--- Manipulating iterators as sequences.
|
||||
-- See @{07-functional.md.Sequences|The Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.types`, `debug`
|
||||
-- @module pl.seq
|
||||
|
||||
local next,assert,pairs,tonumber,type,setmetatable = next,assert,pairs,tonumber,type,setmetatable
|
||||
local strfind,format = string.find,string.format
|
||||
local mrandom = math.random
|
||||
local tsort,tappend = table.sort,table.insert
|
||||
local io = io
|
||||
local utils = require 'pl.utils'
|
||||
local callable = require 'pl.types'.is_callable
|
||||
local function_arg = utils.function_arg
|
||||
local assert_arg = utils.assert_arg
|
||||
local debug = require 'debug'
|
||||
|
||||
local seq = {}
|
||||
|
||||
-- given a number, return a function(y) which returns true if y > x
|
||||
-- @param x a number
|
||||
function seq.greater_than(x)
|
||||
return function(v)
|
||||
return tonumber(v) > x
|
||||
end
|
||||
end
|
||||
|
||||
-- given a number, returns a function(y) which returns true if y < x
|
||||
-- @param x a number
|
||||
function seq.less_than(x)
|
||||
return function(v)
|
||||
return tonumber(v) < x
|
||||
end
|
||||
end
|
||||
|
||||
-- given any value, return a function(y) which returns true if y == x
|
||||
-- @param x a value
|
||||
function seq.equal_to(x)
|
||||
if type(x) == "number" then
|
||||
return function(v)
|
||||
return tonumber(v) == x
|
||||
end
|
||||
else
|
||||
return function(v)
|
||||
return v == x
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- given a string, return a function(y) which matches y against the string.
|
||||
-- @param s a string
|
||||
function seq.matching(s)
|
||||
return function(v)
|
||||
return strfind(v,s)
|
||||
end
|
||||
end
|
||||
|
||||
local nexti
|
||||
|
||||
--- sequence adaptor for a table. Note that if any generic function is
|
||||
-- passed a table, it will automatically use seq.list()
|
||||
-- @param t a list-like table
|
||||
-- @usage sum(list(t)) is the sum of all elements of t
|
||||
-- @usage for x in list(t) do...end
|
||||
function seq.list(t)
|
||||
assert_arg(1,t,'table')
|
||||
if not nexti then
|
||||
nexti = ipairs{}
|
||||
end
|
||||
local key,value = 0
|
||||
return function()
|
||||
key,value = nexti(t,key)
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
--- return the keys of the table.
|
||||
-- @param t an arbitrary table
|
||||
-- @return iterator over keys
|
||||
function seq.keys(t)
|
||||
assert_arg(1,t,'table')
|
||||
local key
|
||||
return function()
|
||||
key = next(t,key)
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
local list = seq.list
|
||||
local function default_iter(iter)
|
||||
if type(iter) == 'table' then return list(iter)
|
||||
else return iter end
|
||||
end
|
||||
|
||||
seq.iter = default_iter
|
||||
|
||||
--- create an iterator over a numerical range. Like the standard Python function xrange.
|
||||
-- @param start a number
|
||||
-- @param finish a number greater than start
|
||||
function seq.range(start,finish)
|
||||
local i = start - 1
|
||||
return function()
|
||||
i = i + 1
|
||||
if i > finish then return nil
|
||||
else return i end
|
||||
end
|
||||
end
|
||||
|
||||
-- count the number of elements in the sequence which satisfy the predicate
|
||||
-- @param iter a sequence
|
||||
-- @param condn a predicate function (must return either true or false)
|
||||
-- @param optional argument to be passed to predicate as second argument.
|
||||
-- @return count
|
||||
function seq.count(iter,condn,arg)
|
||||
local i = 0
|
||||
seq.foreach(iter,function(val)
|
||||
if condn(val,arg) then i = i + 1 end
|
||||
end)
|
||||
return i
|
||||
end
|
||||
|
||||
--- return the minimum and the maximum value of the sequence.
|
||||
-- @param iter a sequence
|
||||
-- @return minimum value
|
||||
-- @return maximum value
|
||||
function seq.minmax(iter)
|
||||
local vmin,vmax = 1e70,-1e70
|
||||
for v in default_iter(iter) do
|
||||
v = tonumber(v)
|
||||
if v < vmin then vmin = v end
|
||||
if v > vmax then vmax = v end
|
||||
end
|
||||
return vmin,vmax
|
||||
end
|
||||
|
||||
--- return the sum and element count of the sequence.
|
||||
-- @param iter a sequence
|
||||
-- @param fn an optional function to apply to the values
|
||||
function seq.sum(iter,fn)
|
||||
local s = 0
|
||||
local i = 0
|
||||
for v in default_iter(iter) do
|
||||
if fn then v = fn(v) end
|
||||
s = s + v
|
||||
i = i + 1
|
||||
end
|
||||
return s,i
|
||||
end
|
||||
|
||||
--- create a table from the sequence. (This will make the result a List.)
|
||||
-- @param iter a sequence
|
||||
-- @return a List
|
||||
-- @usage copy(list(ls)) is equal to ls
|
||||
-- @usage copy(list {1,2,3}) == List{1,2,3}
|
||||
function seq.copy(iter)
|
||||
local res,k = {},1
|
||||
for v in default_iter(iter) do
|
||||
res[k] = v
|
||||
k = k + 1
|
||||
end
|
||||
setmetatable(res, require('pl.List'))
|
||||
return res
|
||||
end
|
||||
|
||||
--- create a table of pairs from the double-valued sequence.
|
||||
-- @param iter a double-valued sequence
|
||||
-- @param i1 used to capture extra iterator values
|
||||
-- @param i2 as with pairs & ipairs
|
||||
-- @usage copy2(ipairs{10,20,30}) == {{1,10},{2,20},{3,30}}
|
||||
-- @return a list-like table
|
||||
function seq.copy2 (iter,i1,i2)
|
||||
local res,k = {},1
|
||||
for v1,v2 in iter,i1,i2 do
|
||||
res[k] = {v1,v2}
|
||||
k = k + 1
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- create a table of 'tuples' from a multi-valued sequence.
|
||||
-- A generalization of copy2 above
|
||||
-- @param iter a multiple-valued sequence
|
||||
-- @return a list-like table
|
||||
function seq.copy_tuples (iter)
|
||||
iter = default_iter(iter)
|
||||
local res = {}
|
||||
local row = {iter()}
|
||||
while #row > 0 do
|
||||
tappend(res,row)
|
||||
row = {iter()}
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- return an iterator of random numbers.
|
||||
-- @param n the length of the sequence
|
||||
-- @param l same as the first optional argument to math.random
|
||||
-- @param u same as the second optional argument to math.random
|
||||
-- @return a sequence
|
||||
function seq.random(n,l,u)
|
||||
local rand
|
||||
assert(type(n) == 'number')
|
||||
if u then
|
||||
rand = function() return mrandom(l,u) end
|
||||
elseif l then
|
||||
rand = function() return mrandom(l) end
|
||||
else
|
||||
rand = mrandom
|
||||
end
|
||||
|
||||
return function()
|
||||
if n == 0 then return nil
|
||||
else
|
||||
n = n - 1
|
||||
return rand()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- return an iterator to the sorted elements of a sequence.
|
||||
-- @param iter a sequence
|
||||
-- @param comp an optional comparison function (comp(x,y) is true if x < y)
|
||||
function seq.sort(iter,comp)
|
||||
local t = seq.copy(iter)
|
||||
tsort(t,comp)
|
||||
return list(t)
|
||||
end
|
||||
|
||||
--- return an iterator which returns elements of two sequences.
|
||||
-- @param iter1 a sequence
|
||||
-- @param iter2 a sequence
|
||||
-- @usage for x,y in seq.zip(ls1,ls2) do....end
|
||||
function seq.zip(iter1,iter2)
|
||||
iter1 = default_iter(iter1)
|
||||
iter2 = default_iter(iter2)
|
||||
return function()
|
||||
return iter1(),iter2()
|
||||
end
|
||||
end
|
||||
|
||||
--- Makes a table where the key/values are the values and value counts of the sequence.
|
||||
-- This version works with 'hashable' values like strings and numbers.
|
||||
-- `pl.tablex.count_map` is more general.
|
||||
-- @param iter a sequence
|
||||
-- @return a map-like table
|
||||
-- @return a table
|
||||
-- @see pl.tablex.count_map
|
||||
function seq.count_map(iter)
|
||||
local t = {}
|
||||
local v
|
||||
for s in default_iter(iter) do
|
||||
v = t[s]
|
||||
if v then t[s] = v + 1
|
||||
else t[s] = 1 end
|
||||
end
|
||||
return setmetatable(t, require('pl.Map'))
|
||||
end
|
||||
|
||||
-- given a sequence, return all the unique values in that sequence.
|
||||
-- @param iter a sequence
|
||||
-- @param returns_table true if we return a table, not a sequence
|
||||
-- @return a sequence or a table; defaults to a sequence.
|
||||
function seq.unique(iter,returns_table)
|
||||
local t = seq.count_map(iter)
|
||||
local res,k = {},1
|
||||
for key in pairs(t) do res[k] = key; k = k + 1 end
|
||||
table.sort(res)
|
||||
if returns_table then
|
||||
return res
|
||||
else
|
||||
return list(res)
|
||||
end
|
||||
end
|
||||
|
||||
--- print out a sequence iter with a separator.
|
||||
-- @param iter a sequence
|
||||
-- @param sep the separator (default space)
|
||||
-- @param nfields maximum number of values per line (default 7)
|
||||
-- @param fmt optional format function for each value
|
||||
function seq.printall(iter,sep,nfields,fmt)
|
||||
local write = io.write
|
||||
if not sep then sep = ' ' end
|
||||
if not nfields then
|
||||
if sep == '\n' then nfields = 1e30
|
||||
else nfields = 7 end
|
||||
end
|
||||
if fmt then
|
||||
local fstr = fmt
|
||||
fmt = function(v) return format(fstr,v) end
|
||||
end
|
||||
local k = 1
|
||||
for v in default_iter(iter) do
|
||||
if fmt then v = fmt(v) end
|
||||
if k < nfields then
|
||||
write(v,sep)
|
||||
k = k + 1
|
||||
else
|
||||
write(v,'\n')
|
||||
k = 1
|
||||
end
|
||||
end
|
||||
write '\n'
|
||||
end
|
||||
|
||||
-- return an iterator running over every element of two sequences (concatenation).
|
||||
-- @param iter1 a sequence
|
||||
-- @param iter2 a sequence
|
||||
function seq.splice(iter1,iter2)
|
||||
iter1 = default_iter(iter1)
|
||||
iter2 = default_iter(iter2)
|
||||
local iter = iter1
|
||||
return function()
|
||||
local ret = iter()
|
||||
if ret == nil then
|
||||
if iter == iter1 then
|
||||
iter = iter2
|
||||
return iter()
|
||||
else return nil end
|
||||
else
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- return a sequence where every element of a sequence has been transformed
|
||||
-- by a function. If you don't supply an argument, then the function will
|
||||
-- receive both values of a double-valued sequence, otherwise behaves rather like
|
||||
-- tablex.map.
|
||||
-- @param fn a function to apply to elements; may take two arguments
|
||||
-- @param iter a sequence of one or two values
|
||||
-- @param arg optional argument to pass to function.
|
||||
function seq.map(fn,iter,arg)
|
||||
fn = function_arg(1,fn)
|
||||
iter = default_iter(iter)
|
||||
return function()
|
||||
local v1,v2 = iter()
|
||||
if v1 == nil then return nil end
|
||||
return fn(v1,arg or v2) or false
|
||||
end
|
||||
end
|
||||
|
||||
--- filter a sequence using a predicate function.
|
||||
-- @param iter a sequence of one or two values
|
||||
-- @param pred a boolean function; may take two arguments
|
||||
-- @param arg optional argument to pass to function.
|
||||
function seq.filter (iter,pred,arg)
|
||||
pred = function_arg(2,pred)
|
||||
return function ()
|
||||
local v1,v2
|
||||
while true do
|
||||
v1,v2 = iter()
|
||||
if v1 == nil then return nil end
|
||||
if pred(v1,arg or v2) then return v1,v2 end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- 'reduce' a sequence using a binary function.
|
||||
-- @func fn a function of two arguments
|
||||
-- @param iter a sequence
|
||||
-- @param initval optional initial value
|
||||
-- @usage seq.reduce(operator.add,seq.list{1,2,3,4}) == 10
|
||||
-- @usage seq.reduce('-',{1,2,3,4,5}) == -13
|
||||
function seq.reduce (fn,iter,initval)
|
||||
fn = function_arg(1,fn)
|
||||
iter = default_iter(iter)
|
||||
local val = initval or iter()
|
||||
if val == nil then return nil end
|
||||
for v in iter do
|
||||
val = fn(val,v)
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
--- take the first n values from the sequence.
|
||||
-- @param iter a sequence of one or two values
|
||||
-- @param n number of items to take
|
||||
-- @return a sequence of at most n items
|
||||
function seq.take (iter,n)
|
||||
iter = default_iter(iter)
|
||||
return function()
|
||||
if n < 1 then return end
|
||||
local val1,val2 = iter()
|
||||
if not val1 then return end
|
||||
n = n - 1
|
||||
return val1,val2
|
||||
end
|
||||
end
|
||||
|
||||
--- skip the first n values of a sequence
|
||||
-- @param iter a sequence of one or more values
|
||||
-- @param n number of items to skip
|
||||
function seq.skip (iter,n)
|
||||
n = n or 1
|
||||
for i = 1,n do
|
||||
if iter() == nil then return list{} end
|
||||
end
|
||||
return iter
|
||||
end
|
||||
|
||||
--- a sequence with a sequence count and the original value.
|
||||
-- enum(copy(ls)) is a roundabout way of saying ipairs(ls).
|
||||
-- @param iter a single or double valued sequence
|
||||
-- @return sequence of (i,v), i = 1..n and v is from iter.
|
||||
function seq.enum (iter)
|
||||
local i = 0
|
||||
iter = default_iter(iter)
|
||||
return function ()
|
||||
local val1,val2 = iter()
|
||||
if not val1 then return end
|
||||
i = i + 1
|
||||
return i,val1,val2
|
||||
end
|
||||
end
|
||||
|
||||
--- map using a named method over a sequence.
|
||||
-- @param iter a sequence
|
||||
-- @param name the method name
|
||||
-- @param arg1 optional first extra argument
|
||||
-- @param arg2 optional second extra argument
|
||||
function seq.mapmethod (iter,name,arg1,arg2)
|
||||
iter = default_iter(iter)
|
||||
return function()
|
||||
local val = iter()
|
||||
if not val then return end
|
||||
local fn = val[name]
|
||||
if not fn then error(type(val).." does not have method "..name) end
|
||||
return fn(val,arg1,arg2)
|
||||
end
|
||||
end
|
||||
|
||||
--- a sequence of (last,current) values from another sequence.
|
||||
-- This will return S(i-1),S(i) if given S(i)
|
||||
-- @param iter a sequence
|
||||
function seq.last (iter)
|
||||
iter = default_iter(iter)
|
||||
local val, l = iter(), nil
|
||||
if val == nil then return list{} end
|
||||
return function ()
|
||||
val,l = iter(),val
|
||||
if val == nil then return nil end
|
||||
return val,l
|
||||
end
|
||||
end
|
||||
|
||||
--- call the function on each element of the sequence.
|
||||
-- @param iter a sequence with up to 3 values
|
||||
-- @param fn a function
|
||||
function seq.foreach(iter,fn)
|
||||
fn = function_arg(2,fn)
|
||||
for i1,i2,i3 in default_iter(iter) do fn(i1,i2,i3) end
|
||||
end
|
||||
|
||||
---------------------- Sequence Adapters ---------------------
|
||||
|
||||
local SMT
|
||||
|
||||
local function SW (iter,...)
|
||||
if callable(iter) then
|
||||
return setmetatable({iter=iter},SMT)
|
||||
else
|
||||
return iter,...
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- can't directly look these up in seq because of the wrong argument order...
|
||||
local map,reduce,mapmethod = seq.map, seq.reduce, seq.mapmethod
|
||||
local overrides = {
|
||||
map = function(self,fun,arg)
|
||||
return map(fun,self,arg)
|
||||
end,
|
||||
reduce = function(self,fun,initval)
|
||||
return reduce(fun,self,initval)
|
||||
end
|
||||
}
|
||||
|
||||
SMT = {
|
||||
__index = function (tbl,key)
|
||||
local fn = overrides[key] or seq[key]
|
||||
if fn then
|
||||
return function(sw,...) return SW(fn(sw.iter,...)) end
|
||||
else
|
||||
return function(sw,...) return SW(mapmethod(sw.iter,key,...)) end
|
||||
end
|
||||
end,
|
||||
__call = function (sw)
|
||||
return sw.iter()
|
||||
end,
|
||||
}
|
||||
|
||||
setmetatable(seq,{
|
||||
__call = function(tbl,iter,extra)
|
||||
if not callable(iter) then
|
||||
if type(iter) == 'table' then iter = seq.list(iter)
|
||||
else return iter
|
||||
end
|
||||
end
|
||||
if extra then
|
||||
return setmetatable({iter=function()
|
||||
return iter(extra)
|
||||
end},SMT)
|
||||
else
|
||||
return setmetatable({iter=iter},SMT)
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
--- create a wrapped iterator over all lines in the file.
|
||||
-- @param f either a filename, file-like object, or 'STDIN' (for standard input)
|
||||
-- @param ... for Lua 5.2 only, optional format specifiers, as in `io.read`.
|
||||
-- @return a sequence wrapper
|
||||
function seq.lines (f,...)
|
||||
local iter,obj
|
||||
if f == 'STDIN' then
|
||||
f = io.stdin
|
||||
elseif type(f) == 'string' then
|
||||
iter,obj = io.lines(f,...)
|
||||
elseif not f.read then
|
||||
error("Pass either a string or a file-like object",2)
|
||||
end
|
||||
if not iter then
|
||||
iter,obj = f:lines(...)
|
||||
end
|
||||
if obj then -- LuaJIT version returns a function operating on a file
|
||||
local lines,file = iter,obj
|
||||
iter = function() return lines(file) end
|
||||
end
|
||||
return SW(iter)
|
||||
end
|
||||
|
||||
function seq.import ()
|
||||
debug.setmetatable(function() end,{
|
||||
__index = function(tbl,key)
|
||||
local s = overrides[key] or seq[key]
|
||||
if s then return s
|
||||
else
|
||||
return function(s,...) return seq.mapmethod(s,key,...) end
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
return seq
|
337
Documentation/compiler/pl/sip.lua
Normal file
337
Documentation/compiler/pl/sip.lua
Normal file
|
@ -0,0 +1,337 @@
|
|||
--- Simple Input Patterns (SIP).
|
||||
-- SIP patterns start with '$', then a
|
||||
-- one-letter type, and then an optional variable in curly braces.
|
||||
--
|
||||
-- sip.match('$v=$q','name="dolly"',res)
|
||||
-- ==> res=={'name','dolly'}
|
||||
-- sip.match('($q{first},$q{second})','("john","smith")',res)
|
||||
-- ==> res=={second='smith',first='john'}
|
||||
--
|
||||
-- Type names:
|
||||
--
|
||||
-- v identifier
|
||||
-- i integer
|
||||
-- f floating-point
|
||||
-- q quoted string
|
||||
-- ([{< match up to closing bracket
|
||||
--
|
||||
-- See @{08-additional.md.Simple_Input_Patterns|the Guide}
|
||||
--
|
||||
-- @module pl.sip
|
||||
|
||||
local loadstring = rawget(_G,'loadstring') or load
|
||||
local unpack = rawget(_G,'unpack') or rawget(table,'unpack')
|
||||
|
||||
local append,concat = table.insert,table.concat
|
||||
local ipairs,type = ipairs,type
|
||||
local io,_G = io,_G
|
||||
local print,rawget = print,rawget
|
||||
|
||||
local patterns = {
|
||||
FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*',
|
||||
INTEGER = '[+%-%d]%d*',
|
||||
IDEN = '[%a_][%w_]*',
|
||||
OPTION = '[%a_][%w_%-]*',
|
||||
}
|
||||
|
||||
local function assert_arg(idx,val,tp)
|
||||
if type(val) ~= tp then
|
||||
error("argument "..idx.." must be "..tp, 2)
|
||||
end
|
||||
end
|
||||
|
||||
local sip = {}
|
||||
|
||||
local brackets = {['<'] = '>', ['('] = ')', ['{'] = '}', ['['] = ']' }
|
||||
local stdclasses = {a=1,c=0,d=1,l=1,p=0,u=1,w=1,x=1,s=0}
|
||||
|
||||
local function group(s)
|
||||
return '('..s..')'
|
||||
end
|
||||
|
||||
-- escape all magic characters except $, which has special meaning
|
||||
-- Also, un-escape any characters after $, so $( and $[ passes through as is.
|
||||
local function escape (spec)
|
||||
return (spec:gsub('[%-%.%+%[%]%(%)%^%%%?%*]','%%%0'):gsub('%$%%(%S)','$%1'))
|
||||
end
|
||||
|
||||
-- Most spaces within patterns can match zero or more spaces.
|
||||
-- Spaces between alphanumeric characters or underscores or between
|
||||
-- patterns that can match these characters, however, must match at least
|
||||
-- one space. Otherwise '$v $v' would match 'abcd' as {'abc', 'd'}.
|
||||
-- This function replaces continuous spaces within a pattern with either
|
||||
-- '%s*' or '%s+' according to this rule. The pattern has already
|
||||
-- been stripped of pattern names by now.
|
||||
local function compress_spaces(patt)
|
||||
return (patt:gsub("()%s+()", function(i1, i2)
|
||||
local before = patt:sub(i1 - 2, i1 - 1)
|
||||
if before:match('%$[vifadxlu]') or before:match('^[^%$]?[%w_]$') then
|
||||
local after = patt:sub(i2, i2 + 1)
|
||||
if after:match('%$[vifadxlu]') or after:match('^[%w_]') then
|
||||
return '%s+'
|
||||
end
|
||||
end
|
||||
return '%s*'
|
||||
end))
|
||||
end
|
||||
|
||||
local pattern_map = {
|
||||
v = group(patterns.IDEN),
|
||||
i = group(patterns.INTEGER),
|
||||
f = group(patterns.FLOAT),
|
||||
o = group(patterns.OPTION),
|
||||
r = '(%S.*)',
|
||||
p = '([%a]?[:]?[\\/%.%w_]+)'
|
||||
}
|
||||
|
||||
function sip.custom_pattern(flag,patt)
|
||||
pattern_map[flag] = patt
|
||||
end
|
||||
|
||||
--- convert a SIP pattern into the equivalent Lua string pattern.
|
||||
-- @param spec a SIP pattern
|
||||
-- @param options a table; only the <code>at_start</code> field is
|
||||
-- currently meaningful and ensures that the pattern is anchored
|
||||
-- at the start of the string.
|
||||
-- @return a Lua string pattern.
|
||||
function sip.create_pattern (spec,options)
|
||||
assert_arg(1,spec,'string')
|
||||
local fieldnames,fieldtypes = {},{}
|
||||
|
||||
if type(spec) == 'string' then
|
||||
spec = escape(spec)
|
||||
else
|
||||
local res = {}
|
||||
for i,s in ipairs(spec) do
|
||||
res[i] = escape(s)
|
||||
end
|
||||
spec = concat(res,'.-')
|
||||
end
|
||||
|
||||
local kount = 1
|
||||
|
||||
local function addfield (name,type)
|
||||
name = name or kount
|
||||
append(fieldnames,name)
|
||||
fieldtypes[name] = type
|
||||
kount = kount + 1
|
||||
end
|
||||
|
||||
local named_vars = spec:find('{%a+}')
|
||||
|
||||
if options and options.at_start then
|
||||
spec = '^'..spec
|
||||
end
|
||||
if spec:sub(-1,-1) == '$' then
|
||||
spec = spec:sub(1,-2)..'$r'
|
||||
if named_vars then spec = spec..'{rest}' end
|
||||
end
|
||||
|
||||
local names
|
||||
|
||||
if named_vars then
|
||||
names = {}
|
||||
spec = spec:gsub('{(%a+)}',function(name)
|
||||
append(names,name)
|
||||
return ''
|
||||
end)
|
||||
end
|
||||
spec = compress_spaces(spec)
|
||||
|
||||
local k = 1
|
||||
local err
|
||||
local r = (spec:gsub('%$%S',function(s)
|
||||
local type,name
|
||||
type = s:sub(2,2)
|
||||
if names then name = names[k]; k=k+1 end
|
||||
-- this kludge is necessary because %q generates two matches, and
|
||||
-- we want to ignore the first. Not a problem for named captures.
|
||||
if not names and type == 'q' then
|
||||
addfield(nil,'Q')
|
||||
else
|
||||
addfield(name,type)
|
||||
end
|
||||
local res
|
||||
if pattern_map[type] then
|
||||
res = pattern_map[type]
|
||||
elseif type == 'q' then
|
||||
-- some Lua pattern matching voodoo; we want to match '...' as
|
||||
-- well as "...", and can use the fact that %n will match a
|
||||
-- previous capture. Adding the extra field above comes from needing
|
||||
-- to accommodate the extra spurious match (which is either ' or ")
|
||||
addfield(name,type)
|
||||
res = '(["\'])(.-)%'..(kount-2)
|
||||
else
|
||||
local endbracket = brackets[type]
|
||||
if endbracket then
|
||||
res = '(%b'..type..endbracket..')'
|
||||
elseif stdclasses[type] or stdclasses[type:lower()] then
|
||||
res = '(%'..type..'+)'
|
||||
else
|
||||
err = "unknown format type or character class"
|
||||
end
|
||||
end
|
||||
return res
|
||||
end))
|
||||
|
||||
if err then
|
||||
return nil,err
|
||||
else
|
||||
return r,fieldnames,fieldtypes
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function tnumber (s)
|
||||
return s == 'd' or s == 'i' or s == 'f'
|
||||
end
|
||||
|
||||
function sip.create_spec_fun(spec,options)
|
||||
local fieldtypes,fieldnames
|
||||
local ls = {}
|
||||
spec,fieldnames,fieldtypes = sip.create_pattern(spec,options)
|
||||
if not spec then return spec,fieldnames end
|
||||
local named_vars = type(fieldnames[1]) == 'string'
|
||||
for i = 1,#fieldnames do
|
||||
append(ls,'mm'..i)
|
||||
end
|
||||
ls[1] = ls[1] or "mm1" -- behave correctly if there are no patterns
|
||||
local fun = ('return (function(s,res)\n\tlocal %s = s:match(%q)\n'):format(concat(ls,','),spec)
|
||||
fun = fun..'\tif not mm1 then return false end\n'
|
||||
local k=1
|
||||
for i,f in ipairs(fieldnames) do
|
||||
if f ~= '_' then
|
||||
local var = 'mm'..i
|
||||
if tnumber(fieldtypes[f]) then
|
||||
var = 'tonumber('..var..')'
|
||||
elseif brackets[fieldtypes[f]] then
|
||||
var = var..':sub(2,-2)'
|
||||
end
|
||||
if named_vars then
|
||||
fun = ('%s\tres.%s = %s\n'):format(fun,f,var)
|
||||
else
|
||||
if fieldtypes[f] ~= 'Q' then -- we skip the string-delim capture
|
||||
fun = ('%s\tres[%d] = %s\n'):format(fun,k,var)
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return fun..'\treturn true\nend)\n', named_vars
|
||||
end
|
||||
|
||||
--- convert a SIP pattern into a matching function.
|
||||
-- The returned function takes two arguments, the line and an empty table.
|
||||
-- If the line matched the pattern, then this function returns true
|
||||
-- and the table is filled with field-value pairs.
|
||||
-- @param spec a SIP pattern
|
||||
-- @param options optional table; {at_start=true} ensures that the pattern
|
||||
-- is anchored at the start of the string.
|
||||
-- @return a function if successful, or nil,error
|
||||
function sip.compile(spec,options)
|
||||
assert_arg(1,spec,'string')
|
||||
local fun,names = sip.create_spec_fun(spec,options)
|
||||
if not fun then return nil,names end
|
||||
if rawget(_G,'_DEBUG') then print(fun) end
|
||||
local chunk,err = loadstring(fun,'tmp')
|
||||
if err then return nil,err end
|
||||
return chunk(),names
|
||||
end
|
||||
|
||||
local cache = {}
|
||||
|
||||
--- match a SIP pattern against a string.
|
||||
-- @param spec a SIP pattern
|
||||
-- @param line a string
|
||||
-- @param res a table to receive values
|
||||
-- @param options (optional) option table
|
||||
-- @return true or false
|
||||
function sip.match (spec,line,res,options)
|
||||
assert_arg(1,spec,'string')
|
||||
assert_arg(2,line,'string')
|
||||
assert_arg(3,res,'table')
|
||||
if not cache[spec] then
|
||||
cache[spec] = sip.compile(spec,options)
|
||||
end
|
||||
return cache[spec](line,res)
|
||||
end
|
||||
|
||||
--- match a SIP pattern against the start of a string.
|
||||
-- @param spec a SIP pattern
|
||||
-- @param line a string
|
||||
-- @param res a table to receive values
|
||||
-- @return true or false
|
||||
function sip.match_at_start (spec,line,res)
|
||||
return sip.match(spec,line,res,{at_start=true})
|
||||
end
|
||||
|
||||
--- given a pattern and a file object, return an iterator over the results
|
||||
-- @param spec a SIP pattern
|
||||
-- @param f a file-like object.
|
||||
function sip.fields (spec,f)
|
||||
assert_arg(1,spec,'string')
|
||||
if not f then return nil,"no file object" end
|
||||
local fun,err = sip.compile(spec)
|
||||
if not fun then return nil,err end
|
||||
local res = {}
|
||||
return function()
|
||||
while true do
|
||||
local line = f:read()
|
||||
if not line then return end
|
||||
if fun(line,res) then
|
||||
local values = res
|
||||
res = {}
|
||||
return unpack(values)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local read_patterns = {}
|
||||
|
||||
--- register a match which will be used in the read function.
|
||||
-- @string spec a SIP pattern
|
||||
-- @func fun a function to be called with the results of the match
|
||||
-- @see read
|
||||
function sip.pattern (spec,fun)
|
||||
assert_arg(1,spec,'string')
|
||||
local pat,named = sip.compile(spec)
|
||||
append(read_patterns,{pat=pat,named=named,callback=fun})
|
||||
end
|
||||
|
||||
--- enter a loop which applies all registered matches to the input file.
|
||||
-- @param f a file-like object
|
||||
-- @array matches optional list of `{spec,fun}` pairs, as for `pattern` above.
|
||||
function sip.read (f,matches)
|
||||
local owned,err
|
||||
if not f then return nil,"no file object" end
|
||||
if type(f) == 'string' then
|
||||
f,err = io.open(f)
|
||||
if not f then return nil,err end
|
||||
owned = true
|
||||
end
|
||||
if matches then
|
||||
for _,p in ipairs(matches) do
|
||||
sip.pattern(p[1],p[2])
|
||||
end
|
||||
end
|
||||
local res = {}
|
||||
for line in f:lines() do
|
||||
for _,item in ipairs(read_patterns) do
|
||||
if item.pat(line,res) then
|
||||
if item.callback then
|
||||
if item.named then
|
||||
item.callback(res)
|
||||
else
|
||||
item.callback(unpack(res))
|
||||
end
|
||||
end
|
||||
res = {}
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if owned then f:close() end
|
||||
end
|
||||
|
||||
return sip
|
138
Documentation/compiler/pl/strict.lua
Normal file
138
Documentation/compiler/pl/strict.lua
Normal file
|
@ -0,0 +1,138 @@
|
|||
--- Checks uses of undeclared global variables.
|
||||
-- All global variables must be 'declared' through a regular assignment
|
||||
-- (even assigning `nil` will do) in a main chunk before being used
|
||||
-- anywhere or assigned to inside a function. Existing metatables `__newindex` and `__index`
|
||||
-- metamethods are respected.
|
||||
--
|
||||
-- You can set any table to have strict behaviour using `strict.module`. Creating a new
|
||||
-- module with `strict.closed_module` makes the module immune to monkey-patching, if
|
||||
-- you don't wish to encourage monkey business.
|
||||
--
|
||||
-- If the global `PENLIGHT_NO_GLOBAL_STRICT` is defined, then this module won't make the
|
||||
-- global environment strict - if you just want to explicitly set table strictness.
|
||||
--
|
||||
-- @module pl.strict
|
||||
|
||||
require 'debug' -- for Lua 5.2
|
||||
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
|
||||
local strict = {}
|
||||
|
||||
local function what ()
|
||||
local d = getinfo(3, "S")
|
||||
return d and d.what or "C"
|
||||
end
|
||||
|
||||
--- make an existing table strict.
|
||||
-- @string[opt] name name of table
|
||||
-- @tab[opt] mod the table to protect - if `nil` then we'll return a new table
|
||||
-- @tab[opt] predeclared - table of variables that are to be considered predeclared.
|
||||
-- @return the given table, or a new table
|
||||
-- @usage
|
||||
-- local M = { hello = "world" }
|
||||
-- strict.module ("Awesome_Module", M, {
|
||||
-- Lua = true, -- defines allowed keys
|
||||
-- })
|
||||
--
|
||||
-- assert(M.hello == "world")
|
||||
-- assert(M.Lua == nil) -- access allowed, but has no value yet
|
||||
-- M.Lua = "Rocks"
|
||||
-- assert(M.Lua == "Rocks")
|
||||
-- M.not_allowed = "bad boy" -- throws an error
|
||||
function strict.module (name,mod,predeclared)
|
||||
local mt, old_newindex, old_index, old_index_type, global
|
||||
if predeclared then
|
||||
global = predeclared.__global
|
||||
end
|
||||
if type(mod) == 'table' then
|
||||
mt = getmetatable(mod)
|
||||
if mt and rawget(mt,'__declared') then return end -- already patched...
|
||||
else
|
||||
mod = {}
|
||||
end
|
||||
if mt == nil then
|
||||
mt = {}
|
||||
setmetatable(mod, mt)
|
||||
else
|
||||
old_newindex = mt.__newindex
|
||||
old_index = mt.__index
|
||||
old_index_type = type(old_index)
|
||||
end
|
||||
mt.__declared = predeclared or {}
|
||||
mt.__newindex = function(t, n, v)
|
||||
if old_newindex then
|
||||
old_newindex(t, n, v)
|
||||
if rawget(t,n)~=nil then return end
|
||||
end
|
||||
if not mt.__declared[n] then
|
||||
if global then
|
||||
local w = what()
|
||||
if w ~= "main" and w ~= "C" then
|
||||
error("assign to undeclared global '"..n.."'", 2)
|
||||
end
|
||||
end
|
||||
mt.__declared[n] = true
|
||||
end
|
||||
rawset(t, n, v)
|
||||
end
|
||||
mt.__index = function(t,n)
|
||||
if not mt.__declared[n] and what() ~= "C" then
|
||||
if old_index then
|
||||
if old_index_type == "table" then
|
||||
local fallback = old_index[n]
|
||||
if fallback ~= nil then
|
||||
return fallback
|
||||
end
|
||||
else
|
||||
local res = old_index(t, n)
|
||||
if res ~= nil then
|
||||
return res
|
||||
end
|
||||
end
|
||||
end
|
||||
local msg = "variable '"..n.."' is not declared"
|
||||
if name then
|
||||
msg = msg .. " in '"..tostring(name).."'"
|
||||
end
|
||||
error(msg, 2)
|
||||
end
|
||||
return rawget(t, n)
|
||||
end
|
||||
return mod
|
||||
end
|
||||
|
||||
--- make all tables in a table strict.
|
||||
-- So `strict.make_all_strict(_G)` prevents monkey-patching
|
||||
-- of any global table
|
||||
-- @tab T the table containing the tables to protect. Table `T` itself will NOT be protected.
|
||||
function strict.make_all_strict (T)
|
||||
for k,v in pairs(T) do
|
||||
if type(v) == 'table' and v ~= T then
|
||||
strict.module(k,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- make a new module table which is closed to further changes.
|
||||
-- @tab mod module table
|
||||
-- @string name module name
|
||||
function strict.closed_module (mod,name)
|
||||
-- No clue to what this is useful for? see tests
|
||||
-- Deprecate this and remove???
|
||||
local M = {}
|
||||
mod = mod or {}
|
||||
local mt = getmetatable(mod)
|
||||
if not mt then
|
||||
mt = {}
|
||||
setmetatable(mod,mt)
|
||||
end
|
||||
mt.__newindex = function(t,k,v)
|
||||
M[k] = v
|
||||
end
|
||||
return strict.module(name,M)
|
||||
end
|
||||
|
||||
if not rawget(_G,'PENLIGHT_NO_GLOBAL_STRICT') then
|
||||
strict.module(nil,_G,{_PROMPT=true,_PROMPT2=true,__global=true})
|
||||
end
|
||||
|
||||
return strict
|
158
Documentation/compiler/pl/stringio.lua
Normal file
158
Documentation/compiler/pl/stringio.lua
Normal file
|
@ -0,0 +1,158 @@
|
|||
--- Reading and writing strings using file-like objects. <br>
|
||||
--
|
||||
-- f = stringio.open(text)
|
||||
-- l1 = f:read() -- read first line
|
||||
-- n,m = f:read ('*n','*n') -- read two numbers
|
||||
-- for line in f:lines() do print(line) end -- iterate over all lines
|
||||
-- f = stringio.create()
|
||||
-- f:write('hello')
|
||||
-- f:write('dolly')
|
||||
-- assert(f:value(),'hellodolly')
|
||||
--
|
||||
-- See @{03-strings.md.File_style_I_O_on_Strings|the Guide}.
|
||||
-- @module pl.stringio
|
||||
|
||||
local unpack = rawget(_G,'unpack') or rawget(table,'unpack')
|
||||
local tonumber = tonumber
|
||||
local concat,append = table.concat,table.insert
|
||||
|
||||
local stringio = {}
|
||||
|
||||
-- Writer class
|
||||
local SW = {}
|
||||
SW.__index = SW
|
||||
|
||||
local function xwrite(self,...)
|
||||
local args = {...} --arguments may not be nil!
|
||||
for i = 1, #args do
|
||||
append(self.tbl,args[i])
|
||||
end
|
||||
end
|
||||
|
||||
function SW:write(arg1,arg2,...)
|
||||
if arg2 then
|
||||
xwrite(self,arg1,arg2,...)
|
||||
else
|
||||
append(self.tbl,arg1)
|
||||
end
|
||||
end
|
||||
|
||||
function SW:writef(fmt,...)
|
||||
self:write(fmt:format(...))
|
||||
end
|
||||
|
||||
function SW:value()
|
||||
return concat(self.tbl)
|
||||
end
|
||||
|
||||
function SW:__tostring()
|
||||
return self:value()
|
||||
end
|
||||
|
||||
function SW:close() -- for compatibility only
|
||||
end
|
||||
|
||||
function SW:seek()
|
||||
end
|
||||
|
||||
-- Reader class
|
||||
local SR = {}
|
||||
SR.__index = SR
|
||||
|
||||
function SR:_read(fmt)
|
||||
local i,str = self.i,self.str
|
||||
local sz = #str
|
||||
if i > sz then return nil end
|
||||
local res
|
||||
if fmt == '*l' or fmt == '*L' then
|
||||
local idx = str:find('\n',i) or (sz+1)
|
||||
res = str:sub(i,fmt == '*l' and idx-1 or idx)
|
||||
self.i = idx+1
|
||||
elseif fmt == '*a' then
|
||||
res = str:sub(i)
|
||||
self.i = sz
|
||||
elseif fmt == '*n' then
|
||||
local _,i2,idx
|
||||
_,idx = str:find ('%s*%d+',i)
|
||||
_,i2 = str:find ('^%.%d+',idx+1)
|
||||
if i2 then idx = i2 end
|
||||
_,i2 = str:find ('^[eE][%+%-]*%d+',idx+1)
|
||||
if i2 then idx = i2 end
|
||||
local val = str:sub(i,idx)
|
||||
res = tonumber(val)
|
||||
self.i = idx+1
|
||||
elseif type(fmt) == 'number' then
|
||||
res = str:sub(i,i+fmt-1)
|
||||
self.i = i + fmt
|
||||
else
|
||||
error("bad read format",2)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function SR:read(...)
|
||||
if select('#',...) == 0 then
|
||||
return self:_read('*l')
|
||||
else
|
||||
local res, fmts = {},{...}
|
||||
for i = 1, #fmts do
|
||||
res[i] = self:_read(fmts[i])
|
||||
end
|
||||
return unpack(res)
|
||||
end
|
||||
end
|
||||
|
||||
function SR:seek(whence,offset)
|
||||
local base
|
||||
whence = whence or 'cur'
|
||||
offset = offset or 0
|
||||
if whence == 'set' then
|
||||
base = 1
|
||||
elseif whence == 'cur' then
|
||||
base = self.i
|
||||
elseif whence == 'end' then
|
||||
base = #self.str
|
||||
end
|
||||
self.i = base + offset
|
||||
return self.i
|
||||
end
|
||||
|
||||
function SR:lines(...)
|
||||
local n, args = select('#',...)
|
||||
if n > 0 then
|
||||
args = {...}
|
||||
end
|
||||
return function()
|
||||
if n == 0 then
|
||||
return self:_read '*l'
|
||||
else
|
||||
return self:read(unpack(args))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SR:close() -- for compatibility only
|
||||
end
|
||||
|
||||
--- create a file-like object which can be used to construct a string.
|
||||
-- The resulting object has an extra `value()` method for
|
||||
-- retrieving the string value. Implements `file:write`, `file:seek`, `file:lines`,
|
||||
-- plus an extra `writef` method which works like `utils.printf`.
|
||||
-- @usage f = create(); f:write('hello, dolly\n'); print(f:value())
|
||||
function stringio.create()
|
||||
return setmetatable({tbl={}},SW)
|
||||
end
|
||||
|
||||
--- create a file-like object for reading from a given string.
|
||||
-- Implements `file:read`.
|
||||
-- @string s The input string.
|
||||
-- @usage fs = open '20 10'; x,y = f:read ('*n','*n'); assert(x == 20 and y == 10)
|
||||
function stringio.open(s)
|
||||
return setmetatable({str=s,i=1},SR)
|
||||
end
|
||||
|
||||
function stringio.lines(s,...)
|
||||
return stringio.open(s):lines(...)
|
||||
end
|
||||
|
||||
return stringio
|
917
Documentation/compiler/pl/stringx.lua
Normal file
917
Documentation/compiler/pl/stringx.lua
Normal file
|
@ -0,0 +1,917 @@
|
|||
--- Python-style extended string library.
|
||||
--
|
||||
-- see 3.6.1 of the Python reference.
|
||||
-- If you want to make these available as string methods, then say
|
||||
-- `stringx.import()` to bring them into the standard `string` table.
|
||||
--
|
||||
-- See @{03-strings.md|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.types`
|
||||
-- @module pl.stringx
|
||||
local utils = require 'pl.utils'
|
||||
local is_callable = require 'pl.types'.is_callable
|
||||
local string = string
|
||||
local find = string.find
|
||||
local type,setmetatable,ipairs = type,setmetatable,ipairs
|
||||
local error = error
|
||||
local gsub = string.gsub
|
||||
local rep = string.rep
|
||||
local sub = string.sub
|
||||
local reverse = string.reverse
|
||||
local concat = table.concat
|
||||
local append = table.insert
|
||||
local remove = table.remove
|
||||
local escape = utils.escape
|
||||
local ceil, max = math.ceil, math.max
|
||||
local assert_arg,usplit = utils.assert_arg,utils.split
|
||||
local lstrip
|
||||
local unpack = utils.unpack
|
||||
local pack = utils.pack
|
||||
|
||||
local function assert_string (n,s)
|
||||
assert_arg(n,s,'string')
|
||||
end
|
||||
|
||||
local function non_empty(s)
|
||||
return #s > 0
|
||||
end
|
||||
|
||||
local function assert_nonempty_string(n,s)
|
||||
assert_arg(n,s,'string',non_empty,'must be a non-empty string')
|
||||
end
|
||||
|
||||
local function makelist(l)
|
||||
return setmetatable(l, require('pl.List'))
|
||||
end
|
||||
|
||||
local stringx = {}
|
||||
|
||||
------------------
|
||||
-- String Predicates
|
||||
-- @section predicates
|
||||
|
||||
--- does s only contain alphabetic characters?
|
||||
-- @string s a string
|
||||
function stringx.isalpha(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^%a+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain digits?
|
||||
-- @string s a string
|
||||
function stringx.isdigit(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^%d+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain alphanumeric characters?
|
||||
-- @string s a string
|
||||
function stringx.isalnum(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^%w+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain whitespace?
|
||||
-- Matches on pattern '%s' so matches space, newline, tabs, etc.
|
||||
-- @string s a string
|
||||
function stringx.isspace(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^%s+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain lower case characters?
|
||||
-- @string s a string
|
||||
function stringx.islower(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^[%l%s]+$') == 1
|
||||
end
|
||||
|
||||
--- does s only contain upper case characters?
|
||||
-- @string s a string
|
||||
function stringx.isupper(s)
|
||||
assert_string(1,s)
|
||||
return find(s,'^[%u%s]+$') == 1
|
||||
end
|
||||
|
||||
local function raw_startswith(s, prefix)
|
||||
return find(s,prefix,1,true) == 1
|
||||
end
|
||||
|
||||
local function raw_endswith(s, suffix)
|
||||
return #s >= #suffix and find(s, suffix, #s-#suffix+1, true) and true or false
|
||||
end
|
||||
|
||||
local function test_affixes(s, affixes, fn)
|
||||
if type(affixes) == 'string' then
|
||||
return fn(s,affixes)
|
||||
elseif type(affixes) == 'table' then
|
||||
for _,affix in ipairs(affixes) do
|
||||
if fn(s,affix) then return true end
|
||||
end
|
||||
return false
|
||||
else
|
||||
error(("argument #2 expected a 'string' or a 'table', got a '%s'"):format(type(affixes)))
|
||||
end
|
||||
end
|
||||
|
||||
--- does s start with prefix or one of prefixes?
|
||||
-- @string s a string
|
||||
-- @param prefix a string or an array of strings
|
||||
function stringx.startswith(s,prefix)
|
||||
assert_string(1,s)
|
||||
return test_affixes(s,prefix,raw_startswith)
|
||||
end
|
||||
|
||||
--- does s end with suffix or one of suffixes?
|
||||
-- @string s a string
|
||||
-- @param suffix a string or an array of strings
|
||||
function stringx.endswith(s,suffix)
|
||||
assert_string(1,s)
|
||||
return test_affixes(s,suffix,raw_endswith)
|
||||
end
|
||||
|
||||
--- Strings and Lists
|
||||
-- @section lists
|
||||
|
||||
--- concatenate the strings using this string as a delimiter.
|
||||
-- Note that the arguments are reversed from `string.concat`.
|
||||
-- @string s the string
|
||||
-- @param seq a table of strings or numbers
|
||||
-- @usage stringx.join(' ', {1,2,3}) == '1 2 3'
|
||||
function stringx.join(s,seq)
|
||||
assert_string(1,s)
|
||||
return concat(seq,s)
|
||||
end
|
||||
|
||||
--- Split a string into a list of lines.
|
||||
-- `"\r"`, `"\n"`, and `"\r\n"` are considered line ends.
|
||||
-- They are not included in the lines unless `keepends` is passed.
|
||||
-- Terminal line end does not produce an extra line.
|
||||
-- Splitting an empty string results in an empty list.
|
||||
-- @string s the string.
|
||||
-- @bool[opt] keep_ends include line ends.
|
||||
-- @return List of lines
|
||||
function stringx.splitlines(s, keep_ends)
|
||||
assert_string(1, s)
|
||||
local res = {}
|
||||
local pos = 1
|
||||
while true do
|
||||
local line_end_pos = find(s, '[\r\n]', pos)
|
||||
if not line_end_pos then
|
||||
break
|
||||
end
|
||||
|
||||
local line_end = sub(s, line_end_pos, line_end_pos)
|
||||
if line_end == '\r' and sub(s, line_end_pos + 1, line_end_pos + 1) == '\n' then
|
||||
line_end = '\r\n'
|
||||
end
|
||||
|
||||
local line = sub(s, pos, line_end_pos - 1)
|
||||
if keep_ends then
|
||||
line = line .. line_end
|
||||
end
|
||||
append(res, line)
|
||||
|
||||
pos = line_end_pos + #line_end
|
||||
end
|
||||
|
||||
if pos <= #s then
|
||||
append(res, sub(s, pos))
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- split a string into a list of strings using a delimiter.
|
||||
-- @function split
|
||||
-- @string s the string
|
||||
-- @string[opt] re a delimiter (defaults to whitespace)
|
||||
-- @int[opt] n maximum number of results
|
||||
-- @return List
|
||||
-- @usage #(stringx.split('one two')) == 2
|
||||
-- @usage stringx.split('one,two,three', ',') == List{'one','two','three'}
|
||||
-- @usage stringx.split('one,two,three', ',', 2) == List{'one','two,three'}
|
||||
function stringx.split(s,re,n)
|
||||
assert_string(1,s)
|
||||
local plain = true
|
||||
if not re then -- default spaces
|
||||
s = lstrip(s)
|
||||
plain = false
|
||||
end
|
||||
local res = usplit(s,re,plain,n)
|
||||
if re and re ~= '' and
|
||||
find(s,re,-#re,true) and
|
||||
(n or math.huge) > #res then
|
||||
res[#res+1] = ""
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- replace all tabs in s with tabsize spaces. If not specified, tabsize defaults to 8.
|
||||
-- Tab stops will be honored.
|
||||
-- @string s the string
|
||||
-- @int tabsize[opt=8] number of spaces to expand each tab
|
||||
-- @return expanded string
|
||||
-- @usage stringx.expandtabs('\tone,two,three', 4) == ' one,two,three'
|
||||
-- @usage stringx.expandtabs(' \tone,two,three', 4) == ' one,two,three'
|
||||
function stringx.expandtabs(s,tabsize)
|
||||
assert_string(1,s)
|
||||
tabsize = tabsize or 8
|
||||
return (s:gsub("([^\t\r\n]*)\t", function(before_tab)
|
||||
if tabsize == 0 then
|
||||
return before_tab
|
||||
else
|
||||
return before_tab .. (" "):rep(tabsize - #before_tab % tabsize)
|
||||
end
|
||||
end))
|
||||
end
|
||||
|
||||
--- Finding and Replacing
|
||||
-- @section find
|
||||
|
||||
local function _find_all(s,sub,first,last,allow_overlap)
|
||||
first = first or 1
|
||||
last = last or #s
|
||||
if sub == '' then return last+1,last-first+1 end
|
||||
local i1,i2 = find(s,sub,first,true)
|
||||
local res
|
||||
local k = 0
|
||||
while i1 do
|
||||
if last and i2 > last then break end
|
||||
res = i1
|
||||
k = k + 1
|
||||
if allow_overlap then
|
||||
i1,i2 = find(s,sub,i1+1,true)
|
||||
else
|
||||
i1,i2 = find(s,sub,i2+1,true)
|
||||
end
|
||||
end
|
||||
return res,k
|
||||
end
|
||||
|
||||
--- find index of first instance of sub in s from the left.
|
||||
-- @string s the string
|
||||
-- @string sub substring
|
||||
-- @int[opt] first first index
|
||||
-- @int[opt] last last index
|
||||
-- @return start index, or nil if not found
|
||||
function stringx.lfind(s,sub,first,last)
|
||||
assert_string(1,s)
|
||||
assert_string(2,sub)
|
||||
local i1, i2 = find(s,sub,first,true)
|
||||
|
||||
if i1 and (not last or i2 <= last) then
|
||||
return i1
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--- find index of first instance of sub in s from the right.
|
||||
-- @string s the string
|
||||
-- @string sub substring
|
||||
-- @int[opt] first first index
|
||||
-- @int[opt] last last index
|
||||
-- @return start index, or nil if not found
|
||||
function stringx.rfind(s,sub,first,last)
|
||||
assert_string(1,s)
|
||||
assert_string(2,sub)
|
||||
return (_find_all(s,sub,first,last,true))
|
||||
end
|
||||
|
||||
--- replace up to n instances of old by new in the string s.
|
||||
-- If n is not present, replace all instances.
|
||||
-- @string s the string
|
||||
-- @string old the target substring
|
||||
-- @string new the substitution
|
||||
-- @int[opt] n optional maximum number of substitutions
|
||||
-- @return result string
|
||||
function stringx.replace(s,old,new,n)
|
||||
assert_string(1,s)
|
||||
assert_string(2,old)
|
||||
assert_string(3,new)
|
||||
return (gsub(s,escape(old),new:gsub('%%','%%%%'),n))
|
||||
end
|
||||
|
||||
--- count all instances of substring in string.
|
||||
-- @string s the string
|
||||
-- @string sub substring
|
||||
-- @bool[opt] allow_overlap allow matches to overlap
|
||||
-- @usage
|
||||
-- assert(stringx.count('banana', 'ana') == 1)
|
||||
-- assert(stringx.count('banana', 'ana', true) == 2)
|
||||
function stringx.count(s,sub,allow_overlap)
|
||||
assert_string(1,s)
|
||||
local _,k = _find_all(s,sub,1,false,allow_overlap)
|
||||
return k
|
||||
end
|
||||
|
||||
--- Stripping and Justifying
|
||||
-- @section strip
|
||||
|
||||
local function _just(s,w,ch,left,right)
|
||||
local n = #s
|
||||
if w > n then
|
||||
if not ch then ch = ' ' end
|
||||
local f1,f2
|
||||
if left and right then
|
||||
local rn = ceil((w-n)/2)
|
||||
local ln = w - n - rn
|
||||
f1 = rep(ch,ln)
|
||||
f2 = rep(ch,rn)
|
||||
elseif right then
|
||||
f1 = rep(ch,w-n)
|
||||
f2 = ''
|
||||
else
|
||||
f2 = rep(ch,w-n)
|
||||
f1 = ''
|
||||
end
|
||||
return f1..s..f2
|
||||
else
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
--- left-justify s with width w.
|
||||
-- @string s the string
|
||||
-- @int w width of justification
|
||||
-- @string[opt=' '] ch padding character
|
||||
-- @usage stringx.ljust('hello', 10, '*') == '*****hello'
|
||||
function stringx.ljust(s,w,ch)
|
||||
assert_string(1,s)
|
||||
assert_arg(2,w,'number')
|
||||
return _just(s,w,ch,true,false)
|
||||
end
|
||||
|
||||
--- right-justify s with width w.
|
||||
-- @string s the string
|
||||
-- @int w width of justification
|
||||
-- @string[opt=' '] ch padding character
|
||||
-- @usage stringx.rjust('hello', 10, '*') == 'hello*****'
|
||||
function stringx.rjust(s,w,ch)
|
||||
assert_string(1,s)
|
||||
assert_arg(2,w,'number')
|
||||
return _just(s,w,ch,false,true)
|
||||
end
|
||||
|
||||
--- center-justify s with width w.
|
||||
-- @string s the string
|
||||
-- @int w width of justification
|
||||
-- @string[opt=' '] ch padding character
|
||||
-- @usage stringx.center('hello', 10, '*') == '**hello***'
|
||||
function stringx.center(s,w,ch)
|
||||
assert_string(1,s)
|
||||
assert_arg(2,w,'number')
|
||||
return _just(s,w,ch,true,true)
|
||||
end
|
||||
|
||||
local function _strip(s,left,right,chrs)
|
||||
if not chrs then
|
||||
chrs = '%s'
|
||||
else
|
||||
chrs = '['..escape(chrs)..']'
|
||||
end
|
||||
local f = 1
|
||||
local t
|
||||
if left then
|
||||
local i1,i2 = find(s,'^'..chrs..'*')
|
||||
if i2 >= i1 then
|
||||
f = i2+1
|
||||
end
|
||||
end
|
||||
if right then
|
||||
if #s < 200 then
|
||||
local i1,i2 = find(s,chrs..'*$',f)
|
||||
if i2 >= i1 then
|
||||
t = i1-1
|
||||
end
|
||||
else
|
||||
local rs = reverse(s)
|
||||
local i1,i2 = find(rs, '^'..chrs..'*')
|
||||
if i2 >= i1 then
|
||||
t = -i2-1
|
||||
end
|
||||
end
|
||||
end
|
||||
return sub(s,f,t)
|
||||
end
|
||||
|
||||
--- trim any characters on the left of s.
|
||||
-- @string s the string
|
||||
-- @string[opt='%s'] chrs default any whitespace character,
|
||||
-- but can be a string of characters to be trimmed
|
||||
function stringx.lstrip(s,chrs)
|
||||
assert_string(1,s)
|
||||
return _strip(s,true,false,chrs)
|
||||
end
|
||||
lstrip = stringx.lstrip
|
||||
|
||||
--- trim any characters on the right of s.
|
||||
-- @string s the string
|
||||
-- @string[opt='%s'] chrs default any whitespace character,
|
||||
-- but can be a string of characters to be trimmed
|
||||
function stringx.rstrip(s,chrs)
|
||||
assert_string(1,s)
|
||||
return _strip(s,false,true,chrs)
|
||||
end
|
||||
|
||||
--- trim any characters on both left and right of s.
|
||||
-- @string s the string
|
||||
-- @string[opt='%s'] chrs default any whitespace character,
|
||||
-- but can be a string of characters to be trimmed
|
||||
-- @usage stringx.strip(' --== Hello ==-- ', "- =") --> 'Hello'
|
||||
function stringx.strip(s,chrs)
|
||||
assert_string(1,s)
|
||||
return _strip(s,true,true,chrs)
|
||||
end
|
||||
|
||||
--- Partitioning Strings
|
||||
-- @section partitioning
|
||||
|
||||
--- split a string using a pattern. Note that at least one value will be returned!
|
||||
-- @string s the string
|
||||
-- @string[opt='%s'] re a Lua string pattern (defaults to whitespace)
|
||||
-- @return the parts of the string
|
||||
-- @usage a,b = line:splitv('=')
|
||||
-- @see utils.splitv
|
||||
function stringx.splitv(s,re)
|
||||
assert_string(1,s)
|
||||
return utils.splitv(s,re)
|
||||
end
|
||||
|
||||
-- The partition functions split a string using a delimiter into three parts:
|
||||
-- the part before, the delimiter itself, and the part afterwards
|
||||
local function _partition(p,delim,fn)
|
||||
local i1,i2 = fn(p,delim)
|
||||
if not i1 or i1 == -1 then
|
||||
return p,'',''
|
||||
else
|
||||
if not i2 then i2 = i1 end
|
||||
return sub(p,1,i1-1),sub(p,i1,i2),sub(p,i2+1)
|
||||
end
|
||||
end
|
||||
|
||||
--- partition the string using first occurance of a delimiter
|
||||
-- @string s the string
|
||||
-- @string ch delimiter (match as plain string, no patterns)
|
||||
-- @return part before ch
|
||||
-- @return ch
|
||||
-- @return part after ch
|
||||
-- @usage {stringx.partition('a,b,c', ','))} == {'a', ',', 'b,c'}
|
||||
-- @usage {stringx.partition('abc', 'x'))} == {'abc', '', ''}
|
||||
function stringx.partition(s,ch)
|
||||
assert_string(1,s)
|
||||
assert_nonempty_string(2,ch)
|
||||
return _partition(s,ch,stringx.lfind)
|
||||
end
|
||||
|
||||
--- partition the string p using last occurance of a delimiter
|
||||
-- @string s the string
|
||||
-- @string ch delimiter (match as plain string, no patterns)
|
||||
-- @return part before ch
|
||||
-- @return ch
|
||||
-- @return part after ch
|
||||
-- @usage {stringx.rpartition('a,b,c', ','))} == {'a,b', ',', 'c'}
|
||||
-- @usage {stringx.rpartition('abc', 'x'))} == {'', '', 'abc'}
|
||||
function stringx.rpartition(s,ch)
|
||||
assert_string(1,s)
|
||||
assert_nonempty_string(2,ch)
|
||||
local a,b,c = _partition(s,ch,stringx.rfind)
|
||||
if a == s then -- no match found
|
||||
return c,b,a
|
||||
end
|
||||
return a,b,c
|
||||
end
|
||||
|
||||
--- return the 'character' at the index.
|
||||
-- @string s the string
|
||||
-- @int idx an index (can be negative)
|
||||
-- @return a substring of length 1 if successful, empty string otherwise.
|
||||
function stringx.at(s,idx)
|
||||
assert_string(1,s)
|
||||
assert_arg(2,idx,'number')
|
||||
return sub(s,idx,idx)
|
||||
end
|
||||
|
||||
|
||||
--- Text handling
|
||||
-- @section text
|
||||
|
||||
|
||||
--- indent a multiline string.
|
||||
-- @tparam string s the (multiline) string
|
||||
-- @tparam integer n the size of the indent
|
||||
-- @tparam[opt=' '] string ch the character to use when indenting
|
||||
-- @return indented string
|
||||
function stringx.indent (s,n,ch)
|
||||
assert_arg(1,s,'string')
|
||||
assert_arg(2,n,'number')
|
||||
local lines = usplit(s ,'\n')
|
||||
local prefix = string.rep(ch or ' ',n)
|
||||
for i, line in ipairs(lines) do
|
||||
lines[i] = prefix..line
|
||||
end
|
||||
return concat(lines,'\n')..'\n'
|
||||
end
|
||||
|
||||
|
||||
--- dedent a multiline string by removing any initial indent.
|
||||
-- useful when working with [[..]] strings.
|
||||
-- Empty lines are ignored.
|
||||
-- @tparam string s the (multiline) string
|
||||
-- @return a string with initial indent zero.
|
||||
-- @usage
|
||||
-- local s = dedent [[
|
||||
-- One
|
||||
--
|
||||
-- Two
|
||||
--
|
||||
-- Three
|
||||
-- ]]
|
||||
-- assert(s == [[
|
||||
-- One
|
||||
--
|
||||
-- Two
|
||||
--
|
||||
-- Three
|
||||
-- ]])
|
||||
function stringx.dedent (s)
|
||||
assert_arg(1,s,'string')
|
||||
local lst = usplit(s,'\n')
|
||||
if #lst>0 then
|
||||
local ind_size = math.huge
|
||||
for i, line in ipairs(lst) do
|
||||
local i1, i2 = lst[i]:find('^%s*[^%s]')
|
||||
if i1 and i2 < ind_size then
|
||||
ind_size = i2
|
||||
end
|
||||
end
|
||||
for i, line in ipairs(lst) do
|
||||
lst[i] = lst[i]:sub(ind_size, -1)
|
||||
end
|
||||
end
|
||||
return concat(lst,'\n')..'\n'
|
||||
end
|
||||
|
||||
|
||||
|
||||
do
|
||||
local buildline = function(words, size, breaklong)
|
||||
-- if overflow is set, a word longer than size, will overflow the size
|
||||
-- otherwise it will be chopped in line-length pieces
|
||||
local line = {}
|
||||
if #words[1] > size then
|
||||
-- word longer than line
|
||||
if not breaklong then
|
||||
line[1] = words[1]
|
||||
remove(words, 1)
|
||||
else
|
||||
line[1] = words[1]:sub(1, size)
|
||||
words[1] = words[1]:sub(size + 1, -1)
|
||||
end
|
||||
else
|
||||
local len = 0
|
||||
while words[1] and (len + #words[1] <= size) or
|
||||
(len == 0 and #words[1] == size) do
|
||||
if words[1] ~= "" then
|
||||
line[#line+1] = words[1]
|
||||
len = len + #words[1] + 1
|
||||
end
|
||||
remove(words, 1)
|
||||
end
|
||||
end
|
||||
return stringx.strip(concat(line, " ")), words
|
||||
end
|
||||
|
||||
--- format a paragraph into lines so that they fit into a line width.
|
||||
-- It will not break long words by default, so lines can be over the length
|
||||
-- to that extent.
|
||||
-- @tparam string s the string to format
|
||||
-- @tparam[opt=70] integer width the margin width
|
||||
-- @tparam[opt=false] boolean breaklong if truthy, words longer than the width given will be forced split.
|
||||
-- @return a list of lines (List object), use `fill` to return a string instead of a `List`.
|
||||
-- @see pl.List
|
||||
-- @see fill
|
||||
stringx.wrap = function(s, width, breaklong)
|
||||
s = s:gsub('\n',' ') -- remove line breaks
|
||||
s = stringx.strip(s) -- remove leading/trailing whitespace
|
||||
if s == "" then
|
||||
return { "" }
|
||||
end
|
||||
width = width or 70
|
||||
local out = {}
|
||||
local words = usplit(s, "%s")
|
||||
while words[1] do
|
||||
out[#out+1], words = buildline(words, width, breaklong)
|
||||
end
|
||||
return makelist(out)
|
||||
end
|
||||
end
|
||||
|
||||
--- format a paragraph so that it fits into a line width.
|
||||
-- @tparam string s the string to format
|
||||
-- @tparam[opt=70] integer width the margin width
|
||||
-- @tparam[opt=false] boolean breaklong if truthy, words longer than the width given will be forced split.
|
||||
-- @return a string, use `wrap` to return a list of lines instead of a string.
|
||||
-- @see wrap
|
||||
function stringx.fill (s,width,breaklong)
|
||||
return concat(stringx.wrap(s,width,breaklong),'\n') .. '\n'
|
||||
end
|
||||
|
||||
--- Template
|
||||
-- @section Template
|
||||
|
||||
|
||||
local function _substitute(s,tbl,safe)
|
||||
local subst
|
||||
if is_callable(tbl) then
|
||||
subst = tbl
|
||||
else
|
||||
function subst(f)
|
||||
local s = tbl[f]
|
||||
if not s then
|
||||
if safe then
|
||||
return f
|
||||
else
|
||||
error("not present in table "..f)
|
||||
end
|
||||
else
|
||||
return s
|
||||
end
|
||||
end
|
||||
end
|
||||
local res = gsub(s,'%${([%w_]+)}',subst)
|
||||
return (gsub(res,'%$([%w_]+)',subst))
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Template = {}
|
||||
stringx.Template = Template
|
||||
Template.__index = Template
|
||||
setmetatable(Template, {
|
||||
__call = function(obj,tmpl)
|
||||
return Template.new(tmpl)
|
||||
end
|
||||
})
|
||||
|
||||
--- Creates a new Template class.
|
||||
-- This is a shortcut to `Template.new(tmpl)`.
|
||||
-- @tparam string tmpl the template string
|
||||
-- @function Template
|
||||
-- @treturn Template
|
||||
function Template.new(tmpl)
|
||||
assert_arg(1,tmpl,'string')
|
||||
local res = {}
|
||||
res.tmpl = tmpl
|
||||
setmetatable(res,Template)
|
||||
return res
|
||||
end
|
||||
|
||||
--- substitute values into a template, throwing an error.
|
||||
-- This will throw an error if no name is found.
|
||||
-- @tparam table tbl a table of name-value pairs.
|
||||
-- @return string with place holders substituted
|
||||
function Template:substitute(tbl)
|
||||
assert_arg(1,tbl,'table')
|
||||
return _substitute(self.tmpl,tbl,false)
|
||||
end
|
||||
|
||||
--- substitute values into a template.
|
||||
-- This version just passes unknown names through.
|
||||
-- @tparam table tbl a table of name-value pairs.
|
||||
-- @return string with place holders substituted
|
||||
function Template:safe_substitute(tbl)
|
||||
assert_arg(1,tbl,'table')
|
||||
return _substitute(self.tmpl,tbl,true)
|
||||
end
|
||||
|
||||
--- substitute values into a template, preserving indentation. <br>
|
||||
-- If the value is a multiline string _or_ a template, it will insert
|
||||
-- the lines at the correct indentation. <br>
|
||||
-- Furthermore, if a template, then that template will be substituted
|
||||
-- using the same table.
|
||||
-- @tparam table tbl a table of name-value pairs.
|
||||
-- @return string with place holders substituted
|
||||
function Template:indent_substitute(tbl)
|
||||
assert_arg(1,tbl,'table')
|
||||
if not self.strings then
|
||||
self.strings = usplit(self.tmpl,'\n')
|
||||
end
|
||||
|
||||
-- the idea is to substitute line by line, grabbing any spaces as
|
||||
-- well as the $var. If the value to be substituted contains newlines,
|
||||
-- then we split that into lines and adjust the indent before inserting.
|
||||
local function subst(line)
|
||||
return line:gsub('(%s*)%$([%w_]+)',function(sp,f)
|
||||
local subtmpl
|
||||
local s = tbl[f]
|
||||
if not s then error("not present in table "..f) end
|
||||
if getmetatable(s) == Template then
|
||||
subtmpl = s
|
||||
s = s.tmpl
|
||||
else
|
||||
s = tostring(s)
|
||||
end
|
||||
if s:find '\n' then
|
||||
local lines = usplit(s, '\n')
|
||||
for i, line in ipairs(lines) do
|
||||
lines[i] = sp..line
|
||||
end
|
||||
s = concat(lines, '\n') .. '\n'
|
||||
end
|
||||
if subtmpl then
|
||||
return _substitute(s, tbl)
|
||||
else
|
||||
return s
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
for i, line in ipairs(self.strings) do
|
||||
lines[i] = subst(line)
|
||||
end
|
||||
return concat(lines,'\n')..'\n'
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Miscelaneous
|
||||
-- @section misc
|
||||
|
||||
--- return an iterator over all lines in a string
|
||||
-- @string s the string
|
||||
-- @return an iterator
|
||||
-- @usage
|
||||
-- local line_no = 1
|
||||
-- for line in stringx.lines(some_text) do
|
||||
-- print(line_no, line)
|
||||
-- line_no = line_no + 1
|
||||
-- end
|
||||
function stringx.lines(s)
|
||||
assert_string(1,s)
|
||||
if not s:find '\n$' then s = s..'\n' end
|
||||
return s:gmatch('([^\n]*)\n')
|
||||
end
|
||||
|
||||
--- inital word letters uppercase ('title case').
|
||||
-- Here 'words' mean chunks of non-space characters.
|
||||
-- @string s the string
|
||||
-- @return a string with each word's first letter uppercase
|
||||
-- @usage stringx.title("hello world") == "Hello World")
|
||||
function stringx.title(s)
|
||||
assert_string(1,s)
|
||||
return (s:gsub('(%S)(%S*)',function(f,r)
|
||||
return f:upper()..r:lower()
|
||||
end))
|
||||
end
|
||||
|
||||
stringx.capitalize = stringx.title
|
||||
|
||||
do
|
||||
local ellipsis = '...'
|
||||
local n_ellipsis = #ellipsis
|
||||
|
||||
--- Return a shortened version of a string.
|
||||
-- Fits string within w characters. Removed characters are marked with ellipsis.
|
||||
-- @string s the string
|
||||
-- @int w the maxinum size allowed
|
||||
-- @bool tail true if we want to show the end of the string (head otherwise)
|
||||
-- @usage ('1234567890'):shorten(8) == '12345...'
|
||||
-- @usage ('1234567890'):shorten(8, true) == '...67890'
|
||||
-- @usage ('1234567890'):shorten(20) == '1234567890'
|
||||
function stringx.shorten(s,w,tail)
|
||||
assert_string(1,s)
|
||||
if #s > w then
|
||||
if w < n_ellipsis then return ellipsis:sub(1,w) end
|
||||
if tail then
|
||||
local i = #s - w + 1 + n_ellipsis
|
||||
return ellipsis .. s:sub(i)
|
||||
else
|
||||
return s:sub(1,w-n_ellipsis) .. ellipsis
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
-- Utility function that finds any patterns that match a long string's an open or close.
|
||||
-- Note that having this function use the least number of equal signs that is possible is a harder algorithm to come up with.
|
||||
-- Right now, it simply returns the greatest number of them found.
|
||||
-- @param s The string
|
||||
-- @return 'nil' if not found. If found, the maximum number of equal signs found within all matches.
|
||||
local function has_lquote(s)
|
||||
local lstring_pat = '([%[%]])(=*)%1'
|
||||
local equals, new_equals, _
|
||||
local finish = 1
|
||||
repeat
|
||||
_, finish, _, new_equals = s:find(lstring_pat, finish)
|
||||
if new_equals then
|
||||
equals = max(equals or 0, #new_equals)
|
||||
end
|
||||
until not new_equals
|
||||
|
||||
return equals
|
||||
end
|
||||
|
||||
--- Quote the given string and preserve any control or escape characters, such that reloading the string in Lua returns the same result.
|
||||
-- @param s The string to be quoted.
|
||||
-- @return The quoted string.
|
||||
function stringx.quote_string(s)
|
||||
assert_string(1,s)
|
||||
-- Find out if there are any embedded long-quote sequences that may cause issues.
|
||||
-- This is important when strings are embedded within strings, like when serializing.
|
||||
-- Append a closing bracket to catch unfinished long-quote sequences at the end of the string.
|
||||
local equal_signs = has_lquote(s .. "]")
|
||||
|
||||
-- Note that strings containing "\r" can't be quoted using long brackets
|
||||
-- as Lua lexer converts all newlines to "\n" within long strings.
|
||||
if (s:find("\n") or equal_signs) and not s:find("\r") then
|
||||
-- If there is an embedded sequence that matches a long quote, then
|
||||
-- find the one with the maximum number of = signs and add one to that number.
|
||||
equal_signs = ("="):rep((equal_signs or -1) + 1)
|
||||
-- Long strings strip out leading newline. We want to retain that, when quoting.
|
||||
if s:find("^\n") then s = "\n" .. s end
|
||||
local lbracket, rbracket =
|
||||
"[" .. equal_signs .. "[",
|
||||
"]" .. equal_signs .. "]"
|
||||
s = lbracket .. s .. rbracket
|
||||
else
|
||||
-- Escape funny stuff. Lua 5.1 does not handle "\r" correctly.
|
||||
s = ("%q"):format(s):gsub("\r", "\\r")
|
||||
end
|
||||
return s
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Python-style formatting operator.
|
||||
-- Calling `text.format_operator()` overloads the % operator for strings to give
|
||||
-- Python/Ruby style formated output.
|
||||
-- This is extended to also do template-like substitution for map-like data.
|
||||
--
|
||||
-- Note this goes further than the original, and will allow these cases:
|
||||
--
|
||||
-- 1. a single value
|
||||
-- 2. a list of values
|
||||
-- 3. a map of var=value pairs
|
||||
-- 4. a function, as in gsub
|
||||
--
|
||||
-- For the second two cases, it uses $-variable substituion.
|
||||
--
|
||||
-- When called, this function will monkey-patch the global `string` metatable by
|
||||
-- adding a `__mod` method.
|
||||
--
|
||||
-- See <a href="http://lua-users.org/wiki/StringInterpolation">the lua-users wiki</a>
|
||||
--
|
||||
-- @usage
|
||||
-- require 'pl.text'.format_operator()
|
||||
-- local out1 = '%s = %5.3f' % {'PI',math.pi} --> 'PI = 3.142'
|
||||
-- local out2 = '$name = $value' % {name='dog',value='Pluto'} --> 'dog = Pluto'
|
||||
function stringx.format_operator()
|
||||
|
||||
local format = string.format
|
||||
|
||||
-- a more forgiving version of string.format, which applies
|
||||
-- tostring() to any value with a %s format.
|
||||
local function formatx (fmt,...)
|
||||
local args = pack(...)
|
||||
local i = 1
|
||||
for p in fmt:gmatch('%%.') do
|
||||
if p == '%s' and type(args[i]) ~= 'string' then
|
||||
args[i] = tostring(args[i])
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return format(fmt,unpack(args))
|
||||
end
|
||||
|
||||
local function basic_subst(s,t)
|
||||
return (s:gsub('%$([%w_]+)',t))
|
||||
end
|
||||
|
||||
getmetatable("").__mod = function(a, b)
|
||||
if b == nil then
|
||||
return a
|
||||
elseif type(b) == "table" and getmetatable(b) == nil then
|
||||
if #b == 0 then -- assume a map-like table
|
||||
return _substitute(a,b,true)
|
||||
else
|
||||
return formatx(a,unpack(b))
|
||||
end
|
||||
elseif type(b) == 'function' then
|
||||
return basic_subst(a,b)
|
||||
else
|
||||
return formatx(a,b)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- import the stringx functions into the global string (meta)table
|
||||
function stringx.import()
|
||||
utils.import(stringx,string)
|
||||
end
|
||||
|
||||
return stringx
|
999
Documentation/compiler/pl/tablex.lua
Normal file
999
Documentation/compiler/pl/tablex.lua
Normal file
|
@ -0,0 +1,999 @@
|
|||
--- Extended operations on Lua tables.
|
||||
--
|
||||
-- See @{02-arrays.md.Useful_Operations_on_Tables|the Guide}
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.types`
|
||||
-- @module pl.tablex
|
||||
local utils = require ('pl.utils')
|
||||
local types = require ('pl.types')
|
||||
local getmetatable,setmetatable,require = getmetatable,setmetatable,require
|
||||
local tsort,append,remove = table.sort,table.insert,table.remove
|
||||
local min = math.min
|
||||
local pairs,type,unpack,select,tostring = pairs,type,utils.unpack,select,tostring
|
||||
local function_arg = utils.function_arg
|
||||
local assert_arg = utils.assert_arg
|
||||
|
||||
local tablex = {}
|
||||
|
||||
-- generally, functions that make copies of tables try to preserve the metatable.
|
||||
-- However, when the source has no obvious type, then we attach appropriate metatables
|
||||
-- like List, Map, etc to the result.
|
||||
local function setmeta (res,tbl,pl_class)
|
||||
local mt = getmetatable(tbl) or pl_class and require('pl.' .. pl_class)
|
||||
return mt and setmetatable(res, mt) or res
|
||||
end
|
||||
|
||||
local function makelist(l)
|
||||
return setmetatable(l, require('pl.List'))
|
||||
end
|
||||
|
||||
local function makemap(m)
|
||||
return setmetatable(m, require('pl.Map'))
|
||||
end
|
||||
|
||||
local function complain (idx,msg)
|
||||
error(('argument %d is not %s'):format(idx,msg),3)
|
||||
end
|
||||
|
||||
local function assert_arg_indexable (idx,val)
|
||||
if not types.is_indexable(val) then
|
||||
complain(idx,"indexable")
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_arg_iterable (idx,val)
|
||||
if not types.is_iterable(val) then
|
||||
complain(idx,"iterable")
|
||||
end
|
||||
end
|
||||
|
||||
local function assert_arg_writeable (idx,val)
|
||||
if not types.is_writeable(val) then
|
||||
complain(idx,"writeable")
|
||||
end
|
||||
end
|
||||
|
||||
--- copy a table into another, in-place.
|
||||
-- @within Copying
|
||||
-- @tab t1 destination table
|
||||
-- @tab t2 source (actually any iterable object)
|
||||
-- @return first table
|
||||
function tablex.update (t1,t2)
|
||||
assert_arg_writeable(1,t1)
|
||||
assert_arg_iterable(2,t2)
|
||||
for k,v in pairs(t2) do
|
||||
t1[k] = v
|
||||
end
|
||||
return t1
|
||||
end
|
||||
|
||||
--- total number of elements in this table.
|
||||
-- Note that this is distinct from `#t`, which is the number
|
||||
-- of values in the array part; this value will always
|
||||
-- be greater or equal. The difference gives the size of
|
||||
-- the hash part, for practical purposes. Works for any
|
||||
-- object with a __pairs metamethod.
|
||||
-- @tab t a table
|
||||
-- @return the size
|
||||
function tablex.size (t)
|
||||
assert_arg_iterable(1,t)
|
||||
local i = 0
|
||||
for k in pairs(t) do i = i + 1 end
|
||||
return i
|
||||
end
|
||||
|
||||
--- make a shallow copy of a table
|
||||
-- @within Copying
|
||||
-- @tab t an iterable source
|
||||
-- @return new table
|
||||
function tablex.copy (t)
|
||||
assert_arg_iterable(1,t)
|
||||
local res = {}
|
||||
for k,v in pairs(t) do
|
||||
res[k] = v
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function cycle_aware_copy(t, cache)
|
||||
if type(t) ~= 'table' then return t end
|
||||
if cache[t] then return cache[t] end
|
||||
assert_arg_iterable(1,t)
|
||||
local res = {}
|
||||
cache[t] = res
|
||||
local mt = getmetatable(t)
|
||||
for k,v in pairs(t) do
|
||||
k = cycle_aware_copy(k, cache)
|
||||
v = cycle_aware_copy(v, cache)
|
||||
res[k] = v
|
||||
end
|
||||
setmetatable(res,mt)
|
||||
return res
|
||||
end
|
||||
|
||||
--- make a deep copy of a table, recursively copying all the keys and fields.
|
||||
-- This supports cycles in tables; cycles will be reproduced in the copy.
|
||||
-- This will also set the copied table's metatable to that of the original.
|
||||
-- @within Copying
|
||||
-- @tab t A table
|
||||
-- @return new table
|
||||
function tablex.deepcopy(t)
|
||||
return cycle_aware_copy(t,{})
|
||||
end
|
||||
|
||||
local abs = math.abs
|
||||
|
||||
local function cycle_aware_compare(t1,t2,ignore_mt,eps,cache)
|
||||
if cache[t1] and cache[t1][t2] then return true end
|
||||
local ty1 = type(t1)
|
||||
local ty2 = type(t2)
|
||||
if ty1 ~= ty2 then return false end
|
||||
-- non-table types can be directly compared
|
||||
if ty1 ~= 'table' then
|
||||
if ty1 == 'number' and eps then return abs(t1-t2) < eps end
|
||||
return t1 == t2
|
||||
end
|
||||
-- as well as tables which have the metamethod __eq
|
||||
local mt = getmetatable(t1)
|
||||
if not ignore_mt and mt and mt.__eq then return t1 == t2 end
|
||||
for k1 in pairs(t1) do
|
||||
if t2[k1]==nil then return false end
|
||||
end
|
||||
for k2 in pairs(t2) do
|
||||
if t1[k2]==nil then return false end
|
||||
end
|
||||
cache[t1] = cache[t1] or {}
|
||||
cache[t1][t2] = true
|
||||
for k1,v1 in pairs(t1) do
|
||||
local v2 = t2[k1]
|
||||
if not cycle_aware_compare(v1,v2,ignore_mt,eps,cache) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- compare two values.
|
||||
-- if they are tables, then compare their keys and fields recursively.
|
||||
-- @within Comparing
|
||||
-- @param t1 A value
|
||||
-- @param t2 A value
|
||||
-- @bool[opt] ignore_mt if true, ignore __eq metamethod (default false)
|
||||
-- @number[opt] eps if defined, then used for any number comparisons
|
||||
-- @return true or false
|
||||
function tablex.deepcompare(t1,t2,ignore_mt,eps)
|
||||
return cycle_aware_compare(t1,t2,ignore_mt,eps,{})
|
||||
end
|
||||
|
||||
--- compare two arrays using a predicate.
|
||||
-- @within Comparing
|
||||
-- @array t1 an array
|
||||
-- @array t2 an array
|
||||
-- @func cmp A comparison function; `bool = cmp(t1_value, t2_value)`
|
||||
-- @return true or false
|
||||
-- @usage
|
||||
-- assert(tablex.compare({ 1, 2, 3 }, { 1, 2, 3 }, "=="))
|
||||
--
|
||||
-- assert(tablex.compare(
|
||||
-- {1,2,3, hello = "world"}, -- fields are not compared!
|
||||
-- {1,2,3}, function(v1, v2) return v1 == v2 end)
|
||||
function tablex.compare (t1,t2,cmp)
|
||||
assert_arg_indexable(1,t1)
|
||||
assert_arg_indexable(2,t2)
|
||||
if #t1 ~= #t2 then return false end
|
||||
cmp = function_arg(3,cmp)
|
||||
for k = 1,#t1 do
|
||||
if not cmp(t1[k],t2[k]) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- compare two list-like tables using an optional predicate, without regard for element order.
|
||||
-- @within Comparing
|
||||
-- @array t1 a list-like table
|
||||
-- @array t2 a list-like table
|
||||
-- @param cmp A comparison function (may be nil)
|
||||
function tablex.compare_no_order (t1,t2,cmp)
|
||||
assert_arg_indexable(1,t1)
|
||||
assert_arg_indexable(2,t2)
|
||||
if cmp then cmp = function_arg(3,cmp) end
|
||||
if #t1 ~= #t2 then return false end
|
||||
local visited = {}
|
||||
for i = 1,#t1 do
|
||||
local val = t1[i]
|
||||
local gotcha
|
||||
for j = 1,#t2 do
|
||||
if not visited[j] then
|
||||
local match
|
||||
if cmp then match = cmp(val,t2[j]) else match = val == t2[j] end
|
||||
if match then
|
||||
gotcha = j
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not gotcha then return false end
|
||||
visited[gotcha] = true
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
--- return the index of a value in a list.
|
||||
-- Like string.find, there is an optional index to start searching,
|
||||
-- which can be negative.
|
||||
-- @within Finding
|
||||
-- @array t A list-like table
|
||||
-- @param val A value
|
||||
-- @int idx index to start; -1 means last element,etc (default 1)
|
||||
-- @return index of value or nil if not found
|
||||
-- @usage find({10,20,30},20) == 2
|
||||
-- @usage find({'a','b','a','c'},'a',2) == 3
|
||||
function tablex.find(t,val,idx)
|
||||
assert_arg_indexable(1,t)
|
||||
idx = idx or 1
|
||||
if idx < 0 then idx = #t + idx + 1 end
|
||||
for i = idx,#t do
|
||||
if t[i] == val then return i end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- return the index of a value in a list, searching from the end.
|
||||
-- Like string.find, there is an optional index to start searching,
|
||||
-- which can be negative.
|
||||
-- @within Finding
|
||||
-- @array t A list-like table
|
||||
-- @param val A value
|
||||
-- @param idx index to start; -1 means last element,etc (default `#t`)
|
||||
-- @return index of value or nil if not found
|
||||
-- @usage rfind({10,10,10},10) == 3
|
||||
function tablex.rfind(t,val,idx)
|
||||
assert_arg_indexable(1,t)
|
||||
idx = idx or #t
|
||||
if idx < 0 then idx = #t + idx + 1 end
|
||||
for i = idx,1,-1 do
|
||||
if t[i] == val then return i end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- return the index (or key) of a value in a table using a comparison function.
|
||||
--
|
||||
-- *NOTE*: the 2nd return value of this function, the value returned
|
||||
-- by the comparison function, has a limitation that it cannot be `false`.
|
||||
-- Because if it is, then it indicates the comparison failed, and the
|
||||
-- function will continue the search. See examples.
|
||||
-- @within Finding
|
||||
-- @tab t A table
|
||||
-- @func cmp A comparison function
|
||||
-- @param arg an optional second argument to the function
|
||||
-- @return index of value, or nil if not found
|
||||
-- @return value returned by comparison function (cannot be `false`!)
|
||||
-- @usage
|
||||
-- -- using an operator
|
||||
-- local lst = { "Rudolph", true, false, 15 }
|
||||
-- local idx, cmp_result = tablex.rfind(lst, "==", "Rudolph")
|
||||
-- assert(idx == 1)
|
||||
-- assert(cmp_result == true)
|
||||
--
|
||||
-- local idx, cmp_result = tablex.rfind(lst, "==", false)
|
||||
-- assert(idx == 3)
|
||||
-- assert(cmp_result == true) -- looking up 'false' works!
|
||||
--
|
||||
-- -- using a function returning the value looked up
|
||||
-- local cmp = function(v1, v2) return v1 == v2 and v2 end
|
||||
-- local idx, cmp_result = tablex.rfind(lst, cmp, "Rudolph")
|
||||
-- assert(idx == 1)
|
||||
-- assert(cmp_result == "Rudolph") -- the value is returned
|
||||
--
|
||||
-- -- NOTE: this fails, since 'false' cannot be returned!
|
||||
-- local idx, cmp_result = tablex.rfind(lst, cmp, false)
|
||||
-- assert(idx == nil) -- looking up 'false' failed!
|
||||
-- assert(cmp_result == nil)
|
||||
function tablex.find_if(t,cmp,arg)
|
||||
assert_arg_iterable(1,t)
|
||||
cmp = function_arg(2,cmp)
|
||||
for k,v in pairs(t) do
|
||||
local c = cmp(v,arg)
|
||||
if c then return k,c end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- return a list of all values in a table indexed by another list.
|
||||
-- @tab tbl a table
|
||||
-- @array idx an index table (a list of keys)
|
||||
-- @return a list-like table
|
||||
-- @usage index_by({10,20,30,40},{2,4}) == {20,40}
|
||||
-- @usage index_by({one=1,two=2,three=3},{'one','three'}) == {1,3}
|
||||
function tablex.index_by(tbl,idx)
|
||||
assert_arg_indexable(1,tbl)
|
||||
assert_arg_indexable(2,idx)
|
||||
local res = {}
|
||||
for i = 1,#idx do
|
||||
res[i] = tbl[idx[i]]
|
||||
end
|
||||
return setmeta(res,tbl,'List')
|
||||
end
|
||||
|
||||
--- apply a function to all values of a table.
|
||||
-- This returns a table of the results.
|
||||
-- Any extra arguments are passed to the function.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun A function that takes at least one argument
|
||||
-- @tab t A table
|
||||
-- @param ... optional arguments
|
||||
-- @usage map(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900,fred=4}
|
||||
function tablex.map(fun,t,...)
|
||||
assert_arg_iterable(1,t)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for k,v in pairs(t) do
|
||||
res[k] = fun(v,...)
|
||||
end
|
||||
return setmeta(res,t)
|
||||
end
|
||||
|
||||
--- apply a function to all values of a list.
|
||||
-- This returns a table of the results.
|
||||
-- Any extra arguments are passed to the function.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun A function that takes at least one argument
|
||||
-- @array t a table (applies to array part)
|
||||
-- @param ... optional arguments
|
||||
-- @return a list-like table
|
||||
-- @usage imap(function(v) return v*v end, {10,20,30,fred=2}) is {100,400,900}
|
||||
function tablex.imap(fun,t,...)
|
||||
assert_arg_indexable(1,t)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for i = 1,#t do
|
||||
res[i] = fun(t[i],...) or false
|
||||
end
|
||||
return setmeta(res,t,'List')
|
||||
end
|
||||
|
||||
--- apply a named method to values from a table.
|
||||
-- @within MappingAndFiltering
|
||||
-- @string name the method name
|
||||
-- @array t a list-like table
|
||||
-- @param ... any extra arguments to the method
|
||||
-- @return a `List` with the results of the method (1st result only)
|
||||
-- @usage
|
||||
-- local Car = {}
|
||||
-- Car.__index = Car
|
||||
-- function Car.new(car)
|
||||
-- return setmetatable(car or {}, Car)
|
||||
-- end
|
||||
-- Car.speed = 0
|
||||
-- function Car:faster(increase)
|
||||
-- self.speed = self.speed + increase
|
||||
-- return self.speed
|
||||
-- end
|
||||
--
|
||||
-- local ferrari = Car.new{ name = "Ferrari" }
|
||||
-- local lamborghini = Car.new{ name = "Lamborghini", speed = 50 }
|
||||
-- local cars = { ferrari, lamborghini }
|
||||
--
|
||||
-- assert(ferrari.speed == 0)
|
||||
-- assert(lamborghini.speed == 50)
|
||||
-- tablex.map_named_method("faster", cars, 10)
|
||||
-- assert(ferrari.speed == 10)
|
||||
-- assert(lamborghini.speed == 60)
|
||||
function tablex.map_named_method (name,t,...)
|
||||
utils.assert_string(1,name)
|
||||
assert_arg_indexable(2,t)
|
||||
local res = {}
|
||||
for i = 1,#t do
|
||||
local val = t[i]
|
||||
local fun = val[name]
|
||||
res[i] = fun(val,...)
|
||||
end
|
||||
return setmeta(res,t,'List')
|
||||
end
|
||||
|
||||
--- apply a function to all values of a table, in-place.
|
||||
-- Any extra arguments are passed to the function.
|
||||
-- @func fun A function that takes at least one argument
|
||||
-- @tab t a table
|
||||
-- @param ... extra arguments passed to `fun`
|
||||
-- @see tablex.foreach
|
||||
function tablex.transform (fun,t,...)
|
||||
assert_arg_iterable(1,t)
|
||||
fun = function_arg(1,fun)
|
||||
for k,v in pairs(t) do
|
||||
t[k] = fun(v,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- generate a table of all numbers in a range.
|
||||
-- This is consistent with a numerical for loop.
|
||||
-- @int start number
|
||||
-- @int finish number
|
||||
-- @int[opt=1] step make this negative for start < finish
|
||||
function tablex.range (start,finish,step)
|
||||
local res
|
||||
step = step or 1
|
||||
if start == finish then
|
||||
res = {start}
|
||||
elseif (start > finish and step > 0) or (finish > start and step < 0) then
|
||||
res = {}
|
||||
else
|
||||
local k = 1
|
||||
res = {}
|
||||
for i=start,finish,step do res[k]=i; k=k+1 end
|
||||
end
|
||||
return makelist(res)
|
||||
end
|
||||
|
||||
--- apply a function to values from two tables.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun a function of at least two arguments
|
||||
-- @tab t1 a table
|
||||
-- @tab t2 a table
|
||||
-- @param ... extra arguments
|
||||
-- @return a table
|
||||
-- @usage map2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23,m=44}
|
||||
function tablex.map2 (fun,t1,t2,...)
|
||||
assert_arg_iterable(1,t1)
|
||||
assert_arg_iterable(2,t2)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for k,v in pairs(t1) do
|
||||
res[k] = fun(v,t2[k],...)
|
||||
end
|
||||
return setmeta(res,t1,'List')
|
||||
end
|
||||
|
||||
--- apply a function to values from two arrays.
|
||||
-- The result will be the length of the shortest array.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun a function of at least two arguments
|
||||
-- @array t1 a list-like table
|
||||
-- @array t2 a list-like table
|
||||
-- @param ... extra arguments
|
||||
-- @usage imap2('+',{1,2,3,m=4},{10,20,30,m=40}) is {11,22,23}
|
||||
function tablex.imap2 (fun,t1,t2,...)
|
||||
assert_arg_indexable(2,t1)
|
||||
assert_arg_indexable(3,t2)
|
||||
fun = function_arg(1,fun)
|
||||
local res,n = {},math.min(#t1,#t2)
|
||||
for i = 1,n do
|
||||
res[i] = fun(t1[i],t2[i],...)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- 'reduce' a list using a binary function.
|
||||
-- @func fun a function of two arguments
|
||||
-- @array t a list-like table
|
||||
-- @array memo optional initial memo value. Defaults to first value in table.
|
||||
-- @return the result of the function
|
||||
-- @usage reduce('+',{1,2,3,4}) == 10
|
||||
function tablex.reduce (fun,t,memo)
|
||||
assert_arg_indexable(2,t)
|
||||
fun = function_arg(1,fun)
|
||||
local n = #t
|
||||
if n == 0 then
|
||||
return memo
|
||||
end
|
||||
local res = memo and fun(memo, t[1]) or t[1]
|
||||
for i = 2,n do
|
||||
res = fun(res,t[i])
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- apply a function to all elements of a table.
|
||||
-- The arguments to the function will be the value,
|
||||
-- the key and _finally_ any extra arguments passed to this function.
|
||||
-- Note that the Lua 5.0 function table.foreach passed the _key_ first.
|
||||
-- @within Iterating
|
||||
-- @tab t a table
|
||||
-- @func fun a function on the elements; `function(value, key, ...)`
|
||||
-- @param ... extra arguments passed to `fun`
|
||||
-- @see tablex.transform
|
||||
function tablex.foreach(t,fun,...)
|
||||
assert_arg_iterable(1,t)
|
||||
fun = function_arg(2,fun)
|
||||
for k,v in pairs(t) do
|
||||
fun(v,k,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- apply a function to all elements of a list-like table in order.
|
||||
-- The arguments to the function will be the value,
|
||||
-- the index and _finally_ any extra arguments passed to this function
|
||||
-- @within Iterating
|
||||
-- @array t a table
|
||||
-- @func fun a function with at least one argument
|
||||
-- @param ... optional arguments
|
||||
function tablex.foreachi(t,fun,...)
|
||||
assert_arg_indexable(1,t)
|
||||
fun = function_arg(2,fun)
|
||||
for i = 1,#t do
|
||||
fun(t[i],i,...)
|
||||
end
|
||||
end
|
||||
|
||||
--- Apply a function to a number of tables.
|
||||
-- A more general version of map
|
||||
-- The result is a table containing the result of applying that function to the
|
||||
-- ith value of each table. Length of output list is the minimum length of all the lists
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun a function of n arguments
|
||||
-- @tab ... n tables
|
||||
-- @usage mapn(function(x,y,z) return x+y+z end, {1,2,3},{10,20,30},{100,200,300}) is {111,222,333}
|
||||
-- @usage mapn(math.max, {1,20,300},{10,2,3},{100,200,100}) is {100,200,300}
|
||||
-- @param fun A function that takes as many arguments as there are tables
|
||||
function tablex.mapn(fun,...)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
local lists = {...}
|
||||
local minn = 1e40
|
||||
for i = 1,#lists do
|
||||
minn = min(minn,#(lists[i]))
|
||||
end
|
||||
for i = 1,minn do
|
||||
local args,k = {},1
|
||||
for j = 1,#lists do
|
||||
args[k] = lists[j][i]
|
||||
k = k + 1
|
||||
end
|
||||
res[#res+1] = fun(unpack(args))
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- call the function with the key and value pairs from a table.
|
||||
-- The function can return a value and a key (note the order!). If both
|
||||
-- are not nil, then this pair is inserted into the result: if the key already exists, we convert the value for that
|
||||
-- key into a table and append into it. If only value is not nil, then it is appended to the result.
|
||||
-- @within MappingAndFiltering
|
||||
-- @func fun A function which will be passed each key and value as arguments, plus any extra arguments to pairmap.
|
||||
-- @tab t A table
|
||||
-- @param ... optional arguments
|
||||
-- @usage pairmap(function(k,v) return v end,{fred=10,bonzo=20}) is {10,20} _or_ {20,10}
|
||||
-- @usage pairmap(function(k,v) return {k,v},k end,{one=1,two=2}) is {one={'one',1},two={'two',2}}
|
||||
function tablex.pairmap(fun,t,...)
|
||||
assert_arg_iterable(1,t)
|
||||
fun = function_arg(1,fun)
|
||||
local res = {}
|
||||
for k,v in pairs(t) do
|
||||
local rv,rk = fun(k,v,...)
|
||||
if rk then
|
||||
if res[rk] then
|
||||
if type(res[rk]) == 'table' then
|
||||
table.insert(res[rk],rv)
|
||||
else
|
||||
res[rk] = {res[rk], rv}
|
||||
end
|
||||
else
|
||||
res[rk] = rv
|
||||
end
|
||||
else
|
||||
res[#res+1] = rv
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local function keys_op(i,v) return i end
|
||||
|
||||
--- return all the keys of a table in arbitrary order.
|
||||
-- @within Extraction
|
||||
-- @tab t A list-like table where the values are the keys of the input table
|
||||
function tablex.keys(t)
|
||||
assert_arg_iterable(1,t)
|
||||
return makelist(tablex.pairmap(keys_op,t))
|
||||
end
|
||||
|
||||
local function values_op(i,v) return v end
|
||||
|
||||
--- return all the values of the table in arbitrary order
|
||||
-- @within Extraction
|
||||
-- @tab t A list-like table where the values are the values of the input table
|
||||
function tablex.values(t)
|
||||
assert_arg_iterable(1,t)
|
||||
return makelist(tablex.pairmap(values_op,t))
|
||||
end
|
||||
|
||||
local function index_map_op (i,v) return i,v end
|
||||
|
||||
--- create an index map from a list-like table. The original values become keys,
|
||||
-- and the associated values are the indices into the original list.
|
||||
-- @array t a list-like table
|
||||
-- @return a map-like table
|
||||
function tablex.index_map (t)
|
||||
assert_arg_indexable(1,t)
|
||||
return makemap(tablex.pairmap(index_map_op,t))
|
||||
end
|
||||
|
||||
local function set_op(i,v) return true,v end
|
||||
|
||||
--- create a set from a list-like table. A set is a table where the original values
|
||||
-- become keys, and the associated values are all true.
|
||||
-- @array t a list-like table
|
||||
-- @return a set (a map-like table)
|
||||
function tablex.makeset (t)
|
||||
assert_arg_indexable(1,t)
|
||||
return setmetatable(tablex.pairmap(set_op,t),require('pl.Set'))
|
||||
end
|
||||
|
||||
--- combine two tables, either as union or intersection. Corresponds to
|
||||
-- set operations for sets () but more general. Not particularly
|
||||
-- useful for list-like tables.
|
||||
-- @within Merging
|
||||
-- @tab t1 a table
|
||||
-- @tab t2 a table
|
||||
-- @bool dup true for a union, false for an intersection.
|
||||
-- @usage merge({alice=23,fred=34},{bob=25,fred=34}) is {fred=34}
|
||||
-- @usage merge({alice=23,fred=34},{bob=25,fred=34},true) is {bob=25,fred=34,alice=23}
|
||||
-- @see tablex.index_map
|
||||
function tablex.merge (t1,t2,dup)
|
||||
assert_arg_iterable(1,t1)
|
||||
assert_arg_iterable(2,t2)
|
||||
local res = {}
|
||||
for k,v in pairs(t1) do
|
||||
if dup or t2[k] then res[k] = v end
|
||||
end
|
||||
if dup then
|
||||
for k,v in pairs(t2) do
|
||||
res[k] = v
|
||||
end
|
||||
end
|
||||
return setmeta(res,t1,'Map')
|
||||
end
|
||||
|
||||
--- the union of two map-like tables.
|
||||
-- If there are duplicate keys, the second table wins.
|
||||
-- @tab t1 a table
|
||||
-- @tab t2 a table
|
||||
-- @treturn tab
|
||||
-- @see tablex.merge
|
||||
function tablex.union(t1, t2)
|
||||
return tablex.merge(t1, t2, true)
|
||||
end
|
||||
|
||||
--- the intersection of two map-like tables.
|
||||
-- @tab t1 a table
|
||||
-- @tab t2 a table
|
||||
-- @treturn tab
|
||||
-- @see tablex.merge
|
||||
function tablex.intersection(t1, t2)
|
||||
return tablex.merge(t1, t2, false)
|
||||
end
|
||||
|
||||
--- a new table which is the difference of two tables.
|
||||
-- With sets (where the values are all true) this is set difference and
|
||||
-- symmetric difference depending on the third parameter.
|
||||
-- @within Merging
|
||||
-- @tab s1 a map-like table or set
|
||||
-- @tab s2 a map-like table or set
|
||||
-- @bool symm symmetric difference (default false)
|
||||
-- @return a map-like table or set
|
||||
function tablex.difference (s1,s2,symm)
|
||||
assert_arg_iterable(1,s1)
|
||||
assert_arg_iterable(2,s2)
|
||||
local res = {}
|
||||
for k,v in pairs(s1) do
|
||||
if s2[k] == nil then res[k] = v end
|
||||
end
|
||||
if symm then
|
||||
for k,v in pairs(s2) do
|
||||
if s1[k] == nil then res[k] = v end
|
||||
end
|
||||
end
|
||||
return setmeta(res,s1,'Map')
|
||||
end
|
||||
|
||||
--- A table where the key/values are the values and value counts of the table.
|
||||
-- @array t a list-like table
|
||||
-- @func cmp a function that defines equality (otherwise uses ==)
|
||||
-- @return a map-like table
|
||||
-- @see seq.count_map
|
||||
function tablex.count_map (t,cmp)
|
||||
assert_arg_indexable(1,t)
|
||||
local res,mask = {},{}
|
||||
cmp = function_arg(2,cmp or '==')
|
||||
local n = #t
|
||||
for i = 1,#t do
|
||||
local v = t[i]
|
||||
if not mask[v] then
|
||||
mask[v] = true
|
||||
-- check this value against all other values
|
||||
res[v] = 1 -- there's at least one instance
|
||||
for j = i+1,n do
|
||||
local w = t[j]
|
||||
local ok = cmp(v,w)
|
||||
if ok then
|
||||
res[v] = res[v] + 1
|
||||
mask[w] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return makemap(res)
|
||||
end
|
||||
|
||||
--- filter an array's values using a predicate function
|
||||
-- @within MappingAndFiltering
|
||||
-- @array t a list-like table
|
||||
-- @func pred a boolean function
|
||||
-- @param arg optional argument to be passed as second argument of the predicate
|
||||
function tablex.filter (t,pred,arg)
|
||||
assert_arg_indexable(1,t)
|
||||
pred = function_arg(2,pred)
|
||||
local res,k = {},1
|
||||
for i = 1,#t do
|
||||
local v = t[i]
|
||||
if pred(v,arg) then
|
||||
res[k] = v
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
return setmeta(res,t,'List')
|
||||
end
|
||||
|
||||
--- return a table where each element is a table of the ith values of an arbitrary
|
||||
-- number of tables. It is equivalent to a matrix transpose.
|
||||
-- @within Merging
|
||||
-- @usage zip({10,20,30},{100,200,300}) is {{10,100},{20,200},{30,300}}
|
||||
-- @array ... arrays to be zipped
|
||||
function tablex.zip(...)
|
||||
return tablex.mapn(function(...) return {...} end,...)
|
||||
end
|
||||
|
||||
local _copy
|
||||
function _copy (dest,src,idest,isrc,nsrc,clean_tail)
|
||||
idest = idest or 1
|
||||
isrc = isrc or 1
|
||||
local iend
|
||||
if not nsrc then
|
||||
nsrc = #src
|
||||
iend = #src
|
||||
else
|
||||
iend = isrc + min(nsrc-1,#src-isrc)
|
||||
end
|
||||
if dest == src then -- special case
|
||||
if idest > isrc and iend >= idest then -- overlapping ranges
|
||||
src = tablex.sub(src,isrc,nsrc)
|
||||
isrc = 1; iend = #src
|
||||
end
|
||||
end
|
||||
for i = isrc,iend do
|
||||
dest[idest] = src[i]
|
||||
idest = idest + 1
|
||||
end
|
||||
if clean_tail then
|
||||
tablex.clear(dest,idest)
|
||||
end
|
||||
return dest
|
||||
end
|
||||
|
||||
--- copy an array into another one, clearing `dest` after `idest+nsrc`, if necessary.
|
||||
-- @within Copying
|
||||
-- @array dest a list-like table
|
||||
-- @array src a list-like table
|
||||
-- @int[opt=1] idest where to start copying values into destination
|
||||
-- @int[opt=1] isrc where to start copying values from source
|
||||
-- @int[opt=#src] nsrc number of elements to copy from source
|
||||
function tablex.icopy (dest,src,idest,isrc,nsrc)
|
||||
assert_arg_indexable(1,dest)
|
||||
assert_arg_indexable(2,src)
|
||||
return _copy(dest,src,idest,isrc,nsrc,true)
|
||||
end
|
||||
|
||||
--- copy an array into another one.
|
||||
-- @within Copying
|
||||
-- @array dest a list-like table
|
||||
-- @array src a list-like table
|
||||
-- @int[opt=1] idest where to start copying values into destination
|
||||
-- @int[opt=1] isrc where to start copying values from source
|
||||
-- @int[opt=#src] nsrc number of elements to copy from source
|
||||
function tablex.move (dest,src,idest,isrc,nsrc)
|
||||
assert_arg_indexable(1,dest)
|
||||
assert_arg_indexable(2,src)
|
||||
return _copy(dest,src,idest,isrc,nsrc,false)
|
||||
end
|
||||
|
||||
function tablex._normalize_slice(self,first,last)
|
||||
local sz = #self
|
||||
if not first then first=1 end
|
||||
if first<0 then first=sz+first+1 end
|
||||
-- make the range _inclusive_!
|
||||
if not last then last=sz end
|
||||
if last < 0 then last=sz+1+last end
|
||||
return first,last
|
||||
end
|
||||
|
||||
--- Extract a range from a table, like 'string.sub'.
|
||||
-- If first or last are negative then they are relative to the end of the list
|
||||
-- eg. sub(t,-2) gives last 2 entries in a list, and
|
||||
-- sub(t,-4,-2) gives from -4th to -2nd
|
||||
-- @within Extraction
|
||||
-- @array t a list-like table
|
||||
-- @int first An index
|
||||
-- @int last An index
|
||||
-- @return a new List
|
||||
function tablex.sub(t,first,last)
|
||||
assert_arg_indexable(1,t)
|
||||
first,last = tablex._normalize_slice(t,first,last)
|
||||
local res={}
|
||||
for i=first,last do append(res,t[i]) end
|
||||
return setmeta(res,t,'List')
|
||||
end
|
||||
|
||||
--- set an array range to a value. If it's a function we use the result
|
||||
-- of applying it to the indices.
|
||||
-- @array t a list-like table
|
||||
-- @param val a value
|
||||
-- @int[opt=1] i1 start range
|
||||
-- @int[opt=#t] i2 end range
|
||||
function tablex.set (t,val,i1,i2)
|
||||
assert_arg_indexable(1,t)
|
||||
i1,i2 = i1 or 1,i2 or #t
|
||||
if types.is_callable(val) then
|
||||
for i = i1,i2 do
|
||||
t[i] = val(i)
|
||||
end
|
||||
else
|
||||
for i = i1,i2 do
|
||||
t[i] = val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- create a new array of specified size with initial value.
|
||||
-- @int n size
|
||||
-- @param val initial value (can be `nil`, but don't expect `#` to work!)
|
||||
-- @return the table
|
||||
function tablex.new (n,val)
|
||||
local res = {}
|
||||
tablex.set(res,val,1,n)
|
||||
return res
|
||||
end
|
||||
|
||||
--- clear out the contents of a table.
|
||||
-- @array t a list
|
||||
-- @param istart optional start position
|
||||
function tablex.clear(t,istart)
|
||||
istart = istart or 1
|
||||
for i = istart,#t do remove(t) end
|
||||
end
|
||||
|
||||
--- insert values into a table.
|
||||
-- similar to `table.insert` but inserts values from given table `values`,
|
||||
-- not the object itself, into table `t` at position `pos`.
|
||||
-- @within Copying
|
||||
-- @array t the list
|
||||
-- @int[opt] position (default is at end)
|
||||
-- @array values
|
||||
function tablex.insertvalues(t, ...)
|
||||
assert_arg(1,t,'table')
|
||||
local pos, values
|
||||
if select('#', ...) == 1 then
|
||||
pos,values = #t+1, ...
|
||||
else
|
||||
pos,values = ...
|
||||
end
|
||||
if #values > 0 then
|
||||
for i=#t,pos,-1 do
|
||||
t[i+#values] = t[i]
|
||||
end
|
||||
local offset = 1 - pos
|
||||
for i=pos,pos+#values-1 do
|
||||
t[i] = values[i + offset]
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
--- remove a range of values from a table.
|
||||
-- End of range may be negative.
|
||||
-- @array t a list-like table
|
||||
-- @int i1 start index
|
||||
-- @int i2 end index
|
||||
-- @return the table
|
||||
function tablex.removevalues (t,i1,i2)
|
||||
assert_arg(1,t,'table')
|
||||
i1,i2 = tablex._normalize_slice(t,i1,i2)
|
||||
for i = i1,i2 do
|
||||
remove(t,i1)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local _find
|
||||
_find = function (t,value,tables)
|
||||
for k,v in pairs(t) do
|
||||
if v == value then return k end
|
||||
end
|
||||
for k,v in pairs(t) do
|
||||
if not tables[v] and type(v) == 'table' then
|
||||
tables[v] = true
|
||||
local res = _find(v,value,tables)
|
||||
if res then
|
||||
res = tostring(res)
|
||||
if type(k) ~= 'string' then
|
||||
return '['..k..']'..res
|
||||
else
|
||||
return k..'.'..res
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- find a value in a table by recursive search.
|
||||
-- @within Finding
|
||||
-- @tab t the table
|
||||
-- @param value the value
|
||||
-- @array[opt] exclude any tables to avoid searching
|
||||
-- @return a fieldspec, e.g. 'a.b' or 'math.sin'
|
||||
-- @usage search(_G,math.sin,{package.path}) == 'math.sin'
|
||||
function tablex.search (t,value,exclude)
|
||||
assert_arg_iterable(1,t)
|
||||
local tables = {[t]=true}
|
||||
if exclude then
|
||||
for _,v in pairs(exclude) do tables[v] = true end
|
||||
end
|
||||
return _find(t,value,tables)
|
||||
end
|
||||
|
||||
--- return an iterator to a table sorted by its keys
|
||||
-- @within Iterating
|
||||
-- @tab t the table
|
||||
-- @func f an optional comparison function (f(x,y) is true if x < y)
|
||||
-- @usage for k,v in tablex.sort(t) do print(k,v) end
|
||||
-- @return an iterator to traverse elements sorted by the keys
|
||||
function tablex.sort(t,f)
|
||||
local keys = {}
|
||||
for k in pairs(t) do keys[#keys + 1] = k end
|
||||
tsort(keys,f)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
return keys[i], t[keys[i]]
|
||||
end
|
||||
end
|
||||
|
||||
--- return an iterator to a table sorted by its values
|
||||
-- @within Iterating
|
||||
-- @tab t the table
|
||||
-- @func f an optional comparison function (f(x,y) is true if x < y)
|
||||
-- @usage for k,v in tablex.sortv(t) do print(k,v) end
|
||||
-- @return an iterator to traverse elements sorted by the values
|
||||
function tablex.sortv(t,f)
|
||||
f = function_arg(2, f or '<')
|
||||
local keys = {}
|
||||
for k in pairs(t) do keys[#keys + 1] = k end
|
||||
tsort(keys,function(x, y) return f(t[x], t[y]) end)
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
return keys[i], t[keys[i]]
|
||||
end
|
||||
end
|
||||
|
||||
--- modifies a table to be read only.
|
||||
-- This only offers weak protection. Tables can still be modified with
|
||||
-- `table.insert` and `rawset`.
|
||||
--
|
||||
-- *NOTE*: for Lua 5.1 length, pairs and ipairs will not work, since the
|
||||
-- equivalent metamethods are only available in Lua 5.2 and newer.
|
||||
-- @tab t the table
|
||||
-- @return the table read only (a proxy).
|
||||
function tablex.readonly(t)
|
||||
local mt = {
|
||||
__index=t,
|
||||
__newindex=function(t, k, v) error("Attempt to modify read-only table", 2) end,
|
||||
__pairs=function() return pairs(t) end,
|
||||
__ipairs=function() return ipairs(t) end,
|
||||
__len=function() return #t end,
|
||||
__metatable=false
|
||||
}
|
||||
return setmetatable({}, mt)
|
||||
end
|
||||
|
||||
return tablex
|
202
Documentation/compiler/pl/template.lua
Normal file
202
Documentation/compiler/pl/template.lua
Normal file
|
@ -0,0 +1,202 @@
|
|||
--- A template preprocessor.
|
||||
-- Originally by [Ricki Lake](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor)
|
||||
--
|
||||
-- There are two rules:
|
||||
--
|
||||
-- * lines starting with # are Lua
|
||||
-- * otherwise, `$(expr)` is the result of evaluating `expr`
|
||||
--
|
||||
-- Example:
|
||||
--
|
||||
-- # for i = 1,3 do
|
||||
-- $(i) Hello, Word!
|
||||
-- # end
|
||||
-- ===>
|
||||
-- 1 Hello, Word!
|
||||
-- 2 Hello, Word!
|
||||
-- 3 Hello, Word!
|
||||
--
|
||||
-- Other escape characters can be used, when the defaults conflict
|
||||
-- with the output language.
|
||||
--
|
||||
-- > for _,n in pairs{'one','two','three'} do
|
||||
-- static int l_${n} (luaState *state);
|
||||
-- > end
|
||||
--
|
||||
-- See @{03-strings.md.Another_Style_of_Template|the Guide}.
|
||||
--
|
||||
-- Dependencies: `pl.utils`
|
||||
-- @module pl.template
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
|
||||
local append,format,strsub,strfind,strgsub = table.insert,string.format,string.sub,string.find,string.gsub
|
||||
|
||||
local APPENDER = "\n__R_size = __R_size + 1; __R_table[__R_size] = "
|
||||
|
||||
local function parseDollarParen(pieces, chunk, exec_pat, newline)
|
||||
local s = 1
|
||||
for term, executed, e in chunk:gmatch(exec_pat) do
|
||||
executed = '('..strsub(executed,2,-2)..')'
|
||||
append(pieces, APPENDER..format("%q", strsub(chunk,s, term - 1)))
|
||||
append(pieces, APPENDER..format("__tostring(%s or '')", executed))
|
||||
s = e
|
||||
end
|
||||
local r
|
||||
if newline then
|
||||
r = format("%q", strgsub(strsub(chunk,s),"\n",""))
|
||||
else
|
||||
r = format("%q", strsub(chunk,s))
|
||||
end
|
||||
if r ~= '""' then
|
||||
append(pieces, APPENDER..r)
|
||||
end
|
||||
end
|
||||
|
||||
local function parseHashLines(chunk,inline_escape,brackets,esc,newline)
|
||||
local exec_pat = "()"..inline_escape.."(%b"..brackets..")()"
|
||||
|
||||
local esc_pat = esc.."+([^\n]*\n?)"
|
||||
local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat
|
||||
local pieces, s = {"return function()\nlocal __R_size, __R_table, __tostring = 0, {}, __tostring", n = 1}, 1
|
||||
while true do
|
||||
local _, e, lua = strfind(chunk,esc_pat1, s)
|
||||
if not e then
|
||||
local ss
|
||||
ss, e, lua = strfind(chunk,esc_pat2, s)
|
||||
parseDollarParen(pieces, strsub(chunk,s, ss), exec_pat, newline)
|
||||
if not e then break end
|
||||
end
|
||||
if strsub(lua, -1, -1) == "\n" then lua = strsub(lua, 1, -2) end
|
||||
append(pieces, "\n"..lua)
|
||||
s = e + 1
|
||||
end
|
||||
append(pieces, "\nreturn __R_table\nend")
|
||||
|
||||
-- let's check for a special case where there is nothing to template, but it's
|
||||
-- just a single static string
|
||||
local short = false
|
||||
if (#pieces == 3) and (pieces[2]:find(APPENDER, 1, true) == 1) then
|
||||
pieces = { "return " .. pieces[2]:sub(#APPENDER+1,-1) }
|
||||
short = true
|
||||
end
|
||||
-- if short == true, the generated function will not return a table of strings,
|
||||
-- but a single string
|
||||
return table.concat(pieces), short
|
||||
end
|
||||
|
||||
local template = {}
|
||||
|
||||
--- expand the template using the specified environment.
|
||||
-- This function will compile and render the template. For more performant
|
||||
-- recurring usage use the two step approach by using `compile` and `ct:render`.
|
||||
-- There are six special fields in the environment table `env`
|
||||
--
|
||||
-- * `_parent`: continue looking up in this table (e.g. `_parent=_G`).
|
||||
-- * `_brackets`: bracket pair that wraps inline Lua expressions, default is '()'.
|
||||
-- * `_escape`: character marking Lua lines, default is '#'
|
||||
-- * `_inline_escape`: character marking inline Lua expression, default is '$'.
|
||||
-- * `_chunk_name`: chunk name for loaded templates, used if there
|
||||
-- is an error in Lua code. Default is 'TMP'.
|
||||
-- * `_debug`: if truthy, the generated code will be printed upon a render error
|
||||
--
|
||||
-- @string str the template string
|
||||
-- @tab[opt] env the environment
|
||||
-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last
|
||||
-- return value (`source_code`) is only returned if the debug option is used.
|
||||
function template.substitute(str,env)
|
||||
env = env or {}
|
||||
local t, err = template.compile(str, {
|
||||
chunk_name = rawget(env,"_chunk_name"),
|
||||
escape = rawget(env,"_escape"),
|
||||
inline_escape = rawget(env,"_inline_escape"),
|
||||
inline_brackets = rawget(env,"_brackets"),
|
||||
newline = nil,
|
||||
debug = rawget(env,"_debug")
|
||||
})
|
||||
if not t then return t, err end
|
||||
|
||||
return t:render(env, rawget(env,"_parent"), rawget(env,"_debug"))
|
||||
end
|
||||
|
||||
--- executes the previously compiled template and renders it.
|
||||
-- @function ct:render
|
||||
-- @tab[opt] env the environment.
|
||||
-- @tab[opt] parent continue looking up in this table (e.g. `parent=_G`).
|
||||
-- @bool[opt] db if thruthy, it will print the code upon a render error
|
||||
-- (provided the template was compiled with the debug option).
|
||||
-- @return `rendered template + nil + source_code`, or `nil + error + source_code`. The last return value
|
||||
-- (`source_code`) is only returned if the template was compiled with the debug option.
|
||||
-- @usage
|
||||
-- local ct, err = template.compile(my_template)
|
||||
-- local rendered , err = ct:render(my_env, parent)
|
||||
local render = function(self, env, parent, db)
|
||||
env = env or {}
|
||||
if parent then -- parent is a bit silly, but for backward compatibility retained
|
||||
setmetatable(env, {__index = parent})
|
||||
end
|
||||
setmetatable(self.env, {__index = env})
|
||||
|
||||
local res, out = xpcall(self.fn, debug.traceback)
|
||||
if not res then
|
||||
if self.code and db then print(self.code) end
|
||||
return nil, out, self.code
|
||||
end
|
||||
return table.concat(out), nil, self.code
|
||||
end
|
||||
|
||||
--- compiles the template.
|
||||
-- Returns an object that can repeatedly be rendered without parsing/compiling
|
||||
-- the template again.
|
||||
-- The options passed in the `opts` table support the following options:
|
||||
--
|
||||
-- * `chunk_name`: chunk name for loaded templates, used if there
|
||||
-- is an error in Lua code. Default is 'TMP'.
|
||||
-- * `escape`: character marking Lua lines, default is '#'
|
||||
-- * `inline_escape`: character marking inline Lua expression, default is '$'.
|
||||
-- * `inline_brackets`: bracket pair that wraps inline Lua expressions, default is '()'.
|
||||
-- * `newline`: string to replace newline characters, default is `nil` (not replacing newlines).
|
||||
-- * `debug`: if truthy, the generated source code will be retained within the compiled template object, default is `nil`.
|
||||
--
|
||||
-- @string str the template string
|
||||
-- @tab[opt] opts the compilation options to use
|
||||
-- @return template object, or `nil + error + source_code`
|
||||
-- @usage
|
||||
-- local ct, err = template.compile(my_template)
|
||||
-- local rendered , err = ct:render(my_env, parent)
|
||||
function template.compile(str, opts)
|
||||
opts = opts or {}
|
||||
local chunk_name = opts.chunk_name or 'TMP'
|
||||
local escape = opts.escape or '#'
|
||||
local inline_escape = opts.inline_escape or '$'
|
||||
local inline_brackets = opts.inline_brackets or '()'
|
||||
|
||||
local code, short = parseHashLines(str,inline_escape,inline_brackets,escape,opts.newline)
|
||||
local env = { __tostring = tostring }
|
||||
local fn, err = utils.load(code, chunk_name,'t',env)
|
||||
if not fn then return nil, err, code end
|
||||
|
||||
if short then
|
||||
-- the template returns a single constant string, let's optimize for that
|
||||
local constant_string = fn()
|
||||
return {
|
||||
fn = fn(),
|
||||
env = env,
|
||||
render = function(self) -- additional params can be ignored
|
||||
-- skip the metatable magic and error handling in the render
|
||||
-- function above for this special case
|
||||
return constant_string, nil, self.code
|
||||
end,
|
||||
code = opts.debug and code or nil,
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
fn = fn(),
|
||||
env = env,
|
||||
render = render,
|
||||
code = opts.debug and code or nil,
|
||||
}
|
||||
end
|
||||
|
||||
return template
|
164
Documentation/compiler/pl/test.lua
Normal file
164
Documentation/compiler/pl/test.lua
Normal file
|
@ -0,0 +1,164 @@
|
|||
--- Useful test utilities.
|
||||
--
|
||||
-- test.asserteq({1,2},{1,2}) -- can compare tables
|
||||
-- test.asserteq(1.2,1.19,0.02) -- compare FP numbers within precision
|
||||
-- T = test.tuple -- used for comparing multiple results
|
||||
-- test.asserteq(T(string.find(" me","me")),T(2,3))
|
||||
--
|
||||
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.pretty`, `pl.path`, `debug`
|
||||
-- @module pl.test
|
||||
|
||||
local tablex = require 'pl.tablex'
|
||||
local utils = require 'pl.utils'
|
||||
local pretty = require 'pl.pretty'
|
||||
local path = require 'pl.path'
|
||||
local type,unpack,pack = type,utils.unpack,utils.pack
|
||||
local clock = os.clock
|
||||
local debug = require 'debug'
|
||||
local io = io
|
||||
|
||||
local function dump(x)
|
||||
if type(x) == 'table' and not (getmetatable(x) and getmetatable(x).__tostring) then
|
||||
return pretty.write(x,' ',true)
|
||||
elseif type(x) == 'string' then
|
||||
return '"'..x..'"'
|
||||
else
|
||||
return tostring(x)
|
||||
end
|
||||
end
|
||||
|
||||
local test = {}
|
||||
|
||||
---- error handling for test results.
|
||||
-- By default, this writes to stderr and exits the program.
|
||||
-- Re-define this function to raise an error and/or redirect output
|
||||
function test.error_handler(file,line,got_text, needed_text,msg)
|
||||
local err = io.stderr
|
||||
err:write(path.basename(file)..':'..line..': assertion failed\n')
|
||||
err:write("got:\t",got_text,'\n')
|
||||
err:write("needed:\t",needed_text,'\n')
|
||||
utils.quit(1,msg or "these values were not equal")
|
||||
end
|
||||
|
||||
local function complain (x,y,msg,where)
|
||||
local i = debug.getinfo(3 + (where or 0))
|
||||
test.error_handler(i.short_src,i.currentline,dump(x),dump(y),msg)
|
||||
end
|
||||
|
||||
--- general test complain message.
|
||||
-- Useful for composing new test functions (see tests/tablex.lua for an example)
|
||||
-- @param x a value
|
||||
-- @param y value to compare first value against
|
||||
-- @param msg message
|
||||
-- @param where extra level offset for errors
|
||||
-- @function complain
|
||||
test.complain = complain
|
||||
|
||||
--- like assert, except takes two arguments that must be equal and can be tables.
|
||||
-- If they are plain tables, it will use tablex.deepcompare.
|
||||
-- @param x any value
|
||||
-- @param y a value equal to x
|
||||
-- @param eps an optional tolerance for numerical comparisons
|
||||
-- @param where extra level offset
|
||||
function test.asserteq (x,y,eps,where)
|
||||
local res = x == y
|
||||
if not res then
|
||||
res = tablex.deepcompare(x,y,true,eps)
|
||||
end
|
||||
if not res then
|
||||
complain(x,y,nil,where)
|
||||
end
|
||||
end
|
||||
|
||||
--- assert that the first string matches the second.
|
||||
-- @param s1 a string
|
||||
-- @param s2 a string
|
||||
-- @param where extra level offset
|
||||
function test.assertmatch (s1,s2,where)
|
||||
if not s1:match(s2) then
|
||||
complain (s1,s2,"these strings did not match",where)
|
||||
end
|
||||
end
|
||||
|
||||
--- assert that the function raises a particular error.
|
||||
-- @param fn a function or a table of the form {function,arg1,...}
|
||||
-- @param e a string to match the error against
|
||||
-- @param where extra level offset
|
||||
function test.assertraise(fn,e,where)
|
||||
local ok, err
|
||||
if type(fn) == 'table' then
|
||||
ok, err = pcall(unpack(fn))
|
||||
else
|
||||
ok, err = pcall(fn)
|
||||
end
|
||||
if ok or err:match(e)==nil then
|
||||
complain (err,e,"these errors did not match",where)
|
||||
end
|
||||
end
|
||||
|
||||
--- a version of asserteq that takes two pairs of values.
|
||||
-- <code>x1==y1 and x2==y2</code> must be true. Useful for functions that naturally
|
||||
-- return two values.
|
||||
-- @param x1 any value
|
||||
-- @param x2 any value
|
||||
-- @param y1 any value
|
||||
-- @param y2 any value
|
||||
-- @param where extra level offset
|
||||
function test.asserteq2 (x1,x2,y1,y2,where)
|
||||
if x1 ~= y1 then complain(x1,y1,nil,where) end
|
||||
if x2 ~= y2 then complain(x2,y2,nil,where) end
|
||||
end
|
||||
|
||||
-- tuple type --
|
||||
|
||||
local tuple_mt = {
|
||||
unpack = unpack
|
||||
}
|
||||
tuple_mt.__index = tuple_mt
|
||||
|
||||
function tuple_mt.__tostring(self)
|
||||
local ts = {}
|
||||
for i=1, self.n do
|
||||
local s = self[i]
|
||||
ts[i] = type(s) == 'string' and ('%q'):format(s) or tostring(s)
|
||||
end
|
||||
return 'tuple(' .. table.concat(ts, ', ') .. ')'
|
||||
end
|
||||
|
||||
function tuple_mt.__eq(a, b)
|
||||
if a.n ~= b.n then return false end
|
||||
for i=1, a.n do
|
||||
if a[i] ~= b[i] then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function tuple_mt.__len(self)
|
||||
return self.n
|
||||
end
|
||||
|
||||
--- encode an arbitrary argument list as a tuple.
|
||||
-- This can be used to compare to other argument lists, which is
|
||||
-- very useful for testing functions which return a number of values.
|
||||
-- Unlike regular array-like tables ('sequences') they may contain nils.
|
||||
-- Tuples understand equality and know how to print themselves out.
|
||||
-- The # operator is defined to be the size, irrespecive of any nils,
|
||||
-- and there is an `unpack` method.
|
||||
-- @usage asserteq(tuple( ('ab'):find 'a'), tuple(1,1))
|
||||
function test.tuple(...)
|
||||
return setmetatable(pack(...), tuple_mt)
|
||||
end
|
||||
|
||||
--- Time a function. Call the function a given number of times, and report the number of seconds taken,
|
||||
-- together with a message. Any extra arguments will be passed to the function.
|
||||
-- @string msg a descriptive message
|
||||
-- @int n number of times to call the function
|
||||
-- @func fun the function
|
||||
-- @param ... optional arguments to fun
|
||||
function test.timer(msg,n,fun,...)
|
||||
local start = clock()
|
||||
for i = 1,n do fun(...) end
|
||||
utils.printf("%s: took %7.2f sec\n",msg,clock()-start)
|
||||
end
|
||||
|
||||
return test
|
26
Documentation/compiler/pl/text.lua
Normal file
26
Documentation/compiler/pl/text.lua
Normal file
|
@ -0,0 +1,26 @@
|
|||
--- Text processing utilities.
|
||||
--
|
||||
-- This provides a Template class (modeled after the same from the Python
|
||||
-- libraries, see string.Template). It also provides similar functions to those
|
||||
-- found in the textwrap module.
|
||||
--
|
||||
-- IMPORTANT: this module has been deprecated and will be removed in a future
|
||||
-- version (2.0). The contents of this module have moved to the `pl.stringx`
|
||||
-- module.
|
||||
--
|
||||
-- See @{03-strings.md.String_Templates|the Guide}.
|
||||
--
|
||||
-- Dependencies: `pl.stringx`, `pl.utils`
|
||||
-- @module pl.text
|
||||
|
||||
local utils = require("pl.utils")
|
||||
|
||||
utils.raise_deprecation {
|
||||
source = "Penlight " .. utils._VERSION,
|
||||
message = "the contents of module 'pl.text' has moved into 'pl.stringx'",
|
||||
version_removed = "2.0.0",
|
||||
deprecated_after = "1.11.0",
|
||||
no_trace = true,
|
||||
}
|
||||
|
||||
return require "pl.stringx"
|
183
Documentation/compiler/pl/types.lua
Normal file
183
Documentation/compiler/pl/types.lua
Normal file
|
@ -0,0 +1,183 @@
|
|||
---- Dealing with Detailed Type Information
|
||||
|
||||
-- Dependencies `pl.utils`
|
||||
-- @module pl.types
|
||||
|
||||
local utils = require 'pl.utils'
|
||||
local math_ceil = math.ceil
|
||||
local assert_arg = utils.assert_arg
|
||||
local types = {}
|
||||
|
||||
--- is the object either a function or a callable object?.
|
||||
-- @param obj Object to check.
|
||||
function types.is_callable (obj)
|
||||
return type(obj) == 'function' or getmetatable(obj) and getmetatable(obj).__call and true
|
||||
end
|
||||
|
||||
--- is the object of the specified type?.
|
||||
-- If the type is a string, then use type, otherwise compare with metatable.
|
||||
--
|
||||
-- NOTE: this function is imported from `utils.is_type`.
|
||||
-- @param obj An object to check
|
||||
-- @param tp The expected type
|
||||
-- @function is_type
|
||||
-- @see utils.is_type
|
||||
types.is_type = utils.is_type
|
||||
|
||||
local fileMT = getmetatable(io.stdout)
|
||||
|
||||
--- a string representation of a type.
|
||||
-- For tables and userdata with metatables, we assume that the metatable has a `_name`
|
||||
-- field. If the field is not present it will return 'unknown table' or
|
||||
-- 'unknown userdata'.
|
||||
-- Lua file objects return the type 'file'.
|
||||
-- @param obj an object
|
||||
-- @return a string like 'number', 'table', 'file' or 'List'
|
||||
function types.type (obj)
|
||||
local t = type(obj)
|
||||
if t == 'table' or t == 'userdata' then
|
||||
local mt = getmetatable(obj)
|
||||
if mt == fileMT then
|
||||
return 'file'
|
||||
elseif mt == nil then
|
||||
return t
|
||||
else
|
||||
-- TODO: the "unknown" is weird, it should just return the type
|
||||
return mt._name or "unknown "..t
|
||||
end
|
||||
else
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
--- is this number an integer?
|
||||
-- @param x a number
|
||||
-- @raise error if x is not a number
|
||||
-- @return boolean
|
||||
function types.is_integer (x)
|
||||
return math_ceil(x)==x
|
||||
end
|
||||
|
||||
--- Check if the object is "empty".
|
||||
-- An object is considered empty if it is:
|
||||
--
|
||||
-- - `nil`
|
||||
-- - a table without any items (key-value pairs or indexes)
|
||||
-- - a string with no content ("")
|
||||
-- - not a nil/table/string
|
||||
-- @param o The object to check if it is empty.
|
||||
-- @param ignore_spaces If the object is a string and this is true the string is
|
||||
-- considered empty if it only contains spaces.
|
||||
-- @return `true` if the object is empty, otherwise a falsy value.
|
||||
function types.is_empty(o, ignore_spaces)
|
||||
if o == nil then
|
||||
return true
|
||||
elseif type(o) == "table" then
|
||||
return next(o) == nil
|
||||
elseif type(o) == "string" then
|
||||
return o == "" or (not not ignore_spaces and (not not o:find("^%s+$")))
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function check_meta (val)
|
||||
if type(val) == 'table' then return true end
|
||||
return getmetatable(val)
|
||||
end
|
||||
|
||||
--- is an object 'array-like'?
|
||||
-- An object is array like if:
|
||||
--
|
||||
-- - it is a table, or
|
||||
-- - it has a metatable with `__len` and `__index` methods
|
||||
--
|
||||
-- NOTE: since `__len` is 5.2+, on 5.1 is usually returns `false` for userdata
|
||||
-- @param val any value.
|
||||
-- @return `true` if the object is array-like, otherwise a falsy value.
|
||||
function types.is_indexable (val)
|
||||
local mt = check_meta(val)
|
||||
if mt == true then return true end
|
||||
return mt and mt.__len and mt.__index and true
|
||||
end
|
||||
|
||||
--- can an object be iterated over with `pairs`?
|
||||
-- An object is iterable if:
|
||||
--
|
||||
-- - it is a table, or
|
||||
-- - it has a metatable with a `__pairs` meta method
|
||||
--
|
||||
-- NOTE: since `__pairs` is 5.2+, on 5.1 is usually returns `false` for userdata
|
||||
-- @param val any value.
|
||||
-- @return `true` if the object is iterable, otherwise a falsy value.
|
||||
function types.is_iterable (val)
|
||||
local mt = check_meta(val)
|
||||
if mt == true then return true end
|
||||
return mt and mt.__pairs and true
|
||||
end
|
||||
|
||||
--- can an object accept new key/pair values?
|
||||
-- An object is iterable if:
|
||||
--
|
||||
-- - it is a table, or
|
||||
-- - it has a metatable with a `__newindex` meta method
|
||||
--
|
||||
-- @param val any value.
|
||||
-- @return `true` if the object is writeable, otherwise a falsy value.
|
||||
function types.is_writeable (val)
|
||||
local mt = check_meta(val)
|
||||
if mt == true then return true end
|
||||
return mt and mt.__newindex and true
|
||||
end
|
||||
|
||||
-- Strings that should evaluate to true. -- TODO: add on/off ???
|
||||
local trues = { yes=true, y=true, ["true"]=true, t=true, ["1"]=true }
|
||||
-- Conditions types should evaluate to true.
|
||||
local true_types = {
|
||||
boolean=function(o, true_strs, check_objs) return o end,
|
||||
string=function(o, true_strs, check_objs)
|
||||
o = o:lower()
|
||||
if trues[o] then
|
||||
return true
|
||||
end
|
||||
-- Check alternative user provided strings.
|
||||
for _,v in ipairs(true_strs or {}) do
|
||||
if type(v) == "string" and o == v:lower() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
number=function(o, true_strs, check_objs) return o ~= 0 end,
|
||||
table=function(o, true_strs, check_objs) if check_objs and next(o) ~= nil then return true end return false end
|
||||
}
|
||||
--- Convert to a boolean value.
|
||||
-- True values are:
|
||||
--
|
||||
-- * boolean: true.
|
||||
-- * string: 'yes', 'y', 'true', 't', '1' or additional strings specified by `true_strs`.
|
||||
-- * number: Any non-zero value.
|
||||
-- * table: Is not empty and `check_objs` is true.
|
||||
-- * everything else: Is not `nil` and `check_objs` is true.
|
||||
--
|
||||
-- @param o The object to evaluate.
|
||||
-- @param[opt] true_strs optional Additional strings that when matched should evaluate to true. Comparison is case insensitive.
|
||||
-- This should be a List of strings. E.g. "ja" to support German.
|
||||
-- @param[opt] check_objs True if objects should be evaluated.
|
||||
-- @return true if the input evaluates to true, otherwise false.
|
||||
function types.to_bool(o, true_strs, check_objs)
|
||||
local true_func
|
||||
if true_strs then
|
||||
assert_arg(2, true_strs, "table")
|
||||
end
|
||||
true_func = true_types[type(o)]
|
||||
if true_func then
|
||||
return true_func(o, true_strs, check_objs)
|
||||
elseif check_objs and o ~= nil then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
return types
|
51
Documentation/compiler/pl/url.lua
Normal file
51
Documentation/compiler/pl/url.lua
Normal file
|
@ -0,0 +1,51 @@
|
|||
--- Python-style URL quoting library.
|
||||
--
|
||||
-- @module pl.url
|
||||
|
||||
local url = {}
|
||||
|
||||
local function quote_char(c)
|
||||
return string.format("%%%02X", string.byte(c))
|
||||
end
|
||||
|
||||
--- Quote the url, replacing special characters using the '%xx' escape.
|
||||
-- @string s the string
|
||||
-- @bool quote_plus Also escape slashes and replace spaces by plus signs.
|
||||
-- @return The quoted string, or if `s` wasn't a string, just plain unaltered `s`.
|
||||
function url.quote(s, quote_plus)
|
||||
if type(s) ~= "string" then
|
||||
return s
|
||||
end
|
||||
|
||||
s = s:gsub("\n", "\r\n")
|
||||
s = s:gsub("([^A-Za-z0-9 %-_%./])", quote_char)
|
||||
if quote_plus then
|
||||
s = s:gsub(" ", "+")
|
||||
s = s:gsub("/", quote_char)
|
||||
else
|
||||
s = s:gsub(" ", "%%20")
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
local function unquote_char(h)
|
||||
return string.char(tonumber(h, 16))
|
||||
end
|
||||
|
||||
--- Unquote the url, replacing '%xx' escapes and plus signs.
|
||||
-- @string s the string
|
||||
-- @return The unquoted string, or if `s` wasn't a string, just plain unaltered `s`.
|
||||
function url.unquote(s)
|
||||
if type(s) ~= "string" then
|
||||
return s
|
||||
end
|
||||
|
||||
s = s:gsub("+", " ")
|
||||
s = s:gsub("%%(%x%x)", unquote_char)
|
||||
s = s:gsub("\r\n", "\n")
|
||||
|
||||
return s
|
||||
end
|
||||
|
||||
return url
|
947
Documentation/compiler/pl/utils.lua
Normal file
947
Documentation/compiler/pl/utils.lua
Normal file
|
@ -0,0 +1,947 @@
|
|||
--- Generally useful routines.
|
||||
-- See @{01-introduction.md.Generally_useful_functions|the Guide}.
|
||||
--
|
||||
-- Dependencies: `pl.compat`, all exported fields and functions from
|
||||
-- `pl.compat` are also available in this module.
|
||||
--
|
||||
-- @module pl.utils
|
||||
local format = string.format
|
||||
local compat = require 'pl.compat'
|
||||
local stdout = io.stdout
|
||||
local append = table.insert
|
||||
local concat = table.concat
|
||||
local _unpack = table.unpack -- always injected by 'compat'
|
||||
local find = string.find
|
||||
local sub = string.sub
|
||||
local next = next
|
||||
local floor = math.floor
|
||||
|
||||
local is_windows = compat.is_windows
|
||||
local err_mode = 'default'
|
||||
local raise
|
||||
local operators
|
||||
local _function_factories = {}
|
||||
|
||||
|
||||
local utils = { _VERSION = "1.13.1" }
|
||||
for k, v in pairs(compat) do utils[k] = v end
|
||||
|
||||
--- Some standard patterns
|
||||
-- @table patterns
|
||||
utils.patterns = {
|
||||
FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*', -- floating point number
|
||||
INTEGER = '[+%-%d]%d*', -- integer number
|
||||
IDEN = '[%a_][%w_]*', -- identifier
|
||||
FILE = '[%a%.\\][:%][%w%._%-\\]*', -- file
|
||||
}
|
||||
|
||||
|
||||
--- Standard meta-tables as used by other Penlight modules
|
||||
-- @table stdmt
|
||||
-- @field List the List metatable
|
||||
-- @field Map the Map metatable
|
||||
-- @field Set the Set metatable
|
||||
-- @field MultiMap the MultiMap metatable
|
||||
utils.stdmt = {
|
||||
List = {_name='List'},
|
||||
Map = {_name='Map'},
|
||||
Set = {_name='Set'},
|
||||
MultiMap = {_name='MultiMap'},
|
||||
}
|
||||
|
||||
|
||||
--- pack an argument list into a table.
|
||||
-- @param ... any arguments
|
||||
-- @return a table with field `n` set to the length
|
||||
-- @function utils.pack
|
||||
-- @see compat.pack
|
||||
-- @see utils.npairs
|
||||
-- @see utils.unpack
|
||||
utils.pack = table.pack -- added here to be symmetrical with unpack
|
||||
|
||||
--- unpack a table and return its contents.
|
||||
--
|
||||
-- NOTE: this implementation differs from the Lua implementation in the way
|
||||
-- that this one DOES honor the `n` field in the table `t`, such that it is 'nil-safe'.
|
||||
-- @param t table to unpack
|
||||
-- @param[opt] i index from which to start unpacking, defaults to 1
|
||||
-- @param[opt] j index of the last element to unpack, defaults to `t.n` or else `#t`
|
||||
-- @return multiple return values from the table
|
||||
-- @function utils.unpack
|
||||
-- @see compat.unpack
|
||||
-- @see utils.pack
|
||||
-- @see utils.npairs
|
||||
-- @usage
|
||||
-- local t = table.pack(nil, nil, nil, 4)
|
||||
-- local a, b, c, d = table.unpack(t) -- this `unpack` is NOT nil-safe, so d == nil
|
||||
--
|
||||
-- local a, b, c, d = utils.unpack(t) -- this is nil-safe, so d == 4
|
||||
function utils.unpack(t, i, j)
|
||||
return _unpack(t, i or 1, j or t.n or #t)
|
||||
end
|
||||
|
||||
--- print an arbitrary number of arguments using a format.
|
||||
-- Output will be sent to `stdout`.
|
||||
-- @param fmt The format (see `string.format`)
|
||||
-- @param ... Extra arguments for format
|
||||
function utils.printf(fmt, ...)
|
||||
utils.assert_string(1, fmt)
|
||||
utils.fprintf(stdout, fmt, ...)
|
||||
end
|
||||
|
||||
--- write an arbitrary number of arguments to a file using a format.
|
||||
-- @param f File handle to write to.
|
||||
-- @param fmt The format (see `string.format`).
|
||||
-- @param ... Extra arguments for format
|
||||
function utils.fprintf(f,fmt,...)
|
||||
utils.assert_string(2,fmt)
|
||||
f:write(format(fmt,...))
|
||||
end
|
||||
|
||||
do
|
||||
local function import_symbol(T,k,v,libname)
|
||||
local key = rawget(T,k)
|
||||
-- warn about collisions!
|
||||
if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then
|
||||
utils.fprintf(io.stderr,"warning: '%s.%s' will not override existing symbol\n",libname,k)
|
||||
return
|
||||
end
|
||||
rawset(T,k,v)
|
||||
end
|
||||
|
||||
local function lookup_lib(T,t)
|
||||
for k,v in pairs(T) do
|
||||
if v == t then return k end
|
||||
end
|
||||
return '?'
|
||||
end
|
||||
|
||||
local already_imported = {}
|
||||
|
||||
--- take a table and 'inject' it into the local namespace.
|
||||
-- @param t The table (table), or module name (string), defaults to this `utils` module table
|
||||
-- @param T An optional destination table (defaults to callers environment)
|
||||
function utils.import(t,T)
|
||||
T = T or _G
|
||||
t = t or utils
|
||||
if type(t) == 'string' then
|
||||
t = require (t)
|
||||
end
|
||||
local libname = lookup_lib(T,t)
|
||||
if already_imported[t] then return end
|
||||
already_imported[t] = libname
|
||||
for k,v in pairs(t) do
|
||||
import_symbol(T,k,v,libname)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- return either of two values, depending on a condition.
|
||||
-- @param cond A condition
|
||||
-- @param value1 Value returned if cond is truthy
|
||||
-- @param value2 Value returned if cond is falsy
|
||||
function utils.choose(cond, value1, value2)
|
||||
return cond and value1 or value2
|
||||
end
|
||||
|
||||
--- convert an array of values to strings.
|
||||
-- @param t a list-like table
|
||||
-- @param[opt] temp (table) buffer to use, otherwise allocate
|
||||
-- @param[opt] tostr custom tostring function, called with (value,index). Defaults to `tostring`.
|
||||
-- @return the converted buffer
|
||||
function utils.array_tostring (t,temp,tostr)
|
||||
temp, tostr = temp or {}, tostr or tostring
|
||||
for i = 1,#t do
|
||||
temp[i] = tostr(t[i],i)
|
||||
end
|
||||
return temp
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- is the object of the specified type?
|
||||
-- If the type is a string, then use type, otherwise compare with metatable
|
||||
-- @param obj An object to check
|
||||
-- @param tp String of what type it should be
|
||||
-- @return boolean
|
||||
-- @usage utils.is_type("hello world", "string") --> true
|
||||
-- -- or check metatable
|
||||
-- local my_mt = {}
|
||||
-- local my_obj = setmetatable(my_obj, my_mt)
|
||||
-- utils.is_type(my_obj, my_mt) --> true
|
||||
function utils.is_type (obj,tp)
|
||||
if type(tp) == 'string' then return type(obj) == tp end
|
||||
local mt = getmetatable(obj)
|
||||
return tp == mt
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- an iterator with indices, similar to `ipairs`, but with a range.
|
||||
-- This is a nil-safe index based iterator that will return `nil` when there
|
||||
-- is a hole in a list. To be safe ensure that table `t.n` contains the length.
|
||||
-- @tparam table t the table to iterate over
|
||||
-- @tparam[opt=1] integer i_start start index
|
||||
-- @tparam[opt=t.n or #t] integer i_end end index
|
||||
-- @tparam[opt=1] integer step step size
|
||||
-- @treturn integer index
|
||||
-- @treturn any value at index (which can be `nil`!)
|
||||
-- @see utils.pack
|
||||
-- @see utils.unpack
|
||||
-- @usage
|
||||
-- local t = utils.pack(nil, 123, nil) -- adds an `n` field when packing
|
||||
--
|
||||
-- for i, v in utils.npairs(t, 2) do -- start at index 2
|
||||
-- t[i] = tostring(t[i])
|
||||
-- end
|
||||
--
|
||||
-- -- t = { n = 3, [2] = "123", [3] = "nil" }
|
||||
function utils.npairs(t, i_start, i_end, step)
|
||||
step = step or 1
|
||||
if step == 0 then
|
||||
error("iterator step-size cannot be 0", 2)
|
||||
end
|
||||
local i = (i_start or 1) - step
|
||||
i_end = i_end or t.n or #t
|
||||
if step < 0 then
|
||||
return function()
|
||||
i = i + step
|
||||
if i < i_end then
|
||||
return nil
|
||||
end
|
||||
return i, t[i]
|
||||
end
|
||||
|
||||
else
|
||||
return function()
|
||||
i = i + step
|
||||
if i > i_end then
|
||||
return nil
|
||||
end
|
||||
return i, t[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- an iterator over all non-integer keys (inverse of `ipairs`).
|
||||
-- It will skip any key that is an integer number, so negative indices or an
|
||||
-- array with holes will not return those either (so it returns slightly less than
|
||||
-- 'the inverse of `ipairs`').
|
||||
--
|
||||
-- This uses `pairs` under the hood, so any value that is iterable using `pairs`
|
||||
-- will work with this function.
|
||||
-- @tparam table t the table to iterate over
|
||||
-- @treturn key
|
||||
-- @treturn value
|
||||
-- @usage
|
||||
-- local t = {
|
||||
-- "hello",
|
||||
-- "world",
|
||||
-- hello = "hallo",
|
||||
-- world = "Welt",
|
||||
-- }
|
||||
--
|
||||
-- for k, v in utils.kpairs(t) do
|
||||
-- print("German: ", v)
|
||||
-- end
|
||||
--
|
||||
-- -- output;
|
||||
-- -- German: hallo
|
||||
-- -- German: Welt
|
||||
function utils.kpairs(t)
|
||||
local index
|
||||
return function()
|
||||
local value
|
||||
while true do
|
||||
index, value = next(t, index)
|
||||
if type(index) ~= "number" or floor(index) ~= index then
|
||||
break
|
||||
end
|
||||
end
|
||||
return index, value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Error handling
|
||||
-- @section Error-handling
|
||||
|
||||
--- assert that the given argument is in fact of the correct type.
|
||||
-- @param n argument index
|
||||
-- @param val the value
|
||||
-- @param tp the type
|
||||
-- @param verify an optional verification function
|
||||
-- @param msg an optional custom message
|
||||
-- @param lev optional stack position for trace, default 2
|
||||
-- @return the validated value
|
||||
-- @raise if `val` is not the correct type
|
||||
-- @usage
|
||||
-- local param1 = assert_arg(1,"hello",'table') --> error: argument 1 expected a 'table', got a 'string'
|
||||
-- local param4 = assert_arg(4,'!@#$%^&*','string',path.isdir,'not a directory')
|
||||
-- --> error: argument 4: '!@#$%^&*' not a directory
|
||||
function utils.assert_arg (n,val,tp,verify,msg,lev)
|
||||
if type(val) ~= tp then
|
||||
error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),lev or 2)
|
||||
end
|
||||
if verify and not verify(val) then
|
||||
error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2)
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
||||
--- creates an Enum or constants lookup table for improved error handling.
|
||||
-- This helps prevent magic strings in code by throwing errors for accessing
|
||||
-- non-existing values, and/or converting strings/identifiers to other values.
|
||||
--
|
||||
-- Calling on the object does the same, but returns a soft error; `nil + err`, if
|
||||
-- the call is succesful (the key exists), it will return the value.
|
||||
--
|
||||
-- When calling with varargs or an array the values will be equal to the keys.
|
||||
-- The enum object is read-only.
|
||||
-- @tparam table|vararg ... the input for the Enum. If varargs or an array then the
|
||||
-- values in the Enum will be equal to the names (must be strings), if a hash-table
|
||||
-- then values remain (any type), and the keys must be strings.
|
||||
-- @return Enum object (read-only table/object)
|
||||
-- @usage -- Enum access at runtime
|
||||
-- local obj = {}
|
||||
-- obj.MOVEMENT = utils.enum("FORWARD", "REVERSE", "LEFT", "RIGHT")
|
||||
--
|
||||
-- if current_movement == obj.MOVEMENT.FORWARD then
|
||||
-- -- do something
|
||||
--
|
||||
-- elseif current_movement == obj.MOVEMENT.REVERES then
|
||||
-- -- throws error due to typo 'REVERES', so a silent mistake becomes a hard error
|
||||
-- -- "'REVERES' is not a valid value (expected one of: 'FORWARD', 'REVERSE', 'LEFT', 'RIGHT')"
|
||||
--
|
||||
-- end
|
||||
-- @usage -- standardized error codes
|
||||
-- local obj = {
|
||||
-- ERR = utils.enum {
|
||||
-- NOT_FOUND = "the item was not found",
|
||||
-- OUT_OF_BOUNDS = "the index is outside the allowed range"
|
||||
-- },
|
||||
--
|
||||
-- some_method = function(self)
|
||||
-- return self.ERR.OUT_OF_BOUNDS
|
||||
-- end,
|
||||
-- }
|
||||
--
|
||||
-- local result, err = obj:some_method()
|
||||
-- if not result then
|
||||
-- if err == obj.ERR.NOT_FOUND then
|
||||
-- -- check on error code, not magic strings
|
||||
--
|
||||
-- else
|
||||
-- -- return the error description, contained in the constant
|
||||
-- return nil, "error: "..err -- "error: the index is outside the allowed range"
|
||||
-- end
|
||||
-- end
|
||||
-- @usage -- validating/converting user-input
|
||||
-- local color = "purple"
|
||||
-- local ansi_colors = utils.enum {
|
||||
-- black = 30,
|
||||
-- red = 31,
|
||||
-- green = 32,
|
||||
-- }
|
||||
-- local color_code, err = ansi_colors(color) -- calling on the object, returns the value from the enum
|
||||
-- if not color_code then
|
||||
-- print("bad 'color', " .. err)
|
||||
-- -- "bad 'color', 'purple' is not a valid value (expected one of: 'black', 'red', 'green')"
|
||||
-- os.exit(1)
|
||||
-- end
|
||||
function utils.enum(...)
|
||||
local first = select(1, ...)
|
||||
local enum = {}
|
||||
local lst
|
||||
|
||||
if type(first) ~= "table" then
|
||||
-- vararg with strings
|
||||
lst = utils.pack(...)
|
||||
for i, value in utils.npairs(lst) do
|
||||
utils.assert_arg(i, value, "string")
|
||||
enum[value] = value
|
||||
end
|
||||
|
||||
else
|
||||
-- table/array with values
|
||||
utils.assert_arg(1, first, "table")
|
||||
lst = {}
|
||||
-- first add array part
|
||||
for i, value in ipairs(first) do
|
||||
if type(value) ~= "string" then
|
||||
error(("expected 'string' but got '%s' at index %d"):format(type(value), i), 2)
|
||||
end
|
||||
lst[i] = value
|
||||
enum[value] = value
|
||||
end
|
||||
-- add key-ed part
|
||||
for key, value in utils.kpairs(first) do
|
||||
if type(key) ~= "string" then
|
||||
error(("expected key to be 'string' but got '%s'"):format(type(key)), 2)
|
||||
end
|
||||
if enum[key] then
|
||||
error(("duplicate entry in array and hash part: '%s'"):format(key), 2)
|
||||
end
|
||||
enum[key] = value
|
||||
lst[#lst+1] = key
|
||||
end
|
||||
end
|
||||
|
||||
if not lst[1] then
|
||||
error("expected at least 1 entry", 2)
|
||||
end
|
||||
|
||||
local valid = "(expected one of: '" .. concat(lst, "', '") .. "')"
|
||||
setmetatable(enum, {
|
||||
__index = function(self, key)
|
||||
error(("'%s' is not a valid value %s"):format(tostring(key), valid), 2)
|
||||
end,
|
||||
__newindex = function(self, key, value)
|
||||
error("the Enum object is read-only", 2)
|
||||
end,
|
||||
__call = function(self, key)
|
||||
if type(key) == "string" then
|
||||
local v = rawget(self, key)
|
||||
if v ~= nil then
|
||||
return v
|
||||
end
|
||||
end
|
||||
return nil, ("'%s' is not a valid value %s"):format(tostring(key), valid)
|
||||
end
|
||||
})
|
||||
|
||||
return enum
|
||||
end
|
||||
|
||||
|
||||
--- process a function argument.
|
||||
-- This is used throughout Penlight and defines what is meant by a function:
|
||||
-- Something that is callable, or an operator string as defined by <code>pl.operator</code>,
|
||||
-- such as '>' or '#'. If a function factory has been registered for the type, it will
|
||||
-- be called to get the function.
|
||||
-- @param idx argument index
|
||||
-- @param f a function, operator string, or callable object
|
||||
-- @param msg optional error message
|
||||
-- @return a callable
|
||||
-- @raise if idx is not a number or if f is not callable
|
||||
function utils.function_arg (idx,f,msg)
|
||||
utils.assert_arg(1,idx,'number')
|
||||
local tp = type(f)
|
||||
if tp == 'function' then return f end -- no worries!
|
||||
-- ok, a string can correspond to an operator (like '==')
|
||||
if tp == 'string' then
|
||||
if not operators then operators = require 'pl.operator'.optable end
|
||||
local fn = operators[f]
|
||||
if fn then return fn end
|
||||
local fn, err = utils.string_lambda(f)
|
||||
if not fn then error(err..': '..f) end
|
||||
return fn
|
||||
elseif tp == 'table' or tp == 'userdata' then
|
||||
local mt = getmetatable(f)
|
||||
if not mt then error('not a callable object',2) end
|
||||
local ff = _function_factories[mt]
|
||||
if not ff then
|
||||
if not mt.__call then error('not a callable object',2) end
|
||||
return f
|
||||
else
|
||||
return ff(f) -- we have a function factory for this type!
|
||||
end
|
||||
end
|
||||
if not msg then msg = " must be callable" end
|
||||
if idx > 0 then
|
||||
error("argument "..idx..": "..msg,2)
|
||||
else
|
||||
error(msg,2)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- assert the common case that the argument is a string.
|
||||
-- @param n argument index
|
||||
-- @param val a value that must be a string
|
||||
-- @return the validated value
|
||||
-- @raise val must be a string
|
||||
-- @usage
|
||||
-- local val = 42
|
||||
-- local param2 = utils.assert_string(2, val) --> error: argument 2 expected a 'string', got a 'number'
|
||||
function utils.assert_string (n, val)
|
||||
return utils.assert_arg(n,val,'string',nil,nil,3)
|
||||
end
|
||||
|
||||
--- control the error strategy used by Penlight.
|
||||
-- This is a global setting that controls how `utils.raise` behaves:
|
||||
--
|
||||
-- - 'default': return `nil + error` (this is the default)
|
||||
-- - 'error': throw a Lua error
|
||||
-- - 'quit': exit the program
|
||||
--
|
||||
-- @param mode either 'default', 'quit' or 'error'
|
||||
-- @see utils.raise
|
||||
function utils.on_error (mode)
|
||||
mode = tostring(mode)
|
||||
if ({['default'] = 1, ['quit'] = 2, ['error'] = 3})[mode] then
|
||||
err_mode = mode
|
||||
else
|
||||
-- fail loudly
|
||||
local err = "Bad argument expected string; 'default', 'quit', or 'error'. Got '"..tostring(mode).."'"
|
||||
if err_mode == 'default' then
|
||||
error(err, 2) -- even in 'default' mode fail loud in this case
|
||||
end
|
||||
raise(err)
|
||||
end
|
||||
end
|
||||
|
||||
--- used by Penlight functions to return errors. Its global behaviour is controlled
|
||||
-- by `utils.on_error`.
|
||||
-- To use this function you MUST use it in conjunction with `return`, since it might
|
||||
-- return `nil + error`.
|
||||
-- @param err the error string.
|
||||
-- @see utils.on_error
|
||||
-- @usage
|
||||
-- if some_condition then
|
||||
-- return utils.raise("some condition was not met") -- MUST use 'return'!
|
||||
-- end
|
||||
function utils.raise (err)
|
||||
if err_mode == 'default' then
|
||||
return nil, err
|
||||
elseif err_mode == 'quit' then
|
||||
return utils.quit(err)
|
||||
else
|
||||
error(err, 2)
|
||||
end
|
||||
end
|
||||
raise = utils.raise
|
||||
|
||||
|
||||
|
||||
--- File handling
|
||||
-- @section files
|
||||
|
||||
--- return the contents of a file as a string
|
||||
-- @param filename The file path
|
||||
-- @param is_bin open in binary mode
|
||||
-- @return file contents
|
||||
function utils.readfile(filename,is_bin)
|
||||
local mode = is_bin and 'b' or ''
|
||||
utils.assert_string(1,filename)
|
||||
local f,open_err = io.open(filename,'r'..mode)
|
||||
if not f then return raise (open_err) end
|
||||
local res,read_err = f:read('*a')
|
||||
f:close()
|
||||
if not res then
|
||||
-- Errors in io.open have "filename: " prefix,
|
||||
-- error in file:read don't, add it.
|
||||
return raise (filename..": "..read_err)
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
--- write a string to a file
|
||||
-- @param filename The file path
|
||||
-- @param str The string
|
||||
-- @param is_bin open in binary mode
|
||||
-- @return true or nil
|
||||
-- @return error message
|
||||
-- @raise error if filename or str aren't strings
|
||||
function utils.writefile(filename,str,is_bin)
|
||||
local mode = is_bin and 'b' or ''
|
||||
utils.assert_string(1,filename)
|
||||
utils.assert_string(2,str)
|
||||
local f,err = io.open(filename,'w'..mode)
|
||||
if not f then return raise(err) end
|
||||
local ok, write_err = f:write(str)
|
||||
f:close()
|
||||
if not ok then
|
||||
-- Errors in io.open have "filename: " prefix,
|
||||
-- error in file:write don't, add it.
|
||||
return raise (filename..": "..write_err)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- return the contents of a file as a list of lines
|
||||
-- @param filename The file path
|
||||
-- @return file contents as a table
|
||||
-- @raise error if filename is not a string
|
||||
function utils.readlines(filename)
|
||||
utils.assert_string(1,filename)
|
||||
local f,err = io.open(filename,'r')
|
||||
if not f then return raise(err) end
|
||||
local res = {}
|
||||
for line in f:lines() do
|
||||
append(res,line)
|
||||
end
|
||||
f:close()
|
||||
return res
|
||||
end
|
||||
|
||||
--- OS functions
|
||||
-- @section OS-functions
|
||||
|
||||
--- execute a shell command and return the output.
|
||||
-- This function redirects the output to tempfiles and returns the content of those files.
|
||||
-- @param cmd a shell command
|
||||
-- @param bin boolean, if true, read output as binary file
|
||||
-- @return true if successful
|
||||
-- @return actual return code
|
||||
-- @return stdout output (string)
|
||||
-- @return errout output (string)
|
||||
function utils.executeex(cmd, bin)
|
||||
local outfile = os.tmpname()
|
||||
local errfile = os.tmpname()
|
||||
|
||||
if is_windows and not outfile:find(':') then
|
||||
outfile = os.getenv('TEMP')..outfile
|
||||
errfile = os.getenv('TEMP')..errfile
|
||||
end
|
||||
cmd = cmd .. " > " .. utils.quote_arg(outfile) .. " 2> " .. utils.quote_arg(errfile)
|
||||
|
||||
local success, retcode = utils.execute(cmd)
|
||||
local outcontent = utils.readfile(outfile, bin)
|
||||
local errcontent = utils.readfile(errfile, bin)
|
||||
os.remove(outfile)
|
||||
os.remove(errfile)
|
||||
return success, retcode, (outcontent or ""), (errcontent or "")
|
||||
end
|
||||
|
||||
--- Quote and escape an argument of a command.
|
||||
-- Quotes a single (or list of) argument(s) of a command to be passed
|
||||
-- to `os.execute`, `pl.utils.execute` or `pl.utils.executeex`.
|
||||
-- @param argument (string or table/list) the argument to quote. If a list then
|
||||
-- all arguments in the list will be returned as a single string quoted.
|
||||
-- @return quoted and escaped argument.
|
||||
-- @usage
|
||||
-- local options = utils.quote_arg {
|
||||
-- "-lluacov",
|
||||
-- "-e",
|
||||
-- "utils = print(require('pl.utils')._VERSION",
|
||||
-- }
|
||||
-- -- returns: -lluacov -e 'utils = print(require('\''pl.utils'\'')._VERSION'
|
||||
function utils.quote_arg(argument)
|
||||
if type(argument) == "table" then
|
||||
-- encode an entire table
|
||||
local r = {}
|
||||
for i, arg in ipairs(argument) do
|
||||
r[i] = utils.quote_arg(arg)
|
||||
end
|
||||
|
||||
return concat(r, " ")
|
||||
end
|
||||
-- only a single argument
|
||||
if is_windows then
|
||||
if argument == "" or argument:find('[ \f\t\v]') then
|
||||
-- Need to quote the argument.
|
||||
-- Quotes need to be escaped with backslashes;
|
||||
-- additionally, backslashes before a quote, escaped or not,
|
||||
-- need to be doubled.
|
||||
-- See documentation for CommandLineToArgvW Windows function.
|
||||
argument = '"' .. argument:gsub([[(\*)"]], [[%1%1\"]]):gsub([[\+$]], "%0%0") .. '"'
|
||||
end
|
||||
|
||||
-- os.execute() uses system() C function, which on Windows passes command
|
||||
-- to cmd.exe. Escape its special characters.
|
||||
return (argument:gsub('["^<>!|&%%]', "^%0"))
|
||||
else
|
||||
if argument == "" or argument:find('[^a-zA-Z0-9_@%+=:,./-]') then
|
||||
-- To quote arguments on posix-like systems use single quotes.
|
||||
-- To represent an embedded single quote close quoted string ('),
|
||||
-- add escaped quote (\'), open quoted string again (').
|
||||
argument = "'" .. argument:gsub("'", [['\'']]) .. "'"
|
||||
end
|
||||
|
||||
return argument
|
||||
end
|
||||
end
|
||||
|
||||
--- error out of this program gracefully.
|
||||
-- @param[opt] code The exit code, defaults to -`1` if omitted
|
||||
-- @param msg The exit message will be sent to `stderr` (will be formatted with the extra parameters)
|
||||
-- @param ... extra arguments for message's format'
|
||||
-- @see utils.fprintf
|
||||
-- @usage utils.quit(-1, "Error '%s' happened", "42")
|
||||
-- -- is equivalent to
|
||||
-- utils.quit("Error '%s' happened", "42") --> Error '42' happened
|
||||
function utils.quit(code, msg, ...)
|
||||
if type(code) == 'string' then
|
||||
utils.fprintf(io.stderr, code, msg, ...)
|
||||
io.stderr:write('\n')
|
||||
code = -1 -- TODO: this is odd, see the test. Which returns 255 as exit code
|
||||
elseif msg then
|
||||
utils.fprintf(io.stderr, msg, ...)
|
||||
io.stderr:write('\n')
|
||||
end
|
||||
os.exit(code, true)
|
||||
end
|
||||
|
||||
|
||||
--- String functions
|
||||
-- @section string-functions
|
||||
|
||||
--- escape any Lua 'magic' characters in a string
|
||||
-- @param s The input string
|
||||
function utils.escape(s)
|
||||
utils.assert_string(1,s)
|
||||
return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1'))
|
||||
end
|
||||
|
||||
--- split a string into a list of strings separated by a delimiter.
|
||||
-- @param s The input string
|
||||
-- @param re optional A Lua string pattern; defaults to '%s+'
|
||||
-- @param plain optional If truthy don't use Lua patterns
|
||||
-- @param n optional maximum number of elements (if there are more, the last will remian un-split)
|
||||
-- @return a list-like table
|
||||
-- @raise error if s is not a string
|
||||
-- @see splitv
|
||||
function utils.split(s,re,plain,n)
|
||||
utils.assert_string(1,s)
|
||||
local i1,ls = 1,{}
|
||||
if not re then re = '%s+' end
|
||||
if re == '' then return {s} end
|
||||
while true do
|
||||
local i2,i3 = find(s,re,i1,plain)
|
||||
if not i2 then
|
||||
local last = sub(s,i1)
|
||||
if last ~= '' then append(ls,last) end
|
||||
if #ls == 1 and ls[1] == '' then
|
||||
return {}
|
||||
else
|
||||
return ls
|
||||
end
|
||||
end
|
||||
append(ls,sub(s,i1,i2-1))
|
||||
if n and #ls == n then
|
||||
ls[#ls] = sub(s,i1)
|
||||
return ls
|
||||
end
|
||||
i1 = i3+1
|
||||
end
|
||||
end
|
||||
|
||||
--- split a string into a number of return values.
|
||||
-- Identical to `split` but returns multiple sub-strings instead of
|
||||
-- a single list of sub-strings.
|
||||
-- @param s the string
|
||||
-- @param re A Lua string pattern; defaults to '%s+'
|
||||
-- @param plain don't use Lua patterns
|
||||
-- @param n optional maximum number of splits
|
||||
-- @return n values
|
||||
-- @usage first,next = splitv('user=jane=doe','=', false, 2)
|
||||
-- assert(first == "user")
|
||||
-- assert(next == "jane=doe")
|
||||
-- @see split
|
||||
function utils.splitv (s,re, plain, n)
|
||||
return _unpack(utils.split(s,re, plain, n))
|
||||
end
|
||||
|
||||
|
||||
--- Functional
|
||||
-- @section functional
|
||||
|
||||
|
||||
--- 'memoize' a function (cache returned value for next call).
|
||||
-- This is useful if you have a function which is relatively expensive,
|
||||
-- but you don't know in advance what values will be required, so
|
||||
-- building a table upfront is wasteful/impossible.
|
||||
-- @param func a function of at least one argument
|
||||
-- @return a function with at least one argument, which is used as the key.
|
||||
function utils.memoize(func)
|
||||
local cache = {}
|
||||
return function(k)
|
||||
local res = cache[k]
|
||||
if res == nil then
|
||||
res = func(k)
|
||||
cache[k] = res
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- associate a function factory with a type.
|
||||
-- A function factory takes an object of the given type and
|
||||
-- returns a function for evaluating it
|
||||
-- @tab mt metatable
|
||||
-- @func fun a callable that returns a function
|
||||
function utils.add_function_factory (mt,fun)
|
||||
_function_factories[mt] = fun
|
||||
end
|
||||
|
||||
local function _string_lambda(f)
|
||||
if f:find '^|' or f:find '_' then
|
||||
local args,body = f:match '|([^|]*)|(.+)'
|
||||
if f:find '_' then
|
||||
args = '_'
|
||||
body = f
|
||||
else
|
||||
if not args then return raise 'bad string lambda' end
|
||||
end
|
||||
local fstr = 'return function('..args..') return '..body..' end'
|
||||
local fn,err = utils.load(fstr)
|
||||
if not fn then return raise(err) end
|
||||
fn = fn()
|
||||
return fn
|
||||
else
|
||||
return raise 'not a string lambda'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- an anonymous function as a string. This string is either of the form
|
||||
-- '|args| expression' or is a function of one argument, '_'
|
||||
-- @param lf function as a string
|
||||
-- @return a function
|
||||
-- @function utils.string_lambda
|
||||
-- @usage
|
||||
-- string_lambda '|x|x+1' (2) == 3
|
||||
-- string_lambda '_+1' (2) == 3
|
||||
utils.string_lambda = utils.memoize(_string_lambda)
|
||||
|
||||
|
||||
--- bind the first argument of the function to a value.
|
||||
-- @param fn a function of at least two values (may be an operator string)
|
||||
-- @param p a value
|
||||
-- @return a function such that f(x) is fn(p,x)
|
||||
-- @raise same as @{function_arg}
|
||||
-- @see func.bind1
|
||||
-- @usage local function f(msg, name)
|
||||
-- print(msg .. " " .. name)
|
||||
-- end
|
||||
--
|
||||
-- local hello = utils.bind1(f, "Hello")
|
||||
--
|
||||
-- print(hello("world")) --> "Hello world"
|
||||
-- print(hello("sunshine")) --> "Hello sunshine"
|
||||
function utils.bind1 (fn,p)
|
||||
fn = utils.function_arg(1,fn)
|
||||
return function(...) return fn(p,...) end
|
||||
end
|
||||
|
||||
|
||||
--- bind the second argument of the function to a value.
|
||||
-- @param fn a function of at least two values (may be an operator string)
|
||||
-- @param p a value
|
||||
-- @return a function such that f(x) is fn(x,p)
|
||||
-- @raise same as @{function_arg}
|
||||
-- @usage local function f(a, b, c)
|
||||
-- print(a .. " " .. b .. " " .. c)
|
||||
-- end
|
||||
--
|
||||
-- local hello = utils.bind1(f, "world")
|
||||
--
|
||||
-- print(hello("Hello", "!")) --> "Hello world !"
|
||||
-- print(hello("Bye", "?")) --> "Bye world ?"
|
||||
function utils.bind2 (fn,p)
|
||||
fn = utils.function_arg(1,fn)
|
||||
return function(x,...) return fn(x,p,...) end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Deprecation
|
||||
-- @section deprecation
|
||||
|
||||
do
|
||||
-- the default implementation
|
||||
local deprecation_func = function(msg, trace)
|
||||
if trace then
|
||||
warn(msg, "\n", trace) -- luacheck: ignore
|
||||
else
|
||||
warn(msg) -- luacheck: ignore
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets a deprecation warning function.
|
||||
-- An application can override this function to support proper output of
|
||||
-- deprecation warnings. The warnings can be generated from libraries or
|
||||
-- functions by calling `utils.raise_deprecation`. The default function
|
||||
-- will write to the 'warn' system (introduced in Lua 5.4, or the compatibility
|
||||
-- function from the `compat` module for earlier versions).
|
||||
--
|
||||
-- Note: only applications should set/change this function, libraries should not.
|
||||
-- @param func a callback with signature: `function(msg, trace)` both arguments are strings, the latter being optional.
|
||||
-- @see utils.raise_deprecation
|
||||
-- @usage
|
||||
-- -- write to the Nginx logs with OpenResty
|
||||
-- utils.set_deprecation_func(function(msg, trace)
|
||||
-- ngx.log(ngx.WARN, msg, (trace and (" " .. trace) or nil))
|
||||
-- end)
|
||||
--
|
||||
-- -- disable deprecation warnings
|
||||
-- utils.set_deprecation_func()
|
||||
function utils.set_deprecation_func(func)
|
||||
if func == nil then
|
||||
deprecation_func = function() end
|
||||
else
|
||||
utils.assert_arg(1, func, "function")
|
||||
deprecation_func = func
|
||||
end
|
||||
end
|
||||
|
||||
--- raises a deprecation warning.
|
||||
-- For options see the usage example below.
|
||||
--
|
||||
-- Note: the `opts.deprecated_after` field is the last version in which
|
||||
-- a feature or option was NOT YET deprecated! Because when writing the code it
|
||||
-- is quite often not known in what version the code will land. But the last
|
||||
-- released version is usually known.
|
||||
-- @param opts options table
|
||||
-- @see utils.set_deprecation_func
|
||||
-- @usage
|
||||
-- warn("@on") -- enable Lua warnings, they are usually off by default
|
||||
--
|
||||
-- function stringx.islower(str)
|
||||
-- raise_deprecation {
|
||||
-- source = "Penlight " .. utils._VERSION, -- optional
|
||||
-- message = "function 'islower' was renamed to 'is_lower'", -- required
|
||||
-- version_removed = "2.0.0", -- optional
|
||||
-- deprecated_after = "1.2.3", -- optional
|
||||
-- no_trace = true, -- optional
|
||||
-- }
|
||||
-- return stringx.is_lower(str)
|
||||
-- end
|
||||
-- -- output: "[Penlight 1.9.2] function 'islower' was renamed to 'is_lower' (deprecated after 1.2.3, scheduled for removal in 2.0.0)"
|
||||
function utils.raise_deprecation(opts)
|
||||
utils.assert_arg(1, opts, "table")
|
||||
if type(opts.message) ~= "string" then
|
||||
error("field 'message' of the options table must be a string", 2)
|
||||
end
|
||||
local trace
|
||||
if not opts.no_trace then
|
||||
trace = debug.traceback("", 2):match("[\n%s]*(.-)$")
|
||||
end
|
||||
local msg
|
||||
if opts.deprecated_after and opts.version_removed then
|
||||
msg = (" (deprecated after %s, scheduled for removal in %s)"):format(
|
||||
tostring(opts.deprecated_after), tostring(opts.version_removed))
|
||||
elseif opts.deprecated_after then
|
||||
msg = (" (deprecated after %s)"):format(tostring(opts.deprecated_after))
|
||||
elseif opts.version_removed then
|
||||
msg = (" (scheduled for removal in %s)"):format(tostring(opts.version_removed))
|
||||
else
|
||||
msg = ""
|
||||
end
|
||||
|
||||
msg = opts.message .. msg
|
||||
|
||||
if opts.source then
|
||||
msg = "[" .. opts.source .."] " .. msg
|
||||
else
|
||||
if msg:sub(1,1) == "@" then
|
||||
-- in Lua 5.4 "@" prefixed messages are control messages to the warn system
|
||||
error("message cannot start with '@'", 2)
|
||||
end
|
||||
end
|
||||
|
||||
deprecation_func(msg, trace)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
return utils
|
||||
|
||||
|
1165
Documentation/compiler/pl/xml.lua
Normal file
1165
Documentation/compiler/pl/xml.lua
Normal file
File diff suppressed because it is too large
Load diff
|
@ -12,29 +12,29 @@ new_type("luautil", "5 Lua utility modules", true)
|
|||
|
||||
not_luadoc = true
|
||||
|
||||
local version = "1.2"
|
||||
project = "TombEngine"
|
||||
local version = "1.8.1"
|
||||
project = " TombEngine"
|
||||
title = "TombEngine " .. version .. " Lua API"
|
||||
description = "TombEngine " .. version .. " scripting interface"
|
||||
full_description = [[Welcome to the TombEngine scripting API. This is a work in progress and some information might be wrong or outdated. Please also note that this is primarily a reference document, not a tutorial, so expect descriptions to be fairly sparse.
|
||||
full_description = [[Welcome to the TombEngine scripting API.
|
||||
|
||||
Note that this is primarily a reference document, not a tutorial, so expect descriptions to be fairly sparse.
|
||||
At the time of writing, there is a tutorial describing the basics of Lua, as well as a number of example scripts, on <a href="https://www.tombengine.com">the TombEngine website</a>.
|
||||
####Module Hierarchy (boring but important)
|
||||
Other than the "special tables" (GameVars, LevelVars and LevelFuncs), every module described herein is held in a master table called TEN.
|
||||
|
||||
####Module Hierarchy
|
||||
Other than the "special tables" (`GameVars`, `LevelVars` and `LevelFuncs`), every module described herein is held in a master table called TEN.
|
||||
For convenience, these modules and classes are automatically put in the global table. For example, you can call GetMoveableByName either of these two ways:
|
||||
local door = TEN.Objects.GetMoveableByName("door_type4_14")
|
||||
local door = GetMoveableByName("door_type4_14")
|
||||
|
||||
####Always check logs/TENLog.txt
|
||||
If you are scripting levels, TombEngine will often kick you back to the title screen, even if `errorMode` (see Flow.Settings) is set to `ErrorMode.WARN` or `ErrorMode.SILENT`.
|
||||
If you are scripting levels, TombEngine will often kick you back to the title screen, even if `errorMode` (see @{Flow.Settings}) is set to `ErrorMode.WARN` or `ErrorMode.SILENT`.
|
||||
|
||||
This might get annoying, but it's on purpose. If your Lua script contains a syntax error (e.g. you're missing `end` at the end of a function), the Lua interpreter will not be able to continue running the script. If it tried to keep running, you'd probably see some pretty strange behaviour, and would possibly get a crash regardless.
|
||||
This might get annoying, but it's on purpose. If your Lua script contains a syntax error (e.g. you're missing `end` at the end of a function), the Lua interpreter will not be able to continue running the script. If it tried to keep running, you'd probably see some pretty strange behaviour, and would possibly get a crash regardless. If this happens, check __logs/TENLog.txt__ and look for an error message with the word "unrecoverable".
|
||||
|
||||
If this happens, check __logs/TENLog.txt__ and look for an error message with the word "unrecoverable".
|
||||
Happy building!
|
||||
|
||||
Enjoy.
|
||||
|
||||
\- _squidshire_
|
||||
\- _squidshire and the TombEngine development team._
|
||||
]]
|
||||
|
||||
convert_opt=true
|
||||
|
@ -51,4 +51,4 @@ custom_display_name_handler = function(item, default_handler)
|
|||
end
|
||||
local hand = default_handler(item)
|
||||
return hand
|
||||
end
|
||||
end
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -106,26 +124,38 @@
|
|||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitLightningArc">EmitLightningArc(src, dest, color, lifetime, amplitude, beamWidth, detail, smooth, endDrift)</a></td>
|
||||
<td class="name" ><a href="#EmitLightningArc">EmitLightningArc(origin, target, color, life, amplitude, beamWidth, detail, smooth, endDrift)</a></td>
|
||||
<td class="summary">Emit a lightning arc.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitParticle">EmitParticle(pos, velocity, spriteIndex, gravity, rot, startColor, endColor, blendMode, startSize, endSize, lifetime, damage, poison)</a></td>
|
||||
<td class="name" ><a href="#EmitParticle">EmitParticle(pos, vel, spriteID, gravity, rotVel, startColor, endColor, blendMode, startSize, endSize, life, damage, poison, spriteSeqID, startRot)</a></td>
|
||||
<td class="summary">Emit a particle.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitAdvancedParticle">EmitAdvancedParticle(ParticleData)</a></td>
|
||||
<td class="summary">Emit a particle with extensive configuration options, including sprite sequence animation, lights, sounds, and damage effects.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitShockwave">EmitShockwave(pos, innerRadius, outerRadius, color, lifetime, speed, angle, hurtsLara)</a></td>
|
||||
<td class="summary">Emit a shockwave, similar to that seen when a harpy projectile hits something.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitLight">EmitLight(pos, color, radius)</a></td>
|
||||
<td class="name" ><a href="#EmitLight">EmitLight(pos[, color][, radius][, shadows][, name])</a></td>
|
||||
<td class="summary">Emit dynamic light that lasts for a single frame.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitSpotLight">EmitSpotLight(pos, dir[, color][, radius][, falloff][, distance][, shadows][, name])</a></td>
|
||||
<td class="summary">Emit dynamic directional spotlight that lasts for a single frame.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitBlood">EmitBlood(pos, count)</a></td>
|
||||
<td class="summary">Emit blood.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitAirBubble">EmitAirBubble(pos[, size][, amp])</a></td>
|
||||
<td class="summary">Emit an air bubble in a water room.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitFire">EmitFire(pos, size)</a></td>
|
||||
<td class="summary">Emit fire for one frame.</td>
|
||||
</tr>
|
||||
|
@ -137,6 +167,21 @@
|
|||
<td class="name" ><a href="#MakeEarthquake">MakeEarthquake(strength)</a></td>
|
||||
<td class="summary">Make an earthquake</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetWind">GetWind()</a></td>
|
||||
<td class="summary">Get the wind vector for the current game frame.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EmitStreamer">EmitStreamer(mov, tag, pos, dir[, rot][, startColor][, endColor][, width][, life][, vel][, expRate][, rotRate][, edgeFeatherMode][, lengthFeatherMode][, blendID])</a></td>
|
||||
<td class="summary">Emit an extending streamer effect.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#Tables">Tables</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#ParticleData">ParticleData</a></td>
|
||||
<td class="summary">Structure for EmitAdvancedParticle table.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
@ -148,7 +193,7 @@
|
|||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "EmitLightningArc"></a>
|
||||
<strong>EmitLightningArc(src, dest, color, lifetime, amplitude, beamWidth, detail, smooth, endDrift)</strong>
|
||||
<strong>EmitLightningArc(origin, target, color, life, amplitude, beamWidth, detail, smooth, endDrift)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Emit a lightning arc.
|
||||
|
@ -157,13 +202,13 @@
|
|||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">src</span>
|
||||
<li><span class="parameter">origin</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
|
||||
|
||||
|
||||
</li>
|
||||
<li><span class="parameter">dest</span>
|
||||
<li><span class="parameter">target</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
|
||||
|
||||
|
@ -173,29 +218,29 @@
|
|||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
(default Color(255, 255, 255))
|
||||
</li>
|
||||
<li><span class="parameter">lifetime</span>
|
||||
<li><span class="parameter">life</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Lifetime in seconds. Clamped to [0, 4.233] for now because of strange internal maths. (default 1.0)
|
||||
Lifetime in seconds. Clamped to [0, 4.233] for now because of strange internal maths. <strong>default: 1</strong>
|
||||
</li>
|
||||
<li><span class="parameter">amplitude</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
"strength" of the lightning - the higher the value, the "taller" the arcs. Clamped to [1, 255]. (default 20)
|
||||
"strength" of the lightning - the higher the value, the "taller" the arcs. Clamped to [1, 255]. <strong>default: 20</strong>
|
||||
</li>
|
||||
<li><span class="parameter">beamWidth</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Clamped to [1, 127]. (default 2)
|
||||
Clamped to [1, 127]. <strong>default 2</strong>
|
||||
</li>
|
||||
<li><span class="parameter">detail</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Higher numbers equal more segments, but it's not a 1:1 correlation. Clamped to [1, 127]. (default 10)
|
||||
Higher numbers equal more segments, but it's not a 1:1 correlation. Clamped to [1, 127]. <strong>default: 10</strong>
|
||||
</li>
|
||||
<li><span class="parameter">smooth</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
If true, the arc will have large, smooth curves; if false, it will have small, jagged spikes. (default false)
|
||||
If true, the arc will have large, smooth curves; if false, it will have small, jagged spikes. <strong>default: false</strong>
|
||||
</li>
|
||||
<li><span class="parameter">endDrift</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
If true, the end of the arc will be able to gradually drift away from its destination in a random direction (default false)
|
||||
If true, the end of the arc will be able to gradually drift away from its destination in a random direction <strong>default: false</strong>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -206,14 +251,10 @@
|
|||
</dd>
|
||||
<dt>
|
||||
<a name = "EmitParticle"></a>
|
||||
<strong>EmitParticle(pos, velocity, spriteIndex, gravity, rot, startColor, endColor, blendMode, startSize, endSize, lifetime, damage, poison)</strong>
|
||||
<strong>EmitParticle(pos, vel, spriteID, gravity, rotVel, startColor, endColor, blendMode, startSize, endSize, life, damage, poison, spriteSeqID, startRot)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
<p>Emit a particle. </p>
|
||||
<pre><code> See the sprite editor in WadTool for DEFAULT_SPRITES to see a list of sprite indices.
|
||||
</code></pre>
|
||||
|
||||
Emit a particle.
|
||||
|
||||
|
||||
|
||||
|
@ -221,59 +262,63 @@
|
|||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
|
||||
|
||||
|
||||
World position.
|
||||
</li>
|
||||
<li><span class="parameter">velocity</span>
|
||||
<li><span class="parameter">vel</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
|
||||
|
||||
|
||||
Directional velocity.
|
||||
</li>
|
||||
<li><span class="parameter">spriteIndex</span>
|
||||
<li><span class="parameter">spriteID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
an index of a sprite in DEFAULT_SPRITES object.
|
||||
Sprite ID in the sprite sequence slot.
|
||||
</li>
|
||||
<li><span class="parameter">gravity</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
(default 0) Specifies whether particle will fall (positive values) or ascend (negative values) over time. Clamped to [-32768, 32767], but values between -1000 and 1000 are recommended; values too high or too low (e.g. under -2000 or above 2000) will cause the velocity of the particle to "wrap around" and switch directions.
|
||||
</li>
|
||||
<li><span class="parameter">rot</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 0) specifies a speed with which it will rotate (0 = no rotation, negative = anticlockwise rotation, positive = clockwise rotation).
|
||||
Effect of gravity. Positive value ascends, negative value descends. <strong>default: 0</strong>
|
||||
</li>
|
||||
<li><span class="parameter">rotVel</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotational velocity in degrees. <strong>default: 0</strong>
|
||||
</li>
|
||||
<li><span class="parameter">startColor</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
(default Color(255, 255, 255)) color at start of life
|
||||
Color at start of life. <strong>default: Color(255, 255, 255)</strong>
|
||||
</li>
|
||||
<li><span class="parameter">endColor</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
(default Color(255, 255, 255)) color to fade to - at the time of writing this fade will finish long before the end of the particle's life due to internal maths
|
||||
Color at end of life. This will finish long before the end of the particle's life due to internal math. <strong>default: Color(255, 255, 255)</strong>
|
||||
</li>
|
||||
<li><span class="parameter">blendMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
|
||||
(default TEN.Effects.BlendID.ALPHABLEND) How will we blend this with its surroundings?
|
||||
Render blend mode. <strong>TEN.Effects.BlendID.ALPHA_BLEND</strong>
|
||||
</li>
|
||||
<li><span class="parameter">startSize</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
(default 10) Size on spawn. A value of 15 is approximately the size of Lara's head.
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Size at start of life. <strong>default: 10</strong>
|
||||
</li>
|
||||
<li><span class="parameter">endSize</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
(default 0) Size on death - the particle will linearly shrink or grow to this size during its lifespan
|
||||
</li>
|
||||
<li><span class="parameter">lifetime</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 2) Lifespan in seconds
|
||||
Size at end of life. <strong>default: 0</strong>
|
||||
</li>
|
||||
<li><span class="parameter">life</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Lifespan in seconds. <strong>default: 2</strong>
|
||||
</li>
|
||||
<li><span class="parameter">damage</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
(default false) specifies whether particle can damage Lara (does a very small amount of damage, like the small lava emitters in TR1)
|
||||
Harm the player on collision. <strong>default: false</strong>
|
||||
</li>
|
||||
<li><span class="parameter">poison</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
(default false) specifies whether particle can poison Lara
|
||||
Poison the player on collision. <strong>default: false</strong>
|
||||
</li>
|
||||
<li><span class="parameter">spriteSeqID</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#SpriteConstants">SpriteConstants</a></span>
|
||||
Sprite sequence slot ID. <strong>default: Objects.ObjID.DEFAULT_SPRITES</strong>
|
||||
</li>
|
||||
<li><span class="parameter">startRot</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotation at start of life. <strong>default: random</strong>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -283,20 +328,77 @@
|
|||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">EmitParticle(
|
||||
yourPositionVarHere,
|
||||
Vec3(<span class="global">math</span>.random(), <span class="global">math</span>.random(), <span class="global">math</span>.random()),
|
||||
<span class="number">22</span>, <span class="comment">-- spriteIndex
|
||||
</span> <span class="number">0</span>, <span class="comment">-- gravity
|
||||
</span> -<span class="number">2</span>, <span class="comment">-- rot
|
||||
</span> Color(<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>), <span class="comment">-- startColor
|
||||
</span> Color(<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="comment">-- endColor
|
||||
</span> TEN.Effects.BlendID.ADDITIVE, <span class="comment">-- blendMode
|
||||
</span> <span class="number">15</span>, <span class="comment">-- startSize
|
||||
</span> <span class="number">50</span>, <span class="comment">-- endSize
|
||||
</span> <span class="number">20</span>, <span class="comment">-- lifetime
|
||||
</span> <span class="keyword">false</span>, <span class="comment">-- damage
|
||||
</span> <span class="keyword">true</span> <span class="comment">-- poison
|
||||
</span> )</pre>
|
||||
pos,
|
||||
Vec3(<span class="global">math</span>.random(), <span class="global">math</span>.random(), <span class="global">math</span>.random()),
|
||||
<span class="number">22</span>, <span class="comment">-- spriteID
|
||||
</span> <span class="number">0</span>, <span class="comment">-- gravity
|
||||
</span> -<span class="number">2</span>, <span class="comment">-- rotVel
|
||||
</span> Color(<span class="number">255</span>, <span class="number">0</span>, <span class="number">0</span>), <span class="comment">-- startColor
|
||||
</span> Color(<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="comment">-- endColor
|
||||
</span> TEN.Effects.BlendID.ADDITIVE, <span class="comment">-- blendMode
|
||||
</span> <span class="number">15</span>, <span class="comment">-- startSize
|
||||
</span> <span class="number">50</span>, <span class="comment">-- endSize
|
||||
</span> <span class="number">20</span>, <span class="comment">-- life
|
||||
</span> <span class="keyword">false</span>, <span class="comment">-- damage
|
||||
</span> <span class="keyword">true</span>, <span class="comment">-- poison
|
||||
</span> Objects.ObjID.DEFAULT_SPRITES, <span class="comment">-- spriteSeqID
|
||||
</span> <span class="number">180</span>) <span class="comment">-- startRot</span></pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "EmitAdvancedParticle"></a>
|
||||
<strong>EmitAdvancedParticle(ParticleData)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Emit a particle with extensive configuration options, including sprite sequence animation, lights, sounds, and damage effects.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">ParticleData</span>
|
||||
<span class="types"><a class="type" href="../1 modules/Effects.html#ParticleData">ParticleData</a></span>
|
||||
Table containing particle data.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example"><span class="keyword">local</span> particle =
|
||||
{
|
||||
pos = GetMoveableByName(<span class="string">"camera_target_6"</span>):GetPosition(),
|
||||
vel = Vec3(<span class="number">0</span>, <span class="number">0</span>, <span class="number">10</span>),
|
||||
spriteSeqID = TEN.Objects.ObjID.CUSTOM_BAR_GRAPHIC,
|
||||
spriteID = <span class="number">0</span>,
|
||||
life = <span class="number">10</span>,
|
||||
maxYVel = <span class="number">0</span>,
|
||||
gravity = <span class="number">0</span>,
|
||||
friction = <span class="number">10</span>,
|
||||
startRot = <span class="number">0</span>,
|
||||
rotVel = <span class="number">0</span>,
|
||||
startSize = <span class="number">80</span>,
|
||||
endSize = <span class="number">80</span>,
|
||||
startColor = TEN.Color(<span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>),
|
||||
endColor = TEN.Color(<span class="number">128</span>, <span class="number">128</span>, <span class="number">128</span>),
|
||||
blendMode = TEN.Effects.BlendID.ADDITIVE,
|
||||
wind = <span class="keyword">false</span>,
|
||||
damage = <span class="keyword">true</span>,
|
||||
poison = <span class="keyword">false</span>,
|
||||
burn = <span class="keyword">false</span>,
|
||||
damageHit = <span class="number">80</span>,
|
||||
soundID = <span class="number">197</span>,
|
||||
light = <span class="keyword">true</span>,
|
||||
lightRadius = <span class="number">6</span>,
|
||||
lightFlicker = <span class="number">5</span>,
|
||||
animated = <span class="keyword">true</span>,
|
||||
frameRate = <span class="number">0.25</span>,
|
||||
animType = TEN.Effects.ParticleAnimationType.LOOP,
|
||||
}
|
||||
EmitAdvancedParticle(particle)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
|
@ -352,7 +454,7 @@
|
|||
</dd>
|
||||
<dt>
|
||||
<a name = "EmitLight"></a>
|
||||
<strong>EmitLight(pos, color, radius)</strong>
|
||||
<strong>EmitLight(pos[, color][, radius][, shadows][, name])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Emit dynamic light that lasts for a single frame.
|
||||
|
@ -364,17 +466,84 @@
|
|||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
position of the light
|
||||
</li>
|
||||
<li><span class="parameter">color</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
light color (default Color(255, 255, 255))
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">radius</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
measured in "clicks" or 256 world units (default 20)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">shadows</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
determines whether light should generate dynamic shadows for applicable moveables (default is false)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "EmitSpotLight"></a>
|
||||
<strong>EmitSpotLight(pos, dir[, color][, radius][, falloff][, distance][, shadows][, name])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Emit dynamic directional spotlight that lasts for a single frame.
|
||||
If you want a light that sticks around, you must call this each frame.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
position of the light
|
||||
</li>
|
||||
<li><span class="parameter">dir</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
normal which indicates light direction
|
||||
</li>
|
||||
<li><span class="parameter">color</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
(default Color(255, 255, 255))
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">radius</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
(default 20) corresponds loosely to both intensity and range
|
||||
overall radius at the endpoint of a light cone, measured in "clicks" or 256 world units (default 10)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">falloff</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
radius, at which light starts to fade out, measured in "clicks" (default 5)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">distance</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
distance, at which light cone fades out, measured in "clicks" (default 20)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">shadows</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
determines whether light should generate dynamic shadows for applicable moveables (default is false)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
if provided, engine will interpolate this light for high framerate mode (be careful not to use same name for different lights)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -402,7 +571,39 @@
|
|||
</li>
|
||||
<li><span class="parameter">count</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
(default 1) "amount" of blood. Higher numbers won't add more blood but will make it more "flickery", with higher numbers turning it into a kind of red orb.
|
||||
Sprite count. <strong>default: 1</strong>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "EmitAirBubble"></a>
|
||||
<strong>EmitAirBubble(pos[, size][, amp])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Emit an air bubble in a water room.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
World position where the effect will be spawned. Must be in a water room.
|
||||
</li>
|
||||
<li><span class="parameter">size</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Sprite size. <strong>Default: 32</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">amp</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Oscillation amplitude. <strong>Default: 32</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -416,7 +617,7 @@
|
|||
<strong>EmitFire(pos, size)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Emit fire for one frame. Will not hurt Lara. Call this each frame if you want a continuous fire.
|
||||
Emit fire for one frame. Will not hurt player. Call this each frame if you want a continuous fire.
|
||||
|
||||
|
||||
|
||||
|
@ -430,7 +631,7 @@
|
|||
</li>
|
||||
<li><span class="parameter">size</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0)
|
||||
Fire size. <strong>default: 1</strong>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -492,6 +693,272 @@
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetWind"></a>
|
||||
<strong>GetWind()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the wind vector for the current game frame.
|
||||
This represents the 3D displacement applied by the engine on things like particles affected by wind.()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Wind vector.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "EmitStreamer"></a>
|
||||
<strong>EmitStreamer(mov, tag, pos, dir[, rot][, startColor][, endColor][, width][, life][, vel][, expRate][, rotRate][, edgeFeatherMode][, lengthFeatherMode][, blendID])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Emit an extending streamer effect.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">mov</span>
|
||||
<span class="types"><span class="type">Moveable</span></span>
|
||||
Moveable object with which to associate the effect.
|
||||
</li>
|
||||
<li><span class="parameter">tag</span>
|
||||
<span class="types"><span class="type">int[opt]</span></span>
|
||||
Numeric tag with which to associate the effect on the moveable. <strong>Default: 0</strong>
|
||||
</li>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
World position.
|
||||
</li>
|
||||
<li><span class="parameter">dir</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Direction vector of movement velocity.
|
||||
</li>
|
||||
<li><span class="parameter">rot</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Start rotation in degrees. <strong>Default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">startColor</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
Color at the start of life. <strong>Default: Color(255, 255, 255, 255))</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">endColor</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
Color at the end of life. <strong>Default: Color(0, 0, 0, 0))</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">width</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Width in world units. <strong>Default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">life</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Lifetime in seconds. <strong>Default: 1</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">vel</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Movement velocity in world units per second. <strong>Default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">expRate</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Width expansion rate in world units per second. <strong>Default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">rotRate</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotation rate in degrees per second. <strong>Default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">edgeFeatherMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Effects.StreamerFeatherMode.html#">StreamerFeatherMode</a></span>
|
||||
Edge feather mode. <strong>Default: Effects.StreamerFeatherMode.NONE</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">lengthFeatherMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Effects.StreamerFeatherMode.html#">StreamerFeatherMode</a></span>
|
||||
Length feather mode. <strong>UNIMPLEMENTED, currently will always leave a fading tail</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">blendID</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
|
||||
Renderer blend ID. <strong>Default: Effects.BlendID.ALPHA_BLEND</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h2 class="section-header "><a name="Tables"></a>Tables</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "ParticleData"></a>
|
||||
<strong>ParticleData</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Structure for EmitAdvancedParticle table.
|
||||
|
||||
|
||||
|
||||
<h3>Fields:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
World position.
|
||||
</li>
|
||||
<li><span class="parameter">vel</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Directional velocity in world units per second.
|
||||
</li>
|
||||
<li><span class="parameter">spriteSeqID</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#SpriteConstants">SpriteConstants</a></span>
|
||||
Sprite sequence slot ID. <strong>default: Objects.ObjID.DEFAULT_SPRITES</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">spriteID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Sprite ID in the sprite sequence slot. <strong>default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">life</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Lifespan in seconds. <strong>default: 2</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">maxYVel</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Maximum vertical velocity in world units per second. <strong>default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">gravity</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Effect of gravity in world units per second. Positive value ascend, negative value descend. <strong>default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">friction</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Friction affecting velocity over time in world units per second. <strong>default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">startRot</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotation at start of life. <strong>default: random</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">rotVel</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotational velocity in degrees per second. <strong>default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">startSize</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Size at start of life. <strong>default: 10</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">endSize</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Size at end of life. <strong>default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">startColor</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
Color at start of life. <strong>default: Color(255, 255, 255)</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">endColor</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
Color at end of life. Note that this will finish long before the end of life due to internal math. <strong>default: Color(255, 255, 255)</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">blendMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
|
||||
Render blend mode. <strong>default: TEN.Effects.BlendID.ALPHA_BLEND</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">damage</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Harm the player on collision. <strong>default: false</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">poison</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Poison the player on collision. <strong>default: false</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">burn</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Burn the player on collision. <strong>default: false</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">wind</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Affect position by wind in outside rooms. <strong>default: false</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">damageHit</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Player damage amount on collision. <strong>default: 2</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">light</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Emit a colored light. CAUTION: Recommended only for a single particle. Too many particles with lights can overwhelm the lighting system. <strong>default: false</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">lightRadius</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Light radius in 1/4 blocks. <strong>default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">lightFlicker</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Interval at which the light should flicker. <strong>default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">soundID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Sound ID to play. CAUTION: Recommended only for a single particle. Too many particles with sounds can overwhelm the sound system. <strong>default: none</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">animated</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Play animates sprite sequence. <strong>default: false</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">animType</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Effects.ParticleAnimationType.html#">ParticleAnimationType</a></span>
|
||||
Animation type of the sprite sequence. <strong>default: TEN.Effects.ParticleAnimationType.LOOP</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">frameRate</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Sprite sequence animation framerate. <strong>default: 1</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
@ -500,7 +967,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -126,6 +144,10 @@ scripts too.</p>
|
|||
<td class="summary">Enable or disable level selection in title flyby.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EnableHomeLevel">EnableHomeLevel(enabled)</a></td>
|
||||
<td class="summary">Enable or disable Home Level entry in the main menu.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EnableLoadSave">EnableLoadSave(enabled)</a></td>
|
||||
<td class="summary">Enable or disable saving and loading of savegames.</td>
|
||||
</tr>
|
||||
|
@ -134,7 +156,7 @@ scripts too.</p>
|
|||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#EnableFlyCheat">EnableFlyCheat(enabled)</a></td>
|
||||
<td class="summary">Enable or disable DOZY mode (fly cheat).</td>
|
||||
<td class="summary">Enable or disable the fly cheat.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EnablePointFilter">EnablePointFilter(enabled)</a></td>
|
||||
|
@ -153,8 +175,28 @@ scripts too.</p>
|
|||
<td class="summary">Returns the level that the game control is running in that moment.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EndLevel">EndLevel([index])</a></td>
|
||||
<td class="summary">Finishes the current level, with optional level index provided.</td>
|
||||
<td class="name" ><a href="#EndLevel">EndLevel([index][, startPos])</a></td>
|
||||
<td class="summary">Finishes the current level, with optional level index and start position index provided.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetStatistics">GetStatistics(game)</a></td>
|
||||
<td class="summary">Get game or level statistics.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetStatistics">SetStatistics(statistics, game)</a></td>
|
||||
<td class="summary">Set game or level statistics.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetGameStatus">GetGameStatus()</a></td>
|
||||
<td class="summary">Get current game status, such as normal game loop, exiting to title, etc.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFreezeMode">GetFreezeMode()</a></td>
|
||||
<td class="summary">Get current freeze mode, such as none, full, spectator or player.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetFreezeMode">SetFreezeMode(freezeMode)</a></td>
|
||||
<td class="summary">Set current freeze mode, such as none, full, spectator or player.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SaveGame">SaveGame(slotID)</a></td>
|
||||
|
@ -185,23 +227,31 @@ scripts too.</p>
|
|||
<td class="summary">Adds one secret to current level secret count and also plays secret music track.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetTotalSecretCount">SetTotalSecretCount(total)</a></td>
|
||||
<td class="summary">Total number of secrets in game.</td>
|
||||
<td class="name" ><a href="#GetTotalSecretCount">GetTotalSecretCount()</a></td>
|
||||
<td class="summary">Get total number of secrets in the game.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetTotalSecretCount">SetTotalSecretCount(count)</a></td>
|
||||
<td class="summary">Set total number of secrets in the game.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#FlipMap">FlipMap(flipmap)</a></td>
|
||||
<td class="summary">Do FlipMap with specific group ID.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFlipMapStatus">GetFlipMapStatus([index])</a></td>
|
||||
<td class="summary">Get current FlipMap status for specific group ID.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#settings_lua">settings.lua </a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetSettings">SetSettings(settings)</a></td>
|
||||
<td class="summary">
|
||||
|
||||
</td>
|
||||
<td class="summary">Set provided settings table to an engine.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetAnimations">SetAnimations(animations)</a></td>
|
||||
<td class="summary">
|
||||
|
||||
</td>
|
||||
<td class="name" ><a href="#GetSettings">GetSettings()</a></td>
|
||||
<td class="summary">Get settings table from an engine.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#strings_lua">strings.lua </a></h2>
|
||||
|
@ -215,6 +265,10 @@ scripts too.</p>
|
|||
<td class="summary">Get translated string.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsStringPresent">IsStringPresent(string)</a></td>
|
||||
<td class="summary">Check if translated string is present.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetLanguageNames">SetLanguageNames(table)</a></td>
|
||||
<td class="summary">Set language names for translations.</td>
|
||||
</tr>
|
||||
|
@ -348,11 +402,11 @@ Must be true or false
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "EnableLoadSave"></a>
|
||||
<strong>EnableLoadSave(enabled)</strong>
|
||||
<a name = "EnableHomeLevel"></a>
|
||||
<strong>EnableHomeLevel(enabled)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Enable or disable saving and loading of savegames.
|
||||
Enable or disable Home Level entry in the main menu. ()
|
||||
|
||||
|
||||
|
||||
|
@ -360,7 +414,29 @@ Must be true or false
|
|||
<ul>
|
||||
<li><span class="parameter">enabled</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
true or false.
|
||||
True or false.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "EnableLoadSave"></a>
|
||||
<strong>EnableLoadSave(enabled)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Enable or disable saving and loading of savegames. ()
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">enabled</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
True or false.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -378,8 +454,7 @@ Must be true or false
|
|||
<strong>EnableFlyCheat(enabled)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Enable or disable DOZY mode (fly cheat).
|
||||
Must be true or false
|
||||
Enable or disable the fly cheat. ()
|
||||
|
||||
|
||||
|
||||
|
@ -387,7 +462,7 @@ Must be true or false
|
|||
<ul>
|
||||
<li><span class="parameter">enabled</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
true or false
|
||||
True or false.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -495,12 +570,13 @@ have an ID of 0, the second an ID of 1, and so on.
|
|||
</dd>
|
||||
<dt>
|
||||
<a name = "EndLevel"></a>
|
||||
<strong>EndLevel([index])</strong>
|
||||
<strong>EndLevel([index][, startPos])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Finishes the current level, with optional level index provided. If level index
|
||||
is not provided or is zero, jumps to next level. If level index is more than
|
||||
level count, jumps to title.
|
||||
Finishes the current level, with optional level index and start position index provided.
|
||||
If level index is not provided or is zero, jumps to next level. If level index is more than
|
||||
level count, jumps to title. If LARA_START_POS objects are present in level, player will be
|
||||
teleported to such object with OCB similar to provided second argument.
|
||||
|
||||
|
||||
|
||||
|
@ -511,6 +587,131 @@ level count, jumps to title.
|
|||
level index (default 0)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">startPos</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
player start position (default 0)
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetStatistics"></a>
|
||||
<strong>GetStatistics(game)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get game or level statistics. For reference about statistics class, see <a href="../2 classes/Flow.Statistics.html#">Flow.Statistics</a>.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">game</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
if true, returns overall game statistics, otherwise returns current level statistics (default: false)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Flow.Statistics.html#">Statistics</a></span>
|
||||
statistics structure representing game or level statistics
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetStatistics"></a>
|
||||
<strong>SetStatistics(statistics, game)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set game or level statistics. For reference about statistics class, see <a href="../2 classes/Flow.Statistics.html#">Flow.Statistics</a>.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">statistics</span>
|
||||
<span class="types"><a class="type" href="../2 classes/Flow.Statistics.html#">Statistics</a></span>
|
||||
statistic object to set
|
||||
</li>
|
||||
<li><span class="parameter">game</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
if true, sets overall game statistics, otherwise sets current level statistics (default: false)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetGameStatus"></a>
|
||||
<strong>GetGameStatus()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get current game status, such as normal game loop, exiting to title, etc.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/Flow.GameStatus.html#">GameStatus</a></span>
|
||||
the current game status
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFreezeMode"></a>
|
||||
<strong>GetFreezeMode()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get current freeze mode, such as none, full, spectator or player.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/Flow.FreezeMode.html#">FreezeMode</a></span>
|
||||
the current freeze mode
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetFreezeMode"></a>
|
||||
<strong>SetFreezeMode(freezeMode)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set current freeze mode, such as none, full, spectator or player. <br/>
|
||||
Freeze mode specifies whether game is in normal mode or paused in a particular way to allow
|
||||
custom menu creation, photo mode or time freeze.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">freezeMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Flow.FreezeMode.html#">FreezeMode</a></span>
|
||||
new freeze mode to set.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
@ -679,20 +880,41 @@ The index argument corresponds to the secret's unique ID, the same that would go
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetTotalSecretCount"></a>
|
||||
<strong>SetTotalSecretCount(total)</strong>
|
||||
<a name = "GetTotalSecretCount"></a>
|
||||
<strong>GetTotalSecretCount()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Total number of secrets in game.
|
||||
Get total number of secrets in the game.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Total number of secrets in the game.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetTotalSecretCount"></a>
|
||||
<strong>SetTotalSecretCount(count)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set total number of secrets in the game.
|
||||
Must be an integer value (0 means no secrets).
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">total</span>
|
||||
<li><span class="parameter">count</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
number of secrets
|
||||
Total number of secrets in the game.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -700,13 +922,63 @@ Must be an integer value (0 means no secrets).
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "FlipMap"></a>
|
||||
<strong>FlipMap(flipmap)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Do FlipMap with specific group ID.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">flipmap</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
ID of flipmap group to actuvate / deactivate.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFlipMapStatus"></a>
|
||||
<strong>GetFlipMapStatus([index])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get current FlipMap status for specific group ID.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">index</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Flipmap group ID to check. If no group specified or group is -1, function returns overall flipmap status (on or off).
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Status of the flipmap group (true means on, false means off).
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h2 class="section-header has-description"><a name="settings_lua"></a>settings.lua </h2>
|
||||
|
||||
<div class="section-description">
|
||||
These functions are called in settings.lua, a file which holds your local settings.
|
||||
settings.lua shouldn't be bundled with any finished levels/games.
|
||||
These functions are called in settings.lua, a file which holds global settings, such as system settings, flare color or animation movesets.
|
||||
</div>
|
||||
<dl class="function">
|
||||
<dt>
|
||||
|
@ -714,9 +986,7 @@ settings.lua shouldn't be bundled with any finished levels/games.
|
|||
<strong>SetSettings(settings)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
|
||||
|
||||
Set provided settings table to an engine.
|
||||
|
||||
|
||||
|
||||
|
@ -724,7 +994,7 @@ settings.lua shouldn't be bundled with any finished levels/games.
|
|||
<ul>
|
||||
<li><span class="parameter">settings</span>
|
||||
<span class="types"><a class="type" href="../2 classes/Flow.Settings.html#">Settings</a></span>
|
||||
a settings object
|
||||
a settings table
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -734,24 +1004,21 @@ settings.lua shouldn't be bundled with any finished levels/games.
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetAnimations"></a>
|
||||
<strong>SetAnimations(animations)</strong>
|
||||
<a name = "GetSettings"></a>
|
||||
<strong>GetSettings()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get settings table from an engine.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">animations</span>
|
||||
<span class="types"><a class="type" href="../2 classes/Flow.Animations.html#">Animations</a></span>
|
||||
an animations object
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Flow.Settings.html#">Settings</a></span>
|
||||
current settings table
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
@ -809,6 +1076,28 @@ You will not need to call them manually.
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsStringPresent"></a>
|
||||
<strong>IsStringPresent(string)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if translated string is present.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">string</span>
|
||||
<span class="types"><span class="type">key</span></span>
|
||||
key for translated string
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetLanguageNames"></a>
|
||||
|
@ -840,7 +1129,6 @@ Specify which translations in the strings table correspond to which languages.
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -126,7 +144,11 @@
|
|||
<td class="summary">Clear an action key.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCursorDisplayPosition">GetCursorDisplayPosition()</a></td>
|
||||
<td class="name" ><a href="#KeyClearAll">KeyClearAll()</a></td>
|
||||
<td class="summary">Clear all action keys.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetMouseDisplayPosition">GetMouseDisplayPosition()</a></td>
|
||||
<td class="summary">Get the display position of the cursor in percent.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -253,8 +275,23 @@
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCursorDisplayPosition"></a>
|
||||
<strong>GetCursorDisplayPosition()</strong>
|
||||
<a name = "KeyClearAll"></a>
|
||||
<strong>KeyClearAll()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Clear all action keys.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetMouseDisplayPosition"></a>
|
||||
<strong>GetMouseDisplayPosition()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the display position of the cursor in percent. ()
|
||||
|
@ -280,7 +317,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -121,6 +139,18 @@
|
|||
<td class="name" ><a href="#SetItemCount">SetItemCount(objectID, count)</a></td>
|
||||
<td class="summary">Set the amount of an item in the player's inventory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetUsedItem">GetUsedItem()</a></td>
|
||||
<td class="summary">Get last item used in the player's inventory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetUsedItem">SetUsedItem(objectID)</a></td>
|
||||
<td class="summary">Set last item used in the player's inventory.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#ClearUsedItem">ClearUsedItem()</a></td>
|
||||
<td class="summary">Clear last item used in the player's inventory.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
@ -242,6 +272,70 @@
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetUsedItem"></a>
|
||||
<strong>GetUsedItem()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get last item used in the player's inventory.
|
||||
This value will be valid only for a single frame after exiting inventory, after which Lara says "No".
|
||||
Therefore, this function must be preferably used either in OnLoop or OnUseItem events.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
Last item used in the inventory.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetUsedItem"></a>
|
||||
<strong>SetUsedItem(objectID)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set last item used in the player's inventory.
|
||||
You will be able to specify only objects which already exist in the inventory.
|
||||
Will only be valid for the next frame. If not processed by the game, Lara will say "No".
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">objectID</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
Object ID of the item to select from inventory.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "ClearUsedItem"></a>
|
||||
<strong>ClearUsedItem()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Clear last item used in the player's inventory.
|
||||
When this function is used in OnUseItem level function, it allows to override existing item functionality.
|
||||
For items without existing functionality, this function is needed to avoid Lara saying "No" after using it.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
@ -250,7 +344,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -106,16 +124,24 @@
|
|||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#AddCallback">AddCallback(CallbackPoint, func)</a></td>
|
||||
<td class="name" ><a href="#AddCallback">AddCallback(point, func)</a></td>
|
||||
<td class="summary">Register a function as a callback.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#RemoveCallback">RemoveCallback(CallbackPoint, LevelFunc)</a></td>
|
||||
<td class="name" ><a href="#RemoveCallback">RemoveCallback(point, func)</a></td>
|
||||
<td class="summary">Deregister a function as a callback.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#HandleEvent">HandleEvent(string, EventType, Moveable)</a></td>
|
||||
<td class="summary">Attempt to find an event set and exectute a particular event from it.</td>
|
||||
<td class="name" ><a href="#HandleEvent">HandleEvent(name, type, activator)</a></td>
|
||||
<td class="summary">Attempt to find an event set and execute a particular event from it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#EnableEvent">EnableEvent(name, type)</a></td>
|
||||
<td class="summary">Attempt to find an event set and enable specified event in it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisableEvent">DisableEvent(name, type)</a></td>
|
||||
<td class="summary">Attempt to find an event set and disable specified event in it.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#Special_objects">Special objects </a></h2>
|
||||
|
@ -150,7 +176,7 @@
|
|||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "AddCallback"></a>
|
||||
<strong>AddCallback(CallbackPoint, func)</strong>
|
||||
<strong>AddCallback(point, func)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Register a function as a callback.
|
||||
|
@ -159,39 +185,46 @@
|
|||
stuff during level start/load/end/save/control phase, but don't want the level
|
||||
designer to add calls to <code>OnStart</code>, <code>OnLoad</code>, etc. in their level script.</p>
|
||||
|
||||
<p>Possible values for CallbackPoint:</p>
|
||||
<p>Possible values for <code>point</code>:</p>
|
||||
<pre class="example"><span class="comment">-- These take functions which accept no arguments
|
||||
</span>PRESTART <span class="comment">-- will be called immediately before OnStart
|
||||
</span>POSTSTART <span class="comment">-- will be called immediately after OnStart
|
||||
</span>PRE_START <span class="comment">-- will be called immediately before OnStart
|
||||
</span>POST_START <span class="comment">-- will be called immediately after OnStart
|
||||
</span>
|
||||
PRESAVE <span class="comment">-- will be called immediately before OnSave
|
||||
</span>POSTSAVE <span class="comment">-- will be called immediately after OnSave
|
||||
PRE_SAVE <span class="comment">-- will be called immediately before OnSave
|
||||
</span>POST_SAVE <span class="comment">-- will be called immediately after OnSave
|
||||
</span>
|
||||
PRELOAD <span class="comment">-- will be called immediately before OnLoad
|
||||
</span>POSTLOAD <span class="comment">-- will be called immediately after OnLoad
|
||||
PRE_LOAD <span class="comment">-- will be called immediately before OnLoad
|
||||
</span>POST_LOAD <span class="comment">-- will be called immediately after OnLoad
|
||||
</span>
|
||||
PRE_FREEZE <span class="comment">-- will be called before entering freeze mode
|
||||
</span>POST_FREEZE <span class="comment">-- will be called immediately after exiting freeze mode
|
||||
</span>
|
||||
<span class="comment">-- These take a LevelEndReason arg, like OnEnd
|
||||
</span>PREEND <span class="comment">-- will be called immediately before OnEnd
|
||||
</span>POSTEND <span class="comment">-- will be called immediately after OnEnd
|
||||
</span>PRE_END <span class="comment">-- will be called immediately before OnEnd
|
||||
</span>POST_END <span class="comment">-- will be called immediately after OnEnd
|
||||
</span>
|
||||
<span class="comment">-- These take functions which accepts a deltaTime argument
|
||||
</span>PRECONTROLPHASE <span class="comment">-- will be called immediately before OnControlPhase
|
||||
</span>POSTCONTROLPHASE <span class="comment">-- will be called immediately after OnControlPhase
|
||||
</span>PRE_LOOP <span class="comment">-- will be called in the beginning of game loop
|
||||
</span>POST_LOOP <span class="comment">-- will be called at the end of game loop
|
||||
</span>
|
||||
<span class="comment">-- These take functions which accepts an objectNumber argument, like OnUseItem
|
||||
</span>PRE_USE_ITEM <span class="comment">-- will be called immediately before OnUseItem
|
||||
</span>POST_USE_ITEM <span class="comment">-- will be called immediately after OnUseItem
|
||||
</span></pre>
|
||||
<p>The order in which two functions with the same CallbackPoint are called is undefined.
|
||||
i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRECONTROLPHASE</code>, both will be called before <code>OnControlPhase</code>, but there is no guarantee that <code>MyFunc</code> will be called before <code>MyFunc2</code>, or vice-versa.</p>
|
||||
i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRELOOP</code>, both will be called in the beginning of game loop, but there is no guarantee that <code>MyFunc</code> will be called before <code>MyFunc2</code>, or vice-versa.</p>
|
||||
|
||||
<p>Any returned value will be discarded.</p>
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">CallbackPoint</span>
|
||||
<span class="types"><span class="type">point</span></span>
|
||||
<li><span class="parameter">point</span>
|
||||
<span class="types"><span class="type">CallbackPoint</span></span>
|
||||
When should the callback be called?
|
||||
</li>
|
||||
<li><span class="parameter">func</span>
|
||||
<span class="types"><span class="type">function</span></span>
|
||||
<span class="types"><span class="type">LevelFunc</span></span>
|
||||
The function to be called (must be in the <a href="../1 modules/Logic.html#LevelFuncs">LevelFuncs</a> hierarchy). Will receive, as an argument, the time in seconds since the last frame.
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -202,13 +235,13 @@ i.e. if you register <code>MyFunc</code> and <code>MyFunc2</code> with <code>PRE
|
|||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">LevelFuncs.MyFunc = <span class="keyword">function</span>(dt) <span class="global">print</span>(dt) <span class="keyword">end</span>
|
||||
TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.MyFunc)</pre>
|
||||
TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRELOOP, LevelFuncs.MyFunc)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "RemoveCallback"></a>
|
||||
<strong>RemoveCallback(CallbackPoint, LevelFunc)</strong>
|
||||
<strong>RemoveCallback(point, func)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Deregister a function as a callback.
|
||||
|
@ -218,13 +251,13 @@ Will have no effect if the function was not registered as a callback
|
|||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">CallbackPoint</span>
|
||||
<span class="types"><span class="type">point</span></span>
|
||||
<li><span class="parameter">point</span>
|
||||
<span class="types"><span class="type">CallbackPoint</span></span>
|
||||
The callback point the function was registered with. See <a href="../1 modules/Logic.html#AddCallback">AddCallback</a>
|
||||
</li>
|
||||
<li><span class="parameter">LevelFunc</span>
|
||||
<span class="types"><span class="type">func</span></span>
|
||||
the function to remove; must be in the LevelFuncs hierarchy.
|
||||
<li><span class="parameter">func</span>
|
||||
<span class="types"><span class="type">LevelFunc</span></span>
|
||||
The function to remove; must be in the LevelFuncs hierarchy.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -233,32 +266,95 @@ Will have no effect if the function was not registered as a callback
|
|||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">TEN.Logic.RemoveCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.MyFunc)</pre>
|
||||
<pre class="example">TEN.Logic.RemoveCallback(TEN.Logic.CallbackPoint.PRELOOP, LevelFuncs.MyFunc)</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "HandleEvent"></a>
|
||||
<strong>HandleEvent(string, EventType, Moveable)</strong>
|
||||
<strong>HandleEvent(name, type, activator)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Attempt to find an event set and exectute a particular event from it.
|
||||
Attempt to find an event set and execute a particular event from it.
|
||||
|
||||
<p>Possible event type values:</p>
|
||||
<pre class="example">ENTER
|
||||
INSIDE
|
||||
LEAVE
|
||||
LOAD
|
||||
SAVE
|
||||
START
|
||||
END
|
||||
LOOP
|
||||
USE_ITEM
|
||||
MENU</pre>
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
Name of the event set to find.
|
||||
</li>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><span class="type">EventType</span></span>
|
||||
Event to execute.
|
||||
</li>
|
||||
<li><span class="parameter">activator</span>
|
||||
<span class="types"><a class="type" href="../2 classes/Objects.Moveable.html#">Moveable</a></span>
|
||||
Optional activator. Default is the player object.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "EnableEvent"></a>
|
||||
<strong>EnableEvent(name, type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Attempt to find an event set and enable specified event in it.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">string</span>
|
||||
<span class="types"><span class="type">name</span></span>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
Name of the event set to find.
|
||||
</li>
|
||||
<li><span class="parameter">EventType</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#pdf-type">type</a></span>
|
||||
Event to execute.
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><span class="type">EventType</span></span>
|
||||
Event to enable.
|
||||
</li>
|
||||
<li><span class="parameter">Moveable</span>
|
||||
<span class="types"><span class="type">activator</span></span>
|
||||
Optional activator. Default is the player object.
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisableEvent"></a>
|
||||
<strong>DisableEvent(name, type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Attempt to find an event set and disable specified event in it.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
Name of the event set to find.
|
||||
</li>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><span class="type">EventType</span></span>
|
||||
Event to disable.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -390,7 +486,7 @@ you can just leave out <code>LevelFuncs.OnStart</code>.</p>
|
|||
<li>The level script itself is run (i.e. any code you put outside the <a href="../1 modules/Logic.html#LevelFuncs">LevelFuncs</a> callbacks is executed).</li>
|
||||
<li>Save data is loaded, if saving from a saved game (will empty <a href="../1 modules/Logic.html#LevelVars">LevelVars</a> and <a href="../1 modules/Logic.html#GameVars">GameVars</a> and repopulate them with what they contained when the game was saved).</li>
|
||||
<li>If loading from a save, <code>OnLoaded</code> will be called. Otherwise, <code>OnStart</code> will be called.</li>
|
||||
<li>The control loop, in which <code>OnControlPhase</code> will be called once per frame, begins.</li>
|
||||
<li>The control loop, in which <code>OnLoop</code> will be called once per frame, begins.</li>
|
||||
</ol>
|
||||
|
||||
</p>
|
||||
|
@ -406,7 +502,7 @@ you can just leave out <code>LevelFuncs.OnStart</code>.</p>
|
|||
<span class="types"><span class="type">function</span></span>
|
||||
Will be called when a saved game is loaded, just <em>after</em> data is loaded
|
||||
</li>
|
||||
<li><span class="parameter">OnControlPhase</span>
|
||||
<li><span class="parameter">OnLoop</span>
|
||||
<span class="types"><span class="type">function(float)</span></span>
|
||||
Will be called during the game's update loop,
|
||||
and provides the delta time (a float representing game time since last call) via its argument.
|
||||
|
@ -420,9 +516,9 @@ and provides the delta time (a float representing game time since last call) via
|
|||
|
||||
<p>(EndReason) Will be called when leaving a level. This includes finishing it, exiting to the menu, or loading a save in a different level. It can take an <code>EndReason</code> arg:</p>
|
||||
|
||||
<pre><code>EXITTOTITLE
|
||||
LEVELCOMPLETE
|
||||
LOADGAME
|
||||
<pre><code>EXIT_TO_TITLE
|
||||
LEVEL_COMPLETE
|
||||
LOAD_GAME
|
||||
DEATH
|
||||
OTHER
|
||||
</code></pre>
|
||||
|
@ -436,6 +532,14 @@ end
|
|||
</code></pre>
|
||||
|
||||
</li>
|
||||
<li><span class="parameter">OnUseItem</span>
|
||||
<span class="types"><span class="type">function</span></span>
|
||||
Will be called when using an item from inventory.
|
||||
</li>
|
||||
<li><span class="parameter">OnFreeze</span>
|
||||
<span class="types"><span class="type">function</span></span>
|
||||
Will be called when any of the Freeze modes are activated.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
@ -450,7 +554,6 @@ end
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -97,7 +115,7 @@
|
|||
<div id="content">
|
||||
|
||||
<h1>Table <code>Objects</code></h1>
|
||||
<p>Moveables, statics, cameras, and so on.</p>
|
||||
<p>Objects including moveables, statics, cameras, and others.</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
@ -473,7 +491,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -106,40 +124,40 @@
|
|||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#PlayAudioTrack">PlayAudioTrack(name, type)</a></td>
|
||||
<td class="summary">Play an audio track</td>
|
||||
<td class="name" ><a href="#PlayAudioTrack">PlayAudioTrack(filename, type)</a></td>
|
||||
<td class="summary">Play an audio track.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetAmbientTrack">SetAmbientTrack(name)</a></td>
|
||||
<td class="summary">Set and play an ambient track</td>
|
||||
<td class="name" ><a href="#SetAmbientTrack">SetAmbientTrack(name, fromStart)</a></td>
|
||||
<td class="summary">Set and play an ambient track.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#StopAudioTracks">StopAudioTracks()</a></td>
|
||||
<td class="summary">Stop any audio tracks currently playing</td>
|
||||
<td class="summary">Stop any audio tracks currently playing.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#StopAudioTrack">StopAudioTrack(type)</a></td>
|
||||
<td class="summary">Stop audio track that is currently playing</td>
|
||||
<td class="summary">Stop audio track that is currently playing.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetAudioTrackLoudness">GetAudioTrackLoudness(type)</a></td>
|
||||
<td class="summary">Get current loudness level for specified track type</td>
|
||||
<td class="summary">Get current loudness level for specified track type.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PlaySound">PlaySound(sound[, position])</a></td>
|
||||
<td class="summary">Play sound effect</td>
|
||||
<td class="name" ><a href="#PlaySound">PlaySound(soundID[, position])</a></td>
|
||||
<td class="summary">Play sound effect.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#StopSound">StopSound(sound)</a></td>
|
||||
<td class="summary">Stop sound effect</td>
|
||||
<td class="name" ><a href="#StopSound">StopSound(soundID)</a></td>
|
||||
<td class="summary">Stop sound effect.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsSoundPlaying">IsSoundPlaying(Sound)</a></td>
|
||||
<td class="summary">Check if the sound effect is playing</td>
|
||||
<td class="name" ><a href="#IsSoundPlaying">IsSoundPlaying(soundID)</a></td>
|
||||
<td class="summary">Check if the sound effect is playing.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsAudioTrackPlaying">IsAudioTrackPlaying(Track)</a></td>
|
||||
<td class="summary">Check if the audio track is playing</td>
|
||||
<td class="summary">Check if the audio track is playing.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCurrentSubtitle">GetCurrentSubtitle()</a></td>
|
||||
|
@ -156,22 +174,22 @@
|
|||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "PlayAudioTrack"></a>
|
||||
<strong>PlayAudioTrack(name, type)</strong>
|
||||
<strong>PlayAudioTrack(filename, type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Play an audio track
|
||||
Play an audio track. Supported formats are wav, mp3 and ogg.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">name</span>
|
||||
<li><span class="parameter">filename</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
of track (without file extension) to play
|
||||
Filename of a track (without file extension) to play.
|
||||
</li>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
|
||||
of the audio track to play
|
||||
Type of the audio track to play.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -182,10 +200,10 @@
|
|||
</dd>
|
||||
<dt>
|
||||
<a name = "SetAmbientTrack"></a>
|
||||
<strong>SetAmbientTrack(name)</strong>
|
||||
<strong>SetAmbientTrack(name, fromStart)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set and play an ambient track
|
||||
Set and play an ambient track.
|
||||
|
||||
|
||||
|
||||
|
@ -193,7 +211,11 @@
|
|||
<ul>
|
||||
<li><span class="parameter">name</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
of track (without file extension) to play
|
||||
Name of track (without file extension) to play.
|
||||
</li>
|
||||
<li><span class="parameter">fromStart</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Specifies whether ambient track should play from the start, or crossfade at a random position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -207,7 +229,7 @@
|
|||
<strong>StopAudioTracks()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Stop any audio tracks currently playing
|
||||
Stop any audio tracks currently playing.
|
||||
|
||||
|
||||
|
||||
|
@ -222,7 +244,7 @@
|
|||
<strong>StopAudioTrack(type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Stop audio track that is currently playing
|
||||
Stop audio track that is currently playing.
|
||||
|
||||
|
||||
|
||||
|
@ -230,7 +252,7 @@
|
|||
<ul>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
|
||||
of the audio track
|
||||
Type of the audio track.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -244,7 +266,7 @@
|
|||
<strong>GetAudioTrackLoudness(type)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get current loudness level for specified track type
|
||||
Get current loudness level for specified track type.
|
||||
|
||||
|
||||
|
||||
|
@ -252,7 +274,7 @@
|
|||
<ul>
|
||||
<li><span class="parameter">type</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Sound.SoundTrackType.html#">SoundTrackType</a></span>
|
||||
of the audio track
|
||||
Type of the audio track.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -260,7 +282,7 @@
|
|||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
current loudness of a specified audio track
|
||||
Current loudness of a specified audio track.
|
||||
</ol>
|
||||
|
||||
|
||||
|
@ -269,18 +291,18 @@
|
|||
</dd>
|
||||
<dt>
|
||||
<a name = "PlaySound"></a>
|
||||
<strong>PlaySound(sound[, position])</strong>
|
||||
<strong>PlaySound(soundID[, position])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Play sound effect
|
||||
Play sound effect.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">sound</span>
|
||||
<li><span class="parameter">soundID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
Sound ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
</li>
|
||||
<li><span class="parameter">position</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
|
@ -296,18 +318,18 @@
|
|||
</dd>
|
||||
<dt>
|
||||
<a name = "StopSound"></a>
|
||||
<strong>StopSound(sound)</strong>
|
||||
<strong>StopSound(soundID)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Stop sound effect
|
||||
Stop sound effect.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">sound</span>
|
||||
<li><span class="parameter">soundID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
Sound ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -318,18 +340,18 @@
|
|||
</dd>
|
||||
<dt>
|
||||
<a name = "IsSoundPlaying"></a>
|
||||
<strong>IsSoundPlaying(Sound)</strong>
|
||||
<strong>IsSoundPlaying(soundID)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if the sound effect is playing
|
||||
Check if the sound effect is playing.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">Sound</span>
|
||||
<li><span class="parameter">soundID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
ID to check. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
Sound ID to check. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -343,7 +365,7 @@
|
|||
<strong>IsAudioTrackPlaying(Track)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if the audio track is playing
|
||||
Check if the audio track is playing.
|
||||
|
||||
|
||||
|
||||
|
@ -351,7 +373,7 @@
|
|||
<ul>
|
||||
<li><span class="parameter">Track</span>
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
filename to check. Should be without extension and without full directory path.
|
||||
Filename to check. Should be without extension and without full directory path.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -366,8 +388,8 @@
|
|||
</dt>
|
||||
<dd>
|
||||
Get current subtitle string for a voice track currently playing.
|
||||
Subtitle file must be in .srt format, have same filename as voice track, and be placed in same directory as voice track.
|
||||
Returns nil if no voice track is playing or no subtitle present.
|
||||
Subtitle file must be in .srt format, have same filename as voice track, and be placed in same directory as voice track.
|
||||
Returns nil if no voice track is playing or no subtitle present.
|
||||
|
||||
|
||||
|
||||
|
@ -376,7 +398,7 @@ Returns nil if no voice track is playing or no subtitle present.
|
|||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
current subtitle string
|
||||
Current subtitle string.
|
||||
</ol>
|
||||
|
||||
|
||||
|
@ -390,7 +412,6 @@ Returns nil if no voice track is playing or no subtitle present.
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -106,7 +124,7 @@
|
|||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#ShowString">ShowString(str, time)</a></td>
|
||||
<td class="name" ><a href="#ShowString">ShowString(str, time, autoDelete)</a></td>
|
||||
<td class="summary">Show some text on-screen.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -128,7 +146,7 @@
|
|||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "ShowString"></a>
|
||||
<strong>ShowString(str, time)</strong>
|
||||
<strong>ShowString(str, time, autoDelete)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Show some text on-screen.
|
||||
|
@ -148,6 +166,13 @@ If not given, the string will have an "infinite" life, and will show
|
|||
until <a href="../1 modules/Strings.html#HideString">HideString</a> is called or until the level is finished.
|
||||
Default: nil (i.e. infinite)
|
||||
</li>
|
||||
<li><span class="parameter">autoDelete</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
should be string automatically deleted after timeout is reached.
|
||||
If not given, the string will remain allocated even after timeout is reached, and can be
|
||||
shown again without re-initialization.
|
||||
Default: true
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
@ -213,7 +238,6 @@ with a call to <a href="../1 modules/Strings.html#ShowString">ShowString</a>, or
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -110,14 +128,14 @@
|
|||
<td class="summary">Determine if there is a clear line of sight between two positions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#CalculateDistance">CalculateDistance(posA, posB)</a></td>
|
||||
<td class="summary">Calculate the distance between two positions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#CalculateHorizontalDistance">CalculateHorizontalDistance(posA, posB)</a></td>
|
||||
<td class="summary">Calculate the horizontal distance between two positions.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetDisplayPosition">GetDisplayPosition(worldPos)</a></td>
|
||||
<td class="summary">Get the projected display space position of a 3D world position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PercentToScreen">PercentToScreen(x, y)</a></td>
|
||||
<td class="summary">Translate a pair display position coordinates to pixel coordinates.</td>
|
||||
</tr>
|
||||
|
@ -126,17 +144,18 @@
|
|||
<td class="summary">Translate a pair of pixel coordinates to display position coordinates.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PickMoveableByDisplayPosition">PickMoveableByDisplayPosition(Display)</a></td>
|
||||
<td class="summary">Pick a moveable by the given display position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PickStaticByDisplayPosition">PickStaticByDisplayPosition(Display)</a></td>
|
||||
<td class="summary">Pick a static mesh by the given display position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PrintLog">PrintLog(message, logLevel[, allowSpam])</a></td>
|
||||
<td class="summary">Write messages within the Log file</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#Fields">Fields</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#worldPos">worldPos</a></td>
|
||||
<td class="summary">Get the projected display space position of a 3D world position.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
@ -186,38 +205,6 @@
|
|||
<span class="global">print</span>(Misc.HasLineOfSight(enemyHead:GetRoomNumber(), enemyHead:GetPosition(), flamePlinthPos))</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "CalculateDistance"></a>
|
||||
<strong>CalculateDistance(posA, posB)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Calculate the distance between two positions.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">posA</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
First position.
|
||||
</li>
|
||||
<li><span class="parameter">posB</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Second position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Distance between two positions.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "CalculateHorizontalDistance"></a>
|
||||
|
@ -250,6 +237,41 @@
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetDisplayPosition"></a>
|
||||
<strong>GetDisplayPosition(worldPos)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the projected display space position of a 3D world position. Returns nil if the world position is behind the camera view.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">worldPos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
3D world position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Projected display space position in percent.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">Example: Display a <span class="global">string</span> at the player<span class="string">'s position.
|
||||
local string = DisplayString('</span>Example', <span class="number">0</span>, <span class="number">0</span>, Color(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>), <span class="keyword">false</span>)
|
||||
<span class="keyword">local</span> displayPos = GetDisplayPosition(Lara:GetPosition())
|
||||
<span class="global">string</span>:SetPosition(PercentToScreen(displayPos.x, displayPos.y))</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PercentToScreen"></a>
|
||||
|
@ -333,6 +355,62 @@ To be used with <a href="../2 classes/Strings.DisplayString.html#DisplayString:G
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PickMoveableByDisplayPosition"></a>
|
||||
<strong>PickMoveableByDisplayPosition(Display)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Pick a moveable by the given display position.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">Display</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
space position in percent.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Objects.Moveable.html#">Moveable</a></span>
|
||||
Picked moveable (nil if no moveable was found under the cursor).
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PickStaticByDisplayPosition"></a>
|
||||
<strong>PickStaticByDisplayPosition(Display)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Pick a static mesh by the given display position.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">Display</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
space position in percent.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Objects.Static.html#">Static</a></span>
|
||||
Picked static mesh (nil if no static mesh was found under the cursor).
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PrintLog"></a>
|
||||
|
@ -355,7 +433,7 @@ To be used with <a href="../2 classes/Strings.DisplayString.html#DisplayString:G
|
|||
to be displayed within the Log
|
||||
</li>
|
||||
<li><span class="parameter">logLevel</span>
|
||||
<span class="types"><span class="type">Misc.LogLevel</span></span>
|
||||
<span class="types"><a class="type" href="../4 enums/Util.LogLevel.html#">LogLevel</a></span>
|
||||
log level to be displayed
|
||||
</li>
|
||||
<li><span class="parameter">allowSpam</span>
|
||||
|
@ -377,38 +455,6 @@ PrintLog(<span class="string">'test error log'</span>, LogLevel.ERROR)
|
|||
</span>PrintLog(<span class="string">'test spam log'</span>, LogLevel.INFO, <span class="keyword">true</span>) </pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
<h2 class="section-header "><a name="Fields"></a>Fields</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "worldPos"></a>
|
||||
<strong>worldPos</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the projected display space position of a 3D world position. Returns nil if the world position is behind the camera view.
|
||||
|
||||
|
||||
|
||||
<ul>
|
||||
<li><span class="parameter">worldPos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
3D world position.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">Example: Display a <span class="global">string</span> at the player<span class="string">'s position.
|
||||
local string = DisplayString('</span>Example', <span class="number">0</span>, <span class="number">0</span>, Color(<span class="number">255</span>, <span class="number">255</span>, <span class="number">255</span>), <span class="keyword">false</span>)
|
||||
<span class="keyword">local</span> displayPos = GetDisplayPosition(Lara:GetPosition())
|
||||
<span class="global">string</span>:SetPosition(PercentToScreen(displayPos.x, displayPos.y))</pre>
|
||||
</ul>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
@ -417,7 +463,6 @@ local string = DisplayString('</span>Example', <span class="number">0</span>, <s
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -105,10 +123,6 @@
|
|||
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetAspectRatio">GetAspectRatio()</a></td>
|
||||
<td class="summary">Get the display resolution's aspect ratio.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#FadeIn">FadeIn(speed)</a></td>
|
||||
<td class="summary">Do a full-screen fade-in from black.</td>
|
||||
|
@ -126,12 +140,48 @@
|
|||
<td class="summary">Set field of view.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFOV">GetFOV()</a></td>
|
||||
<td class="summary">Get field of view.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCameraType">GetCameraType()</a></td>
|
||||
<td class="summary">Shows the mode of the game camera.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PlayFlyBy">PlayFlyBy(flyby)</a></td>
|
||||
<td class="summary">Enable FlyBy with specific ID</td>
|
||||
<td class="name" ><a href="#GetCameraPosition">GetCameraPosition()</a></td>
|
||||
<td class="summary">Gets current camera position.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCameraTarget">GetCameraTarget()</a></td>
|
||||
<td class="summary">Gets current camera target.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCameraRoom">GetCameraRoom()</a></td>
|
||||
<td class="summary">Gets current room where camera is positioned.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetPostProcessMode">SetPostProcessMode(effect)</a></td>
|
||||
<td class="summary">Sets the post-process effect mode, like negative or monochrome.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetPostProcessStrength">SetPostProcessStrength(strength)</a></td>
|
||||
<td class="summary">Sets the post-process effect strength.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#SetPostProcessTint">SetPostProcessTint(tint)</a></td>
|
||||
<td class="summary">Sets the post-process tint.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#PlayFlyby">PlayFlyby(seqID)</a></td>
|
||||
<td class="summary">Play a flyby sequence.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFlybyPosition">GetFlybyPosition(seqID, progress[, loop])</a></td>
|
||||
<td class="summary">Get a flyby sequence's position at a specified progress point in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFlybyRotation">GetFlybyRotation(seqID, progress[, loop])</a></td>
|
||||
<td class="summary">Get a flyby sequence's rotation at a specified progress point in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#ResetObjCamera">ResetObjCamera()</a></td>
|
||||
|
@ -141,6 +191,10 @@
|
|||
<td class="name" ><a href="#FlashScreen">FlashScreen(color, speed)</a></td>
|
||||
<td class="summary">Flash screen.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetAspectRatio">GetAspectRatio()</a></td>
|
||||
<td class="summary">Get the display resolution's aspect ratio.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
@ -150,27 +204,6 @@
|
|||
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "GetAspectRatio"></a>
|
||||
<strong>GetAspectRatio()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the display resolution's aspect ratio.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Display resolution's aspect ratio.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "FadeIn"></a>
|
||||
<strong>FadeIn(speed)</strong>
|
||||
|
@ -184,7 +217,7 @@
|
|||
<ul>
|
||||
<li><span class="parameter">speed</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second.
|
||||
(default 1.0). Speed in units per second. A value of 1 will make the fade take one second.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -206,7 +239,7 @@
|
|||
<ul>
|
||||
<li><span class="parameter">speed</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). Speed in "amount" per second. A value of 1 will make the fade take one second.
|
||||
(default 1.0). Speed in units per second. A value of 1 will make the fade take one second.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -262,6 +295,27 @@
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFOV"></a>
|
||||
<strong>GetFOV()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get field of view.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
current FOV angle in degrees
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCameraType"></a>
|
||||
|
@ -284,8 +338,8 @@
|
|||
|
||||
<h3>Usage:</h3>
|
||||
<ul>
|
||||
<pre class="example">LevelFuncs.OnControlPhase = <span class="keyword">function</span>()
|
||||
<span class="keyword">if</span> (View.GetCameraType() == CameraType.Combat) <span class="keyword">then</span>
|
||||
<pre class="example">LevelFuncs.OnLoop = <span class="keyword">function</span>()
|
||||
<span class="keyword">if</span> (View.GetCameraType() == CameraType.COMBAT) <span class="keyword">then</span>
|
||||
<span class="comment">--Do your Actions here.
|
||||
</span> <span class="keyword">end</span>
|
||||
<span class="keyword">end</span></pre>
|
||||
|
@ -293,19 +347,82 @@
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PlayFlyBy"></a>
|
||||
<strong>PlayFlyBy(flyby)</strong>
|
||||
<a name = "GetCameraPosition"></a>
|
||||
<strong>GetCameraPosition()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Enable FlyBy with specific ID
|
||||
Gets current camera position.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
current camera position
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCameraTarget"></a>
|
||||
<strong>GetCameraTarget()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Gets current camera target.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
current camera target
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCameraRoom"></a>
|
||||
<strong>GetCameraRoom()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Gets current room where camera is positioned.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Objects.Room.html#">Room</a></span>
|
||||
current room of the camera
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetPostProcessMode"></a>
|
||||
<strong>SetPostProcessMode(effect)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Sets the post-process effect mode, like negative or monochrome.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">flyby</span>
|
||||
<span class="types"><span class="type">short</span></span>
|
||||
(ID of flyby)
|
||||
<li><span class="parameter">effect</span>
|
||||
<span class="types"><a class="type" href="../4 enums/View.PostProcessMode.html#">PostProcessMode</a></span>
|
||||
type to set.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -313,6 +430,146 @@
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetPostProcessStrength"></a>
|
||||
<strong>SetPostProcessStrength(strength)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Sets the post-process effect strength.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">strength</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). How strong the effect is.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "SetPostProcessTint"></a>
|
||||
<strong>SetPostProcessTint(tint)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Sets the post-process tint.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">tint</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
value to use.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "PlayFlyby"></a>
|
||||
<strong>PlayFlyby(seqID)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Play a flyby sequence.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">seqID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Flyby sequence ID.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFlybyPosition"></a>
|
||||
<strong>GetFlybyPosition(seqID, progress[, loop])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get a flyby sequence's position at a specified progress point in percent.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">seqID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Flyby sequence ID.
|
||||
</li>
|
||||
<li><span class="parameter">progress</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Progress point in percent. Clamped to [0, 100].
|
||||
</li>
|
||||
<li><span class="parameter">loop</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Smooth the position near start and end points, as if the sequence is looped.
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Position at the given progress point.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFlybyRotation"></a>
|
||||
<strong>GetFlybyRotation(seqID, progress[, loop])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get a flyby sequence's rotation at a specified progress point in percent.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">seqID</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Flyby sequence ID.
|
||||
</li>
|
||||
<li><span class="parameter">progress</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Progress point in percent. Clamped to [0, 100].
|
||||
</li>
|
||||
<li><span class="parameter">loop</span>
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Smooth the position near start and end points, as if the sequence is looped.
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Rotation.html#">Rotation</a></span>
|
||||
Rotation at the given progress point.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "ResetObjCamera"></a>
|
||||
|
@ -346,7 +603,7 @@
|
|||
</li>
|
||||
<li><span class="parameter">speed</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
(default 1.0). Speed in "amount" per second. Value of 1 will make flash take one second. Clamped to [0.005, 1.0].
|
||||
(default 1.0). Speed in units per second. Value of 1 will make flash take one second. Clamped to [0.005, 1.0].
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -354,6 +611,27 @@
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetAspectRatio"></a>
|
||||
<strong>GetAspectRatio()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the display resolution's aspect ratio.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Display resolution's aspect ratio.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
@ -362,7 +640,6 @@
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
766
Documentation/doc/2 classes/Collision.Probe.html
Normal file
766
Documentation/doc/2 classes/Collision.Probe.html
Normal file
|
@ -0,0 +1,766 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>1 Modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <here>Collision.Probe</here></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
|
||||
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
|
||||
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
|
||||
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
|
||||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>Class <code>Collision.Probe</code></h1>
|
||||
<p>Represents a collision probe in the game world.</p>
|
||||
<p> Provides collision information from a reference world position.</p>
|
||||
|
||||
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#Probe">Probe(pos[, roomNumber])</a></td>
|
||||
<td class="summary">Create a Probe at a specified world position in a room.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Probe">Probe(pos, roomNumber, dir, dist)</a></td>
|
||||
<td class="summary">Create a Probe that casts from an origin world position in a room in a given direction for a specified distance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Probe">Probe(pos, roomNumber, rot, dist)</a></td>
|
||||
<td class="summary">Create a Probe that casts from an origin world position in a room in the direction of a given rotation for a specified distance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Probe">Probe(pos, roomNumber, rot, relOffset)</a></td>
|
||||
<td class="summary">Create a Probe that casts from an origin world position, where a given relative offset is rotated according to a given rotation.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetPosition">GetPosition()</a></td>
|
||||
<td class="summary">Get the world position of this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetRoom">GetRoom()</a></td>
|
||||
<td class="summary">Get the Room object of this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetRoomName">GetRoomName()</a></td>
|
||||
<td class="summary">Get the room name of this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFloorHeight">GetFloorHeight()</a></td>
|
||||
<td class="summary">Get the floor height at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCeilingHeight">GetCeilingHeight()</a></td>
|
||||
<td class="summary">Get the ceiling height at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetWaterSurfaceHeight">GetWaterSurfaceHeight()</a></td>
|
||||
<td class="summary">Get the water surface height at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFloorNormal">GetFloorNormal()</a></td>
|
||||
<td class="summary">Get the normal of the floor at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCeilingNormal">GetCeilingNormal()</a></td>
|
||||
<td class="summary">Get the normal of the ceiling at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetFloorMaterialType">GetFloorMaterialType()</a></td>
|
||||
<td class="summary">Get the material type of the floor at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#GetCeilingMaterialType">GetCeilingMaterialType()</a></td>
|
||||
<td class="summary">Get the material type of the ceiling at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsSteepFloor">IsSteepFloor()</a></td>
|
||||
<td class="summary">Check if the floor at this Probe is steep.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsSteepCeiling">IsSteepCeiling()</a></td>
|
||||
<td class="summary">Check if the ceiling at this Probe is steep.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsWall">IsWall()</a></td>
|
||||
<td class="summary">Check if the Probe is inside a wall.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsInsideSolidGeometry">IsInsideSolidGeometry()</a></td>
|
||||
<td class="summary">Check if this Probe is inside solid geometry (below a floor, above a ceiling, inside a bridge, or inside a wall).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsClimbableWall">IsClimbableWall(headingAngle)</a></td>
|
||||
<td class="summary">Check if there is a climbable wall in the given heading angle at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsMonkeySwing">IsMonkeySwing()</a></td>
|
||||
<td class="summary">Check if there is a monkey swing sector at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#IsDeath">IsDeath()</a></td>
|
||||
<td class="summary">Check if there is a death sector at this Probe.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#Preview">Preview()</a></td>
|
||||
<td class="summary">Preview this Probe in the Collision Stats debug page.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "Probe"></a>
|
||||
<strong>Probe(pos[, roomNumber])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Create a Probe at a specified world position in a room.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
World position.
|
||||
</li>
|
||||
<li><span class="parameter">roomNumber</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Room number. Must be used if probing a position in an overlapping room.
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Collision.Probe.html#Probe">Probe</a></span>
|
||||
A new Probe.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Probe"></a>
|
||||
<strong>Probe(pos, roomNumber, dir, dist)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Create a Probe that casts from an origin world position in a room in a given direction for a specified distance.
|
||||
Required to correctly traverse between rooms.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Origin world position to cast from.
|
||||
</li>
|
||||
<li><span class="parameter">roomNumber</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Origin room number.
|
||||
</li>
|
||||
<li><span class="parameter">dir</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Direction in which to cast.
|
||||
</li>
|
||||
<li><span class="parameter">dist</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Distance to cast.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Collision.Probe.html#Probe">Probe</a></span>
|
||||
A new Probe.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Probe"></a>
|
||||
<strong>Probe(pos, roomNumber, rot, dist)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Create a Probe that casts from an origin world position in a room in the direction of a given rotation for a specified distance.
|
||||
Required to correctly traverse between rooms.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Origin world position to cast from.
|
||||
</li>
|
||||
<li><span class="parameter">roomNumber</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Origin room number.
|
||||
</li>
|
||||
<li><span class="parameter">rot</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Rotation.html#">Rotation</a></span>
|
||||
Rotation defining the direction in which to cast.
|
||||
</li>
|
||||
<li><span class="parameter">dist</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Distance to cast.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Collision.Probe.html#Probe">Probe</a></span>
|
||||
A new Probe.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Probe"></a>
|
||||
<strong>Probe(pos, roomNumber, rot, relOffset)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Create a Probe that casts from an origin world position, where a given relative offset is rotated according to a given rotation.
|
||||
Required to correctly traverse between rooms.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Origin world position to cast from.
|
||||
</li>
|
||||
<li><span class="parameter">roomNumber</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Origin room number.
|
||||
</li>
|
||||
<li><span class="parameter">rot</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Rotation.html#">Rotation</a></span>
|
||||
Rotation according to which the input relative offset is rotated.
|
||||
</li>
|
||||
<li><span class="parameter">relOffset</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Relative offset to cast.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Collision.Probe.html#Probe">Probe</a></span>
|
||||
A new Probe.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetPosition"></a>
|
||||
<strong>GetPosition()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the world position of this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
World position.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetRoom"></a>
|
||||
<strong>GetRoom()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the Room object of this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">Room</span></span>
|
||||
Room object.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetRoomName"></a>
|
||||
<strong>GetRoomName()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the room name of this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="https://www.lua.org/manual/5.4/manual.html#6.4">string</a></span>
|
||||
Room name.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFloorHeight"></a>
|
||||
<strong>GetFloorHeight()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the floor height at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Floor height. <strong>nil: no floor exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCeilingHeight"></a>
|
||||
<strong>GetCeilingHeight()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the ceiling height at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Ceiling height. <strong>nil: no ceiling exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetWaterSurfaceHeight"></a>
|
||||
<strong>GetWaterSurfaceHeight()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the water surface height at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Water surface height. <strong>nil: no water surface exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFloorNormal"></a>
|
||||
<strong>GetFloorNormal()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the normal of the floor at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Floor normal. <strong>nil: no floor exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCeilingNormal"></a>
|
||||
<strong>GetCeilingNormal()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the normal of the ceiling at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec3.html#">Vec3</a></span>
|
||||
Ceiling normal. <strong>nil: no ceiling exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetFloorMaterialType"></a>
|
||||
<strong>GetFloorMaterialType()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the material type of the floor at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/Collision.MaterialType.html#">MaterialType</a></span>
|
||||
Floor material type. <strong>nil: no floor exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "GetCeilingMaterialType"></a>
|
||||
<strong>GetCeilingMaterialType()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the material type of the ceiling at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/Collision.MaterialType.html#">MaterialType</a></span>
|
||||
Ceiling material type. <strong>nil: no ceiling exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsSteepFloor"></a>
|
||||
<strong>IsSteepFloor()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if the floor at this Probe is steep.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Steep floor status. <strong>true: is a steep floor, false: isn't a steep floor, nil: no floor exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsSteepCeiling"></a>
|
||||
<strong>IsSteepCeiling()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if the ceiling at this Probe is steep.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Steep ceiling status. <strong>true: is a steep ceiling, false: isn't a steep ceiling, nil: no ceiling exists</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsWall"></a>
|
||||
<strong>IsWall()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if the Probe is inside a wall. Can be used to determine if a wall and ceiling exist.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Wall status. <strong>true: is a wall, false: isn't a wall</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsInsideSolidGeometry"></a>
|
||||
<strong>IsInsideSolidGeometry()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if this Probe is inside solid geometry (below a floor, above a ceiling, inside a bridge, or inside a wall).
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Inside geometry status. <strong>true: is inside, false: is outside</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsClimbableWall"></a>
|
||||
<strong>IsClimbableWall(headingAngle)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if there is a climbable wall in the given heading angle at this Probe.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">headingAngle</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Heading angle at which to check for a climbable wall.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Climbable wall status. <strong>true: is climbable wall, false: isn't climbable</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsMonkeySwing"></a>
|
||||
<strong>IsMonkeySwing()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if there is a monkey swing sector at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Monkey swing sector status. <strong>true: is a monkey swing, false: isn't a monkey swing</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "IsDeath"></a>
|
||||
<strong>IsDeath()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Check if there is a death sector at this Probe.
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">bool</span></span>
|
||||
Death sector status. <strong>true: is a death sector, false: isn't a death sector</strong>
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "Preview"></a>
|
||||
<strong>Preview()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Preview this Probe in the Collision Stats debug page.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
|
@ -1,529 +0,0 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.1.0 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo"></div>
|
||||
<div id="product_name"><big><b></b></big></div>
|
||||
<div id="product_description"></div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
|
||||
<div id="main">
|
||||
|
||||
|
||||
<!-- Menu -->
|
||||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>1 Modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../1 modules/Effects.html">Effects</a></li>
|
||||
<li> <a href="../1 modules/Flow.html">Flow</a></li>
|
||||
<li> <a href="../1 modules/Input.html">Input</a></li>
|
||||
<li> <a href="../1 modules/Inventory.html">Inventory</a></li>
|
||||
<li> <a href="../1 modules/Logic.html">Logic</a></li>
|
||||
<li> <a href="../1 modules/Objects.html">Objects</a></li>
|
||||
<li> <a href="../1 modules/Sound.html">Sound</a></li>
|
||||
<li> <a href="../1 modules/Strings.html">Strings</a></li>
|
||||
<li> <a href="../1 modules/Util.html">Util</a></li>
|
||||
<li> <a href="../1 modules/View.html">View</a></li>
|
||||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <here>DisplaySprite</here></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Flow.Level.html">Flow.Level</a></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Moveable.html">Objects.Moveable</a></li>
|
||||
<li> <a href="../2 classes/Objects.Room.html">Objects.Room</a></li>
|
||||
<li> <a href="../2 classes/Objects.Sink.html">Objects.Sink</a></li>
|
||||
<li> <a href="../2 classes/Objects.SoundSource.html">Objects.SoundSource</a></li>
|
||||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/DisplaySprite.AlignMode.html">DisplaySprite.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/DisplaySprite.ScaleMode.html">DisplaySprite.ScaleMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>Class <code>DisplaySprite</code></h1>
|
||||
<p>Represents a screen-space display sprite.</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
<table class="function_list">
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite">DisplaySprite(ID, int, pos, rot, scale[, color])</a></td>
|
||||
<td class="summary">Create a DisplaySprite object.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetObjectID">DisplaySprite:GetObjectID()</a></td>
|
||||
<td class="summary">Get the object ID of the sprite sequence object used by the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetSpriteID">DisplaySprite:GetSpriteID()</a></td>
|
||||
<td class="summary">Get the sprite ID in the sprite sequence object used by the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetPosition">DisplaySprite:GetPosition()</a></td>
|
||||
<td class="summary">Get the display position of the display sprite in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetRotation">DisplaySprite:GetRotation()</a></td>
|
||||
<td class="summary">Get the rotation of the display sprite in degrees.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetScale">DisplaySprite:GetScale()</a></td>
|
||||
<td class="summary">Get the horizontal and vertical scale of the display sprite in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:GetColor">DisplaySprite:GetColor()</a></td>
|
||||
<td class="summary">Get the color of the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetObjectID">DisplaySprite:SetObjectID(New)</a></td>
|
||||
<td class="summary">Set the sprite sequence object ID used by the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetSpriteID">DisplaySprite:SetSpriteID(New)</a></td>
|
||||
<td class="summary">Set the sprite ID in the sprite sequence object used by the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetPosition">DisplaySprite:SetPosition(New)</a></td>
|
||||
<td class="summary">Set the display position of the display sprite in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetRotation">DisplaySprite:SetRotation(New)</a></td>
|
||||
<td class="summary">Set the rotation of the display sprite in degrees.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetScale">DisplaySprite:SetScale(New)</a></td>
|
||||
<td class="summary">Set the horizontal and vertical scale of the display sprite in percent.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:SetColor">DisplaySprite:SetColor(New)</a></td>
|
||||
<td class="summary">Set the color of the display sprite.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#DisplaySprite:Draw">DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode])</a></td>
|
||||
<td class="summary">Draw the display sprite in display space for the current frame.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<h2 class="section-header "><a name="Functions"></a>Functions</h2>
|
||||
|
||||
<dl class="function">
|
||||
<dt>
|
||||
<a name = "DisplaySprite"></a>
|
||||
<strong>DisplaySprite(ID, int, pos, rot, scale[, color])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Create a DisplaySprite object.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">ID</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
of the sprite sequence object.
|
||||
</li>
|
||||
<li><span class="parameter">int</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
spriteID ID of the sprite in the sequence.
|
||||
</li>
|
||||
<li><span class="parameter">pos</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Display position in percent.
|
||||
</li>
|
||||
<li><span class="parameter">rot</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotation in degrees.
|
||||
</li>
|
||||
<li><span class="parameter">scale</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Horizontal and vertical scale in percent. Scaling is interpreted by the DisplaySpriteEnum.ScaleMode passed to the Draw() function call.
|
||||
</li>
|
||||
<li><span class="parameter">color</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
Color. <strong>Default: Color(255, 255, 255, 255)</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/DisplaySprite.html#">DisplaySprite</a></span>
|
||||
A new DisplaySprite object.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetObjectID"></a>
|
||||
<strong>DisplaySprite:GetObjectID()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the object ID of the sprite sequence object used by the display sprite. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
Sprite sequence object ID.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetSpriteID"></a>
|
||||
<strong>DisplaySprite:GetSpriteID()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the sprite ID in the sprite sequence object used by the display sprite. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
Sprite ID in the sprite sequence object.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetPosition"></a>
|
||||
<strong>DisplaySprite:GetPosition()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the display position of the display sprite in percent. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Display position in percent.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetRotation"></a>
|
||||
<strong>DisplaySprite:GetRotation()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the rotation of the display sprite in degrees. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
Rotation in degrees.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetScale"></a>
|
||||
<strong>DisplaySprite:GetScale()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the horizontal and vertical scale of the display sprite in percent. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
Horizontal and vertical scale in percent.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:GetColor"></a>
|
||||
<strong>DisplaySprite:GetColor()</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Get the color of the display sprite. ()
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Returns:</h3>
|
||||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Color.html#">Color</a></span>
|
||||
Color.
|
||||
</ol>
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetObjectID"></a>
|
||||
<strong>DisplaySprite:SetObjectID(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the sprite sequence object ID used by the display sprite. (Objects.ObjID)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
sprite sequence object ID.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetSpriteID"></a>
|
||||
<strong>DisplaySprite:SetSpriteID(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the sprite ID in the sprite sequence object used by the display sprite. (int)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><span class="type">int</span></span>
|
||||
sprite ID in the sprite sequence object.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetPosition"></a>
|
||||
<strong>DisplaySprite:SetPosition(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the display position of the display sprite in percent. (Vec2)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><a class="type" href="../3 primitive classes/Vec2.html#">Vec2</a></span>
|
||||
display position in percent.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetRotation"></a>
|
||||
<strong>DisplaySprite:SetRotation(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the rotation of the display sprite in degrees. (float)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
rotation in degrees.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetScale"></a>
|
||||
<strong>DisplaySprite:SetScale(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the horizontal and vertical scale of the display sprite in percent. (Vec2)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
horizontal and vertical scale in percent.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:SetColor"></a>
|
||||
<strong>DisplaySprite:SetColor(New)</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Set the color of the display sprite. (Color)
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">New</span>
|
||||
<span class="types"><span class="type">float</span></span>
|
||||
color.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "DisplaySprite:Draw"></a>
|
||||
<strong>DisplaySprite:Draw([priority][, alignMode][, scaleMode][, blendMode])</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Draw the display sprite in display space for the current frame.
|
||||
|
||||
|
||||
|
||||
<h3>Parameters:</h3>
|
||||
<ul>
|
||||
<li><span class="parameter">priority</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Objects.ObjID.html#">ObjID</a></span>
|
||||
Draw priority. Can be thought of as a layer, with higher values having precedence. <strong>Default: 0</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">alignMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/DisplaySprite.AlignMode.html#">AlignMode</a></span>
|
||||
Align mode interpreting an offset from the sprite's position. <strong>Default: DisplaySprite.AlignMode.CENTER</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">scaleMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/DisplaySprite.ScaleMode.html#">ScaleMode</a></span>
|
||||
Scale mode interpreting the display sprite's horizontal and vertical scale. <strong>Default: DisplaySprite.ScaleMode.FIT</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
<li><span class="parameter">blendMode</span>
|
||||
<span class="types"><a class="type" href="../4 enums/Effects.BlendID.html#">BlendID</a></span>
|
||||
Blend mode. <strong>Default: Effects.BlendID.ALPHABLEND</strong>
|
||||
(<em>optional</em>)
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-09 18:25:22 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
</html>
|
|
@ -3,7 +3,7 @@
|
|||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<head>
|
||||
<title>TombEngine 1.2 Lua API</title>
|
||||
<title>TombEngine 1.8.1 Lua API</title>
|
||||
<link rel="stylesheet" href="../ldoc.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div id="navigation">
|
||||
<br/>
|
||||
<h1>TombEngine</h1>
|
||||
<h1> TombEngine</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="../index.html">Index</a></li>
|
||||
|
@ -45,14 +45,10 @@
|
|||
</ul>
|
||||
<h2>2 Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
<li> <a href="../2 classes/Flow.Animations.html">Flow.Animations</a></li>
|
||||
<li> <a href="../2 classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../2 classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../2 classes/Collision.Probe.html">Collision.Probe</a></li>
|
||||
<li> <here>Flow.Level</here></li>
|
||||
<li> <a href="../2 classes/Flow.Mirror.html">Flow.Mirror</a></li>
|
||||
<li> <a href="../2 classes/Flow.Settings.html">Flow.Settings</a></li>
|
||||
<li> <a href="../2 classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../2 classes/Flow.Statistics.html">Flow.Statistics</a></li>
|
||||
<li> <a href="../2 classes/Objects.AIObject.html">Objects.AIObject</a></li>
|
||||
<li> <a href="../2 classes/Objects.Camera.html">Objects.Camera</a></li>
|
||||
<li> <a href="../2 classes/Objects.LaraObject.html">Objects.LaraObject</a></li>
|
||||
|
@ -63,33 +59,55 @@
|
|||
<li> <a href="../2 classes/Objects.Static.html">Objects.Static</a></li>
|
||||
<li> <a href="../2 classes/Objects.Volume.html">Objects.Volume</a></li>
|
||||
<li> <a href="../2 classes/Strings.DisplayString.html">Strings.DisplayString</a></li>
|
||||
<li> <a href="../2 classes/View.DisplaySprite.html">View.DisplaySprite</a></li>
|
||||
</ul>
|
||||
<h2>3 Primitive Classes</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../3 primitive classes/Flow.Fog.html">Flow.Fog</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Horizon.html">Flow.Horizon</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.InventoryItem.html">Flow.InventoryItem</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.LensFlare.html">Flow.LensFlare</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.SkyLayer.html">Flow.SkyLayer</a></li>
|
||||
<li> <a href="../3 primitive classes/Flow.Starfield.html">Flow.Starfield</a></li>
|
||||
<li> <a href="../3 primitive classes/Color.html">Color</a></li>
|
||||
<li> <a href="../3 primitive classes/Rotation.html">Rotation</a></li>
|
||||
<li> <a href="../3 primitive classes/Time.html">Time</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec2.html">Vec2</a></li>
|
||||
<li> <a href="../3 primitive classes/Vec3.html">Vec3</a></li>
|
||||
</ul>
|
||||
<h2>4 Enums</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../4 enums/Collision.MaterialType.html">Collision.MaterialType</a></li>
|
||||
<li> <a href="../4 enums/Effects.BlendID.html">Effects.BlendID</a></li>
|
||||
<li> <a href="../4 enums/Effects.EffectID.html">Effects.EffectID</a></li>
|
||||
<li> <a href="../4 enums/Effects.StreamerFeatherMode.html">Effects.StreamerFeatherMode</a></li>
|
||||
<li> <a href="../4 enums/Effects.ParticleAnimationType.html">Effects.ParticleAnimationType</a></li>
|
||||
<li> <a href="../4 enums/Flow.ErrorMode.html">Flow.ErrorMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.FreezeMode.html">Flow.FreezeMode</a></li>
|
||||
<li> <a href="../4 enums/Flow.GameStatus.html">Flow.GameStatus</a></li>
|
||||
<li> <a href="../4 enums/Input.ActionID.html">Input.ActionID</a></li>
|
||||
<li> <a href="../4 enums/Objects.AmmoType.html">Objects.AmmoType</a></li>
|
||||
<li> <a href="../4 enums/Objects.HandStatus.html">Objects.HandStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.WeaponType.html">Objects.WeaponType</a></li>
|
||||
<li> <a href="../4 enums/Objects.MoveableStatus.html">Objects.MoveableStatus</a></li>
|
||||
<li> <a href="../4 enums/Objects.ObjID.html">Objects.ObjID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomFlagID.html">Objects.RoomFlagID</a></li>
|
||||
<li> <a href="../4 enums/Objects.RoomReverb.html">Objects.RoomReverb</a></li>
|
||||
<li> <a href="../4 enums/Sound.SoundTrackType.html">Sound.SoundTrackType</a></li>
|
||||
<li> <a href="../4 enums/Strings.DisplayStringOption.html">Strings.DisplayStringOption</a></li>
|
||||
<li> <a href="../4 enums/Util.LogLevel.html">Util.LogLevel</a></li>
|
||||
<li> <a href="../4 enums/View.AlignMode.html">View.AlignMode</a></li>
|
||||
<li> <a href="../4 enums/View.CameraType.html">View.CameraType</a></li>
|
||||
<li> <a href="../4 enums/View.PostProcessMode.html">View.PostProcessMode</a></li>
|
||||
<li> <a href="../4 enums/View.ScaleMode.html">View.ScaleMode</a></li>
|
||||
</ul>
|
||||
<h2>5 Lua utility modules</h2>
|
||||
<ul class="nowrap">
|
||||
<li> <a href="../5 lua utility modules/CustomBar.html">CustomBar</a></li>
|
||||
<li> <a href="../5 lua utility modules/Diary.html">Diary</a></li>
|
||||
<li> <a href="../5 lua utility modules/EventSequence.html">EventSequence</a></li>
|
||||
<li> <a href="../5 lua utility modules/Timer.html">Timer</a></li>
|
||||
<li> <a href="../5 lua utility modules/Type.html">Type</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
@ -125,19 +143,31 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#layer1">layer1</a></td>
|
||||
<td class="summary">(<a href="../2 classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Primary sky layer</td>
|
||||
<td class="summary">(<a href="../3 primitive classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Primary sky cloud layer.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#layer2">layer2</a></td>
|
||||
<td class="summary">(<a href="../2 classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Secondary sky layer</td>
|
||||
<td class="summary">(<a href="../3 primitive classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Secondary sky cloud layer.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#horizon1">horizon1</a></td>
|
||||
<td class="summary">(<a href="../3 primitive classes/Flow.Horizon.html#">Flow.Horizon</a>) First horizon layer.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#horizon2">horizon2</a></td>
|
||||
<td class="summary">(<a href="../3 primitive classes/Flow.Horizon.html#">Flow.Horizon</a>) Second horizon layer.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#starfield">starfield</a></td>
|
||||
<td class="summary">(<a href="../3 primitive classes/Flow.Starfield.html#">Flow.Starfield</a>) Starfield in the sky.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#lensFlare">lensFlare</a></td>
|
||||
<td class="summary">(<a href="../3 primitive classes/Flow.LensFlare.html#">Flow.LensFlare</a>) Global lens flare.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#fog">fog</a></td>
|
||||
<td class="summary">(<a href="../2 classes/Flow.Fog.html#">Flow.Fog</a>) omni fog RGB color and distance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#horizon">horizon</a></td>
|
||||
<td class="summary">(bool) Draw sky layer?</td>
|
||||
<td class="summary">(<a href="../3 primitive classes/Flow.Fog.html#">Flow.Fog</a>) Global distance fog, with specified RGB color and distance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#storm">storm</a></td>
|
||||
|
@ -160,24 +190,20 @@
|
|||
<td class="summary">(bool) Enable occasional screen shake effect.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#mirror">mirror</a></td>
|
||||
<td class="summary">(<a href="../2 classes/Flow.Mirror.html#">Flow.Mirror</a>) Location and size of the level's mirror, if present.</td>
|
||||
<td class="name" ><a href="#farView">farView</a></td>
|
||||
<td class="summary">(int) The maximum draw distance for level.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#unlimitedAir">unlimitedAir</a></td>
|
||||
<td class="summary">(bool) Enable unlimited oxygen supply when in water.</td>
|
||||
<td class="name" ><a href="#resetHub">resetHub</a></td>
|
||||
<td class="summary">(bool) Reset hub data.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#objects">objects</a></td>
|
||||
<td class="summary">(table of <a href="../2 classes/Flow.InventoryItem.html#">Flow.InventoryItem</a>s) table of inventory object overrides</td>
|
||||
<td class="summary">(table of <a href="../3 primitive classes/Flow.InventoryItem.html#">Flow.InventoryItem</a>s) A table of inventory object layout overrides.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#secrets">secrets</a></td>
|
||||
<td class="summary">(short) Set Secrets for Level</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#farView">farView</a></td>
|
||||
<td class="summary">(int) The maximum draw distance for level.</td>
|
||||
<td class="summary">(short) Set total secret count for current level.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2><a href="#Functions">Functions</a></h2>
|
||||
|
@ -280,7 +306,7 @@
|
|||
<strong>layer1</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(<a href="../2 classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Primary sky layer
|
||||
(<a href="../3 primitive classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Primary sky cloud layer.
|
||||
|
||||
|
||||
|
||||
|
@ -295,7 +321,67 @@
|
|||
<strong>layer2</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(<a href="../2 classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Secondary sky layer
|
||||
(<a href="../3 primitive classes/Flow.SkyLayer.html#">Flow.SkyLayer</a>) Secondary sky cloud layer.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "horizon1"></a>
|
||||
<strong>horizon1</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(<a href="../3 primitive classes/Flow.Horizon.html#">Flow.Horizon</a>) First horizon layer.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "horizon2"></a>
|
||||
<strong>horizon2</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(<a href="../3 primitive classes/Flow.Horizon.html#">Flow.Horizon</a>) Second horizon layer.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "starfield"></a>
|
||||
<strong>starfield</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(<a href="../3 primitive classes/Flow.Starfield.html#">Flow.Starfield</a>) Starfield in the sky.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "lensFlare"></a>
|
||||
<strong>lensFlare</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(<a href="../3 primitive classes/Flow.LensFlare.html#">Flow.LensFlare</a>) Global lens flare.
|
||||
|
||||
|
||||
|
||||
|
@ -310,24 +396,8 @@
|
|||
<strong>fog</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(<a href="../2 classes/Flow.Fog.html#">Flow.Fog</a>) omni fog RGB color and distance.
|
||||
As seen in TR4's Desert Railroad.
|
||||
If not provided, distance fog will be black.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "horizon"></a>
|
||||
<strong>horizon</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(bool) Draw sky layer? (default: false)
|
||||
(<a href="../3 primitive classes/Flow.Fog.html#">Flow.Fog</a>) Global distance fog, with specified RGB color and distance.
|
||||
If not provided, distance fog will not be visible.
|
||||
|
||||
|
||||
|
||||
|
@ -403,7 +473,7 @@ Invisible
|
|||
|
||||
<p>e.g. <code>myLevel.laraType = LaraType.Divesuit</code></p>
|
||||
|
||||
<p> <strong>(not yet fully implemented)</strong>
|
||||
<p> <strong>Not yet fully implemented.</strong> Only types <code>Normal</code> and <code>Young</code> are guaranteed to work.
|
||||
|
||||
|
||||
|
||||
|
@ -430,13 +500,12 @@ Invisible
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "mirror"></a>
|
||||
<strong>mirror</strong>
|
||||
<a name = "farView"></a>
|
||||
<strong>farView</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(<a href="../2 classes/Flow.Mirror.html#">Flow.Mirror</a>) Location and size of the level's mirror, if present. </p>
|
||||
|
||||
<p> <strong>(not yet implemented)</strong>
|
||||
(int) The maximum draw distance for level.
|
||||
Given in sectors (blocks). Must be at least 4.
|
||||
|
||||
|
||||
|
||||
|
@ -447,13 +516,12 @@ Invisible
|
|||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "unlimitedAir"></a>
|
||||
<strong>unlimitedAir</strong>
|
||||
<a name = "resetHub"></a>
|
||||
<strong>resetHub</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(bool) Enable unlimited oxygen supply when in water. </p>
|
||||
|
||||
<p> <strong>(not yet implemented)</strong>
|
||||
(bool) Reset hub data.
|
||||
Resets the state for all previous levels, including items, flipmaps and statistics.
|
||||
|
||||
|
||||
|
||||
|
@ -468,7 +536,7 @@ Invisible
|
|||
<strong>objects</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(table of <a href="../2 classes/Flow.InventoryItem.html#">Flow.InventoryItem</a>s) table of inventory object overrides
|
||||
(table of <a href="../3 primitive classes/Flow.InventoryItem.html#">Flow.InventoryItem</a>s) A table of inventory object layout overrides.
|
||||
|
||||
|
||||
|
||||
|
@ -483,26 +551,7 @@ Invisible
|
|||
<strong>secrets</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(short) Set Secrets for Level
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "farView"></a>
|
||||
<strong>farView</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
(int) The maximum draw distance for level.
|
||||
Given in sectors (blocks).
|
||||
Must be at least 4.</p>
|
||||
|
||||
<p>This is equivalent to TRNG's LevelFarView variable.
|
||||
(short) Set total secret count for current level.
|
||||
|
||||
|
||||
|
||||
|
@ -530,7 +579,7 @@ Must be at least 4.</p>
|
|||
<ol>
|
||||
|
||||
<span class="types"><a class="type" href="../2 classes/Flow.Level.html#Level">Level</a></span>
|
||||
a Level object
|
||||
a Level object.
|
||||
</ol>
|
||||
|
||||
|
||||
|
@ -544,7 +593,6 @@ Must be at least 4.</p>
|
|||
</div> <!-- id="main" -->
|
||||
<div id="about">
|
||||
<i>generated by <a href="https://github.com/hispidence/TEN-LDoc">TEN-LDoc</a> (a fork of <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a>)</i>
|
||||
<i style="float:right;">Last updated 2023-11-11 12:12:55 </i>
|
||||
</div> <!-- id="about" -->
|
||||
</div> <!-- id="container" -->
|
||||
</body>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue