mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-04-07 02:22:22 +00:00
Compare commits
800 commits
Author | SHA1 | Date | |
---|---|---|---|
|
297eab9e5e | ||
|
da63ad7e07 | ||
|
ddcae703e3 | ||
|
6cf25c0ec6 | ||
|
663723f00a | ||
|
1e8ce1733b | ||
|
47475bb455 | ||
|
71138cbe1a | ||
|
85c79d2f1c | ||
|
13bcb5c5b1 | ||
|
465941f357 | ||
|
237722c0b2 | ||
|
f971f89e1e | ||
|
94c2936bfa | ||
|
2d4a054440 | ||
|
031f827da5 | ||
|
451873ae52 | ||
|
9c81ff263a | ||
|
620bd76e76 | ||
|
2d99ce609d | ||
|
0904a1ceb7 | ||
|
c74fabffda | ||
|
092067482f | ||
|
dac058107a | ||
|
9a21c638fa | ||
|
97a74eb677 | ||
|
f84c8ea629 | ||
|
e920766b10 | ||
|
e006aa8238 | ||
|
73d3d7eec1 | ||
|
3cf2c52fce | ||
|
1580c23556 | ||
|
b14a02e735 | ||
|
679e3771de | ||
|
d9127bf28a | ||
|
fa7dce495b | ||
|
02b20dbd09 | ||
|
6ad5f18ef1 | ||
|
047ecd426f | ||
|
fb3af2831b | ||
|
5a0d645ede | ||
|
27c0886ffb | ||
|
163c4b99a4 | ||
|
eb2d478770 | ||
|
3f5305af58 | ||
|
8538658e83 | ||
|
8b2149e315 | ||
|
c285eb385d | ||
|
2dde6d903e | ||
|
4bf63bb379 | ||
|
95d232ca72 | ||
|
90f190f5e1 | ||
|
566c17a964 | ||
|
a5636899f2 | ||
|
17c0812ae4 | ||
|
3a7848d67c | ||
|
def1a26b12 | ||
|
eab20602b1 | ||
|
69fa4f8dbd | ||
|
966991601c | ||
|
01f3447e5b | ||
|
9821b6a075 | ||
|
6938567c6c | ||
|
0b94d7583c | ||
|
4c48bae203 | ||
|
ad1cfcfeaa | ||
|
eca4e2c309 | ||
|
167207e98c | ||
|
e06ad170de | ||
|
3714a507c2 | ||
|
63c679ee81 | ||
|
be64736dd4 | ||
|
f6bc9705d2 | ||
|
5d8c18dcab | ||
|
1a18ff5294 | ||
|
3f4659b5d5 | ||
|
6024e377ba | ||
|
ee3c1e43c9 | ||
|
a9ac6987a6 | ||
|
fab640da4c | ||
|
fd0cc40b9c | ||
|
6d4539814e | ||
|
896d4c53a3 | ||
|
d8e9b1b35d | ||
|
66d908f39b | ||
|
45236a644f | ||
|
fa21d85820 | ||
|
e922403aa8 | ||
|
ff37abb0c7 | ||
|
90b5a6538a | ||
|
866fc3e247 | ||
|
41a76ab91d | ||
|
b640049912 | ||
|
9335bc2f4f | ||
|
49f4fedecf | ||
|
6149f6a1d0 | ||
|
9d98805dfb | ||
|
e7d1e701c4 | ||
|
5c64437189 | ||
|
5968e3faa0 | ||
|
566e761546 | ||
|
f09c6a5d63 | ||
|
794396df79 | ||
|
380fb3d44f | ||
|
dedb3a49bd | ||
|
9535805c02 | ||
|
44b0d7f658 | ||
|
1826971301 | ||
|
db9c37d18b | ||
|
7e0e041527 | ||
|
e2ba77a546 | ||
|
76278e8b97 | ||
|
6e68526680 | ||
|
0ecfe18f49 | ||
|
e8fbae4b3e | ||
|
f38e6b48db | ||
|
a1f13499f9 | ||
|
539dc4a3dc | ||
|
2e037832d3 | ||
|
987f765c20 | ||
|
b345b4e21b | ||
|
9c31c53cc5 | ||
|
fe95b28a35 | ||
|
317e0499f7 | ||
|
b5d8b44503 | ||
|
35a8c3c9af | ||
|
2e3b3569bf | ||
|
878195bdec | ||
|
aabefd1bfe | ||
|
4de08db0e7 | ||
|
67a3c9b031 | ||
|
d4deaa35ca | ||
|
51a7b8e6a7 | ||
|
65362d93aa | ||
|
a7b45ea14d | ||
|
9d89a059aa | ||
|
dc510baf4f | ||
|
b5ac2745d6 | ||
|
0a00807e57 | ||
|
ff526954b6 | ||
|
42b3640bc5 | ||
|
6caaedd269 | ||
|
8b250457ab | ||
|
644a807731 | ||
|
724bca0eec | ||
|
2c421c3b71 | ||
|
5b9e0a62ab | ||
|
d6bf906647 | ||
|
1ad849d939 | ||
|
044f8b396d | ||
|
893f204b30 | ||
|
486b215706 | ||
|
d8c09c200a | ||
|
e452c176b4 | ||
|
833f315a16 | ||
|
a77797d6a6 | ||
|
783b7b6594 | ||
|
114a1d3b14 | ||
|
6fee3ec363 | ||
|
2b10d588e4 | ||
|
e3577912c8 | ||
|
ac8c7d730a | ||
|
5d5b9a379a | ||
|
8f7242d314 | ||
|
103e549615 | ||
|
ab841b94f7 | ||
|
624e6201e8 | ||
|
8b25e95553 | ||
|
ddb0b7dd9d | ||
|
806850e408 | ||
|
09109bb176 | ||
|
3df51c5979 | ||
|
2a00b386ba | ||
|
15b31e7dc5 | ||
|
fa7d44e0c7 | ||
|
859e9fe3da | ||
|
de22dec56b | ||
|
641136fee3 | ||
|
b08195e2da | ||
|
31cd263e33 | ||
|
a502a5453f | ||
|
2208136403 | ||
|
faacfa018a | ||
|
1a8bb31d2a | ||
|
459356a48d | ||
|
beaba494b5 | ||
|
3e576bd1f3 | ||
|
25caf4b8e8 | ||
|
d5690074e1 | ||
|
823b053e60 | ||
|
4c1c1bc051 | ||
|
bca1a7143d | ||
|
161bbec262 | ||
|
3e43056f5f | ||
|
c33755b007 | ||
|
e7d81937ae | ||
|
53e9ed0d96 | ||
|
05b349c72f | ||
|
463426ad47 | ||
|
655c2482c9 | ||
|
0c85bac71b | ||
|
aed893b6b8 | ||
|
337d7ddbf4 | ||
|
ff80bf1aa2 | ||
|
1497191e3c | ||
|
3945f26d92 | ||
|
4fa694fe82 | ||
|
edb38ce70e | ||
|
536138273f | ||
|
68c2baa7c1 | ||
|
05e20bcdda | ||
|
f1ab19ba0b | ||
|
b7b60e7468 | ||
|
bbeb2517c0 | ||
|
2917d39ef1 | ||
|
cc20d7e4e9 | ||
|
5dc7e62b19 | ||
|
6424ebaf98 | ||
|
7024ebfe7f | ||
|
a371c4ee27 | ||
|
ba0ac97372 | ||
|
8dafdfc5e2 | ||
|
0d8b0d419c | ||
|
0d1a740bc7 | ||
|
0db41f4279 | ||
|
15d1277158 | ||
|
2b7b2ea455 | ||
|
e8133893a0 | ||
|
f1650c42d9 | ||
|
606d7ac110 | ||
|
90533079c8 | ||
|
fe14d1b056 | ||
|
8250124c51 | ||
|
8c0a280a3e | ||
|
9e5d02dab0 | ||
|
827826b9f9 | ||
|
763e85b3ae | ||
|
b21e967581 | ||
|
06e2cb2b1b | ||
|
581c48e337 | ||
|
56edc3db9c | ||
|
52adc2113f | ||
|
69b89dc6ac | ||
|
f7e074d88f | ||
|
7ffda37513 | ||
|
24763aad65 | ||
|
360389638b | ||
|
2ddb5ad50d | ||
|
3070c03fc0 | ||
|
1bf9ebabcc | ||
|
9cc4fe1ed2 | ||
|
fee2986907 | ||
|
669a055594 | ||
|
24f9313952 | ||
|
a140b749ff | ||
|
0f506e768b | ||
|
792c1afd95 | ||
|
4ff68e07e8 | ||
|
5a160b0e88 | ||
|
a934e0fe4b | ||
|
b6b4a87cbf | ||
|
acdc559d1f | ||
|
5319caaaea | ||
|
0b6637cc67 | ||
|
1e30c2b81d | ||
|
26ab792f9c | ||
|
7e88247ed5 | ||
|
f24bdced10 | ||
|
31e13e6e64 | ||
|
103bca7284 | ||
|
d43a270142 | ||
|
4c5a0ff662 | ||
|
4d4851e179 | ||
|
58cd326d85 | ||
|
50f905b821 | ||
|
072bff44e6 | ||
|
af53c0cb83 | ||
|
6a44b72db3 | ||
|
b20e2a9d34 | ||
|
11ecc6cb0b | ||
|
2024b3bd71 | ||
|
78b615fce5 | ||
|
892746056e | ||
|
d14c757076 | ||
|
320784b20d | ||
|
7b7d012255 | ||
|
a20127b063 | ||
|
9b92cb0897 | ||
|
43e9885a08 | ||
|
ac7e1a557d | ||
|
ea801acdb8 | ||
|
4583cb8280 | ||
|
cb97b7f672 | ||
|
1d347eaf66 | ||
|
c3cc6f184e | ||
|
bf127088ca | ||
|
9749ec350a | ||
|
e5fc8fdded | ||
|
2d0f0a3607 | ||
|
25e86c04eb | ||
|
f19d32b29b | ||
|
dc48af195d | ||
|
33c0c83d59 | ||
|
02c6076bfd | ||
|
3209aaa996 | ||
|
8a26ed7664 | ||
|
50d1bfe783 | ||
|
63c0917d24 | ||
|
87a43777ab | ||
|
23904bad52 | ||
|
52cda620b2 | ||
|
1c33bcfceb | ||
|
db182819ae | ||
|
ab2a4f1318 | ||
|
80adfebd23 | ||
|
aee68d80ea | ||
|
457a1b9690 | ||
|
943cb2ca64 | ||
|
556a84a46f | ||
|
b52cf4d47e | ||
|
e2bfaf8109 | ||
|
c68a5c29e1 | ||
|
11f79bb1a6 | ||
|
54f331a64a | ||
|
74b58c5bb8 | ||
|
292c8150b4 | ||
|
b1fd85b711 | ||
|
d9572e3e30 | ||
|
0860b7a68b | ||
|
759fca6921 | ||
|
3b6e7d0ce1 | ||
|
ae9d3d42cf | ||
|
29f2cc302b | ||
|
7011a2ef2c | ||
|
ec03b55d1a | ||
|
9131644412 | ||
|
49bb172a09 | ||
|
73c4015046 | ||
|
1900262df4 | ||
|
f43106017f | ||
|
eb4486a7ac | ||
|
1d5229ee8c | ||
|
eee7d0aac1 | ||
|
de12a24bc9 | ||
|
6ea88c9a3f | ||
|
de14d514f3 | ||
|
99de3cf4f5 | ||
|
7f2b2061e6 | ||
|
1d745fd1f9 | ||
|
7a29f65683 | ||
|
1ca36b7e49 | ||
|
a120209567 | ||
|
b1016c7f48 | ||
|
91c894d4da | ||
|
065a870e7a | ||
|
b87eb89853 | ||
|
c90807dec6 | ||
|
0695ad1659 | ||
|
b5b9559d6f | ||
|
3988aae73e | ||
|
0f6c44d671 | ||
|
8d5e719026 | ||
|
0cfc275f82 | ||
|
c210340081 | ||
|
c675ba8086 | ||
|
9f54610fbd | ||
|
02a1d9f4a1 | ||
|
97217b55d1 | ||
|
dd33f4e498 | ||
|
f8949e17c8 | ||
|
d6f020fd6a | ||
|
05ac126d6f | ||
|
cfea900afd | ||
|
8a9c4edce8 | ||
|
4da820ef61 | ||
|
2c975bb344 | ||
|
1b14c49815 | ||
|
8699053887 | ||
|
003bb9dfb4 | ||
|
0f479f3e88 | ||
|
c63bebd7d8 | ||
|
b147602d78 | ||
|
f53502c9ca | ||
|
50d165e173 | ||
|
eca82511c6 | ||
|
6d8d7ee923 | ||
|
c8413a9a04 | ||
|
8e8b3608fb | ||
|
65a2b83abd | ||
|
19331ee385 | ||
|
ef51b30387 | ||
|
f008cc257d | ||
|
ccb46d7e3b | ||
|
42a9784804 | ||
|
82afdb1e2c | ||
|
62ac7e9966 | ||
|
6da151eba1 | ||
|
856949a5f9 | ||
|
d8b3faa871 | ||
|
92c0d6157c | ||
|
fa14550d38 | ||
|
604c9d25bf | ||
|
4d0bf1607a | ||
|
63fdab8422 | ||
|
87d9371a5c | ||
|
3d5fedcf39 | ||
|
637651f4e1 | ||
|
e9bde1e4e4 | ||
|
34c18ab860 | ||
|
dab528acda | ||
|
e8955f17ea | ||
|
033cf7c7d3 | ||
|
5138a25420 | ||
|
12a864abf5 | ||
|
151606e255 | ||
|
14ef6a1c42 | ||
|
9dabb68d7f | ||
|
f73f2f1ba9 | ||
|
e263506b3f | ||
|
3e362e872c | ||
|
f83cc1b91d | ||
|
6bd6379c87 | ||
|
a02e44100e | ||
|
99e3ae9773 | ||
|
300fb9905b | ||
|
ffdd6df828 | ||
|
2cf5046d38 | ||
|
cc69370575 | ||
|
263fcfbc2f | ||
|
2ebf571129 | ||
|
90824c2093 | ||
|
8f359f3849 | ||
|
08891068c8 | ||
|
b6f08e7fb1 | ||
|
fa14ca93d2 | ||
|
dfbd093348 | ||
|
e928cabfb2 | ||
|
03b56bd41f | ||
|
7249c2ec18 | ||
|
353455e1ad | ||
|
b10de1b240 | ||
|
3c931ecbf1 | ||
|
c6056d441b | ||
|
7c25af973c | ||
|
81df8fa139 | ||
|
8a294683bb | ||
|
c4e92df106 | ||
|
2b3663e18d | ||
|
0d1f20fea3 | ||
|
95b7056427 | ||
|
c8a1b6563e | ||
|
48211572e5 | ||
|
d61c5d3b16 | ||
|
15b0555546 | ||
|
cf2352893f | ||
|
894e1976e3 | ||
|
904c45060b | ||
|
87fcf8d8e8 | ||
|
d2405a9ad8 | ||
|
d664b9f607 | ||
|
13ef558fff | ||
|
3968dc84fd | ||
|
330111d5fc | ||
|
fac4e411bf | ||
|
73eca0848c | ||
|
37a4265e06 | ||
|
69efb404bf | ||
|
99422d0cf4 | ||
|
8d2a6ca419 | ||
|
f25fff1e3d | ||
|
e18849fa42 | ||
|
5a47dd5e62 | ||
|
b6da3613ac | ||
|
241637a980 | ||
|
9726d80e05 | ||
|
94e477e466 | ||
|
5bb245a33a | ||
|
6a235dc25f | ||
|
207d391fcd | ||
|
f44c127fbd | ||
|
c52ad67a7c | ||
|
35f9aef729 | ||
|
46fa12cb26 | ||
|
8ddd126378 | ||
|
ce07e8fe28 | ||
|
7af8c70bf9 | ||
|
3f151321f6 | ||
|
c2cf41baf9 | ||
|
9908209f58 | ||
|
6800d15872 | ||
|
b2c8f3ebc5 | ||
|
64661f54ea | ||
|
69252071ba | ||
|
9032e78349 | ||
|
702a7664de | ||
|
2ff9adecb2 | ||
|
4600f1d7ff | ||
|
79219ae201 | ||
|
87b9fca732 | ||
|
98cb23ca63 | ||
|
a50635bcd7 | ||
|
76c37d9cc0 | ||
|
c8b4eac618 | ||
|
f8af7adcd7 | ||
|
dd289ed0e1 | ||
|
3ed200d08a | ||
|
ff6d55aafc | ||
|
fa468e0673 | ||
|
f140c39063 | ||
|
51ef277e21 | ||
|
a7c1f6f021 | ||
|
ceb79f1897 | ||
|
3b4a5667ea | ||
|
bbe4927a20 | ||
|
ee428b9081 | ||
|
40bcec5044 | ||
|
a80aa89e09 | ||
|
660a22d647 | ||
|
494c30a239 | ||
|
71e7db63aa | ||
|
8aaa268423 | ||
|
a75746d610 | ||
|
045bd4dbda | ||
|
28cd3a3f8f | ||
|
f21216ecad | ||
|
abfe98ce8a | ||
|
988b4b4960 | ||
|
4937fa51c0 | ||
|
1b3d515777 | ||
|
b26f53125d | ||
|
844e84fc16 | ||
|
581d0dfc15 | ||
|
147a6df629 | ||
|
b3c1b46925 | ||
|
f4f805f4c9 | ||
|
fc57fa4064 | ||
|
0e077c6e42 | ||
|
a1f1ec6d65 | ||
|
ba94df47f0 | ||
|
454234ef5f | ||
|
e50b7a2719 | ||
|
5958687795 | ||
|
69cecb74df | ||
|
95138b1e5b | ||
|
efa571043b | ||
|
4990434db7 | ||
|
bbab8969d1 | ||
|
834e8d0d7d | ||
|
a7fdbbe35b | ||
|
6a4e175f86 | ||
|
a93a28230d | ||
|
701363347f | ||
|
a1a9438319 | ||
|
491a706610 | ||
|
963e93e8e8 | ||
|
c13285092c | ||
|
ccd8f1acc6 | ||
|
a31eacf1bd | ||
|
e15046d66c | ||
|
78a3c5d571 | ||
|
e6809acf63 | ||
|
c64005966f | ||
|
2a79339735 | ||
|
dd512ec36a | ||
|
e7dab06f82 | ||
|
94139513db | ||
|
7108c61bec | ||
|
772dbfae26 | ||
|
b30c3ff8d4 | ||
|
d5d38e94ef | ||
|
89893f9a24 | ||
|
079ed491a9 | ||
|
a622d5163b | ||
|
7df42c95d2 | ||
|
91c7209146 | ||
|
ade8626df7 | ||
|
3ece4a964f | ||
|
586a70ea1d | ||
|
bee14a6df7 | ||
|
0a5353532b | ||
|
1201f06a55 | ||
|
f82097b6b8 | ||
|
73d9aa29c4 | ||
|
85ee52128c | ||
|
a68f0fcb35 | ||
|
5e54db46c4 | ||
|
b654594adb | ||
|
b10a2d1fd5 | ||
|
216330a7e2 | ||
|
90e2e5a4ad | ||
|
7669a99c7f | ||
|
69c4dce477 | ||
|
db69d14995 | ||
|
6f749d61b1 | ||
|
24fc2e5146 | ||
|
10b75fd8b9 | ||
|
3e24b5a74b | ||
|
960cb7034a | ||
|
3fa74da2b5 | ||
|
5f2b7e3d57 | ||
|
d0ee56f25f | ||
|
1538e69f93 | ||
|
fa5ad1212e | ||
|
86adb94d7d | ||
|
9ed62eee58 | ||
|
c3da9b237b | ||
|
b0460de935 | ||
|
8dd125c8f3 | ||
|
5e38c800f6 | ||
|
920dbaf1e0 | ||
|
d8b931fbcf | ||
|
2c975fe48f | ||
|
d6ca5673dc | ||
|
a9ab865add | ||
|
c8c25ef6f7 | ||
|
6c0c7aac0f | ||
|
9e2b17e715 | ||
|
ab79dbd22a | ||
|
23af0c0209 | ||
|
bee93f28b3 | ||
|
d684e99d86 | ||
|
557ad9da1a | ||
|
ec0aaa72a3 | ||
|
ce7a9d5a3d | ||
|
0291726c09 | ||
|
c7679722fb | ||
|
8db9724c5d | ||
|
cddf70f46b | ||
|
4a3794ea2b | ||
|
44d5481828 | ||
|
629fe05083 | ||
|
d411d60685 | ||
|
2bfea938b3 | ||
|
96ec279663 | ||
|
697ad9d3a7 | ||
|
764b9abaf4 | ||
|
84ad8ec37a | ||
|
17318af62f | ||
|
b39a748984 | ||
|
b534aca263 | ||
|
adc9e7bf22 | ||
|
560b45dd16 | ||
|
bbffdde2dc | ||
|
063c50fce4 | ||
|
5429b6f189 | ||
|
d39fb653aa | ||
|
e08f00bfcd | ||
|
edf59e4f73 | ||
|
52e7394418 | ||
|
a982d4e524 | ||
|
458cfcb48c | ||
|
5bdf0aff81 | ||
|
1929b129ee | ||
|
ce23e95d0b | ||
|
684112474b | ||
|
fe296de42f | ||
|
996d998ebb | ||
|
125d039e3d | ||
|
6db2e69f9a | ||
|
82fd7fcf68 | ||
|
a2c3388e49 | ||
|
944ec75687 | ||
|
3b8b76328c | ||
|
e6c1d66c35 | ||
|
51eb94f251 | ||
|
7e76b42f11 | ||
|
23cb7f4e09 | ||
|
a04c0d2aa2 | ||
|
ad8d76b212 | ||
|
de8974d03e | ||
|
f8db5a7c6d | ||
|
e6bb7697f9 | ||
|
a3e1342bdb | ||
|
ba85107a85 | ||
|
0e392f91d2 | ||
|
685925398c | ||
|
ec6de55d3c | ||
|
00d1d237bc | ||
|
6de7c45618 | ||
|
004832f6ec | ||
|
82e92811e4 | ||
|
9a6316221c | ||
|
2d654ddcff | ||
|
5d2b57394e | ||
|
17ae2dbe4d | ||
|
a27b7ee6a5 | ||
|
5694c77d16 | ||
|
0c58509417 | ||
|
202fc67f93 | ||
|
b9cf1f1262 | ||
|
a8fddbb7d3 | ||
|
04406b191f | ||
|
290d065a79 | ||
|
3fab06941a | ||
|
f78d653b1e | ||
|
179da9241c | ||
|
7115176c0e | ||
|
fee7794789 | ||
|
5012616cb0 | ||
|
90a016c6e0 | ||
|
6617684a8d | ||
|
8afd373e4f | ||
|
3e75750ad6 | ||
|
17fd017d6f | ||
|
daa1487aef | ||
|
f023b7097f | ||
|
2c59385633 | ||
|
b30368f026 | ||
|
8ce331b563 | ||
|
dc91918c1f | ||
|
633d2ba8a4 | ||
|
bc4a66e9f7 | ||
|
7a36a8bdd8 | ||
|
d631c517b6 | ||
|
b773702a47 | ||
|
0eab97283f | ||
|
ad92a5f27d | ||
|
144672fada | ||
|
4f02d4b556 | ||
|
923e0187bd | ||
|
52d39b7260 | ||
|
81b27ea84a | ||
|
0d52f1ae7c | ||
|
0f98f0fd4a | ||
|
af80d9956b | ||
|
e29b4d35b3 | ||
|
77cf1f8685 | ||
|
11179a2a71 | ||
|
b2348e1de0 | ||
|
06cccbb646 | ||
|
c1a9ce3404 | ||
|
c569e87bd0 | ||
|
100eaf9137 | ||
|
ee42d2a570 | ||
|
564cac859a | ||
|
69b55ccc03 | ||
|
7ea67748fa | ||
|
9af3c502da | ||
|
ec7bf4767a | ||
|
1ce8d2ea6e | ||
|
f884bd2217 | ||
|
79a7aa70b9 | ||
|
c8daf483f3 | ||
|
166b79720c | ||
|
121e080697 | ||
|
280dfdd3f8 | ||
|
d85e86141c | ||
|
655822ec1a | ||
|
062180e9a8 | ||
|
9f2b9e1b46 | ||
|
ff63e5bd73 | ||
|
1dce501b70 | ||
|
ed585f8c04 | ||
|
b0a0769534 | ||
|
0b6269f607 | ||
|
ba781c53ef | ||
|
a76702cb36 | ||
|
5aba29006b | ||
|
9167de1631 | ||
|
afdc0c9dc8 | ||
|
db6ca6c5f8 | ||
|
fd5506b376 | ||
|
1b71caa1fe | ||
|
6d6a2efada | ||
|
2923b718e1 | ||
|
e3f4ae3038 | ||
|
d45956f55e | ||
|
f892b32335 | ||
|
df8b486c98 | ||
|
e02ebfe486 | ||
|
f281de7a3c | ||
|
d70b571769 | ||
|
ef6e2bd583 | ||
|
a0fa90ddd5 | ||
|
802005f571 | ||
|
4d0a5af475 | ||
|
c5225b2fa1 | ||
|
8c8ae71d65 | ||
|
3e7cd8a98b | ||
|
9823b294df | ||
|
c07c78c666 | ||
|
79a5ed0482 | ||
|
721b581376 | ||
|
3d115760a0 | ||
|
a8e2a47da8 | ||
|
f6792c3a05 | ||
|
bc4749d95a | ||
|
90eed12e97 | ||
|
2058ce69a4 | ||
|
4c4aa5534c | ||
|
dc6a7436ee | ||
|
35120caf80 | ||
|
9ace0811ce | ||
|
d4b0e1f588 | ||
|
18cf3641b1 | ||
|
01ead27dd9 | ||
|
b8e536d409 | ||
|
7e0d6bdd87 | ||
|
9f8bee4bf1 | ||
|
b3e9ef3ad9 |
162 changed files with 19990 additions and 19956 deletions
15
.gitignore
vendored
15
.gitignore
vendored
|
@ -1,14 +1,15 @@
|
|||
*.o
|
||||
*.d
|
||||
*.orig
|
||||
*.rej
|
||||
*.patch
|
||||
*.diff
|
||||
*.exe
|
||||
|
||||
distro/archlinux/*
|
||||
distro/archbsd/*
|
||||
!distro/archlinux/git/PKGBUILD
|
||||
!distro/archlinux/release/PKGBUILD
|
||||
!distro/archbsd/release/PKGBUILD
|
||||
!distro/archbsd/git/PKGBUILD
|
||||
!distro/archlinux/this/Makefile
|
||||
gmqcc
|
||||
gmqpak
|
||||
qcvm
|
||||
testsuite
|
||||
|
||||
build/
|
||||
.idea/
|
||||
|
|
14
.travis.yml
14
.travis.yml
|
@ -1,14 +0,0 @@
|
|||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
# Change this to your needs
|
||||
script: make && make check
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "irc.freenode.org#kf-engine"
|
||||
template:
|
||||
- "[%{commit} : %{author}] %{message}"
|
||||
- "%{build_url}"
|
||||
skip_join: true
|
5
AUTHORS
5
AUTHORS
|
@ -1,9 +1,10 @@
|
|||
Authors:
|
||||
Dale Weiler - Charismatic Visionary / Programmer
|
||||
Wolfgang Bumiller - Main Programmer
|
||||
Dale `graphitemaster` Weiler - Charismatic Visionary / Programmer
|
||||
Wolfgang `Blub\w` Bumiller - Main Programmer
|
||||
|
||||
Thanks to:
|
||||
Forest `LordHavoc` Hale - Technical support and assistance
|
||||
Rudolf `divVerent` Polzer - Technical support and assistance
|
||||
Matthias `matthiaskrgr` Krüger - Miscellaneous assistance
|
||||
Samual `Samual` Lenks - Preprocessor assistance
|
||||
Igor `ignatenkobrain` Gnatenko - Fedora packages
|
||||
|
|
90
CHANGES
90
CHANGES
|
@ -1,90 +0,0 @@
|
|||
Release v0.2.9
|
||||
* Preprocessor:
|
||||
- __VA_ARGS__ support
|
||||
_ __VA_ARGS__ indexing
|
||||
- Predefined macros like __DATE__, __TIME__, ...
|
||||
(check the manpage for a full list)
|
||||
- Signed numbers as single token in the
|
||||
- Fixes some issues with #if operations on macros.
|
||||
- Speed improvements
|
||||
* Language:
|
||||
- Untyped `nil` keyword.
|
||||
- Removed the `noreturn` keyword.
|
||||
- Added generic attribute syntax and reintroduced `noreturn`
|
||||
as [[noreturn]].
|
||||
- Added [[deprecated]] and [[deprecated("message")]].
|
||||
- Support for `static` variables in functions.
|
||||
- Support for labeled loops.
|
||||
- UTF-8 Support
|
||||
- enum support: without enum-types
|
||||
(ie no `typedef enum { } foo;`)
|
||||
- Accessing vector components via the dot operator on all
|
||||
expressions. Eg: (3 * v).y
|
||||
- Type restricted variadict parameters:
|
||||
ie: void print(string...);
|
||||
- Accessing varargs from QC via: ...(index, type)
|
||||
- New operators: ** (exponentiation), % (modulo), etc
|
||||
- Enumeration attributes: flag, reverse
|
||||
* Compilation:
|
||||
- Various optimizations and progs-size reductions.
|
||||
- A new spell-checking algorithm tries to hint you at existing
|
||||
variables on error.
|
||||
- Some problems with VM related vector-instructions issues
|
||||
have been solved in both DP and our own executor. A new
|
||||
compatbility option (enabled by default) has been added for
|
||||
now: -flegacy-vector-maths
|
||||
- Compiler intrinsics: __builtin_floor, __builtin_mod,
|
||||
__builtin_exp, __builtin_isnan
|
||||
- Improved memory tracing
|
||||
- Speed improvements
|
||||
* QCVM:
|
||||
- Improved commandline argument handling.
|
||||
- More builtins: sqrt(), normalize(), floor()
|
||||
* Commandline:
|
||||
- Nicer memory dumps
|
||||
- Support for making individual warnings an error
|
||||
- via -Werror-<warning>
|
||||
- added --add-info
|
||||
* Testsuite:
|
||||
- Support for QCFLAGS to run tests with several additional
|
||||
flags.
|
||||
- Added support for preprocessor tests
|
||||
- Added preprocessor tests
|
||||
- Added defs.qh (auto included) for qcvm definitions
|
||||
* Syntax Highlighting:
|
||||
- Added various syntax highlighting description files for
|
||||
various text editors / integrated development envirorments,
|
||||
including support for: geany, kate, kwrite, kdevelop, QtCreator,
|
||||
gtksourceview, gedit, sany, nano, jedit
|
||||
* Build:
|
||||
- Build scripts for building debian, archlinux and archbsd
|
||||
packages for x86, and x86_64.
|
||||
- Makefile targets for gource visualization, and render of
|
||||
gource visualization.
|
||||
|
||||
|
||||
2012-12-27 Hotfix v0.2.2
|
||||
* Liferanges
|
||||
* Crashes
|
||||
|
||||
2012-12-23 Hotfix v0.2.1
|
||||
* General bugfixes
|
||||
|
||||
2012-12-23 Release 0.2
|
||||
* Preprocessor:
|
||||
- Added xonotic compatible preprocessor.
|
||||
* Language
|
||||
- Basic xonotic compatibility
|
||||
- Array support
|
||||
- Added fteqcc's string escape sequences.
|
||||
- Support for `noref`.
|
||||
- Support for `goto` with labels like in fteqcc.
|
||||
- `break` and `continue`.
|
||||
- Short circuit logic.
|
||||
- Support for translatable strings via _("str") like in
|
||||
fteqcc.
|
||||
* Compilation
|
||||
- Warnings about uninitialized values
|
||||
|
||||
2012-11-17 Release 0.1
|
||||
* Compiles id1 code
|
35
CMakeLists.txt
Normal file
35
CMakeLists.txt
Normal file
|
@ -0,0 +1,35 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
project(gmqcc)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||
|
||||
set(SOURCE_FILES
|
||||
algo.h
|
||||
ast.cpp ast.h
|
||||
code.cpp
|
||||
conout.cpp
|
||||
fold.cpp fold.h
|
||||
ftepp.cpp
|
||||
gmqcc.h
|
||||
intrin.cpp intrin.h
|
||||
ir.cpp
|
||||
ir.h
|
||||
lexer.cpp lexer.h
|
||||
opts.cpp
|
||||
parser.cpp parser.h
|
||||
stat.cpp
|
||||
utf8.cpp
|
||||
util.cpp)
|
||||
add_library(gmqcclib ${SOURCE_FILES})
|
||||
|
||||
add_executable(gmqcc main.cpp)
|
||||
target_link_libraries(gmqcc gmqcclib)
|
||||
|
||||
add_executable(testsuite test.cpp)
|
||||
target_link_libraries(testsuite gmqcclib)
|
||||
|
||||
add_executable(qcvm exec.cpp)
|
||||
target_link_libraries(qcvm gmqcclib)
|
44
INSTALL
44
INSTALL
|
@ -1,44 +0,0 @@
|
|||
Installing gmqcc
|
||||
|
||||
1. Prerequisites
|
||||
- A C-Compiler such as gcc or clang
|
||||
- GNU Make. This document will assume GNU-Make to be executed via
|
||||
`make'. On BSD systems you probably have to use `gmake' instead.
|
||||
|
||||
2. Compilation
|
||||
Run the GNU make program `make' or `gmake'.
|
||||
|
||||
make
|
||||
|
||||
If no error appears, the following binary files will have been
|
||||
created:
|
||||
- gmqcc
|
||||
- qcvm
|
||||
|
||||
3. Installation
|
||||
The `install' target will install the 2 binaries to /usr/local/bin
|
||||
by default.
|
||||
The Makefile honors the following variables:
|
||||
|
||||
- DESTDIR: The installation directory root.
|
||||
- PREFIX: The installation prefix, default: /usr/local
|
||||
- BINDIR: Directory for binary executables,
|
||||
deafult: $PREFIX/bin
|
||||
|
||||
To install to /usr/local run:
|
||||
|
||||
make install
|
||||
|
||||
To install to /usr run:
|
||||
|
||||
make PREFIX=/usr install
|
||||
|
||||
To install to a package-staging directory such as $pkgdir when
|
||||
writing a build script file:
|
||||
|
||||
make DESTDIR=$pkgdir install
|
||||
|
||||
|
||||
|
||||
ArchLinux PKGBUILDs (release and git build) can be found in the
|
||||
respective folders in ./distro/arch
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (C) 2012, 2013
|
||||
Copyright (C) 2012, 2013, 2014, 2015
|
||||
Dale Weiler
|
||||
Wolfgang Bumiller
|
||||
|
||||
|
|
420
Makefile
420
Makefile
|
@ -1,266 +1,204 @@
|
|||
DESTDIR :=
|
||||
OPTIONAL:=
|
||||
PREFIX := /usr/local
|
||||
BINDIR := $(PREFIX)/bin
|
||||
DATADIR := $(PREFIX)/share
|
||||
MANDIR := $(DATADIR)/man
|
||||
# Compilation options:
|
||||
# * LTO - Link time optimization [default=0]
|
||||
# * ASAN - Address sanitizer [default=0]
|
||||
# * UBSAN - Undefined behavior sanitizer [default=0]
|
||||
# * DEBUG - Debug build [default=0]
|
||||
# * UNUSED - Remove unused references [default=1]
|
||||
# * SRCDIR - Out of tree builds [default=./]
|
||||
LTO ?= 0
|
||||
ASAN ?= 0
|
||||
UBSAN ?= 0
|
||||
DEBUG ?= 0
|
||||
UNUSED ?= 1
|
||||
SRCDIR ?= ./
|
||||
|
||||
UNAME ?= $(shell uname)
|
||||
CYGWIN = $(findstring CYGWIN, $(UNAME))
|
||||
MINGW = $(findstring MINGW32, $(UNAME))
|
||||
|
||||
CC ?= clang
|
||||
# linker flags and optional additional libraries if required
|
||||
LDFLAGS +=
|
||||
LIBS += -lm
|
||||
|
||||
CFLAGS += -Wall -Wextra -Werror -fno-strict-aliasing $(OPTIONAL)
|
||||
ifneq ($(shell git describe --always 2>/dev/null),)
|
||||
CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\""
|
||||
endif
|
||||
#turn on tons of warnings if clang is present
|
||||
# but also turn off the STUPID ONES
|
||||
ifeq ($(CC), clang)
|
||||
CFLAGS += \
|
||||
-Weverything \
|
||||
-Wno-padded \
|
||||
-Wno-format-nonliteral \
|
||||
-Wno-disabled-macro-expansion \
|
||||
-Wno-conversion \
|
||||
-Wno-missing-prototypes \
|
||||
-Wno-float-equal \
|
||||
-Wno-unknown-warning-option
|
||||
# Determine if we're building for Windows or not so we can set the right file
|
||||
# extensions for the binaries and exclude the testsuite because it doesn't build
|
||||
# for that platform.
|
||||
ifeq ($(OS),Windows_NT)
|
||||
GMQCC := gmqcc.exe
|
||||
QCVM := qcvm.exe
|
||||
else
|
||||
#Tiny C Compiler doesn't know what -pedantic-errors is
|
||||
# and instead of ignoring .. just errors.
|
||||
ifneq ($(CC), tcc)
|
||||
CFLAGS += -pedantic-errors
|
||||
GMQCC := gmqcc
|
||||
QCVM := qcvm
|
||||
TESTSUITE := testsuite
|
||||
endif
|
||||
|
||||
# C++ compiler
|
||||
CXX ?= clang++
|
||||
|
||||
# Build artifact directories
|
||||
OBJDIR := .build/objs
|
||||
DEPDIR := .build/deps
|
||||
|
||||
# Collect all the source files for GMQCC.
|
||||
GSRCS := ast.cpp
|
||||
GSRCS += code.cpp
|
||||
GSRCS += conout.cpp
|
||||
GSRCS += fold.cpp
|
||||
GSRCS += ftepp.cpp
|
||||
GSRCS += intrin.cpp
|
||||
GSRCS += ir.cpp
|
||||
GSRCS += lexer.cpp
|
||||
GSRCS += main.cpp
|
||||
GSRCS += opts.cpp
|
||||
GSRCS += parser.cpp
|
||||
GSRCS += stat.cpp
|
||||
GSRCS += utf8.cpp
|
||||
GSRCS += util.cpp
|
||||
|
||||
# Collect all the source files for QCVM.
|
||||
QSRCS := exec.cpp
|
||||
QSRCS += stat.cpp
|
||||
QSRCS += util.cpp
|
||||
|
||||
# Collect all the source files for TESTSUITE.
|
||||
TSRCS := conout.cpp
|
||||
TSRCS += opts.cpp
|
||||
TSRCS += stat.cpp
|
||||
TSRCS += test.cpp
|
||||
TSRCS += util.cpp
|
||||
|
||||
#
|
||||
# Compilation flags
|
||||
#
|
||||
CXXFLAGS := -Wall
|
||||
CXXFLAGS += -Wextra
|
||||
CXXFLAGS += -Wno-parentheses
|
||||
CXXFLAGS += -Wno-class-memaccess
|
||||
CXXFLAGS += -Wno-implicit-fallthrough
|
||||
CXXFLAGS += -std=c++11
|
||||
|
||||
# Disable some unneeded features.
|
||||
CXXFLAGS += -fno-exceptions
|
||||
CXXFLAGS += -fno-rtti
|
||||
CXXFLAGS += -fno-asynchronous-unwind-tables
|
||||
|
||||
# Give each function and data it's own section so the linker can remove unused
|
||||
# references to each, producing smaller, tighter binaries.
|
||||
ifeq ($(UNUSED),1)
|
||||
CXXFLAGS += -ffunction-sections
|
||||
CXXFLAGS += -fdata-sections
|
||||
endif
|
||||
|
||||
# Enable link-time optimizations if requested.
|
||||
ifeq ($(LTO),1)
|
||||
CXXFLAGS += -flto
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
# Ensure there is a frame-pointer in debug builds.
|
||||
CXXFLAGS += -fno-omit-frame-pointer
|
||||
|
||||
# Disable all optimizations in debug builds.
|
||||
CXXFLAGS += -O0
|
||||
|
||||
# Enable debug symbols.
|
||||
CXXFLAGS += -g
|
||||
else
|
||||
# Disable all the stack protection features in release builds.
|
||||
CXXFLAGS += -fno-stack-protector
|
||||
CXXFLAGS += -fno-stack-check
|
||||
|
||||
# Disable frame pointer in release builds when AddressSanitizer isn't present.
|
||||
ifeq ($(ASAN),1)
|
||||
CXXFLAGS += -fno-omit-frame-pointer
|
||||
else
|
||||
CFLAGS += -Wno-pointer-sign -fno-common
|
||||
CXXFLAGS += -fomit-frame-pointer
|
||||
endif
|
||||
|
||||
# Highest optimization flag in release builds.
|
||||
CXXFLAGS += -O3
|
||||
endif
|
||||
|
||||
ifeq ($(track), no)
|
||||
CFLAGS += -DNOTRACK
|
||||
# Sanitizer selection
|
||||
ifeq ($(ASAN),1)
|
||||
CXXFLAGS += -fsanitize=address
|
||||
endif
|
||||
ifeq ($(UBSAN),1)
|
||||
CXXFLAGS += -fsanitize=undefined
|
||||
endif
|
||||
|
||||
OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o
|
||||
OBJ_P = util.o fs.o conout.o opts.o pak.o
|
||||
OBJ_T = test.o util.o conout.o fs.o
|
||||
OBJ_C = main.o lexer.o parser.o fs.o
|
||||
OBJ_X = exec-standalone.o util.o conout.o fs.o
|
||||
#
|
||||
# Dependency flags
|
||||
#
|
||||
DEPFLAGS := -MMD
|
||||
DEPFLAGS += -MP
|
||||
|
||||
ifneq ("$(CYGWIN)", "")
|
||||
#nullify the common variables that
|
||||
#most *nix systems have (for windows)
|
||||
PREFIX :=
|
||||
BINDIR :=
|
||||
DATADIR :=
|
||||
MANDIR :=
|
||||
QCVM = qcvm.exe
|
||||
GMQCC = gmqcc.exe
|
||||
TESTSUITE = testsuite.exe
|
||||
PAK = pak.exe
|
||||
#
|
||||
# Linker flags
|
||||
#
|
||||
LDFLAGS :=
|
||||
|
||||
# Remove unreferenced sections
|
||||
ifeq ($(UNUSED),1)
|
||||
LDFLAGS += -Wl,--gc-sections
|
||||
endif
|
||||
|
||||
# Enable link-time optimizations if request.
|
||||
ifeq ($(LTO),1)
|
||||
LDFLAGS += -flto
|
||||
endif
|
||||
|
||||
# Sanitizer selection
|
||||
ifeq ($(ASAN),1)
|
||||
LDFLAGS += -fsanitize=address
|
||||
endif
|
||||
ifeq ($(UBSAN),1)
|
||||
LDFLAGS += -fsanitize=undefined
|
||||
endif
|
||||
|
||||
# Strip the binaries when not a debug build
|
||||
ifneq (,$(findstring -g,$(CXXFLAGS)))
|
||||
STRIP := true
|
||||
else
|
||||
ifneq ("$(MINGW)", "")
|
||||
#nullify the common variables that
|
||||
#most *nix systems have (for windows)
|
||||
PREFIX :=
|
||||
BINDIR :=
|
||||
DATADIR :=
|
||||
MANDIR :=
|
||||
QCVM = qcvm.exe
|
||||
GMQCC = gmqcc.exe
|
||||
TESTSUITE = testsuite.exe
|
||||
PAK = pak.exe
|
||||
else
|
||||
QCVM = qcvm
|
||||
GMQCC = gmqcc
|
||||
TESTSUITE = testsuite
|
||||
PAK = pak
|
||||
endif
|
||||
STRIP := strip
|
||||
endif
|
||||
|
||||
#gource flags
|
||||
GOURCEFLAGS= \
|
||||
--date-format "%d %B, %Y" \
|
||||
--seconds-per-day 0.01 \
|
||||
--auto-skip-seconds 1 \
|
||||
--title "GMQCC" \
|
||||
--key \
|
||||
--camera-mode overview \
|
||||
--highlight-all-users \
|
||||
--file-idle-time 0 \
|
||||
--hide progress,mouse \
|
||||
--stop-at-end \
|
||||
--max-files 99999999999 \
|
||||
--max-file-lag 0.000001 \
|
||||
--bloom-multiplier 1.3 \
|
||||
--logo doc/html/gmqcc.png \
|
||||
-1280x720
|
||||
all: $(GMQCC) $(QCVM) $(TESTSUITE)
|
||||
|
||||
#ffmpeg flags for gource
|
||||
FFMPEGFLAGS= \
|
||||
-y \
|
||||
-r 60 \
|
||||
-f image2pipe \
|
||||
-vcodec ppm \
|
||||
-i - \
|
||||
-vcodec libx264 \
|
||||
-preset ultrafast \
|
||||
-crf 1 \
|
||||
-threads 0 \
|
||||
-bf 0
|
||||
# Build artifact directories.
|
||||
$(DEPDIR):
|
||||
@mkdir -p $(DEPDIR)
|
||||
$(OBJDIR):
|
||||
@mkdir -p $(OBJDIR)
|
||||
|
||||
#splint flags
|
||||
SPLINTFLAGS = \
|
||||
-redef \
|
||||
-noeffect \
|
||||
-nullderef \
|
||||
-usedef \
|
||||
-type \
|
||||
-mustfreeonly \
|
||||
-nullstate \
|
||||
-varuse \
|
||||
-mustfreefresh \
|
||||
-compdestroy \
|
||||
-compmempass \
|
||||
-nullpass \
|
||||
-onlytrans \
|
||||
-predboolint \
|
||||
-boolops \
|
||||
-exportlocal \
|
||||
-incondefs \
|
||||
-macroredef \
|
||||
-retvalint \
|
||||
-nullret \
|
||||
-predboolothers \
|
||||
-globstate \
|
||||
-dependenttrans \
|
||||
-branchstate \
|
||||
-compdef \
|
||||
-temptrans \
|
||||
-usereleased \
|
||||
-warnposix \
|
||||
-shiftimplementation \
|
||||
+charindex \
|
||||
-kepttrans \
|
||||
-unqualifiedtrans \
|
||||
+matchanyintegral \
|
||||
+voidabstract \
|
||||
-nullassign \
|
||||
-unrecog \
|
||||
-casebreak \
|
||||
-retvalbool \
|
||||
-retvalother \
|
||||
-mayaliasunique \
|
||||
-realcompare \
|
||||
-observertrans \
|
||||
-shiftnegative \
|
||||
-freshtrans \
|
||||
-abstract \
|
||||
-statictrans \
|
||||
-castfcnptr
|
||||
$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d | $(OBJDIR) $(DEPDIR)
|
||||
$(CXX) -MT $@ $(DEPFLAGS) -MF $(DEPDIR)/$*.Td $(CXXFLAGS) -c -o $@ $<
|
||||
@mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
|
||||
|
||||
#standard rules
|
||||
default: all
|
||||
%.o: %.c
|
||||
$(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS)
|
||||
$(GMQCC): $(filter %.o,$(GSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
exec-standalone.o: exec.c
|
||||
$(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS) -DQCVM_EXECUTOR=1
|
||||
$(QCVM): $(filter %.o,$(QSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
$(QCVM): $(OBJ_X)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
|
||||
$(TESTSUITE): $(filter %.o,$(TSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
$(GMQCC): $(OBJ_C) $(OBJ_D)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
|
||||
# Determine if the tests should be run.
|
||||
RUNTESTS := true
|
||||
ifdef TESTSUITE
|
||||
RUNTESTS := ./$(TESTSUITE)
|
||||
endif
|
||||
|
||||
$(TESTSUITE): $(OBJ_T)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
|
||||
|
||||
$(PAK): $(OBJ_P)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
all: $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK)
|
||||
|
||||
check: all
|
||||
@ ./$(TESTSUITE)
|
||||
test: all
|
||||
@ ./$(TESTSUITE)
|
||||
test: $(QCVM) $(TESTSUITE)
|
||||
@$(RUNTESTS)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4 *.exe
|
||||
rm -rf $(DEPDIR) $(OBJDIR)
|
||||
|
||||
splint:
|
||||
@ splint $(SPLINTFLAGS) *.c *.h
|
||||
.PHONY: test clean $(DEPDIR) $(OBJDIR)
|
||||
|
||||
gource:
|
||||
@ gource $(GOURCEFLAGS)
|
||||
# Dependencies
|
||||
$(filter %.d,$(GSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
|
||||
gource-record:
|
||||
@ gource $(GOURCEFLAGS) -o - | ffmpeg $(FFMPEGFLAGS) gource.mp4
|
||||
$(filter %.d,$(QSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
|
||||
depend:
|
||||
@makedepend -Y -w 65536 2> /dev/null \
|
||||
$(subst .o,.c,$(OBJ_D))
|
||||
@makedepend -a -Y -w 65536 2> /dev/null \
|
||||
$(subst .o,.c,$(OBJ_T))
|
||||
@makedepend -a -Y -w 65536 2> /dev/null \
|
||||
$(subst .o,.c,$(OBJ_C))
|
||||
@makedepend -a -Y -w 65536 2> /dev/null \
|
||||
$(subst .o,.c,$(OBJ_X))
|
||||
@makedepend -a -Y -w 65536 2> /dev/null \
|
||||
$(subst .o,.c,$(OBJ_P))
|
||||
|
||||
#install rules
|
||||
install: install-gmqcc install-qcvm install-doc
|
||||
install-gmqcc: $(GMQCC)
|
||||
install -d -m755 $(DESTDIR)$(BINDIR)
|
||||
install -m755 $(GMQCC) $(DESTDIR)$(BINDIR)/$(GMQCC)
|
||||
install-qcvm: $(QCVM)
|
||||
install -d -m755 $(DESTDIR)$(BINDIR)
|
||||
install -m755 $(QCVM) $(DESTDIR)$(BINDIR)/$(QCVM)
|
||||
install-doc:
|
||||
install -d -m755 $(DESTDIR)$(MANDIR)/man1
|
||||
install -m644 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/
|
||||
install -m644 doc/qcvm.1 $(DESTDIR)$(MANDIR)/man1/
|
||||
|
||||
uninstall:
|
||||
rm $(DESTDIR)$(BINDIR)/gmqcc
|
||||
rm $(DESTDIR)$(BINDIR)/qcvm
|
||||
rm $(DESTDIR)$(MANDIR)/man1/doc/gmqcc.1
|
||||
rm $(DESTDIR)$(MANDIR)/man1/doc/qcvm.1
|
||||
|
||||
# DO NOT DELETE
|
||||
|
||||
util.o: gmqcc.h opts.def
|
||||
code.o: gmqcc.h opts.def
|
||||
ast.o: gmqcc.h opts.def ast.h ir.h
|
||||
ir.o: gmqcc.h opts.def ir.h
|
||||
conout.o: gmqcc.h opts.def
|
||||
ftepp.o: gmqcc.h opts.def lexer.h
|
||||
opts.o: gmqcc.h opts.def
|
||||
fs.o: gmqcc.h opts.def
|
||||
utf8.o: gmqcc.h opts.def
|
||||
correct.o: gmqcc.h opts.def
|
||||
|
||||
test.o: gmqcc.h opts.def
|
||||
util.o: gmqcc.h opts.def
|
||||
conout.o: gmqcc.h opts.def
|
||||
fs.o: gmqcc.h opts.def
|
||||
|
||||
main.o: gmqcc.h opts.def lexer.h
|
||||
lexer.o: gmqcc.h opts.def lexer.h
|
||||
parser.o: gmqcc.h opts.def lexer.h ast.h ir.h intrin.h
|
||||
fs.o: gmqcc.h opts.def
|
||||
|
||||
util.o: gmqcc.h opts.def
|
||||
conout.o: gmqcc.h opts.def
|
||||
fs.o: gmqcc.h opts.def
|
||||
|
||||
util.o: gmqcc.h opts.def
|
||||
fs.o: gmqcc.h opts.def
|
||||
conout.o: gmqcc.h opts.def
|
||||
opts.o: gmqcc.h opts.def
|
||||
pak.o: gmqcc.h opts.def
|
||||
$(filter %.d,$(TSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
|
|
15
README
15
README
|
@ -1,14 +1 @@
|
|||
GMQCC: An improved Quake C compiler
|
||||
|
||||
For licensing: see the LICENSE file.
|
||||
For installation notes: see the INSTALL file.
|
||||
For a list of authors: see the AUTHORS file.
|
||||
For a list of changes: see the CHANGES file.
|
||||
|
||||
For documentation:
|
||||
See the manpages, or visit the documentation online at
|
||||
http://graphitemaster.github.com/gmqcc/doc.html
|
||||
|
||||
For syntax highlighting description files, or information
|
||||
regarding how to install them:
|
||||
See the README in syntax directory
|
||||
An improved QuakeC compiler
|
||||
|
|
157
TODO
157
TODO
|
@ -1,157 +0,0 @@
|
|||
GMQCC is quite feature complete. But that doesn't address the fact that
|
||||
it can be improved. This is a list of things that we'd like to support
|
||||
in the distant future. When the time comes, we can just select a topic
|
||||
from here and open a ticket for it on the issue tracker. But for the
|
||||
meantime, this is sort of a cultivating flat file database.
|
||||
|
||||
Optimizations:
|
||||
The following are optimizations that can be implemented after the
|
||||
transformation into static-single assignment (SSA).
|
||||
|
||||
Global Value Numbering:
|
||||
Eliminate redundancy by constructing a value graph of the source
|
||||
then determining which values are computed by equivalent expressions.
|
||||
Similar to Common Subexpression Elimination (CSE), however expressions
|
||||
are determined via underlying equivalence, opposed to lexically identical
|
||||
expressions (CSE).
|
||||
|
||||
Spare Conditional Constant Propagation:
|
||||
Simultaneously remove dead code and propagates constants. This is
|
||||
not the same as individual dead code elimination and constant propagation
|
||||
passes. This is multipass.
|
||||
|
||||
The following are optimizations that can be implemented before the
|
||||
transformation into a binary (code generator).
|
||||
|
||||
Code factoring:
|
||||
The process of finding sequences of code that are identical,
|
||||
or can be parameterized or reordered to be identical.
|
||||
Which can be replaced with calls to a shared subroutine. To
|
||||
reduce duplicated code. (Size optimization)
|
||||
|
||||
The following are optimizations that can be implemented anywhere, ideally
|
||||
these are functional language optimizations.
|
||||
|
||||
Removing Recursion:
|
||||
Tail recursive algorithms can be converted to iteration, which
|
||||
does not have to have call overhead.
|
||||
|
||||
|
||||
Language Features:
|
||||
The following are language features that we'd like to see implemented in the
|
||||
future.
|
||||
|
||||
Enumerations:
|
||||
Like C
|
||||
|
||||
AST Macros:
|
||||
Macros with sanity. Not textual substiution.
|
||||
|
||||
Classes:
|
||||
Like C++, but minus the stupidity:
|
||||
- No type operator overloads
|
||||
- Keep operator overloading for basic operators though.
|
||||
- No inheritance
|
||||
- No virtuals / pure virtuals
|
||||
- Essentially "C structs but with operators" :)
|
||||
|
||||
Arrays:
|
||||
They're currently implemented, but support in the engine
|
||||
plus implicit bounds checks (and ability to turn the bounds
|
||||
checking off)
|
||||
|
||||
Exceptions:
|
||||
I feel like all languages suck at implementing this. This would
|
||||
require support from the engine, but it would help catch bugs. We
|
||||
could make it fast using a neat method of "frame pointers".
|
||||
|
||||
Overloaded Functions:
|
||||
Ability to make individual functions with the same name, but take
|
||||
different amount of arguments or type of arguments.
|
||||
|
||||
Default Argument Substiution:
|
||||
Ability to specify default values for arguments in functions.
|
||||
void foo(string bar, string baz="default");
|
||||
Supplying just one argument will expand the second argument to
|
||||
become "default", otherwise if two arguments are specified then
|
||||
the "default" string is overrode with what ever the user passes.
|
||||
|
||||
Character Type:
|
||||
A char type would be nice to have. Essentially implemented as a
|
||||
string, we can both "get" and "set" indices inside strings with
|
||||
the help of builtin functions.
|
||||
|
||||
{
|
||||
string foo = "test";
|
||||
foo[0] = 'r';
|
||||
|
||||
print("it's time to ", foo);
|
||||
}
|
||||
|
||||
Array Accessor With C-Semantics:
|
||||
Also the ability to use them as array accessors:
|
||||
|
||||
{
|
||||
float hugearray['Z'];
|
||||
|
||||
hugearray['a'] = 100.0f;
|
||||
}
|
||||
|
||||
Keep existing "pointer-like" semantics as well. In C arrays
|
||||
simple work as pointers, a[1] -> *(a+1), or 1[a] -> *(1+a)
|
||||
so we should allow both forms of syntax. As well as operand
|
||||
reversal.
|
||||
|
||||
{
|
||||
float h['Z'];
|
||||
*(h+'a') = 100;
|
||||
*('a'+h) = 'a'[h];
|
||||
}
|
||||
|
||||
FTEQCC Inline Assembly:
|
||||
This is still up for debate, mainly because a) it's syntax is
|
||||
just utter crap. b) If we do an assembler, it should be nice.
|
||||
we could provide a -std=fteqcc for the assembler itself :P
|
||||
just like the compiler; although I think that's just insane.
|
||||
|
||||
Please see Assembler below.
|
||||
|
||||
Namespaces:
|
||||
There is already a ticket open on this. They'd work just like C++
|
||||
identically even.
|
||||
|
||||
Standalone QCVM:
|
||||
The following are QCVM additions:
|
||||
|
||||
Proper ASM disassembly:
|
||||
Proper disassembly of compiled .dat files. Annotated if possible
|
||||
when -g (is used during compilation)
|
||||
|
||||
Debugging:
|
||||
A step-through debugger -d (with separate compilation as well)
|
||||
Called -> qcdb Optionally alias to qcvm -d :)
|
||||
|
||||
We should be able to see the assembly and source it matches to
|
||||
and the state of OFS_* and calls.
|
||||
|
||||
Testsuite:
|
||||
The following are things we'd like to see added to the testsuite
|
||||
in the distant future:
|
||||
|
||||
Multithreading:
|
||||
Chances are when we start adding more and more tests, executing
|
||||
them individually will be midly slow (even if that's a whole minute)
|
||||
It would be nice to add a -j paramater to allow multiple threads to
|
||||
be used and so we can execute many tests in parallel.
|
||||
|
||||
Interface:
|
||||
Ability to select individual tests, or set parameters manually
|
||||
opposed to using the static task-template files. (A method to
|
||||
override them rather).
|
||||
|
||||
|
||||
Assembler:
|
||||
Possibly support for a future assembler for QCASM. But we're not
|
||||
entirely sure if it makes sense.
|
||||
|
||||
|
18
algo.h
Normal file
18
algo.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef GMQCC_ALGO_HDR
|
||||
#define GMQCC_ALGO_HDR
|
||||
|
||||
namespace algo {
|
||||
|
||||
template<typename ITER>
|
||||
void shiftback(ITER element, ITER end) {
|
||||
//typename ITER::value_type backup(move(*element)); // hold the element
|
||||
typename std::remove_reference<decltype(*element)>::type backup(move(*element)); // hold the element
|
||||
ITER p = element++;
|
||||
for (; element != end; p = element++)
|
||||
*p = move(*element);
|
||||
*p = move(backup);
|
||||
}
|
||||
|
||||
} // ::algo
|
||||
|
||||
#endif
|
276
code.c
276
code.c
|
@ -1,276 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Dale Weiler
|
||||
* Wolfgang Bumiller
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "gmqcc.h"
|
||||
|
||||
/* This is outrageous! */
|
||||
#define QCINT_ENTRY void*
|
||||
#define QCINT_TO_HASH_ENTRY(q) ((void*)(uintptr_t)(q))
|
||||
#define HASH_ENTRY_TO_QCINT(h) ((qcint)(uintptr_t)(h))
|
||||
|
||||
void code_push_statement(code_t *code, prog_section_statement *stmt, int linenum)
|
||||
{
|
||||
vec_push(code->statements, *stmt);
|
||||
vec_push(code->linenums, linenum);
|
||||
}
|
||||
|
||||
void code_pop_statement(code_t *code)
|
||||
{
|
||||
vec_pop(code->statements);
|
||||
vec_pop(code->linenums);
|
||||
}
|
||||
|
||||
code_t *code_init() {
|
||||
static prog_section_function empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
|
||||
static prog_section_statement empty_statement = {0,{0},{0},{0}};
|
||||
static prog_section_def empty_def = {0, 0, 0};
|
||||
|
||||
code_t *code = (code_t*)mem_a(sizeof(code_t));
|
||||
int i = 0;
|
||||
|
||||
memset(code, 0, sizeof(code_t));
|
||||
code->entfields = 0;
|
||||
code->string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
|
||||
|
||||
/*
|
||||
* The way progs.dat is suppose to work is odd, there needs to be
|
||||
* some null (empty) statements, functions, and 28 globals
|
||||
*/
|
||||
for(; i < 28; i++)
|
||||
vec_push(code->globals, 0);
|
||||
|
||||
vec_push(code->chars, '\0');
|
||||
vec_push(code->functions, empty_function);
|
||||
|
||||
code_push_statement(code, &empty_statement, 0);
|
||||
|
||||
vec_push(code->defs, empty_def);
|
||||
vec_push(code->fields, empty_def);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
|
||||
|
||||
uint32_t code_genstring(code_t *code, const char *str)
|
||||
{
|
||||
uint32_t off;
|
||||
size_t hash;
|
||||
QCINT_ENTRY existing;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
if (!*str) {
|
||||
if (!code->string_cached_empty) {
|
||||
code->string_cached_empty = vec_size(code->chars);
|
||||
vec_push(code->chars, 0);
|
||||
}
|
||||
return code->string_cached_empty;
|
||||
}
|
||||
|
||||
if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
|
||||
hash = ((unsigned char*)str)[strlen(str)-1];
|
||||
existing = code_util_str_htgeth(code->string_cache, str, hash);
|
||||
} else {
|
||||
hash = util_hthash(code->string_cache, str);
|
||||
existing = util_htgeth(code->string_cache, str, hash);
|
||||
}
|
||||
|
||||
if (existing)
|
||||
return HASH_ENTRY_TO_QCINT(existing);
|
||||
|
||||
off = vec_size(code->chars);
|
||||
vec_upload(code->chars, str, strlen(str)+1);
|
||||
|
||||
util_htseth(code->string_cache, str, hash, QCINT_TO_HASH_ENTRY(off));
|
||||
return off;
|
||||
}
|
||||
|
||||
qcint code_alloc_field (code_t *code, size_t qcsize)
|
||||
{
|
||||
qcint pos = (qcint)code->entfields;
|
||||
code->entfields += qcsize;
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool code_write(code_t *code, const char *filename, const char *lnofile) {
|
||||
prog_header code_header;
|
||||
FILE *fp = NULL;
|
||||
size_t it = 2;
|
||||
|
||||
code_header.statements.offset = sizeof(prog_header);
|
||||
code_header.statements.length = vec_size(code->statements);
|
||||
code_header.defs.offset = code_header.statements.offset + (sizeof(prog_section_statement) * vec_size(code->statements));
|
||||
code_header.defs.length = vec_size(code->defs);
|
||||
code_header.fields.offset = code_header.defs.offset + (sizeof(prog_section_def) * vec_size(code->defs));
|
||||
code_header.fields.length = vec_size(code->fields);
|
||||
code_header.functions.offset = code_header.fields.offset + (sizeof(prog_section_field) * vec_size(code->fields));
|
||||
code_header.functions.length = vec_size(code->functions);
|
||||
code_header.globals.offset = code_header.functions.offset + (sizeof(prog_section_function) * vec_size(code->functions));
|
||||
code_header.globals.length = vec_size(code->globals);
|
||||
code_header.strings.offset = code_header.globals.offset + (sizeof(int32_t) * vec_size(code->globals));
|
||||
code_header.strings.length = vec_size(code->chars);
|
||||
code_header.version = 6;
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
|
||||
code_header.crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
|
||||
else
|
||||
code_header.crc16 = code->crc;
|
||||
code_header.entfield = code->entfields;
|
||||
|
||||
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
|
||||
util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n");
|
||||
|
||||
/* >= + P */
|
||||
vec_push(code->chars, '\0'); /* > */
|
||||
vec_push(code->chars, '\0'); /* = */
|
||||
vec_push(code->chars, '\0'); /* P */
|
||||
}
|
||||
|
||||
/* ensure all data is in LE format */
|
||||
util_endianswap(&code_header.version, 1, sizeof(code_header.version));
|
||||
util_endianswap(&code_header.crc16, 1, sizeof(code_header.crc16));
|
||||
util_endianswap(&code_header.statements, 2, sizeof(code_header.statements.offset));
|
||||
util_endianswap(&code_header.defs, 2, sizeof(code_header.statements.offset));
|
||||
util_endianswap(&code_header.fields, 2, sizeof(code_header.statements.offset));
|
||||
util_endianswap(&code_header.functions, 2, sizeof(code_header.statements.offset));
|
||||
util_endianswap(&code_header.strings, 2, sizeof(code_header.statements.offset));
|
||||
util_endianswap(&code_header.globals, 2, sizeof(code_header.statements.offset));
|
||||
util_endianswap(&code_header.entfield, 1, sizeof(code_header.entfield));
|
||||
util_endianswap(code->statements, vec_size(code->statements), sizeof(prog_section_statement));
|
||||
util_endianswap(code->defs, vec_size(code->defs), sizeof(prog_section_def));
|
||||
util_endianswap(code->fields, vec_size(code->fields), sizeof(prog_section_field));
|
||||
util_endianswap(code->functions, vec_size(code->functions), sizeof(prog_section_function));
|
||||
util_endianswap(code->globals, vec_size(code->globals), sizeof(int32_t));
|
||||
|
||||
if (lnofile) {
|
||||
uint32_t version = 1;
|
||||
|
||||
fp = fs_file_open(lnofile, "wb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
util_endianswap(&version, 1, sizeof(version));
|
||||
util_endianswap(code->linenums, vec_size(code->linenums), sizeof(code->linenums[0]));
|
||||
|
||||
|
||||
if (fs_file_write("LNOF", 4, 1, fp) != 1 ||
|
||||
fs_file_write(&version, sizeof(version), 1, fp) != 1 ||
|
||||
fs_file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
|
||||
fs_file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
|
||||
fs_file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
|
||||
fs_file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
|
||||
fs_file_write(code->linenums, sizeof(code->linenums[0]), vec_size(code->linenums), fp) != vec_size(code->linenums))
|
||||
{
|
||||
con_err("failed to write lno file\n");
|
||||
}
|
||||
|
||||
fs_file_close(fp);
|
||||
fp = NULL;
|
||||
}
|
||||
|
||||
fp = fs_file_open(filename, "wb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
if (1 != fs_file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
|
||||
vec_size(code->statements) != fs_file_write(code->statements, sizeof(prog_section_statement), vec_size(code->statements), fp) ||
|
||||
vec_size(code->defs) != fs_file_write(code->defs, sizeof(prog_section_def) , vec_size(code->defs) , fp) ||
|
||||
vec_size(code->fields) != fs_file_write(code->fields, sizeof(prog_section_field) , vec_size(code->fields) , fp) ||
|
||||
vec_size(code->functions) != fs_file_write(code->functions, sizeof(prog_section_function) , vec_size(code->functions) , fp) ||
|
||||
vec_size(code->globals) != fs_file_write(code->globals, sizeof(int32_t) , vec_size(code->globals) , fp) ||
|
||||
vec_size(code->chars) != fs_file_write(code->chars, 1 , vec_size(code->chars) , fp))
|
||||
{
|
||||
fs_file_close(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
util_debug("GEN","HEADER:\n");
|
||||
util_debug("GEN"," version: = %d\n", code_header.version );
|
||||
util_debug("GEN"," crc16: = %d\n", code_header.crc16 );
|
||||
util_debug("GEN"," entfield: = %d\n", code_header.entfield);
|
||||
util_debug("GEN"," statements = {.offset = % 8d, .length = % 8d}\n", code_header.statements.offset, code_header.statements.length);
|
||||
util_debug("GEN"," defs = {.offset = % 8d, .length = % 8d}\n", code_header.defs .offset, code_header.defs .length);
|
||||
util_debug("GEN"," fields = {.offset = % 8d, .length = % 8d}\n", code_header.fields .offset, code_header.fields .length);
|
||||
util_debug("GEN"," functions = {.offset = % 8d, .length = % 8d}\n", code_header.functions .offset, code_header.functions .length);
|
||||
util_debug("GEN"," globals = {.offset = % 8d, .length = % 8d}\n", code_header.globals .offset, code_header.globals .length);
|
||||
util_debug("GEN"," strings = {.offset = % 8d, .length = % 8d}\n", code_header.strings .offset, code_header.strings .length);
|
||||
|
||||
/* FUNCTIONS */
|
||||
util_debug("GEN", "FUNCTIONS:\n");
|
||||
for (; it < vec_size(code->functions); it++) {
|
||||
size_t j = code->functions[it].entry;
|
||||
util_debug("GEN", " {.entry =% 5d, .firstlocal =% 5d, .locals =% 5d, .profile =% 5d, .name =% 5d, .file =% 5d, .nargs =% 5d, .argsize ={%d,%d,%d,%d,%d,%d,%d,%d} }\n",
|
||||
code->functions[it].entry,
|
||||
code->functions[it].firstlocal,
|
||||
code->functions[it].locals,
|
||||
code->functions[it].profile,
|
||||
code->functions[it].name,
|
||||
code->functions[it].file,
|
||||
code->functions[it].nargs,
|
||||
code->functions[it].argsize[0],
|
||||
code->functions[it].argsize[1],
|
||||
code->functions[it].argsize[2],
|
||||
code->functions[it].argsize[3],
|
||||
code->functions[it].argsize[4],
|
||||
code->functions[it].argsize[5],
|
||||
code->functions[it].argsize[6],
|
||||
code->functions[it].argsize[7]
|
||||
|
||||
);
|
||||
util_debug("GEN", " NAME: %s\n", &code->chars[code->functions[it].name]);
|
||||
/* Internal functions have no code */
|
||||
if (code->functions[it].entry >= 0) {
|
||||
util_debug("GEN", " CODE:\n");
|
||||
for (;;) {
|
||||
if (code->statements[j].opcode != INSTR_DONE)
|
||||
util_debug("GEN", " %-12s {% 5i,% 5i,% 5i}\n",
|
||||
asm_instr[code->statements[j].opcode].m,
|
||||
code->statements[j].o1.s1,
|
||||
code->statements[j].o2.s1,
|
||||
code->statements[j].o3.s1
|
||||
);
|
||||
else {
|
||||
util_debug("GEN", " DONE {0x00000,0x00000,0x00000}\n");
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec_free(code->statements);
|
||||
vec_free(code->linenums);
|
||||
vec_free(code->defs);
|
||||
vec_free(code->fields);
|
||||
vec_free(code->functions);
|
||||
vec_free(code->globals);
|
||||
vec_free(code->chars);
|
||||
|
||||
util_htdel(code->string_cache);
|
||||
|
||||
fs_file_close(fp);
|
||||
mem_d(code);
|
||||
return true;
|
||||
}
|
348
code.cpp
Normal file
348
code.cpp
Normal file
|
@ -0,0 +1,348 @@
|
|||
#include <string.h>
|
||||
#include "gmqcc.h"
|
||||
|
||||
/*
|
||||
* We could use the old method of casting to uintptr_t then to void*
|
||||
* or qcint_t; however, it's incredibly unsafe for two reasons.
|
||||
* 1) The compilers aliasing optimization can legally make it unstable
|
||||
* (it's undefined behaviour).
|
||||
*
|
||||
* 2) The cast itself depends on fresh storage (newly allocated in which
|
||||
* ever function is using the cast macros), the contents of which are
|
||||
* transferred in a way that the obligation to release storage is not
|
||||
* propagated.
|
||||
*/
|
||||
typedef union {
|
||||
void *enter;
|
||||
qcint_t leave;
|
||||
} code_hash_entry_t;
|
||||
|
||||
/* Some sanity macros */
|
||||
#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
|
||||
#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
|
||||
|
||||
void code_push_statement(code_t *code, prog_section_statement_t *stmt_in, lex_ctx_t ctx)
|
||||
{
|
||||
prog_section_statement_t stmt = *stmt_in;
|
||||
|
||||
if (OPTS_FLAG(TYPELESS_STORES)) {
|
||||
switch (stmt.opcode) {
|
||||
case INSTR_LOAD_S:
|
||||
case INSTR_LOAD_ENT:
|
||||
case INSTR_LOAD_FLD:
|
||||
case INSTR_LOAD_FNC:
|
||||
stmt.opcode = INSTR_LOAD_F;
|
||||
break;
|
||||
case INSTR_STORE_S:
|
||||
case INSTR_STORE_ENT:
|
||||
case INSTR_STORE_FLD:
|
||||
case INSTR_STORE_FNC:
|
||||
stmt.opcode = INSTR_STORE_F;
|
||||
break;
|
||||
case INSTR_STOREP_S:
|
||||
case INSTR_STOREP_ENT:
|
||||
case INSTR_STOREP_FLD:
|
||||
case INSTR_STOREP_FNC:
|
||||
stmt.opcode = INSTR_STOREP_F;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (OPTS_FLAG(SORT_OPERANDS)) {
|
||||
uint16_t pair;
|
||||
|
||||
switch (stmt.opcode) {
|
||||
case INSTR_MUL_F:
|
||||
case INSTR_MUL_V:
|
||||
case INSTR_ADD_F:
|
||||
case INSTR_EQ_F:
|
||||
case INSTR_EQ_S:
|
||||
case INSTR_EQ_E:
|
||||
case INSTR_EQ_FNC:
|
||||
case INSTR_NE_F:
|
||||
case INSTR_NE_V:
|
||||
case INSTR_NE_S:
|
||||
case INSTR_NE_E:
|
||||
case INSTR_NE_FNC:
|
||||
case INSTR_AND:
|
||||
case INSTR_OR:
|
||||
case INSTR_BITAND:
|
||||
case INSTR_BITOR:
|
||||
if (stmt.o1.u1 < stmt.o2.u1) {
|
||||
uint16_t a = stmt.o2.u1;
|
||||
stmt.o1.u1 = stmt.o2.u1;
|
||||
stmt.o2.u1 = a;
|
||||
}
|
||||
break;
|
||||
|
||||
case INSTR_MUL_VF: pair = INSTR_MUL_FV; goto case_pair_gen;
|
||||
case INSTR_MUL_FV: pair = INSTR_MUL_VF; goto case_pair_gen;
|
||||
case INSTR_LT: pair = INSTR_GT; goto case_pair_gen;
|
||||
case INSTR_GT: pair = INSTR_LT; goto case_pair_gen;
|
||||
case INSTR_LE: pair = INSTR_GE; goto case_pair_gen;
|
||||
case INSTR_GE: pair = INSTR_LE;
|
||||
|
||||
case_pair_gen:
|
||||
if (stmt.o1.u1 < stmt.o2.u1) {
|
||||
uint16_t x = stmt.o1.u1;
|
||||
stmt.o1.u1 = stmt.o2.u1;
|
||||
stmt.o2.u1 = x;
|
||||
stmt.opcode = pair;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
code->statements.push_back(stmt);
|
||||
code->linenums.push_back(ctx.line);
|
||||
code->columnnums.push_back(ctx.column);
|
||||
}
|
||||
|
||||
void code_pop_statement(code_t *code)
|
||||
{
|
||||
code->statements.pop_back();
|
||||
code->linenums.pop_back();
|
||||
code->columnnums.pop_back();
|
||||
}
|
||||
|
||||
void *code_t::operator new(std::size_t bytes) {
|
||||
return mem_a(bytes);
|
||||
}
|
||||
|
||||
void code_t::operator delete(void *ptr) {
|
||||
mem_d(ptr);
|
||||
}
|
||||
|
||||
code_t::code_t()
|
||||
{
|
||||
static lex_ctx_t empty_ctx = {0, 0, 0};
|
||||
static prog_section_function_t empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
|
||||
static prog_section_statement_t empty_statement = {0,{0},{0},{0}};
|
||||
static prog_section_def_t empty_def = {0, 0, 0};
|
||||
|
||||
string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
|
||||
|
||||
// The way progs.dat is suppose to work is odd, there needs to be
|
||||
// some null (empty) statements, functions, and 28 globals
|
||||
globals.insert(globals.begin(), 28, 0);
|
||||
|
||||
chars.push_back('\0');
|
||||
functions.push_back(empty_function);
|
||||
|
||||
code_push_statement(this, &empty_statement, empty_ctx);
|
||||
|
||||
defs.push_back(empty_def);
|
||||
fields.push_back(empty_def);
|
||||
}
|
||||
|
||||
code_t::~code_t()
|
||||
{
|
||||
util_htdel(string_cache);
|
||||
}
|
||||
|
||||
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
|
||||
|
||||
uint32_t code_genstring(code_t *code, const char *str) {
|
||||
size_t hash;
|
||||
code_hash_entry_t existing;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
if (!*str) {
|
||||
if (!code->string_cached_empty) {
|
||||
code->string_cached_empty = code->chars.size();
|
||||
code->chars.push_back(0);
|
||||
}
|
||||
return code->string_cached_empty;
|
||||
}
|
||||
|
||||
if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
|
||||
hash = ((unsigned char*)str)[strlen(str)-1];
|
||||
CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash);
|
||||
} else {
|
||||
hash = util_hthash(code->string_cache, str);
|
||||
CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash);
|
||||
}
|
||||
|
||||
if (CODE_HASH_ENTER(existing))
|
||||
return CODE_HASH_LEAVE(existing);
|
||||
|
||||
CODE_HASH_LEAVE(existing) = code->chars.size();
|
||||
code->chars.insert(code->chars.end(), str, str + strlen(str) + 1);
|
||||
|
||||
util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing));
|
||||
return CODE_HASH_LEAVE(existing);
|
||||
}
|
||||
|
||||
qcint_t code_alloc_field (code_t *code, size_t qcsize)
|
||||
{
|
||||
qcint_t pos = (qcint_t)code->entfields;
|
||||
code->entfields += qcsize;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static size_t code_size_generic(code_t *code, prog_header_t *code_header, bool lno) {
|
||||
size_t size = 0;
|
||||
if (lno) {
|
||||
size += 4; /* LNOF */
|
||||
size += sizeof(uint32_t); /* version */
|
||||
size += sizeof(code_header->defs.length);
|
||||
size += sizeof(code_header->globals.length);
|
||||
size += sizeof(code_header->fields.length);
|
||||
size += sizeof(code_header->statements.length);
|
||||
size += sizeof(code->linenums[0]) * code->linenums.size();
|
||||
size += sizeof(code->columnnums[0]) * code->columnnums.size();
|
||||
} else {
|
||||
size += sizeof(prog_header_t);
|
||||
size += sizeof(prog_section_statement_t) * code->statements.size();
|
||||
size += sizeof(prog_section_def_t) * code->defs.size();
|
||||
size += sizeof(prog_section_field_t) * code->fields.size();
|
||||
size += sizeof(prog_section_function_t) * code->functions.size();
|
||||
size += sizeof(int32_t) * code->globals.size();
|
||||
size += 1 * code->chars.size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
#define code_size_binary(C, H) code_size_generic((C), (H), false)
|
||||
#define code_size_debug(C, H) code_size_generic((C), (H), true)
|
||||
|
||||
static void code_create_header(code_t *code, prog_header_t *code_header, const char *filename, const char *lnofile) {
|
||||
size_t i;
|
||||
|
||||
code_header->statements.offset = sizeof(prog_header_t);
|
||||
code_header->statements.length = code->statements.size();
|
||||
code_header->defs.offset = code_header->statements.offset + (sizeof(prog_section_statement_t) * code->statements.size());
|
||||
code_header->defs.length = code->defs.size();
|
||||
code_header->fields.offset = code_header->defs.offset + (sizeof(prog_section_def_t) * code->defs.size());
|
||||
code_header->fields.length = code->fields.size();
|
||||
code_header->functions.offset = code_header->fields.offset + (sizeof(prog_section_field_t) * code->fields.size());
|
||||
code_header->functions.length = code->functions.size();
|
||||
code_header->globals.offset = code_header->functions.offset + (sizeof(prog_section_function_t) * code->functions.size());
|
||||
code_header->globals.length = code->globals.size();
|
||||
code_header->strings.offset = code_header->globals.offset + (sizeof(int32_t) * code->globals.size());
|
||||
code_header->strings.length = code->chars.size();
|
||||
code_header->version = 6;
|
||||
code_header->skip = 0;
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
|
||||
code_header->crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
|
||||
else
|
||||
code_header->crc16 = code->crc;
|
||||
code_header->entfield = code->entfields;
|
||||
|
||||
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
|
||||
/* >= + P */
|
||||
code->chars.push_back('\0'); /* > */
|
||||
code->chars.push_back('\0'); /* = */
|
||||
code->chars.push_back('\0'); /* P */
|
||||
}
|
||||
|
||||
/* ensure all data is in LE format */
|
||||
util_swap_header(*code_header);
|
||||
util_swap_statements(code->statements);
|
||||
util_swap_defs_fields(code->defs);
|
||||
util_swap_defs_fields(code->fields);
|
||||
util_swap_functions(code->functions);
|
||||
util_swap_globals(code->globals);
|
||||
|
||||
if (!OPTS_OPTION_BOOL(OPTION_QUIET)) {
|
||||
if (lnofile)
|
||||
con_out("writing '%s' and '%s'...\n", filename, lnofile);
|
||||
else
|
||||
con_out("writing '%s'\n", filename);
|
||||
}
|
||||
|
||||
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
|
||||
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
{
|
||||
char buffer[1024];
|
||||
con_out("\nOptimizations:\n");
|
||||
for (i = 0; i < COUNT_OPTIMIZATIONS; ++i) {
|
||||
if (opts_optimizationcount[i]) {
|
||||
util_optimizationtostr(opts_opt_list[i].name, buffer, sizeof(buffer));
|
||||
con_out(
|
||||
" %s: %u\n",
|
||||
buffer,
|
||||
(unsigned int)opts_optimizationcount[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void code_stats(const char *filename, const char *lnofile, code_t *code, prog_header_t *code_header) {
|
||||
if (OPTS_OPTION_BOOL(OPTION_QUIET) ||
|
||||
OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
return;
|
||||
|
||||
con_out("\nFile statistics:\n");
|
||||
con_out(" dat:\n");
|
||||
con_out(" name: %s\n", filename);
|
||||
con_out(" size: %u (bytes)\n", code_size_binary(code, code_header));
|
||||
con_out(" crc: 0x%04X\n", code->crc);
|
||||
|
||||
if (lnofile) {
|
||||
con_out(" lno:\n");
|
||||
con_out(" name: %s\n", lnofile);
|
||||
con_out(" size: %u (bytes)\n", code_size_debug(code, code_header));
|
||||
}
|
||||
|
||||
con_out("\n");
|
||||
}
|
||||
|
||||
bool code_write(code_t *code, const char *filename, const char *lnofile) {
|
||||
prog_header_t code_header;
|
||||
FILE *fp = nullptr;
|
||||
|
||||
code_create_header(code, &code_header, filename, lnofile);
|
||||
|
||||
if (lnofile) {
|
||||
uint32_t version = 1;
|
||||
|
||||
fp = fopen(lnofile, "wb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
util_endianswap(&version, 1, sizeof(version));
|
||||
util_endianswap(&code->linenums[0], code->linenums.size(), sizeof(code->linenums[0]));
|
||||
util_endianswap(&code->columnnums[0], code->columnnums.size(), sizeof(code->columnnums[0]));
|
||||
|
||||
if (fwrite("LNOF", 4, 1, fp) != 1 ||
|
||||
fwrite(&version, sizeof(version), 1, fp) != 1 ||
|
||||
fwrite(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
|
||||
fwrite(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
|
||||
fwrite(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
|
||||
fwrite(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
|
||||
fwrite(&code->linenums[0], sizeof(code->linenums[0]), code->linenums.size(), fp) != code->linenums.size() ||
|
||||
fwrite(&code->columnnums[0], sizeof(code->columnnums[0]), code->columnnums.size(), fp) != code->columnnums.size())
|
||||
{
|
||||
con_err("failed to write lno file\n");
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
fp = nullptr;
|
||||
}
|
||||
|
||||
fp = fopen(filename, "wb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
if (1 != fwrite(&code_header, sizeof(prog_header_t) , 1 , fp) ||
|
||||
code->statements.size() != fwrite(&code->statements[0], sizeof(prog_section_statement_t), code->statements.size(), fp) ||
|
||||
code->defs.size() != fwrite(&code->defs[0], sizeof(prog_section_def_t) , code->defs.size() , fp) ||
|
||||
code->fields.size() != fwrite(&code->fields[0], sizeof(prog_section_field_t) , code->fields.size() , fp) ||
|
||||
code->functions.size() != fwrite(&code->functions[0], sizeof(prog_section_function_t) , code->functions.size() , fp) ||
|
||||
code->globals.size() != fwrite(&code->globals[0], sizeof(int32_t) , code->globals.size() , fp) ||
|
||||
code->chars.size() != fwrite(&code->chars[0], 1 , code->chars.size() , fp))
|
||||
{
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
code_stats(filename, lnofile, code, &code_header);
|
||||
return true;
|
||||
}
|
448
conout.c
448
conout.c
|
@ -1,448 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Dale Weiler
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "gmqcc.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* isatty/STDERR_FILENO/STDOUT_FILNO
|
||||
* + some other things likewise.
|
||||
*/
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <io.h>
|
||||
/*
|
||||
* Windows and it's posix underscore bullshit. We simply fix this
|
||||
* with yay, another macro :P
|
||||
*/
|
||||
# define isatty _isatty
|
||||
#endif
|
||||
|
||||
#define GMQCC_IS_STDOUT(X) ((FILE*)((void*)X) == stdout)
|
||||
#define GMQCC_IS_STDERR(X) ((FILE*)((void*)X) == stderr)
|
||||
#define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X))
|
||||
|
||||
typedef struct {
|
||||
FILE *handle_err;
|
||||
FILE *handle_out;
|
||||
|
||||
int color_err;
|
||||
int color_out;
|
||||
} con_t;
|
||||
|
||||
/*
|
||||
* Doing colored output on windows is fucking stupid. The linux way is
|
||||
* the real way. So we emulate it on windows :)
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
* Windows doesn't have constants for FILENO, sadly but the docs tell
|
||||
* use the constant values.
|
||||
*/
|
||||
#undef STDERR_FILENO
|
||||
#undef STDOUT_FILENO
|
||||
#define STDERR_FILENO 2
|
||||
#define STDOUT_FILENO 1
|
||||
|
||||
enum {
|
||||
RESET = 0,
|
||||
BOLD = 1,
|
||||
BLACK = 30,
|
||||
RED,
|
||||
GREEN,
|
||||
YELLOW,
|
||||
BLUE,
|
||||
MAGENTA,
|
||||
CYAN,
|
||||
GRAY,
|
||||
WHITE = GRAY
|
||||
};
|
||||
|
||||
enum {
|
||||
WBLACK,
|
||||
WBLUE,
|
||||
WGREEN = 2,
|
||||
WRED = 4,
|
||||
WINTENSE = 8,
|
||||
WCYAN = WBLUE | WGREEN,
|
||||
WMAGENTA = WBLUE | WRED,
|
||||
WYELLOW = WGREEN | WRED,
|
||||
WWHITE = WBLUE | WGREEN | WRED
|
||||
};
|
||||
|
||||
static const int ansi2win[] = {
|
||||
WBLACK,
|
||||
WRED,
|
||||
WGREEN,
|
||||
WYELLOW,
|
||||
WBLUE,
|
||||
WMAGENTA,
|
||||
WCYAN,
|
||||
WWHITE
|
||||
};
|
||||
|
||||
static int win_fputs(FILE *h, const char *str) {
|
||||
/* state for translate */
|
||||
int acolor;
|
||||
int wcolor;
|
||||
int icolor;
|
||||
|
||||
int state = 0;
|
||||
|
||||
/* attributes */
|
||||
int intense = -1;
|
||||
int colors[] = {-1, -1 };
|
||||
int colorpos = 1;
|
||||
int length = 0;
|
||||
CONSOLE_SCREEN_BUFFER_INFO cinfo;
|
||||
GetConsoleScreenBufferInfo (
|
||||
(GMQCC_IS_STDOUT(h)) ?
|
||||
GetStdHandle(STD_OUTPUT_HANDLE) :
|
||||
GetStdHandle(STD_ERROR_HANDLE), &cinfo
|
||||
);
|
||||
icolor = cinfo.wAttributes;
|
||||
|
||||
while (*str) {
|
||||
if (*str == '\x1B')
|
||||
state = '\x1B';
|
||||
else if (state == '\x1B' && *str == '[')
|
||||
state = '[';
|
||||
else if (state == '[') {
|
||||
if (*str != 'm') {
|
||||
colors[colorpos] = *str;
|
||||
colorpos--;
|
||||
} else {
|
||||
int find;
|
||||
int mult;
|
||||
for (find = colorpos + 1, acolor = 0, mult = 1; find < 2; find++) {
|
||||
acolor += (colors[find] - 48) * mult;
|
||||
mult *= 10;
|
||||
}
|
||||
|
||||
/* convert to windows color */
|
||||
if (acolor == BOLD)
|
||||
intense = WINTENSE;
|
||||
else if (acolor == RESET) {
|
||||
intense = WBLACK;
|
||||
wcolor = icolor;
|
||||
}
|
||||
else if (BLACK <= acolor && acolor <= WHITE)
|
||||
wcolor = ansi2win[acolor - 30];
|
||||
else if (acolor == 90) {
|
||||
/* special gray really white man */
|
||||
wcolor = WWHITE;
|
||||
intense = WBLACK;
|
||||
}
|
||||
|
||||
SetConsoleTextAttribute (
|
||||
(GMQCC_IS_STDOUT(h)) ?
|
||||
GetStdHandle(STD_OUTPUT_HANDLE) :
|
||||
GetStdHandle(STD_ERROR_HANDLE),
|
||||
|
||||
wcolor | intense | (icolor & 0xF0)
|
||||
);
|
||||
colorpos = 1;
|
||||
state = -1;
|
||||
}
|
||||
} else {
|
||||
fs_file_write(str, 1, 1, stdout);
|
||||
length ++;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
/* restore */
|
||||
SetConsoleTextAttribute(
|
||||
(GMQCC_IS_STDOUT(h)) ?
|
||||
GetStdHandle(STD_OUTPUT_HANDLE) :
|
||||
GetStdHandle(STD_ERROR_HANDLE),
|
||||
icolor
|
||||
);
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We use standard files as default. These can be changed at any time
|
||||
* with con_change(F, F)
|
||||
*/
|
||||
static con_t console;
|
||||
|
||||
/*
|
||||
* Enables color on output if supported.
|
||||
* NOTE: The support for checking colors is NULL. On windows this will
|
||||
* always work, on *nix it depends if the term has colors.
|
||||
*
|
||||
* NOTE: This prevents colored output to piped stdout/err via isatty
|
||||
* checks.
|
||||
*/
|
||||
static void con_enablecolor() {
|
||||
if (console.handle_err == stderr || console.handle_err == stdout)
|
||||
console.color_err = !!(isatty(STDERR_FILENO));
|
||||
if (console.handle_out == stderr || console.handle_out == stdout)
|
||||
console.color_out = !!(isatty(STDOUT_FILENO));
|
||||
}
|
||||
|
||||
/*
|
||||
* Does a write to the handle with the format string and list of
|
||||
* arguments. This colorizes for windows as well via translate
|
||||
* step.
|
||||
*/
|
||||
static int con_write(FILE *handle, const char *fmt, va_list va) {
|
||||
int ln;
|
||||
#ifndef _WIN32
|
||||
ln = vfprintf(handle, fmt, va);
|
||||
#else
|
||||
{
|
||||
char data[4096];
|
||||
memset(data, 0, sizeof(data));
|
||||
#ifdef _MSC_VER
|
||||
vsnprintf_s(data, sizeof(data), sizeof(data), fmt, va);
|
||||
#else
|
||||
vsnprintf(data, sizeof(data), fmt, va);
|
||||
#endif
|
||||
ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(handle, data) : fs_file_puts(handle, data);
|
||||
}
|
||||
#endif
|
||||
return ln;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* EXPOSED INTERFACE BEGINS
|
||||
*********************************************************************/
|
||||
|
||||
void con_close() {
|
||||
if (!GMQCC_IS_DEFINE(console.handle_err))
|
||||
fs_file_close(console.handle_err);
|
||||
if (!GMQCC_IS_DEFINE(console.handle_out))
|
||||
fs_file_close(console.handle_out);
|
||||
}
|
||||
|
||||
void con_color(int state) {
|
||||
if (state)
|
||||
con_enablecolor();
|
||||
else {
|
||||
console.color_err = 0;
|
||||
console.color_out = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void con_init() {
|
||||
console.handle_err = stderr;
|
||||
console.handle_out = stdout;
|
||||
con_enablecolor();
|
||||
}
|
||||
|
||||
void con_reset() {
|
||||
con_close();
|
||||
con_init ();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is clever, say you want to change the console to use two
|
||||
* files for out/err. You pass in two strings, it will properly
|
||||
* close the existing handles (if they're not std* handles) and
|
||||
* open them. Now say you want TO use stdout and stderr, this
|
||||
* allows you to do that so long as you cast them to (char*).
|
||||
* Say you need stdout for out, but want a file for error, you can
|
||||
* do this too, just cast the stdout for (char*) and stick to a
|
||||
* string for the error file.
|
||||
*/
|
||||
int con_change(const char *out, const char *err) {
|
||||
con_close();
|
||||
|
||||
if (!out) out = (const char *)((!console.handle_out) ? stdout : console.handle_out);
|
||||
if (!err) err = (const char *)((!console.handle_err) ? stderr : console.handle_err);
|
||||
|
||||
if (GMQCC_IS_DEFINE(out)) {
|
||||
console.handle_out = GMQCC_IS_STDOUT(out) ? stdout : stderr;
|
||||
con_enablecolor();
|
||||
} else if (!(console.handle_out = fs_file_open(out, "w"))) return 0;
|
||||
|
||||
if (GMQCC_IS_DEFINE(err)) {
|
||||
console.handle_err = GMQCC_IS_STDOUT(err) ? stdout : stderr;
|
||||
con_enablecolor();
|
||||
} else if (!(console.handle_err = fs_file_open(err, "w"))) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Defaultizer because stdio.h shouldn't be used anywhere except here
|
||||
* and inside file.c To prevent mis-match of wrapper-interfaces.
|
||||
*/
|
||||
FILE *con_default_out() {
|
||||
return (console.handle_out = stdout);
|
||||
}
|
||||
FILE *con_default_err() {
|
||||
return (console.handle_err = stderr);
|
||||
}
|
||||
|
||||
int con_verr(const char *fmt, va_list va) {
|
||||
return con_write(console.handle_err, fmt, va);
|
||||
}
|
||||
int con_vout(const char *fmt, va_list va) {
|
||||
return con_write(console.handle_out, fmt, va);
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard stdout/stderr printf functions used generally where they need
|
||||
* to be used.
|
||||
*/
|
||||
int con_err(const char *fmt, ...) {
|
||||
va_list va;
|
||||
int ln = 0;
|
||||
va_start(va, fmt);
|
||||
con_verr(fmt, va);
|
||||
va_end (va);
|
||||
return ln;
|
||||
}
|
||||
int con_out(const char *fmt, ...) {
|
||||
va_list va;
|
||||
int ln = 0;
|
||||
va_start(va, fmt);
|
||||
con_vout(fmt, va);
|
||||
va_end (va);
|
||||
return ln;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility console message writes for lexer contexts. These will allow
|
||||
* for reporting of file:line based on lexer context, These are used
|
||||
* heavily in the parser/ir/ast.
|
||||
*/
|
||||
void con_vprintmsg_c(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap, const char *condname) {
|
||||
/* color selection table */
|
||||
static int sel[] = {
|
||||
CON_WHITE,
|
||||
CON_CYAN,
|
||||
CON_RED
|
||||
};
|
||||
|
||||
int err = !!(level == LVL_ERROR);
|
||||
int color = (err) ? console.color_err : console.color_out;
|
||||
int (*print) (const char *, ...) = (err) ? &con_err : &con_out;
|
||||
int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout;
|
||||
|
||||
if (color)
|
||||
print("\033[0;%dm%s:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, sel[level], msgtype);
|
||||
else
|
||||
print("%s:%d: %s: ", name, (int)line, msgtype);
|
||||
|
||||
vprint(msg, ap);
|
||||
if (condname)
|
||||
print(" [%s]\n", condname);
|
||||
else
|
||||
print("\n");
|
||||
}
|
||||
|
||||
void con_vprintmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap) {
|
||||
con_vprintmsg_c(level, name, line, msgtype, msg, ap, NULL);
|
||||
}
|
||||
|
||||
void con_printmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, ...) {
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
con_vprintmsg(level, name, line, msgtype, msg, va);
|
||||
va_end (va);
|
||||
}
|
||||
|
||||
void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
|
||||
con_vprintmsg(lvl, ((lex_ctx*)ctx)->file, ((lex_ctx*)ctx)->line, msgtype, msg, ap);
|
||||
}
|
||||
|
||||
void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...) {
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
con_cvprintmsg(ctx, lvl, msgtype, msg, va);
|
||||
va_end (va);
|
||||
}
|
||||
|
||||
/* General error interface */
|
||||
size_t compile_errors = 0;
|
||||
size_t compile_warnings = 0;
|
||||
|
||||
size_t compile_Werrors = 0;
|
||||
static lex_ctx first_werror;
|
||||
|
||||
void compile_show_werrors()
|
||||
{
|
||||
con_cprintmsg((void*)&first_werror, LVL_ERROR, "first warning", "was here");
|
||||
}
|
||||
|
||||
void vcompile_error(lex_ctx ctx, const char *msg, va_list ap)
|
||||
{
|
||||
++compile_errors;
|
||||
con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", msg, ap);
|
||||
}
|
||||
|
||||
void compile_error(lex_ctx ctx, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vcompile_error(ctx, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap)
|
||||
{
|
||||
const char *msgtype = "warning";
|
||||
int lvl = LVL_WARNING;
|
||||
char warn_name[1024];
|
||||
|
||||
if (!OPTS_WARN(warntype))
|
||||
return false;
|
||||
|
||||
warn_name[0] = '-';
|
||||
warn_name[1] = 'W';
|
||||
(void)util_strtononcmd(opts_warn_list[warntype].name, warn_name+2, sizeof(warn_name)-2);
|
||||
|
||||
++compile_warnings;
|
||||
if (OPTS_WERROR(warntype)) {
|
||||
if (!compile_Werrors)
|
||||
first_werror = ctx;
|
||||
++compile_Werrors;
|
||||
msgtype = "Werror";
|
||||
if (OPTS_FLAG(BAIL_ON_WERROR)) {
|
||||
msgtype = "error";
|
||||
++compile_errors;
|
||||
}
|
||||
lvl = LVL_ERROR;
|
||||
}
|
||||
|
||||
con_vprintmsg_c(lvl, ctx.file, ctx.line, msgtype, fmt, ap, warn_name);
|
||||
|
||||
return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR);
|
||||
}
|
||||
|
||||
bool GMQCC_WARN compile_warning(lex_ctx ctx, int warntype, const char *fmt, ...)
|
||||
{
|
||||
bool r;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
r = vcompile_warning(ctx, warntype, fmt, ap);
|
||||
va_end(ap);
|
||||
return r;
|
||||
}
|
226
conout.cpp
Normal file
226
conout.cpp
Normal file
|
@ -0,0 +1,226 @@
|
|||
#include <stdio.h>
|
||||
#include "gmqcc.h"
|
||||
|
||||
#define GMQCC_IS_STDOUT(X) ((X) == stdout)
|
||||
#define GMQCC_IS_STDERR(X) ((X) == stderr)
|
||||
#define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X))
|
||||
|
||||
struct con_t {
|
||||
FILE *handle_err;
|
||||
FILE *handle_out;
|
||||
int color_err;
|
||||
int color_out;
|
||||
};
|
||||
|
||||
static con_t console;
|
||||
|
||||
/*
|
||||
* Enables color on output if supported.
|
||||
* NOTE: The support for checking colors is nullptr. On windows this will
|
||||
* always work, on *nix it depends if the term has colors.
|
||||
*
|
||||
* NOTE: This prevents colored output to piped stdout/err via isatty
|
||||
* checks.
|
||||
*/
|
||||
static void con_enablecolor(void) {
|
||||
console.color_err = util_isatty(console.handle_err);
|
||||
console.color_out = util_isatty(console.handle_out);
|
||||
}
|
||||
|
||||
/*
|
||||
* Does a write to the handle with the format string and list of
|
||||
* arguments. This colorizes for windows as well via translate
|
||||
* step.
|
||||
*/
|
||||
static int con_write(FILE *handle, const char *fmt, va_list va) {
|
||||
return vfprintf(handle, fmt, va);
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* EXPOSED INTERFACE BEGINS
|
||||
*********************************************************************/
|
||||
|
||||
void con_close() {
|
||||
if (!GMQCC_IS_DEFINE(console.handle_err))
|
||||
fclose(console.handle_err);
|
||||
if (!GMQCC_IS_DEFINE(console.handle_out))
|
||||
fclose(console.handle_out);
|
||||
}
|
||||
|
||||
void con_color(int state) {
|
||||
if (state)
|
||||
con_enablecolor();
|
||||
else {
|
||||
console.color_err = 0;
|
||||
console.color_out = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void con_init() {
|
||||
console.handle_err = stderr;
|
||||
console.handle_out = stdout;
|
||||
con_enablecolor();
|
||||
}
|
||||
|
||||
void con_reset() {
|
||||
con_close();
|
||||
con_init();
|
||||
}
|
||||
|
||||
/*
|
||||
* Defaultizer because stdio.h shouldn't be used anywhere except here
|
||||
* and inside file.c To prevent mis-match of wrapper-interfaces.
|
||||
*/
|
||||
FILE *con_default_out() {
|
||||
return console.handle_out = stdout;
|
||||
}
|
||||
|
||||
FILE *con_default_err() {
|
||||
return console.handle_err = stderr;
|
||||
}
|
||||
|
||||
int con_verr(const char *fmt, va_list va) {
|
||||
return con_write(console.handle_err, fmt, va);
|
||||
}
|
||||
int con_vout(const char *fmt, va_list va) {
|
||||
return con_write(console.handle_out, fmt, va);
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard stdout/stderr printf functions used generally where they need
|
||||
* to be used.
|
||||
*/
|
||||
int con_err(const char *fmt, ...) {
|
||||
va_list va;
|
||||
int ln = 0;
|
||||
va_start(va, fmt);
|
||||
con_verr(fmt, va);
|
||||
va_end(va);
|
||||
return ln;
|
||||
}
|
||||
int con_out(const char *fmt, ...) {
|
||||
va_list va;
|
||||
int ln = 0;
|
||||
va_start(va, fmt);
|
||||
con_vout(fmt, va);
|
||||
va_end (va);
|
||||
return ln;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility console message writes for lexer contexts. These will allow
|
||||
* for reporting of file:line based on lexer context, These are used
|
||||
* heavily in the parser/ir/ast.
|
||||
*/
|
||||
static void con_vprintmsg_c(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap, const char *condname) {
|
||||
/* color selection table */
|
||||
static int sel[] = {
|
||||
CON_WHITE,
|
||||
CON_CYAN,
|
||||
CON_RED
|
||||
};
|
||||
|
||||
int err = !!(level == LVL_ERROR);
|
||||
int color = (err) ? console.color_err : console.color_out;
|
||||
int (*print) (const char *, ...) = (err) ? &con_err : &con_out;
|
||||
int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout;
|
||||
|
||||
if (color)
|
||||
print("\033[0;%dm%s:%d:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, (int)column, sel[level], msgtype);
|
||||
else
|
||||
print("%s:%d:%d: %s: ", name, (int)line, (int)column, msgtype);
|
||||
|
||||
vprint(msg, ap);
|
||||
if (condname)
|
||||
print(" [%s]\n", condname);
|
||||
else
|
||||
print("\n");
|
||||
}
|
||||
|
||||
void con_vprintmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap) {
|
||||
con_vprintmsg_c(level, name, line, column, msgtype, msg, ap, nullptr);
|
||||
}
|
||||
|
||||
void con_printmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...) {
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
con_vprintmsg(level, name, line, column, msgtype, msg, va);
|
||||
va_end (va);
|
||||
}
|
||||
|
||||
void con_cvprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
|
||||
con_vprintmsg(lvl, ctx.file, ctx.line, ctx.column, msgtype, msg, ap);
|
||||
}
|
||||
|
||||
void con_cprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, ...) {
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
con_cvprintmsg(ctx, lvl, msgtype, msg, va);
|
||||
va_end (va);
|
||||
}
|
||||
|
||||
/* General error interface: TODO seperate as part of the compiler front-end */
|
||||
size_t compile_errors = 0;
|
||||
size_t compile_warnings = 0;
|
||||
size_t compile_Werrors = 0;
|
||||
static lex_ctx_t first_werror;
|
||||
|
||||
void compile_show_werrors()
|
||||
{
|
||||
con_cprintmsg(first_werror, LVL_ERROR, "first warning", "was here");
|
||||
}
|
||||
|
||||
void vcompile_error(lex_ctx_t ctx, const char *msg, va_list ap)
|
||||
{
|
||||
++compile_errors;
|
||||
con_cvprintmsg(ctx, LVL_ERROR, "error", msg, ap);
|
||||
}
|
||||
|
||||
void compile_error_(lex_ctx_t ctx, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vcompile_error(ctx, msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
bool GMQCC_WARN vcompile_warning(lex_ctx_t ctx, int warntype, const char *fmt, va_list ap)
|
||||
{
|
||||
const char *msgtype = "warning";
|
||||
int lvl = LVL_WARNING;
|
||||
char warn_name[1024];
|
||||
|
||||
if (!OPTS_WARN(warntype))
|
||||
return false;
|
||||
|
||||
warn_name[0] = '-';
|
||||
warn_name[1] = 'W';
|
||||
(void)util_strtononcmd(opts_warn_list[warntype].name, warn_name+2, sizeof(warn_name)-2);
|
||||
|
||||
++compile_warnings;
|
||||
if (OPTS_WERROR(warntype)) {
|
||||
if (!compile_Werrors)
|
||||
first_werror = ctx;
|
||||
++compile_Werrors;
|
||||
msgtype = "Werror";
|
||||
if (OPTS_FLAG(BAIL_ON_WERROR)) {
|
||||
msgtype = "error";
|
||||
++compile_errors;
|
||||
}
|
||||
lvl = LVL_ERROR;
|
||||
}
|
||||
|
||||
con_vprintmsg_c(lvl, ctx.file, ctx.line, ctx.column, msgtype, fmt, ap, warn_name);
|
||||
|
||||
return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR);
|
||||
}
|
||||
|
||||
bool GMQCC_WARN compile_warning_(lex_ctx_t ctx, int warntype, const char *fmt, ...)
|
||||
{
|
||||
bool r;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
r = vcompile_warning(ctx, warntype, fmt, ap);
|
||||
va_end(ap);
|
||||
return r;
|
||||
}
|
547
correct.c
547
correct.c
|
@ -1,547 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Dale Weiler
|
||||
* Wolfgang Bumiller
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "gmqcc.h"
|
||||
|
||||
/*
|
||||
* This is a very clever method for correcting mistakes in QuakeC code
|
||||
* most notably when invalid identifiers are used or inproper assignments;
|
||||
* we can proprly lookup in multiple dictonaries (depening on the rules
|
||||
* of what the task is trying to acomplish) to find the best possible
|
||||
* match.
|
||||
*
|
||||
*
|
||||
* A little about how it works, and probability theory:
|
||||
*
|
||||
* When given an identifier (which we will denote I), we're essentially
|
||||
* just trying to choose the most likely correction for that identifier.
|
||||
* (the actual "correction" can very well be the identifier itself).
|
||||
* There is actually no way to know for sure that certian identifers
|
||||
* such as "lates", need to be corrected to "late" or "latest" or any
|
||||
* other permutations that look lexically the same. This is why we
|
||||
* must advocate the usage of probabilities. This means that instead of
|
||||
* just guessing, instead we're trying to find the correction for C,
|
||||
* out of all possible corrections that maximizes the probability of C
|
||||
* for the original identifer I.
|
||||
*
|
||||
* Thankfully there exists some theroies for probalistic interpretations
|
||||
* of data. Since we're operating on two distictive intepretations, the
|
||||
* transposition from I to C. We need something that can express how much
|
||||
* degree of I should rationally change to become C. this is called the
|
||||
* Bayesian interpretation. You can read more about it from here:
|
||||
* http://www.celiagreen.com/charlesmccreery/statistics/bayestutorial.pdf
|
||||
* (which is probably the only good online documentation for bayes theroy
|
||||
* no lie. Everything else just sucks ..)
|
||||
*
|
||||
* Bayes' Thereom suggests something like the following:
|
||||
* AC P(I|C) P(C) / P(I)
|
||||
*
|
||||
* However since P(I) is the same for every possibility of I, we can
|
||||
* completley ignore it giving just:
|
||||
* AC P(I|C) P(C)
|
||||
*
|
||||
* This greatly helps visualize how the parts of the expression are performed
|
||||
* there is essentially three, from right to left we perform the following:
|
||||
*
|
||||
* 1: P(C), the probability that a proposed correction C will stand on its
|
||||
* own. This is called the language model.
|
||||
*
|
||||
* 2: P(I|C), the probability that I would be used, when the programmer
|
||||
* really meant C. This is the error model.
|
||||
*
|
||||
* 3: AC, the control mechanisim, an enumerator if you will, one that
|
||||
* enumerates all feasible values of C, to determine the one that
|
||||
* gives the greatest probability score.
|
||||
*
|
||||
* In reality the requirement for a more complex expression involving
|
||||
* two seperate models is considerably a waste. But one must recognize
|
||||
* that P(C|I) is already conflating two factors. It's just much simpler
|
||||
* to seperate the two models and deal with them explicitaly. To properly
|
||||
* estimate P(C|I) you have to consider both the probability of C and
|
||||
* probability of the transposition from C to I. It's simply much more
|
||||
* cleaner, and direct to seperate the two factors.
|
||||
*
|
||||
* Research tells us that 80% to 95% of all spelling errors have an edit
|
||||
* distance no greater than one. Knowing this we can optimize for most
|
||||
* cases of mistakes without taking a performance hit. Which is what we
|
||||
* base longer edit distances off of. Opposed to the original method of
|
||||
* I had concieved of checking everything.
|
||||
*
|
||||
* A little information on additional algorithms used:
|
||||
*
|
||||
* Initially when I implemented this corrector, it was very slow.
|
||||
* Need I remind you this is essentially a brute force attack on strings,
|
||||
* and since every transformation requires dynamic memory allocations,
|
||||
* you can easily imagine where most of the runtime conflated. Yes
|
||||
* It went right to malloc. More than THREE MILLION malloc calls are
|
||||
* performed for an identifier about 16 bytes long. This was such a
|
||||
* shock to me. A forward allocator (or as some call it a bump-point
|
||||
* allocator, or just a memory pool) was implemented. To combat this.
|
||||
*
|
||||
* But of course even other factors were making it slow. Initially
|
||||
* this used a hashtable. And hashtables have a good constant lookup
|
||||
* time complexity. But the problem wasn't in the hashtable, it was
|
||||
* in the hashing (despite having one of the fastest hash functions
|
||||
* known). Remember those 3 million mallocs? Well for every malloc
|
||||
* there is also a hash. After 3 million hashes .. you start to get
|
||||
* very slow. To combat this I had suggested burst tries to Blub.
|
||||
* The next day he had implemented them. Sure enough this brought
|
||||
* down the runtime by a factor > 100%
|
||||
*
|
||||
* The trie initially was designed to work on all strings, but later it
|
||||
* became aparent that not only was this not a requirement. It was also
|
||||
* slowing down get/sets' for the trie. To fully understand, only
|
||||
* correct_alpha needs to be understood by the trie system, knowing this
|
||||
* We can combat the slowness using a very clever but evil optimization.
|
||||
* By Setting a fixed sized amount of branches for the trie using a
|
||||
* char-to-index map into the branches. We've complelty made the trie
|
||||
* accesses entierly constant in lookup time. No really, a lookup is
|
||||
* literally trie[str[0]] [str[1]] [2] .... .value.
|
||||
*
|
||||
*
|
||||
* Future Work (If we really need it)
|
||||
*
|
||||
* Currently we can only distinguish one source of error in the
|
||||
* language model we use. This could become an issue for identifiers
|
||||
* that have close colliding rates, e.g colate->coat yields collate.
|
||||
*
|
||||
* Currently the error model has been fairly trivial, the smaller the
|
||||
* edit distance the smaller the error. This usually causes some un-
|
||||
* expected problems. e.g reciet->recite yields recipt. For QuakeC
|
||||
* this could become a problem when lots of identifiers are involved.
|
||||
*/
|
||||
|
||||
|
||||
#define CORRECT_POOL_SIZE (128*1024*1024)
|
||||
/*
|
||||
* A forward allcator for the corrector. This corrector requires a lot
|
||||
* of allocations. This forward allocator combats all those allocations
|
||||
* and speeds us up a little. It also saves us space in a way since each
|
||||
* allocation isn't wasting a little header space for when NOTRACK isn't
|
||||
* defined.
|
||||
*/
|
||||
static unsigned char **correct_pool_data = NULL;
|
||||
static unsigned char *correct_pool_this = NULL;
|
||||
static size_t correct_pool_addr = 0;
|
||||
|
||||
static GMQCC_INLINE void correct_pool_new(void) {
|
||||
correct_pool_addr = 0;
|
||||
correct_pool_this = (unsigned char *)mem_a(CORRECT_POOL_SIZE);
|
||||
|
||||
vec_push(correct_pool_data, correct_pool_this);
|
||||
}
|
||||
|
||||
static GMQCC_INLINE void *correct_pool_alloc(size_t bytes) {
|
||||
void *data;
|
||||
if (correct_pool_addr + bytes>= CORRECT_POOL_SIZE)
|
||||
correct_pool_new();
|
||||
|
||||
data = (void*)correct_pool_this;
|
||||
correct_pool_this += bytes;
|
||||
correct_pool_addr += bytes;
|
||||
return data;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE void correct_pool_delete(void) {
|
||||
size_t i;
|
||||
for (i = 0; i < vec_size(correct_pool_data); ++i)
|
||||
mem_d(correct_pool_data[i]);
|
||||
|
||||
correct_pool_data = NULL;
|
||||
correct_pool_this = NULL;
|
||||
correct_pool_addr = 0;
|
||||
}
|
||||
|
||||
|
||||
static GMQCC_INLINE char *correct_pool_claim(const char *data) {
|
||||
char *claim = util_strdup(data);
|
||||
return claim;
|
||||
}
|
||||
|
||||
/*
|
||||
* _ is valid in identifiers. I've yet to implement numerics however
|
||||
* because they're only valid after the first character is of a _, or
|
||||
* alpha character.
|
||||
*/
|
||||
static const char correct_alpha[] = "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"_"; /* TODO: Numbers ... */
|
||||
|
||||
static const size_t correct_alpha_index[0x80] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 52,
|
||||
0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/*
|
||||
* A fast space efficent trie for a dictionary of identifiers. This is
|
||||
* faster than a hashtable for one reason. A hashtable itself may have
|
||||
* fast constant lookup time, but the hash itself must be very fast. We
|
||||
* have one of the fastest hash functions for strings, but if you do a
|
||||
* lost of hashing (which we do, almost 3 million hashes per identifier)
|
||||
* a hashtable becomes slow.
|
||||
*/
|
||||
correct_trie_t* correct_trie_new() {
|
||||
correct_trie_t *t = (correct_trie_t*)mem_a(sizeof(correct_trie_t));
|
||||
t->value = NULL;
|
||||
t->entries = NULL;
|
||||
return t;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE void correct_trie_del_sub(correct_trie_t *t) {
|
||||
size_t i;
|
||||
if (!t->entries)
|
||||
return;
|
||||
for (i = 0; i < sizeof(correct_alpha)-1; ++i) {
|
||||
correct_trie_del_sub(&t->entries[i]);
|
||||
}
|
||||
mem_d(t->entries);
|
||||
}
|
||||
|
||||
static GMQCC_INLINE void correct_trie_del(correct_trie_t *t) {
|
||||
size_t i;
|
||||
if (t->entries) {
|
||||
for (i = 0; i < sizeof(correct_alpha)-1; ++i)
|
||||
correct_trie_del_sub(&t->entries[i]);
|
||||
mem_d(t->entries);
|
||||
}
|
||||
mem_d(t);
|
||||
}
|
||||
|
||||
static GMQCC_INLINE void* correct_trie_get(const correct_trie_t *t, const char *key) {
|
||||
const unsigned char *data = (const unsigned char*)key;
|
||||
|
||||
while (*data) {
|
||||
if (!t->entries)
|
||||
return NULL;
|
||||
t = t->entries + correct_alpha_index[*data];
|
||||
++data;
|
||||
}
|
||||
return t->value;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE void correct_trie_set(correct_trie_t *t, const char *key, void * const value) {
|
||||
const unsigned char *data = (const unsigned char*)key;
|
||||
while (*data) {
|
||||
if (!t->entries) {
|
||||
t->entries = (correct_trie_t*)mem_a(sizeof(correct_trie_t)*(sizeof(correct_alpha)-1));
|
||||
memset(t->entries, 0, sizeof(correct_trie_t)*(sizeof(correct_alpha)-1));
|
||||
}
|
||||
t = t->entries + correct_alpha_index[*data];
|
||||
++data;
|
||||
}
|
||||
t->value = value;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Implementation of the corrector algorithm commences. A very efficent
|
||||
* brute-force attack (thanks to tries and mempool :-)).
|
||||
*/
|
||||
static GMQCC_INLINE size_t *correct_find(correct_trie_t *table, const char *word) {
|
||||
return (size_t*)correct_trie_get(table, word);
|
||||
}
|
||||
|
||||
static GMQCC_INLINE bool correct_update(correct_trie_t* *table, const char *word) {
|
||||
size_t *data = correct_find(*table, word);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
(*data)++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void correct_add(correct_trie_t* table, size_t ***size, const char *ident) {
|
||||
size_t *data = NULL;
|
||||
const char *add = ident;
|
||||
|
||||
if (!correct_update(&table, add)) {
|
||||
data = (size_t*)mem_a(sizeof(size_t));
|
||||
*data = 1;
|
||||
|
||||
vec_push((*size), data);
|
||||
correct_trie_set(table, add, data);
|
||||
}
|
||||
}
|
||||
|
||||
void correct_del(correct_trie_t* dictonary, size_t **data) {
|
||||
size_t i;
|
||||
const size_t vs = vec_size(data);
|
||||
|
||||
for (i = 0; i < vs; i++)
|
||||
mem_d(data[i]);
|
||||
|
||||
vec_free(data);
|
||||
correct_trie_del(dictonary);
|
||||
}
|
||||
|
||||
/*
|
||||
* correcting logic for the following forms of transformations:
|
||||
* 1) deletion
|
||||
* 2) transposition
|
||||
* 3) alteration
|
||||
* 4) insertion
|
||||
*
|
||||
* These functions could take an additional size_t **size paramater
|
||||
* and store back the results of their new length in an array that
|
||||
* is the same as **array for the memcmp in correct_exists. I'm just
|
||||
* not able to figure out how to do that just yet. As my brain is
|
||||
* not in the mood to figure out that logic. This is a reminder to
|
||||
* do it, or for someone else to :-) correct_edit however would also
|
||||
* need to take a size_t ** to carry it along (would all the argument
|
||||
* overhead be worth it?)
|
||||
*/
|
||||
static GMQCC_INLINE size_t correct_deletion(const char *ident, char **array) {
|
||||
size_t itr = 0;
|
||||
const size_t len = strlen(ident);
|
||||
|
||||
for (; itr < len; itr++) {
|
||||
char *a = (char*)correct_pool_alloc(len+1);
|
||||
memcpy(a, ident, itr);
|
||||
memcpy(a + itr, ident + itr + 1, len - itr);
|
||||
array[itr] = a;
|
||||
}
|
||||
|
||||
return itr;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE size_t correct_transposition(const char *ident, char **array) {
|
||||
size_t itr = 0;
|
||||
const size_t len = strlen(ident);
|
||||
|
||||
for (; itr < len - 1; itr++) {
|
||||
char tmp;
|
||||
char *a = (char*)correct_pool_alloc(len+1);
|
||||
memcpy(a, ident, len+1);
|
||||
tmp = a[itr];
|
||||
a[itr ] = a[itr+1];
|
||||
a[itr+1] = tmp;
|
||||
array[itr] = a;
|
||||
}
|
||||
|
||||
return itr;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE size_t correct_alteration(const char *ident, char **array) {
|
||||
size_t itr = 0;
|
||||
size_t jtr = 0;
|
||||
size_t ktr = 0;
|
||||
const size_t len = strlen(ident);
|
||||
|
||||
for (; itr < len; itr++) {
|
||||
for (jtr = 0; jtr < sizeof(correct_alpha)-1; jtr++, ktr++) {
|
||||
char *a = (char*)correct_pool_alloc(len+1);
|
||||
memcpy(a, ident, len+1);
|
||||
a[itr] = correct_alpha[jtr];
|
||||
array[ktr] = a;
|
||||
}
|
||||
}
|
||||
|
||||
return ktr;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE size_t correct_insertion(const char *ident, char **array) {
|
||||
size_t itr = 0;
|
||||
size_t jtr = 0;
|
||||
const size_t len = strlen(ident);
|
||||
|
||||
for (; itr <= len; itr++) {
|
||||
for (jtr = 0; jtr < sizeof(correct_alpha)-1; jtr++) {
|
||||
char *a = (char*)correct_pool_alloc(len+2);
|
||||
memcpy(a, ident, itr);
|
||||
memcpy(a + itr + 1, ident + itr, len - itr + 1);
|
||||
a[itr] = correct_alpha[jtr];
|
||||
array[itr * (sizeof(correct_alpha)-1) + jtr] = a;
|
||||
}
|
||||
}
|
||||
|
||||
return (len+1)*(sizeof(correct_alpha)-1);
|
||||
}
|
||||
|
||||
static GMQCC_INLINE size_t correct_size(const char *ident) {
|
||||
/*
|
||||
* deletion = len
|
||||
* transposition = len - 1
|
||||
* alteration = len * sizeof(correct_alpha)
|
||||
* insertion = (len + 1) * sizeof(correct_alpha)
|
||||
*/
|
||||
|
||||
register size_t len = strlen(ident);
|
||||
return (len) + (len - 1) + (len * (sizeof(correct_alpha)-1)) + ((len + 1) * (sizeof(correct_alpha)-1));
|
||||
}
|
||||
|
||||
static GMQCC_INLINE char **correct_edit(const char *ident, size_t **lens) {
|
||||
size_t next;
|
||||
size_t size = correct_size(ident);
|
||||
char **find = (char**)correct_pool_alloc(size * sizeof(char*));
|
||||
|
||||
if (!find || !(*lens = (size_t*)correct_pool_alloc(size * sizeof(size_t))))
|
||||
return NULL;
|
||||
|
||||
next = correct_deletion (ident, find);
|
||||
next += correct_transposition(ident, find+next);
|
||||
next += correct_alteration (ident, find+next);
|
||||
/*****/ correct_insertion (ident, find+next);
|
||||
|
||||
/* precompute lengths */
|
||||
for (next = 0; next < size; next++)
|
||||
(*lens)[next] = strlen(find[next]);
|
||||
|
||||
return find;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE int correct_exist(char **array, register size_t *sizes, size_t rows, char *ident, register size_t len) {
|
||||
size_t itr;
|
||||
for (itr = 0; itr < rows; itr++) {
|
||||
/*
|
||||
* We can save tons of calls to memcmp if we simply ignore comparisions
|
||||
* that we know cannot contain the same length.
|
||||
*/
|
||||
if (sizes[itr] == len && !memcmp(array[itr], ident, len))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE char **correct_known_resize(char **res, size_t *allocated, size_t size) {
|
||||
size_t oldallocated = *allocated;
|
||||
char **out;
|
||||
if (size < oldallocated)
|
||||
return res;
|
||||
|
||||
out = (char**)correct_pool_alloc(sizeof(*res) * oldallocated + 32);
|
||||
memcpy(out, res, sizeof(*res) * oldallocated);
|
||||
|
||||
*allocated += 32;
|
||||
return out;
|
||||
}
|
||||
|
||||
static char **correct_known(correction_t *corr, correct_trie_t* table, char **array, size_t rows, size_t *next) {
|
||||
size_t itr = 0;
|
||||
size_t jtr = 0;
|
||||
size_t len = 0;
|
||||
size_t row = 0;
|
||||
size_t nxt = 8;
|
||||
char **res = (char**)correct_pool_alloc(sizeof(char *) * nxt);
|
||||
char **end = NULL;
|
||||
size_t *bit = NULL;
|
||||
|
||||
for (; itr < rows; itr++) {
|
||||
if (!array[itr][0])
|
||||
continue;
|
||||
if (vec_size(corr->edits) > itr+1) {
|
||||
end = corr->edits[itr+1];
|
||||
bit = corr->lens [itr+1];
|
||||
} else {
|
||||
end = correct_edit(array[itr], &bit);
|
||||
vec_push(corr->edits, end);
|
||||
vec_push(corr->lens, bit);
|
||||
}
|
||||
row = correct_size(array[itr]);
|
||||
|
||||
for (jtr = 0; jtr < row; jtr++) {
|
||||
if (correct_find(table, end[jtr]) && !correct_exist(res, bit, len, end[jtr], bit[jtr])) {
|
||||
res = correct_known_resize(res, &nxt, len+1);
|
||||
res[len++] = end[jtr];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*next = len;
|
||||
return res;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE char *correct_maximum(correct_trie_t* table, char **array, size_t rows) {
|
||||
char *str = NULL;
|
||||
size_t *itm = NULL;
|
||||
size_t itr = 0;
|
||||
size_t top = 0;
|
||||
|
||||
for (; itr < rows; itr++) {
|
||||
if ((itm = correct_find(table, array[itr])) && (*itm > top)) {
|
||||
top = *itm;
|
||||
str = array[itr];
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the exposed interface:
|
||||
* takes a table for the dictonary a vector of sizes (used for internal
|
||||
* probability calculation), and an identifier to "correct".
|
||||
*/
|
||||
void correct_init(correction_t *c)
|
||||
{
|
||||
correct_pool_new();
|
||||
c->edits = NULL;
|
||||
c->lens = NULL;
|
||||
}
|
||||
|
||||
void correct_free(correction_t *c)
|
||||
{
|
||||
vec_free(c->edits);
|
||||
vec_free(c->lens);
|
||||
correct_pool_delete();
|
||||
}
|
||||
|
||||
char *correct_str(correction_t *corr, correct_trie_t* table, const char *ident) {
|
||||
char **e1 = NULL;
|
||||
char **e2 = NULL;
|
||||
char *e1ident = NULL;
|
||||
char *e2ident = NULL;
|
||||
size_t e1rows = 0;
|
||||
size_t e2rows = 0;
|
||||
size_t *bits = NULL;
|
||||
|
||||
/* needs to be allocated for free later */
|
||||
if (correct_find(table, ident))
|
||||
return correct_pool_claim(ident);
|
||||
|
||||
if ((e1rows = correct_size(ident))) {
|
||||
if (vec_size(corr->edits) > 0)
|
||||
e1 = corr->edits[0];
|
||||
else {
|
||||
e1 = correct_edit(ident, &bits);
|
||||
vec_push(corr->edits, e1);
|
||||
vec_push(corr->lens, bits);
|
||||
}
|
||||
|
||||
if ((e1ident = correct_maximum(table, e1, e1rows)))
|
||||
return correct_pool_claim(e1ident);
|
||||
}
|
||||
|
||||
e2 = correct_known(corr, table, e1, e1rows, &e2rows);
|
||||
if (e2rows && ((e2ident = correct_maximum(table, e2, e2rows))))
|
||||
return correct_pool_claim(e2ident);
|
||||
|
||||
|
||||
return util_strdup(ident);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
DROPBOX := dropbox_uploader.sh
|
||||
UNAME := $(shell uname -m)
|
||||
DOWNLOAD:= ../doc/html/download.c
|
||||
BRANCH := $(shell git branch | sed -n -e 's/^\* \(.*\)/\1/p')
|
||||
ifneq ($(shell uname -m), x86_64)
|
||||
$(error Cannot build packages without an x86_64 capable CPU)
|
||||
endif
|
||||
|
||||
.NOTPARALLEL: base
|
||||
.NOTPARALLEL: upload
|
||||
|
||||
base:
|
||||
$(MAKE) -C deb/
|
||||
$(MAKE) -C deb/ CARCH=i686
|
||||
$(MAKE) -C archlinux/this/
|
||||
$(MAKE) -C archlinux/this/ CARCH=i686
|
||||
$(MAKE) -C win32/
|
||||
@mv deb/*.deb ./
|
||||
@mv archlinux/this/*pkg.tar.xz ./
|
||||
@mv win32/*.zip ./
|
||||
|
||||
upload:
|
||||
@echo "APPKEY:76vh3q42hnvmzm3" > dropbox_config
|
||||
@echo "APPSECRET:tmeecht2cmh72xa" >> dropbox_config
|
||||
@echo "ACCESS_LEVEL:sandbox" >> dropbox_config
|
||||
@echo "OAUTH_ACCESS_TOKEN:w0bxzf0dft8edfq" >> dropbox_config
|
||||
@echo "OAUTH_ACCESS_TOKEN_SECRET:9vosx7x8gy4kgjk" >> dropbox_config
|
||||
@wget -q "http://raw.github.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh"
|
||||
@chmod +x dropbox_uploader.sh
|
||||
@sed -i -e "s/~\/.dropbox_uploader/.\/dropbox_config/g" $$(basename $(DROPBOX))
|
||||
@find . -type f -regex ".*/.*\.\(xz\|deb\|zip\)" -exec ./$$(basename $(DROPBOX)) upload {} \;
|
||||
@rm dropbox_config dropbox_uploader.sh
|
||||
|
||||
website:
|
||||
$(CC) $(DOWNLOAD) -o html.gen
|
||||
@./html.gen ../
|
||||
@rm html.gen
|
||||
@git stash
|
||||
@git checkout gh-pages
|
||||
@rm -f ../download.html
|
||||
@mv -f download.html ../download.html
|
||||
@cd ..; git add download.html; git commit -m 'update download page'; git push origin gh-pages;
|
||||
@git checkout $(BRANCH)
|
||||
@git stash apply
|
||||
|
||||
clean:
|
||||
@rm -f *.deb
|
||||
@rm -f *.pkg.tar.xz
|
||||
@rm -f *.zip
|
||||
@rm -f *.gen
|
||||
@rm -f *.html
|
||||
|
||||
all: base upload
|
|
@ -1,55 +0,0 @@
|
|||
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
|
||||
# Contributor: Wolfgang Bumiller <blub@speed.at>
|
||||
|
||||
pkgname=gmqcc-git
|
||||
pkgver=20130127
|
||||
pkgrel=1
|
||||
pkgdesc="An Improved Quake C Compiler"
|
||||
arch=('i686' 'x86_64')
|
||||
depends=()
|
||||
conflicts=('gmqcc')
|
||||
provides=('gmqcc=0.2.4')
|
||||
makedepends=('git')
|
||||
url="https://github.com/graphitemaster/gmqcc.git"
|
||||
license=('MIT')
|
||||
|
||||
_gitroot="git://github.com/graphitemaster/gmqcc.git"
|
||||
_gitname="gmqcc"
|
||||
|
||||
build() {
|
||||
cd $srcdir
|
||||
msg "Connecting to the GIT server..."
|
||||
if [[ -d $srcdir/$_gitname ]] ; then
|
||||
cd $_gitname
|
||||
msg "Removing build files..."
|
||||
git clean -dfx
|
||||
msg "Updating..."
|
||||
git pull --no-tags
|
||||
msg "The local files are updated."
|
||||
else
|
||||
msg "Cloning..."
|
||||
git clone $_gitroot $_gitname --depth 1
|
||||
msg "Clone done."
|
||||
fi
|
||||
|
||||
msg "Starting compilation..."
|
||||
cd "$srcdir"/"$_gitname"
|
||||
|
||||
msg "Compiling..."
|
||||
gmake
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "$srcdir"/"$_gitname"
|
||||
gmake check
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir"/"$_gitname"
|
||||
msg "Compiling and installing to pkgdir this time..."
|
||||
gmake install DESTDIR=$pkgdir PREFIX=/usr
|
||||
msg "Compiling done."
|
||||
|
||||
install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
|
||||
install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
|
||||
# Contributor: Wolfgang Bumiller <blub@speed.at>
|
||||
|
||||
pkgname=gmqcc
|
||||
pkgver=0.2.2
|
||||
pkgrel=1
|
||||
pkgdesc="An Improved Quake C Compiler"
|
||||
arch=('i686' 'x86_64')
|
||||
depends=()
|
||||
url="https://github.com/graphitemaster/gmqcc.git"
|
||||
license=('MIT')
|
||||
source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
|
||||
sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353')
|
||||
|
||||
_gitname=graphitemaster-gmqcc-de24486/
|
||||
|
||||
build() {
|
||||
msg "Starting compilation..."
|
||||
cd "$srcdir"/"$_gitname"
|
||||
|
||||
msg "Compiling..."
|
||||
gmake
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "$srcdir"/"$_gitname"
|
||||
gmake check
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir"/"$_gitname"
|
||||
msg "Compiling and installing to pkgdir this time..."
|
||||
gmake install DESTDIR=$pkgdir PREFIX=/usr
|
||||
msg "Compiling done."
|
||||
|
||||
install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
|
||||
install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
|
||||
|
||||
pkgname=gmqcc-git
|
||||
pkgver=0.2.612.g160e7cf
|
||||
pkgver(){
|
||||
cd gmqcc
|
||||
git describe --tags | sed -e 's/^gmqcc\-//' -e 's/-/./g'
|
||||
}
|
||||
pkgrel=1
|
||||
pkgdesc="An Improved Quake C Compiler"
|
||||
arch=('i686' 'x86_64')
|
||||
depends=('glibc')
|
||||
conflicts=('gmqcc')
|
||||
provides=('gmqcc=0.2.4')
|
||||
makedepends=('git')
|
||||
url="https://github.com/graphitemaster/gmqcc.git"
|
||||
license=('MIT')
|
||||
source=('gmqcc::git://github.com/graphitemaster/gmqcc.git')
|
||||
sha1sums=('SKIP')
|
||||
|
||||
|
||||
build() {
|
||||
msg "Starting compilation..."
|
||||
cd "$srcdir"/"gmqcc"
|
||||
|
||||
msg "Compiling..."
|
||||
make
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "$srcdir"/"gmqcc"
|
||||
make check
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir"/"gmqcc"
|
||||
msg "Compiling and installing to pkgdir this time..."
|
||||
make install DESTDIR=$pkgdir PREFIX=/usr
|
||||
msg "Compiling done."
|
||||
|
||||
install -Dm644 syntax/geany/filetypes.qc ${pkgdir}/usr/share/geany/filetypes.qc
|
||||
install -Dm644 syntax/gtksourceview/qc.lang ${pkgdir}/usr/share/gtksourceview-3.0/language-specs/qc.lang
|
||||
# jedit
|
||||
install -Dm644 syntax/kate/qc.xml ${pkgdir}/usr/share/apps/katepart/syntax/qc.xml
|
||||
install -Dm644 syntax/nano/qc.nanorc ${pkgdir}/usr/share/nano/qc.nanorc
|
||||
|
||||
install -Dm644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
|
||||
|
||||
pkgname=gmqcc
|
||||
pkgver=0.2.2
|
||||
pkgrel=1
|
||||
pkgdesc="An Improved Quake C Compiler"
|
||||
arch=('i686' 'x86_64')
|
||||
depends=('glibc')
|
||||
url="https://github.com/graphitemaster/gmqcc.git"
|
||||
license=('MIT')
|
||||
source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
|
||||
sha1sums=('e0fe99af9a55d36cd9e0909a96d1b14f2db8b757')
|
||||
|
||||
_gitname=graphitemaster-gmqcc-de24486/
|
||||
|
||||
build() {
|
||||
msg "Starting compilation..."
|
||||
cd "$srcdir"/"$_gitname"
|
||||
|
||||
msg "Compiling..."
|
||||
make
|
||||
}
|
||||
|
||||
check() {
|
||||
cd "$srcdir"/"$_gitname"
|
||||
make check
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$srcdir"/"$_gitname"
|
||||
msg "Compiling and installing to pkgdir this time..."
|
||||
make install DESTDIR=$pkgdir PREFIX=/usr
|
||||
msg "Compiling done."
|
||||
|
||||
install -D LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
BASEDIR := ../../../
|
||||
PREFIX := /usr
|
||||
HEADER := $(BASEDIR)/gmqcc.h
|
||||
MAJOR := $(shell sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER))
|
||||
MINOR := $(shell sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER))
|
||||
PATCH := $(shell sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER))
|
||||
PKGREL := 1
|
||||
CARCH := $(shell uname -m)
|
||||
PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH)
|
||||
PKG := $(PKGDIR).pkg.tar.xz
|
||||
PKGINFO := $(PKGDIR)/.PKGINFO
|
||||
DESTDIR := distro/archlinux/this/$(PKGDIR)
|
||||
CFLAGS :=
|
||||
|
||||
|
||||
ifneq (, $(findstring i686, $(CARCH)))
|
||||
CFLAGS += -m32
|
||||
LDFLAGS += -m32
|
||||
endif
|
||||
|
||||
base:
|
||||
$(MAKE) -C $(BASEDIR) clean
|
||||
CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
|
||||
$(MAKE) -C $(BASEDIR) "DESTDIR=$(DESTDIR)" "PREFIX=$(PREFIX)" install
|
||||
@echo "pkgname = gmqcc" > $(PKGINFO)
|
||||
@echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO)
|
||||
@echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO)
|
||||
@echo "url = https://github.com/graphitemaster/gmqcc.git" >> $(PKGINFO)
|
||||
@echo "builddate = `date -u \"+%s\"`" >> $(PKGINFO)
|
||||
@echo "packager = Unknown Packager" >> $(PKGINFO)
|
||||
@echo "size = `du -sk $(PKGDIR) | awk '{print $$1 * 1024}'`" >> $(PKGINFO)
|
||||
@echo "arch = $(CARCH)" >> $(PKGINFO)
|
||||
@echo "license = MIT" >> $(PKGINFO)
|
||||
@echo "conflict = gmqcc" >> $(PKGINFO)
|
||||
@echo "depend = glibc" >> $(PKGINFO)
|
||||
@echo "makepkgopt = strip" >> $(PKGINFO)
|
||||
@echo "makepkgopt = docs" >> $(PKGINFO)
|
||||
@echo "makepkgopt = libtool" >> $(PKGINFO)
|
||||
@echo "makepkgopt = emptydirs" >> $(PKGINFO)
|
||||
@echo "makepkgopt = zipman" >> $(PKGINFO)
|
||||
@echo "makepkgopt = purge" >> $(PKGINFO)
|
||||
@echo "makepkgopt = !upx" >> $(PKGINFO)
|
||||
@tar -cJvf $(PKG) -C $(PKGDIR)/ .PKGINFO usr/
|
||||
@rm -rf $(PKGDIR)
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(BASEDIR) clean
|
||||
@rm -f *.pkg.tar.xz
|
||||
|
||||
all: base
|
|
@ -1,43 +0,0 @@
|
|||
BASEDIR := ../..
|
||||
PREFIX := /usr
|
||||
HEADER := $(BASEDIR)/gmqcc.h
|
||||
MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)`
|
||||
MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)`
|
||||
PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)`
|
||||
DEBDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)
|
||||
CARCH := $(shell uname -m)
|
||||
DEB := $(DEBDIR)-$(CARCH).deb
|
||||
CONTROL := $(DEBDIR)/DEBIAN/control
|
||||
|
||||
ifneq (, $(findstring i686, $(CARCH)))
|
||||
CFLAGS := -m32
|
||||
endif
|
||||
|
||||
base:
|
||||
$(MAKE) -C $(BASEDIR) clean
|
||||
$(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
|
||||
@install -d -m755 $(DEBDIR)/DEBIAN
|
||||
@echo "Package: gmqcc" > $(CONTROL)
|
||||
@echo "Version: $(MAJOR).$(MINOR).$(PATCH)" >> $(CONTROL)
|
||||
@echo "Section: user/hidden" >> $(CONTROL)
|
||||
@echo "Priority: optional" >> $(CONTROL)
|
||||
@echo "Architecture: $(CARCH)" >> $(CONTROL)
|
||||
@echo "Installed-Size: `du -ks $($(DEBDIR)/usr) | cut -f 1`" >> $(CONTROL)
|
||||
@echo "Maintainer: Dale Weiler <killfieldengine@gmail.com>" >> $(CONTROL)
|
||||
@echo "Description: An improved Quake C Compiler" >> $(CONTROL)
|
||||
@echo " For an enduring period of time the options for a decent compiler for the Quake C programming language" >> $(CONTROL)
|
||||
@echo " were confined to a specific compiler known as QCC. Attempts were made to extend and improve upon the" >> $(CONTROL)
|
||||
@echo " design of QCC, but many foreseen the consequences of building on a broken foundation. The solution" >> $(CONTROL)
|
||||
@echo " was obvious, a new compiler; one born from the NIH realm of sarcastic wit. We welcome you. You won't" >> $(CONTROL)
|
||||
@echo " find a better Quake C compiler." >> $(CONTROL)
|
||||
@tar czf data.tar.gz -C $(DEBDIR)/ . --exclude=DEBIAN
|
||||
@tar czf control.tar.gz -C $(DEBDIR)/DEBIAN/ .
|
||||
@echo 2.0 > debian-binary
|
||||
@ar r $(DEB) debian-binary control.tar.gz data.tar.gz
|
||||
@rm -rf debian-binary control.tar.gz data.tar.gz $(DEBDIR)
|
||||
|
||||
clean:
|
||||
$(MAKE) -C $(BASEDIR) clean
|
||||
@rm -f *.deb
|
||||
|
||||
all: base
|
|
@ -1,17 +0,0 @@
|
|||
BASEDIR := ../..
|
||||
HEADER := $(BASEDIR)/gmqcc.h
|
||||
MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)`
|
||||
MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)`
|
||||
PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)`
|
||||
BINDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)
|
||||
|
||||
base:
|
||||
$(MAKE) CC=i486-mingw32-gcc UNAME=MINGW32 -C $(BASEDIR) clean
|
||||
$(MAKE) CC=i486-mingw32-gcc UNAME=MINGW32 -C $(BASEDIR) DESTDIR=distro/win32/$(BINDIR) PREFIX=/ install
|
||||
@zip -r $(BINDIR)-win32.zip $(BINDIR)
|
||||
@rm -rf $(BINDIR)
|
||||
clean:
|
||||
$(MAKE) -C $(BASEDIR) clean
|
||||
@rm -f *.zip
|
||||
|
||||
all: base
|
141
doc/gmqcc.1
141
doc/gmqcc.1
|
@ -153,7 +153,7 @@ Adds compiler information to the generated binary file. Currently
|
|||
this includes the following globals:
|
||||
.Bl -tag -width indent -compact
|
||||
.It Li reserved:version
|
||||
String containing the compiler version as printed by the --version
|
||||
String containing the compiler version as printed by the \-\-version
|
||||
parameter.
|
||||
.El
|
||||
.It Fl -correct , Fl -no-correct
|
||||
|
@ -168,6 +168,11 @@ DEBUG OPTION. Print the code's intermediate representation after the
|
|||
optimization and finalization passes to stdout before generating the
|
||||
binary. The instructions will be enumerated, and values will contain a
|
||||
list of liferanges.
|
||||
.It Fl force-crc= Ns Ar CRC
|
||||
Force the produced progs file to use the specified CRC.
|
||||
.It Fl state-fps= Ns Ar NUM
|
||||
Activate \-femulate-state and set the emulated FPS to
|
||||
.Ar NUM Ns .
|
||||
.El
|
||||
.Sh COMPILE WARNINGS
|
||||
.Bl -tag -width Ds
|
||||
|
@ -181,6 +186,9 @@ variables can be opened using
|
|||
.Ql #pragma noref 1
|
||||
and closed via
|
||||
.Ql #pragma noref 0 Ns .
|
||||
.It Fl W Ns Cm unused-component
|
||||
Generate a warning about vector variables which are declared but not all their
|
||||
components are used.
|
||||
.It Fl W Ns Cm used-uninitialized
|
||||
Generate a warning if it is possible that a variable can be used
|
||||
without prior initialization. Note that this warning is not
|
||||
|
@ -320,6 +328,34 @@ marked as such.
|
|||
Warn about possible mistakes caused by missing or wrong parenthesis,
|
||||
like an assignment in an 'if' condition when there's no additional set
|
||||
of parens around the assignment.
|
||||
.It Fl W Ns Cm unsafe-types
|
||||
When passing variadic parameters via
|
||||
.Li ...(N)
|
||||
it can happen that incompatible types are passed to functions. This
|
||||
enables several warnings when static typechecking cannot guarantee
|
||||
consistent behavior.
|
||||
.It Fl W Ns Cm breakdef
|
||||
When compiling original id1 QC there is a definition for `break`
|
||||
which conflicts with the 'break' keyword in GMQCC. Enabling this
|
||||
will print a warning when the definition occurs. The definition is
|
||||
ignored for both cases.
|
||||
.It Fl W Ns Cm const-overwrite
|
||||
When compiling original QuakeWorld QC there are instances where
|
||||
code overwrites constants. This is considered an error, however
|
||||
for QuakeWorld to compile it needs to be treated as a warning
|
||||
instead, as such this warning only works when \-std=qcc.
|
||||
.It Fl W Ns Cm directive-inmacro
|
||||
Warn about the use of preprocessor directives inside macros.
|
||||
.It Fl W Ns Cm builtins
|
||||
When using a function that is not explicitly defined, the compiler
|
||||
will search its intrinsics table for something that matches that
|
||||
function name by appending "__builtin_" to it. This behaviour may
|
||||
be unexpected, so enabling this will produce a diagnostic when
|
||||
such a function is resolved to a builtin.
|
||||
.It Fl W Ns Cm inexact-compares
|
||||
When comparing an inexact value such as `1.0/3.0' the result is
|
||||
pathologically wrong. Enabling this will trigger a compiler warning
|
||||
on such expressions.
|
||||
.El
|
||||
.Sh COMPILE FLAGS
|
||||
.Bl -tag -width Ds
|
||||
|
@ -340,7 +376,7 @@ features used in the Xonotic codebase. If you need more, write a
|
|||
ticket.
|
||||
.It Fl f Ns Cm ftepp-predefs
|
||||
Enable some predefined macros. This only works in combination with
|
||||
\'-fftepp' and is currently not included by '-std=fteqcc'. The
|
||||
\'\-fftepp' and is currently not included by '\-std=fteqcc'. The
|
||||
following macros will be added:
|
||||
.Bd -literal -offset indent
|
||||
__LINE__
|
||||
|
@ -373,6 +409,45 @@ only the first component will be 0, while the other two will become
|
|||
the first to of the global return value. This behavior is odd and
|
||||
relying on it should be discouraged, and thus is not supported by
|
||||
gmqcc.
|
||||
.It Fl f Ns Cm ftepp-mathdefs
|
||||
Enable math constant definitions. This only works in combination
|
||||
with \'\-fftepp' and is currently not included by '\-std=fteqcc'.
|
||||
The following macros will be added:
|
||||
.Bd -literal -offset indent
|
||||
M_E
|
||||
M_LOG2E
|
||||
M_LOG10E
|
||||
M_LN2
|
||||
M_LN10
|
||||
M_PI
|
||||
M_PI_2
|
||||
M_PI_4
|
||||
M_1_PI
|
||||
M_2_PI
|
||||
M_2_SQRTPI
|
||||
M_SQRT2
|
||||
M_SQRT1_2
|
||||
M_TAU
|
||||
.Ed
|
||||
.It Fl f Ns Cm ftepp-indirect-expansion
|
||||
Enable indirect macro expansion. This only works in combination
|
||||
with '-fftepp' and is currently not included by '-std=fteqcc'.
|
||||
Enabling this behavior will allow the preprocessor to operate more
|
||||
like the standard C preprocessor in that it will allow arguments
|
||||
of macros which are macro-expanded to be substituted into the
|
||||
definition of the macro.
|
||||
.Pp
|
||||
As an example:
|
||||
.Bd -literal -offset indent
|
||||
#define STR1(x) #x
|
||||
#define STR2(x) STR1(x)
|
||||
#define THE_ANSWER 42
|
||||
#define THE_ANSWER_STR STR2(THE_ANSWER) /* "42" */
|
||||
|
||||
.Ed
|
||||
With this enabled, an expansion of THE_ANSWER_STR will yield
|
||||
the string "42". With this disabled an expansion of THE_ANSWER_STR
|
||||
will yield "THE_ANSWER"
|
||||
.It Fl f Ns Cm relaxed-switch
|
||||
Allow switch cases to use non constant variables.
|
||||
.It Fl f Ns Cm short-logic
|
||||
|
@ -506,6 +581,60 @@ Example:
|
|||
void printA() = #1; // the usual way
|
||||
void printB() = #2-1; // with a constant expression
|
||||
.Ed
|
||||
.It Fl f Ns Cm return-assignments
|
||||
Enabiling this option will allow assigning values or expressions to the
|
||||
return keyword as if it were a local variable of the same type as the
|
||||
function's signature's return type.
|
||||
.Pp
|
||||
Example:
|
||||
.Bd -literal -offset indent
|
||||
float bar() { return 1024; }
|
||||
float fun() {
|
||||
return = bar();
|
||||
return; // returns value of bar
|
||||
}
|
||||
.Ed
|
||||
.It Fl f Ns Cm unsafe-varargs
|
||||
When passing on varargs to a different functions, this turns some
|
||||
static error cases into warnings. Like when the caller's varargs are
|
||||
restricted to a different type than the callee's parameter. Or a list
|
||||
of unrestricted varargs is passed into restricted varargs.
|
||||
.It Fl f Ns Cm typeless-stores
|
||||
Always use STORE_F, LOAD_F, STOREP_F when accessing scalar variables.
|
||||
This is somewhat incorrect assembly instruction use, but in all engines
|
||||
they do exactly the same. This makes disassembly output harder to read,
|
||||
breaks decompilers, but causes the output file to be better compressible.
|
||||
.It Fl f Ns Cm sort-operands
|
||||
In commutative instructions, always put the lower-numbered operand first.
|
||||
This shaves off 1 byte of entropy from all these instructions, reducing
|
||||
compressed size of the output file.
|
||||
.It Fl f Ns Cm emulate-state
|
||||
Emulate OP_STATE operations in code rather than using the instruction.
|
||||
The desired fps can be set via -state-fps=NUM, defaults to 10.
|
||||
Specifying \-state-fps implicitly sets this flag. Defaults to off in all
|
||||
standards.
|
||||
.It Fl f Ns Cm arithmetic-exceptions
|
||||
Turn on arithmetic exception tests in the compiler. In constant expressions
|
||||
which trigger exceptions like division by zero, overflow, underflow, etc,
|
||||
the following flag will produce diagnostics for what triggered that
|
||||
exception.
|
||||
.It Fl f Ns Cm split-vector-parameters
|
||||
With this flag immediate vector literals which only ever appear as function
|
||||
parameters won't be stored as vector immediates. Instead, the 3 floats making
|
||||
up the vector will be copied separately. Essentially this turns a vector-store
|
||||
instruction into 3 float-store instructions for such cases. This increases
|
||||
code size but can dramatically reduce the amount of vector globals, which is
|
||||
after all limited to 64k. There's at least one known codebase where this
|
||||
lowers the number of globals from over 80k down to around 3k. In other code
|
||||
bases it doesn't reduce the globals at all but only increases code size.
|
||||
Just try it and see whether it helps you.
|
||||
.It Fl f Ns Cm default-eraseable
|
||||
Force all expressions to be "eraseable" which permits the compiler to
|
||||
remove unused functions, variables and statements. This is equivlant to
|
||||
putting [[eraseable]] on all definitions. This is dangerous as it breaks
|
||||
auto cvars, definitions for functions the engine may be looking for and
|
||||
translatable strings. Instead, you can mark a definition with [[noerase]]
|
||||
to prevent this from happening.
|
||||
.El
|
||||
.Sh OPTIMIZATIONS
|
||||
.Bl -tag -width Ds
|
||||
|
@ -550,7 +679,7 @@ string being added.
|
|||
.Pp
|
||||
For example the following code will only generate 1 string:
|
||||
.Bd -literal -offset indent
|
||||
print("Hell you!\\n");
|
||||
print("Hello you!\\n");
|
||||
print("you!\\n"); // trailing substring of "Hello you!\\n"
|
||||
.Ed
|
||||
.Pp
|
||||
|
@ -582,6 +711,10 @@ in this case, the y component of a vector. This optimization will turn
|
|||
such a multiplication into a direct component access. If the factor is
|
||||
anything other than 1, a float-multiplication will be added, which is
|
||||
still faster than a vector multiplication.
|
||||
.It Fl O Ns Cm const-fold-dce
|
||||
For constant expressions that result in dead code (such as a branch whos
|
||||
condition can be evaluated at compile-time), this will eliminate the branch
|
||||
and else body (if present) to produce more optimal code.
|
||||
.El
|
||||
.Sh CONFIG
|
||||
The configuration file is similar to regular .ini files. Comments
|
||||
|
@ -631,7 +764,7 @@ A documented example for a gmqcc.ini file.
|
|||
.Sh AUTHOR
|
||||
See <http://graphitemaster.github.com/gmqcc>.
|
||||
.Sh BUGS
|
||||
Currently the '-fftepp-predefs' flag is not included by '-std=fteqcc',
|
||||
Currently the '\-fftepp-predefs' flag is not included by '\-std=fteqcc',
|
||||
partially because it is not entirely conformant to fteqcc.
|
||||
.Pp
|
||||
Please report bugs on <http://github.com/graphitemaster/gmqcc/issues>,
|
||||
|
|
|
@ -1,284 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
/*
|
||||
* protect some information, not that I care, but this is just to stay
|
||||
* safer.
|
||||
*/
|
||||
#define SECURITY_BASE "\
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
#define SECURITY_TOKEN "\
|
||||
P29hdXRoX2NvbnN1bWVyX2tleT03NnZoM3E0Mmhudm16bTMmb2F1dGhfdG9rZW49\
|
||||
dzBieHpmMGRmdDhlZGZxJm9hdXRoX3NpZ25hdHVyZV9tZXRob2Q9UExBSU5URVhU\
|
||||
Jm9hdXRoX3NpZ25hdHVyZT10bWVlY2h0MmNtaDcyeGElMjY5dm9zeDd4OGd5NGtn\
|
||||
amsmb2F1dGhfdGltZXN0YW1wPSZvYXV0aF9ub25jZT0xMjE2NQo="
|
||||
|
||||
int isbase64(char c) {
|
||||
return !!(c && strchr(SECURITY_BASE, c) != NULL);
|
||||
}
|
||||
char value(char c) {
|
||||
const char *load = SECURITY_BASE;
|
||||
const char *find = strchr(load, c);
|
||||
|
||||
return (find) ? find - load : 0;
|
||||
}
|
||||
|
||||
int security_decode(unsigned char *dest, const unsigned char *src, int srclen) {
|
||||
unsigned char *p;
|
||||
|
||||
if(!*src)
|
||||
return 0;
|
||||
|
||||
*dest = 0;
|
||||
p = dest;
|
||||
|
||||
do {
|
||||
*p++ = (value(src[0]) << 2) | (value(src[1]) >> 4);
|
||||
*p++ = (value(src[1]) << 4) | (value(src[2]) >> 2);
|
||||
*p++ = (value(src[2]) << 6) | (value(src[3]) >> 0);
|
||||
|
||||
if(!isbase64(src[1])) {
|
||||
p -= 2;
|
||||
break;
|
||||
}
|
||||
else if(!isbase64(src[2])) {
|
||||
p -= 2;
|
||||
break;
|
||||
}
|
||||
else if(!isbase64(src[3])) {
|
||||
p--;
|
||||
break;
|
||||
}
|
||||
src += 4;
|
||||
|
||||
while(*src && (*src == 13 || *src == 10))
|
||||
src++;
|
||||
} while(srclen-= 4);
|
||||
|
||||
*p = 0;
|
||||
return p-dest;
|
||||
}
|
||||
|
||||
#define BASEURL " https://api-content.dropbox.com/1/files/sandbox/"
|
||||
|
||||
/*
|
||||
* If more platforms are supported add the entries between the start
|
||||
* tag here, and the end tag below. Nothing else needs to be done
|
||||
* <tag> (the table needs to match the HTML too)
|
||||
*/
|
||||
#define ARCHLINUX_32_REF "%sgmqcc-%c.%c.%c-1-i686.pkg.tar.xz%s"
|
||||
#define ARCHLINUX_64_REF "%sgmqcc-%c.%c.%c-1-x86_64.pkg.tar.xz%s"
|
||||
#define DEBIAN_32_REF "%sgmqcc-%c.%c.%c-i686.deb%s"
|
||||
#define DEBIAN_64_REF "%sgmqcc-%c.%c.%c-x86_64.deb%s"
|
||||
#define WINDOWS_32_REF "%sgmqcc-%c.%c.%c-win32.zip%s"
|
||||
#define WINDOWS_64_REF "%sgmqcc-%c.%c.%c-win64.zip%s"
|
||||
|
||||
#define HTML "\
|
||||
<!doctype html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<meta charset=\"utf-8\">\
|
||||
<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\">\
|
||||
<title>GMQCC</title>\
|
||||
<link rel=\"stylesheet\" href=\"stylesheets/styles.css\">\
|
||||
<link rel=\"stylesheet\" href=\"stylesheets/pygment_trac.css\">\
|
||||
<script src=\"javascripts/scale.fix.js\"></script>\
|
||||
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\">\
|
||||
<!--[if lt IE 9]>\
|
||||
<script src=\"//html5shiv.googlecode.com/svn/trunk/html5.js\"></script>\
|
||||
<![endif]-->\
|
||||
</head>\
|
||||
<body>\
|
||||
<a href=\"https://github.com/graphitemaster/gmqcc\"><div class=\"fork\"></div></a>\
|
||||
<div class=\"wrapper\">\
|
||||
<header>\
|
||||
<h1 class=\"header\">GMQCC</h1>\
|
||||
<p class=\"header\">An Improved Quake C Compiler</p>\
|
||||
<ul>\
|
||||
<li class=\"buttons\"><a href=index.html>Index</a></li>\
|
||||
<li class=\"download\"><a href=\"download.html\">Download</a></li>\
|
||||
<li class=\"buttons\"><a href=\"https://github.com/graphitemaster/gmqcc/issues\">Issues</a></li>\
|
||||
<li class=\"buttons\"><a href=\"doc.html\">Documentation</a></li>\
|
||||
<li class=\"buttons\"><a href=\"https://github.com/graphitemaster/gmqcc\">View On GitHub</a></li>\
|
||||
</ul>\
|
||||
</header>\
|
||||
<section>\
|
||||
<table>\
|
||||
<tr>\
|
||||
<th>Operating System / Distribution</th>\
|
||||
<th>x86 Architecture</th>\
|
||||
<th>x86_64 Architecture</th>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td>Archlinux</td>\
|
||||
<td><a href=\"%s\">Download</a></td>\
|
||||
<td><a href=\"%s\">Download</a></td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td>Debian</td>\
|
||||
<td><a href=\"%s\">Download</a></td>\
|
||||
<td><a href=\"%s\">Download</a></td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td>Windows</td>\
|
||||
<td><a href=\"%s\">Download</a></td>\
|
||||
<td><a href=\"%s\">Download</a></td>\
|
||||
</tr>\
|
||||
</table>\
|
||||
</section>\
|
||||
<footer>\
|
||||
<script type=\"text/javascript\" src=\"http://www.ohloh.net/p/602517/widgets/project_partner_badge.js\"></script>\
|
||||
</footer>\
|
||||
</div>\
|
||||
<!--[if !IE]><script>fixScale(document);</script><![endif]-->\
|
||||
</body>\
|
||||
</html>\
|
||||
"
|
||||
|
||||
static char build_table[][4096] = {
|
||||
ARCHLINUX_32_REF, ARCHLINUX_64_REF,
|
||||
DEBIAN_32_REF, DEBIAN_64_REF,
|
||||
WINDOWS_32_REF, WINDOWS_64_REF
|
||||
};
|
||||
/* </tag> */
|
||||
|
||||
#define ISXDIGIT(c) ((c >= 48 && c <= 57) || ((c & ~0x20) >= 65 && (c & ~0x20) <= 70))
|
||||
typedef struct {
|
||||
char *data;
|
||||
unsigned int len;
|
||||
unsigned int size;
|
||||
} url_t;
|
||||
void escape(url_t *str) {
|
||||
char *p, *ptr;
|
||||
char hexstr[3];
|
||||
unsigned int i=0;
|
||||
unsigned long l=0;
|
||||
|
||||
p = str->data;
|
||||
for(i=0; i < str->len; i++) {
|
||||
if((p - str->data) >= str->len)
|
||||
break;
|
||||
if(*p == '%' &&
|
||||
((p - str->data)+2) < str->len &&
|
||||
ISXDIGIT(*(p+1)) &&
|
||||
ISXDIGIT(*(p+2))
|
||||
) {
|
||||
p++;
|
||||
hexstr[0] = *p++;
|
||||
hexstr[1] = *p++;
|
||||
hexstr[2] = 0;
|
||||
l = strtoul(hexstr, &ptr, 16);
|
||||
str->data[i] = (char)(l & 0x7f);
|
||||
continue;
|
||||
}
|
||||
if(*p == '+') {
|
||||
*p = ' ';
|
||||
}
|
||||
str->data[i] = *p++;
|
||||
}
|
||||
str->data[i] = 0;
|
||||
str->len = i;
|
||||
}
|
||||
|
||||
void version(const char *directory, char *major, char *minor, char *patch) {
|
||||
FILE *handle;
|
||||
char file[4096];
|
||||
size_t size = 0;
|
||||
char *data = NULL;
|
||||
snprintf(file, sizeof(file), "%s/gmqcc.h", directory);
|
||||
|
||||
handle = fopen(file, "r");
|
||||
if (!handle) {
|
||||
fprintf(stderr, "failed to open %s for reading version (%s)\n",
|
||||
file, strerror(errno)
|
||||
);
|
||||
abort();
|
||||
}
|
||||
|
||||
while (getline(&data, &size, handle) != EOF) {
|
||||
|
||||
#define TEST(TYPE, STORE) \
|
||||
if (strstr(data, "#define GMQCC_VERSION_" TYPE )) { \
|
||||
char *get = data; \
|
||||
while (!isdigit(*get)) \
|
||||
get++; \
|
||||
*STORE = *get; \
|
||||
}
|
||||
|
||||
TEST("MAJOR", major)
|
||||
TEST("MINOR", minor)
|
||||
TEST("PATCH", patch)
|
||||
|
||||
#undef TEST
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
void genhtml() {
|
||||
FILE *fp = fopen("download.html", "w");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "failed to generate HTML: %s\n", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
fprintf(fp, HTML,
|
||||
build_table[0], build_table[1],
|
||||
build_table[2], build_table[3],
|
||||
build_table[4], build_table[5]
|
||||
);
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Builds a list of download links with the right version and handles the
|
||||
* rest of the magic.
|
||||
*/
|
||||
void build(const char *directory) {
|
||||
/* Figure out version number */
|
||||
char find[3];
|
||||
char decode[4096];
|
||||
size_t size;
|
||||
version(directory, &find[0], &find[1], &find[2]);
|
||||
|
||||
/*
|
||||
* decode the secuity stuff for preparing the URLs which will be used
|
||||
* as links.
|
||||
*/
|
||||
memset(decode, 0, sizeof(decode));
|
||||
security_decode(decode, SECURITY_TOKEN, strlen(SECURITY_TOKEN));
|
||||
|
||||
for (size = 0; size < sizeof(build_table) / sizeof(*build_table); size++) {
|
||||
char *load = strdup(build_table[size]);
|
||||
url_t esc = { NULL, 0 };
|
||||
|
||||
snprintf(build_table[size], 4096, load, BASEURL, find[0], find[1], find[2], decode);
|
||||
esc.data = strdup(build_table[size]);
|
||||
esc.size = strlen(build_table[size]);
|
||||
esc.len = esc.size;
|
||||
|
||||
/* Yes we also need to escape URLs just incase */
|
||||
escape(&esc);
|
||||
free(load);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now generate the HTML file for those download links by asking tinyurl to
|
||||
*/
|
||||
genhtml();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
size_t itr;
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
if (!argc) {
|
||||
printf("usage: %s [gmqcc.h location]\n", argv[-1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
build(*argv);
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
|
@ -1,92 +0,0 @@
|
|||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
margin: 0;
|
||||
line-height: 1.8em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: #CDC9C9;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color:#232323;
|
||||
margin:36px 0 10px;
|
||||
}
|
||||
|
||||
.head-ltitle, .head-rtitle, .head-vol {
|
||||
font: bold 0.8em Arial, Helvectia, sans-serif;
|
||||
}
|
||||
|
||||
.head-vol {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.name {
|
||||
font: bold 0.8em Monospace, serif;
|
||||
}
|
||||
|
||||
.ftype {
|
||||
font: normal 1em Monospace, serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #c30000;
|
||||
margin-top: 0.3em;
|
||||
margin-bottom: 0.3em;
|
||||
line-height: 1.3;
|
||||
font: normal 1.4em Arvo, Monaco, sans-serif;
|
||||
/*font: bold 1.4em Arial, Helvetica, sans-serif*/
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 1em;
|
||||
margin-top: 1em;
|
||||
padding-left: 1em;
|
||||
border-top: 1px #cccccc solid;
|
||||
font: normal 0.9em Arial, Helvetica, sans-serif;
|
||||
text-align: justify;
|
||||
border: 0;
|
||||
padding-bottom: 1em;
|
||||
border-bottom: 1px solid #f0e0e0;
|
||||
}
|
||||
|
||||
.list-tag {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
border: 1px dashed #ffffff;
|
||||
background: #ddd8d8;
|
||||
}
|
||||
|
||||
.flag {
|
||||
font: normal 1em Monospace, serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color:#C30000;
|
||||
font-weight:200;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
|
@ -69,7 +69,9 @@ Append a string parameter to be passed to
|
|||
The following builtin functions are available:
|
||||
.Bl -ohang
|
||||
.It Li 1) void print(string...) = #1;
|
||||
.D1 Print the passed strings to stdout. At most 8 strings are allowed.
|
||||
.Bd -unfilled -offset indent -compact
|
||||
Print the passed strings to stdout. At most 8 strings are allowed.
|
||||
.Ed
|
||||
.It Li 2) string ftos(float) = #2;
|
||||
.D1 Convert a float to a string.
|
||||
.It Li 3) entity spawn() = #3;
|
||||
|
@ -79,7 +81,7 @@ The following builtin functions are available:
|
|||
.It Li 5) string vtos(vector) = #5;
|
||||
.D1 Convert a vector to a string.
|
||||
.It Li 6) void error(string...) = #6;
|
||||
.D1 Print at most 8 strings to stdout and then exit with an error.
|
||||
.D1 Print strings to stdout and then exit with an error (limited to 8 arguments)
|
||||
.It Li 7) float vlen(vector) = #7;
|
||||
.D1 Get the length of a vector.
|
||||
.It Li 8) string etos(entity) = #8;
|
||||
|
|
|
@ -1,759 +0,0 @@
|
|||
\documentclass{article}
|
||||
|
||||
%%% PACKAGES
|
||||
\usepackage{geometry}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[parfill]{parskip}
|
||||
\usepackage{subfig}
|
||||
\usepackage{listings}
|
||||
\usepackage{color}
|
||||
\usepackage{sectsty}
|
||||
|
||||
%%% GEOMETRY FOR DOCUMENT
|
||||
\geometry{a4paper}
|
||||
|
||||
%%% HEADERS/FOOTERS APPEARANCE
|
||||
\usepackage{fancyhdr} % This should be set AFTER setting up the page geometry
|
||||
\pagestyle{fancy} % options: empty , plain , fancy
|
||||
\renewcommand{\headrulewidth}{0pt} % customise the layout...
|
||||
\lhead{}\chead{}\rhead{}
|
||||
\lfoot{}\cfoot{\thepage}\rfoot{}
|
||||
|
||||
%%% SECTION TITLE APPEARANCE
|
||||
\allsectionsfont{\sffamily\mdseries\upshape} % (See the fntguide.pdf for font help)
|
||||
|
||||
%%% ToC APPEARANCE
|
||||
\usepackage[nottoc,notlof,notlot]{tocbibind} % Put the bibliography in the ToC
|
||||
\usepackage[titles,subfigure]{tocloft} % Alter the style of the Table of Contents
|
||||
\renewcommand{\cftsecfont}{\rmfamily\mdseries\upshape}
|
||||
\renewcommand{\cftsecpagefont}{\rmfamily\mdseries\upshape} % No bold!
|
||||
|
||||
%%% listing language definitions
|
||||
%%% BNF for now, QuakeC will be later
|
||||
\definecolor{keyword1}{RGB}{0,102,153}
|
||||
\definecolor{keyword2}{RGB}{0,153,102}
|
||||
\definecolor{keyword3}{RGB}{0,153,255}
|
||||
\definecolor{comment}{RGB}{204,0,0}
|
||||
\definecolor{function}{RGB}{153,102,255}
|
||||
\definecolor{digit}{RGB}{255,0,0}
|
||||
\definecolor{string}{RGB}{255,0,204}
|
||||
\definecolor{rule}{RGB}{192,192,192}
|
||||
\definecolor{back}{RGB}{250,250,250}
|
||||
|
||||
\lstdefinelanguage{bnf}{
|
||||
keywordstyle={\color{keyword2}\bfseries},
|
||||
keywords={},
|
||||
otherkeywords={::=,|},
|
||||
morecomment=[s][\color{comment}]{(*}{*)},
|
||||
stringstyle=\color{string},
|
||||
showstringspaces=false,
|
||||
frame=none,
|
||||
rulecolor=\color{rule},
|
||||
backgroundcolor=\color{back}
|
||||
}
|
||||
|
||||
%% Title Information %%
|
||||
\title{The GMQCC QuakeC Programming Language}
|
||||
\author{Dale Weiler}
|
||||
\date{\today}
|
||||
|
||||
\begin{document}
|
||||
|
||||
%% Title Page %%
|
||||
\maketitle
|
||||
\thispagestyle{empty}
|
||||
\raggedright
|
||||
\abstract
|
||||
This document specifies the form and establishes the interpretation of programs written in
|
||||
the GMQCC QuakeC programming language variant (refereed simply as QuakeC throughout this
|
||||
document). It specifies:
|
||||
\begin{itemize}
|
||||
\item the representation of QuakeC programs;
|
||||
\item the syntax and constraints of the QuakeC language;
|
||||
\item the semantic rules for interpreting QuakeC programs;
|
||||
\item the representation of input data to be processed by QuakeC programs;
|
||||
\item the representation of output data produced by QuakeC programs;
|
||||
\item the restrictions and limits imposed by a conforming implementation of QuakeC.
|
||||
\end{itemize}
|
||||
This document does not specify
|
||||
\begin{itemize}
|
||||
\item the mechanism by which QuakeC programs are transformed for use by a data-
|
||||
processing system;
|
||||
\item the mechanism by which QuakeC programs are invoked for use by a data-processing
|
||||
system;
|
||||
\item the mechanism by which input data are transformed for use by a QuakeC program;
|
||||
\item the size or complexity of a program and its data that will exceed the capacity
|
||||
of any specific data-processing system or the capacity of a particular
|
||||
execution environment;
|
||||
\item all minimal requirements of a data-processing system that is capable of
|
||||
supporting a conforming implementation.
|
||||
\end{itemize}
|
||||
|
||||
%% Table Of Contents %%
|
||||
\newpage
|
||||
\thispagestyle{empty}
|
||||
\tableofcontents
|
||||
\newpage
|
||||
|
||||
%% Begin Contents %%
|
||||
\raggedright % No weird TEX spacing on lines to fill page
|
||||
|
||||
%% -> Terms, definitions, and symbols %%
|
||||
\section{Terms, definitions, and symbols}
|
||||
\subsection*{argument}
|
||||
Expression in the comma-separated list bounded by the parentheses in a function call
|
||||
expression, or a sequence of preprocessing tokens in the comma-separated list bounded
|
||||
by the parentheses in a function-like macro invocation.
|
||||
|
||||
\subsection*{behavior}
|
||||
External appearance or action
|
||||
|
||||
\subsection*{implementation-defined behavior}
|
||||
Unspecified behavior where each implementation documents how the choice is made.
|
||||
|
||||
\subsection*{undefined behavior}
|
||||
Behavior, upon use of a non-portable or erroneous program construct or of erroneous data,
|
||||
for which this document imposes no actual requirements.
|
||||
|
||||
\subsection*{unspecified behavior}
|
||||
Use of an unspecified value, or other behavior where this document provides two or more
|
||||
possibilities and imposes no further requirements on which is chosen in any instance.
|
||||
|
||||
\subsection*{constraint}
|
||||
Restriction, either syntactic or semantic, by which the exposition of language elements
|
||||
is to be interpreted.
|
||||
|
||||
\subsection*{diagnostic message}
|
||||
Message belonging to an implementation-defined subset of the implementation's message
|
||||
output.
|
||||
|
||||
\subsection*{object}
|
||||
Region of data storage in the execution environment, the contents of which can represent
|
||||
values.
|
||||
|
||||
\subsection*{parameter}
|
||||
Object declared as part of a function declaration or definition that acquires a value on
|
||||
entry to the function, or an identifier from the comma-separated list bounded by the
|
||||
parentheses immediately following the macro name in a function-like macro definition.
|
||||
|
||||
\subsection*{recommended practice}
|
||||
Specification that is strongly recommended as being in keeping with the intent of this
|
||||
document, but that may be impractical for some implementations.
|
||||
|
||||
\subsection*{value}
|
||||
Precise meaning of the contents of an object when interpreted as having a specific type.
|
||||
|
||||
\subsection*{implementation}
|
||||
Particular set of software, running in a particular translation environment under
|
||||
particular control options, that performs translation of programs for, and supports
|
||||
execution of functions in, a particular execution environment.
|
||||
|
||||
\subsection*{implementation-defined value}
|
||||
Unspecified value where each implementation documents how the choice is made.
|
||||
|
||||
\subsection*{unspecified value}
|
||||
Valid value of the relevant type where this document imposes no requirements on which
|
||||
value is chosen in any instance.
|
||||
|
||||
%% -> Conformance %%
|
||||
\section{Conformance}
|
||||
In this document, "shall" is to be interpreted as a requirement on an implementation
|
||||
or on a program; conversely, "shall not" is to be interpreted as a prohibition. \\
|
||||
If a "shall" or "shall not" requirement that appears outside of a constraint is violated,
|
||||
the behavior is undefined. Undefined behavior is otherwise indicated in this document by
|
||||
the words "undefined behavior" or by the omission of any explicit definition of behavior.
|
||||
There is no difference in emphasis among these three; they all describe "behavior that is
|
||||
undefined".
|
||||
|
||||
%% -> Enviroment %%
|
||||
\section{Environment}
|
||||
An implementation that translates QuakeC source files and executes QuakeC programs in two
|
||||
data processing-system environments, which will be called the translation environment and
|
||||
the execution environment in this document. Their characteristics define and constrain the
|
||||
results of executing QuakeC programs constructed according to the syntactic and semantic
|
||||
rules for conforming implementations.
|
||||
\subsection{Conceptual models}
|
||||
\subsubsection{Translation environment}
|
||||
\paragraph*{Translation steps}
|
||||
The precedence among the syntax rules of translation is specified by the following steps
|
||||
\begin{enumerate}
|
||||
\item Physical source file characters are mapped, in an implementation-defined manner,
|
||||
to the source character set (introducing new-line characters for end-of-line
|
||||
indicators) if necessary. Trigraph and digraph sequences are replaced by their
|
||||
corresponding single-character internal representations.
|
||||
\item The source file is decomposed into preprocessing tokens and sequences of white-
|
||||
space characters (including comments). A source file shall not end in a partial
|
||||
preprocessing token or in a partial comment. Each comment is replaced by one
|
||||
space character. New-line characters are retained. Whether each nonempty
|
||||
sequences of white-space characters other than new-line is retained or replaced
|
||||
by one space character is implementation-defined.
|
||||
\item Preprocessing directives are executed, macro invocations are expanded
|
||||
recursively. A \#include preprocessing directive causes the named header or
|
||||
source file to be processed from step one through step three, recursively. All
|
||||
preprocessing directives are then deleted.
|
||||
\item Each source character set member and escape sequence in character constants and
|
||||
string literals is converted to the corresponding member of the execution
|
||||
character set; if there is no corresponding member, it is converted to an
|
||||
implementation-defined member other than the null character.
|
||||
\item Adjacent string literal tokens are concatenated.
|
||||
\item White-space characters seperating tokens are no longer significant. Each
|
||||
preprocessing token is converted into a token. The resulting tokens are then
|
||||
syntactically and semantically analyzed and translated.
|
||||
\end{enumerate}
|
||||
\subparagraph*{Footnotes}
|
||||
Implementations shall behave as if these steps occur separately, even though many are likely
|
||||
to be folded together in practice. Source files need not be stored as file, nor need there
|
||||
be any one-to-one correspondence between these items and any external representation. The
|
||||
description is conceptual only, and does not specify any particular implementation.
|
||||
|
||||
\paragraph*{Diagnostics}
|
||||
A conforming implementation shall produce at least on diagnostic message(identified in an
|
||||
implementation-defined manner) if a source file contains a violation of any syntax rule or
|
||||
constraint, even if the behavior is also explicitly specified as undefined or
|
||||
implementation-defined. Diagnostic messages need not be produced in other circumstances.
|
||||
|
||||
%% ->-> Execution environments %%
|
||||
\subsubsection{Execution environment}
|
||||
A conforming execution environment shall provide at minimal the following 15 definitions
|
||||
for built in functions, with an accompanying header or source file that defines them.
|
||||
\begin{enumerate}
|
||||
\item entity () spawn
|
||||
\item void (entity) remove
|
||||
|
||||
\item string (float) ftos
|
||||
\item string (vector) vtos
|
||||
\item string (entity) etos
|
||||
\item float (string) stof
|
||||
|
||||
\item void (string, ...) dprint
|
||||
\item void (entity) eprint
|
||||
|
||||
\item float (float) rint
|
||||
\item float (float) floor
|
||||
\item float (float) ceil
|
||||
\item float (float) fabs
|
||||
\item float (float) sin
|
||||
\item float (float) cos
|
||||
\item float (float) sqrt
|
||||
\end{enumerate}
|
||||
The numbers of which these built-ins are assigned is implementation-defined;
|
||||
an implementation is allowed to use these built-ins however it sees fit.
|
||||
|
||||
\pagebreak
|
||||
%% -> Language %%
|
||||
\section{Language}
|
||||
\subsection{Notation}
|
||||
The syntax notation used in this document is that of a BNF specification. A set of
|
||||
derivation rules, often written as:
|
||||
\begin{lstlisting}[language=bnf]
|
||||
symbol ::= expression
|
||||
\end{lstlisting}
|
||||
Where symbol is a nonterminal, and the expression consists of one or more sequences of
|
||||
symbols; more sequences are separated by a vertical bar \textbar, indicating a choice,
|
||||
the whole being a possible substitution for the symbol on the left. Symbols that never
|
||||
appear on the left side are terminals.
|
||||
\linebreak
|
||||
|
||||
This document defines language syntax throughout it's way at defining language
|
||||
constructs If you're interested in a summary of the language syntax, one is given in
|
||||
annex A.
|
||||
|
||||
%% -> Concepts %%
|
||||
\subsection{Concepts}
|
||||
%% ->-> Scopes of identifiers %%
|
||||
\subsubsection{Scopes of identifiers}
|
||||
An identifier can denote an object; a function, or enumeration; a label name; a macro
|
||||
name; or a macro parameter. The same identifier can denote different items at different
|
||||
points in the program. A member of an enumeration is called an enumeration constant.
|
||||
Macro names and macro parameters are not considered further here, because prior to the
|
||||
semantic phase of program translation any occurrences of macro names in the source file
|
||||
are replaced by the preprocessing token sequences that constitute their macro definitions.
|
||||
\linebreak
|
||||
|
||||
For each different item that an identifier designates, the identifier is visible (i.e,
|
||||
can be used) only within a region of program text called its scope. Different items
|
||||
designated by the same identifier either have different scopes, or are in different name
|
||||
spaces. There are four kinds of scopes: function, file, block and function prototype.
|
||||
(A function prototype is a declaration of a function that declares the types of its
|
||||
parameters.)
|
||||
\linebreak
|
||||
|
||||
A label name is the only kind of identifier that has function scope. It can be used (in
|
||||
a goto statement) anywhere in the function in which it appears, and is declared
|
||||
implicitly by its syntactic appearance (prefixed by a colon :, and suffixed with a
|
||||
statement).
|
||||
\linebreak
|
||||
|
||||
Every other identifier has scope determined by the placement of its declaration (in a
|
||||
declarator or type specifier). If the declarator or type specifier that declares the
|
||||
identifier appears outside any block or list of parameters, the identifier has file
|
||||
scope, which terminates at the end of the file. If the declartor or type specifier that
|
||||
declares the identifier appears inside a block or within the list of parameter
|
||||
declarations in a function definition, the identifier has block scope, which terminates
|
||||
at the end of the associated block. If the declarator or type specifier that declares
|
||||
the identifier appears within the list of parameter declarations in a function prototype
|
||||
(not part of a function definition), the identifier has function prototype scope, which
|
||||
terminates at the end of the function declarator. If an identifier designates two
|
||||
different items in the same name space, the scopes might overlap. If so, the scope of
|
||||
one item (the inner scope) will be a strict subset of the scope of the other item (the
|
||||
outer scope). Within the inner scope, the identifier designates the item declared in the
|
||||
inner scope; the item declared in the outer scope is hidden (and not visible) within
|
||||
the inner scope.
|
||||
\linebreak
|
||||
|
||||
Unless explicitly stated otherwise, where this document uses the term "identifier" to
|
||||
refer to some item (as opposed to the syntactic construct), it refers to the item in the
|
||||
relevant name space whose declaration is visible at the point the identifier occurs.
|
||||
\linebreak
|
||||
|
||||
Two identifiers have the same scope if and only if their scopes terminate at the same
|
||||
point.
|
||||
\linebreak
|
||||
|
||||
Each enumeration constant has scope that begins just after the appearance of its defining
|
||||
enumerator in an enumerator list. Any other identifier has scope that begins just after
|
||||
the completion of its declarator.
|
||||
|
||||
%% ->-> Name spaces of identifiers %%
|
||||
\subsubsection{Name spaces of identifiers}
|
||||
If more than one declaration of a particular identifier is visible at any point in a
|
||||
source file, the syntactic context disambiguates uses that refer to different items.
|
||||
Thus, there are separate name spaces for various categories of identifiers, as follows:
|
||||
\linebreak
|
||||
\begin{itemize}
|
||||
\item Label names (disambiguated by the syntax of the label declaration and use);
|
||||
\item Enumerations (disambiguated by following the keyword enum);
|
||||
\item All other identifiers, called ordinary identifiers (declared in ordinary
|
||||
declarators or as enumeration constants).
|
||||
\end{itemize}
|
||||
|
||||
%% ->-> Types %%
|
||||
\subsubsection{Types}
|
||||
The meaning of a value stored in an object returned by a function is determined by the
|
||||
type of the expression used to access it. (An identifier declared to be an object is the simplest
|
||||
such expression; the type is specified in the declaration of the identifier.) Types are
|
||||
partitioned into object types (types that fully describe objects), function types(types
|
||||
that describe functions), and incomplete types(types that describe objects but lack
|
||||
information).
|
||||
\linebreak
|
||||
|
||||
An object declared type bool is large enough to store the values 0 and 1.
|
||||
\linebreak
|
||||
|
||||
An object declared type float is a real type; An object declared type vector is a
|
||||
comprised set of three floats that respectively represent the \underline{x,y,z}
|
||||
components of a three-dimensional vector.
|
||||
\linebreak
|
||||
|
||||
An enumeration comprises a set of named integer constant values. Each distinct
|
||||
enumeration constitutes a different enumerated type.
|
||||
\linebreak
|
||||
|
||||
Enumeration types and float are collectively called arithmetic types. Each arithmetic
|
||||
type belongs to one type domain.
|
||||
\linebreak
|
||||
|
||||
The void type comprises an empty set of values; it is an incomplete type that cannot be
|
||||
completed.
|
||||
\linebreak
|
||||
|
||||
A number of derived types can be constructed from the object, function and incomplete
|
||||
types, as follows:
|
||||
\linebreak
|
||||
|
||||
\begin{itemize}
|
||||
\item An array type describes a contiguously allocated nonempty set of objects with a
|
||||
particular object type, called the element type. Array types are characterized
|
||||
by their element type and by the number of elements in the array. An array type
|
||||
is said to be derived from its element type, and if its element is type T, the
|
||||
array type is sometimes called "array of T". The construction of an array type
|
||||
from an element type is called "array type derivation".
|
||||
\item A function type describes a function with a specified return type. A function
|
||||
type is characterized by its return type and the number and types of its
|
||||
parameters. A function type is said to be derived from its return type, and if
|
||||
its return type is T, the function type is sometimes called "function returning
|
||||
T". The construction of a function type from a return type is called "function
|
||||
type derivation".
|
||||
\end{itemize}
|
||||
|
||||
Arithmetic types are collectively called scalar types. Arrays and vectors are
|
||||
collectively called aggregate types.
|
||||
\linebreak
|
||||
|
||||
An array of unknown size is an incomplete type. It is completed, for an identifier of
|
||||
that type, by specifying the size in a later declaration. Arrays are required to have
|
||||
known constant size.
|
||||
\linebreak
|
||||
|
||||
A type is characterized by its type category, which is either the outermost derivation
|
||||
of a derived type (as noted above in the construction of derived types), or the type
|
||||
itself if the type consists of no derived types.
|
||||
\linebreak
|
||||
|
||||
Any type so far mentioned is an unqualified type. Each unqualified type has several
|
||||
qualified versions of its type, corresponding to the combinations of one, two, or all
|
||||
two of const and volatile qualifiers. The qualified or unqualified versions of a type
|
||||
are distinct types that belong to the same type category and have the same representation.
|
||||
A derived type is not qualified by the qualifiers (if any) of the type from which it
|
||||
is derived.
|
||||
\linebreak
|
||||
|
||||
%% ->-> Compatible types and composite type %%
|
||||
\subsubsection{Compatible types and composite type}
|
||||
Two types have compatible type if their types are the same.
|
||||
\linebreak
|
||||
|
||||
All declarations that refer to the same object or function shall have compatible type;
|
||||
otherwise the behavior is undefined.
|
||||
\linebreak
|
||||
|
||||
A composite type can be constructed from two types that are compatible; it is a type that
|
||||
is compatible with both of the two types and satisfies the following conditions:
|
||||
\begin{itemize}
|
||||
\item If one type is an array, the composite type is an array of that size.
|
||||
\item If only one type is a function type with a parameter type list(a function
|
||||
prototype), the composite type is a function prototype with the parameter type
|
||||
list.
|
||||
\item If both types are function types with parameter type lists, the type of each
|
||||
parameter in the composite parameter type list is the composite type of the
|
||||
corresponding parameters.
|
||||
\end{itemize}
|
||||
These rules apply recursively to types from which the two types are derived.
|
||||
\linebreak
|
||||
|
||||
%% ->Conversions %%
|
||||
\subsection{Conversions}
|
||||
Several operators convert operand values from one type to another automatically. This
|
||||
sub-clause specifies the result required from such an implicit conversion.
|
||||
\linebreak
|
||||
|
||||
Conversion from an operand value to a compatible type causes no change to the value or
|
||||
the representation.
|
||||
\linebreak
|
||||
|
||||
TODO: Specify all implicit conversions.
|
||||
|
||||
%% ->->Aritmetic operands %%
|
||||
\subsubsection{Arithmetic operands}
|
||||
\paragraph*{Boolean type}
|
||||
When any scalar value is converted to bool, the result is 0 if the value compares equal
|
||||
to 0; otherwise the result is 1.
|
||||
|
||||
%% ->->Other operands %%
|
||||
\subsubsection{Other operands}
|
||||
\paragraph{Lvalues, arrays and function designators}
|
||||
An lvalue is an expression with an object type or an incomplete type other than void;
|
||||
if an lvalue does not designate an object when it is evaluated, the behavior is undefined.
|
||||
When an object is said to have a particular type, the type is specified by the lvalue
|
||||
used to designate the object. A modifiable lvalue is an lvalue that does not have an
|
||||
array type, does not have an incomplete type, and does not have a const-qualified type.
|
||||
\linebreak
|
||||
|
||||
Except when it is the operand of the unary \& operator, the ++ operator, the -- operator,
|
||||
or the left operand of the . operator or an assignment operator, an lvalue that does not
|
||||
have array type is converted to the value stored in the designated object (and is no
|
||||
longer an lvalue). If the lvalue has qualified type, the value has the unqualified
|
||||
version of the type of the lvalue; otherwise, the value has the type of the lvalue. If
|
||||
the lvalue has an incomplete type and does not have array type, the behavior is undefined.
|
||||
\linebreak
|
||||
|
||||
A function designator is an expression that has function type.
|
||||
|
||||
\paragraph*{void}
|
||||
The (nonexistent) value of a void expression (an expression that has type void) shall not
|
||||
be used in any way, and implicit conversions (except to void) shall not be applied to
|
||||
such an expression. If an expression of any other type is evaluated as a void expression,
|
||||
its value or designator is discarded. (A void expression is only evaluated for its
|
||||
side effects.)
|
||||
\pagebreak
|
||||
|
||||
\subsection{Lexical elements}
|
||||
\paragraph*{Syntax}
|
||||
\begin{lstlisting}[language=bnf]
|
||||
token ::= keyword
|
||||
| identifier
|
||||
| constant
|
||||
| string-literal
|
||||
| punctuator
|
||||
preprocessing-token ::= header-name
|
||||
| identifier
|
||||
| pp-number
|
||||
| string-literal
|
||||
| punctuator
|
||||
\end{lstlisting}
|
||||
\paragraph*{Constraints}
|
||||
Each preprocessing token that is converted to a token shall have the lexical form of a
|
||||
keyword, an identifier, a constant, a string literal, or a punctuator.
|
||||
|
||||
\paragraph*{Semantics}
|
||||
A token is the minimal lexical element of the language in translation steps six and seven.
|
||||
The categories of tokens are: keywords, identifiers, constants, string literals, and
|
||||
punctuators. A preprocessing token is the minimal lexical element of the language in
|
||||
translation steps three through five. The categories of preprocessing tokens are: header
|
||||
names, identifiers, preprocessing numbers, string literals, punctuators and other single
|
||||
non-white-space characters that do not lexically match the other preprocessing token
|
||||
categories. If a ' or a " character matches the last category, the behavior is undefined.
|
||||
Preprocessing tokens can be separated by white space; this consists of comments (described
|
||||
later), or white-space characters (space, horizontal tab, new-line, vertical tab, and form
|
||||
-feed), or both. In certain circumstances during translation step four, white space (or
|
||||
the absence thereof) serves as more than preprocessing token separation. White space may
|
||||
appear within a preprocessing token only as part of a header name or between the quotation
|
||||
characters in a string literal.
|
||||
\linebreak
|
||||
|
||||
If the input stream has been parsed into preprocessing tokens up to a given character, the
|
||||
next preprocessing token is the longest sequence of characters that could constitute a
|
||||
preprocessing token. There is one exception to this rule: header name preprocessing tokens
|
||||
are recognized only within \#include preprocessing directives and in implementation-defined
|
||||
locations within \#pragma directives. In such contexts, a sequence of characters that
|
||||
could be either a header name or string literal is recognized as the former.
|
||||
|
||||
%% ->-> Keywords %%
|
||||
\subsubsection{Keywords}
|
||||
\paragraph*{Syntax}
|
||||
\begin{lstlisting}[language=bnf]
|
||||
keyword ::= enum | break | return | void
|
||||
| case | float | volatile | for
|
||||
| while | const | goto | bool
|
||||
| continue | if | static | default
|
||||
| inline | do | switch | else
|
||||
| vector | entity
|
||||
\end{lstlisting}
|
||||
\paragraph*{Semantics}
|
||||
The above tokens (case sensitive) are reserved (in translation step seven and eight) for
|
||||
use as keywords, and shall not be used otherwise.
|
||||
|
||||
%% ->->Identifiers %%
|
||||
\subsubsection{Identifiers}
|
||||
\begin{lstlisting}[language=bnf]
|
||||
identifier ::= nondigit
|
||||
| identifier nondigit
|
||||
| identifier digit
|
||||
|
||||
nondigit ::= _ | a | b | c | d | e | f | g | h | i
|
||||
| j | k | l | m | n | o | p | q | r | s
|
||||
| t | u | v | w | x | y | z | A | B | C
|
||||
| D | E | F | G | H | I | J | K | L | M
|
||||
| N | P | Q | R | S | T | U | V | W | X
|
||||
| Y | Z
|
||||
|
||||
digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
|
||||
\end{lstlisting}
|
||||
\paragraph*{Semantics}
|
||||
An identifier is a sequence of nondigit characters (including the underscore \_, the lower
|
||||
case and upper case Latin letters, and other characters) and digits, which designates one
|
||||
or more items. Lowercase and uppercase letters are distinct. There is a specific limit of
|
||||
65535 characters for an identifier.
|
||||
\linebreak
|
||||
|
||||
When preprocessing tokens are converted to tokens during translation step six, if a
|
||||
preprocessing token could not be converted to either a keyword or an identifier, it is
|
||||
converted to a keyword.
|
||||
|
||||
\paragraph*{Predefined identifiers}
|
||||
Any identifiers that begin with the prefix \_\_builtin, or are within the reserved name
|
||||
space are reserved by the implementation.
|
||||
|
||||
%% ->->Constants %%
|
||||
\subsubsection{Constants}
|
||||
\begin{lstlisting}[language=bnf]
|
||||
constant ::= integer-constant
|
||||
| floating-constant
|
||||
| enumeration-constant
|
||||
| character-constant
|
||||
| vector-constant
|
||||
|
||||
integer-constant ::= decimal-constant
|
||||
| octal-constant
|
||||
| hexadecimal-constant
|
||||
|
||||
decimal-constant ::= nonzero-digit
|
||||
| decimal-constant digit
|
||||
|
||||
octal-constant ::= 0
|
||||
| octal-constant octal-digit
|
||||
|
||||
hexadecimal-constant ::= hexdecimal-prefix
|
||||
hexadecimal-digit
|
||||
| hexadecimal-digit
|
||||
hexadecimal-constant
|
||||
|
||||
hexadecimal-prefix: ::= 0x | 0X
|
||||
|
||||
nonzero-digit ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
|
||||
| 9
|
||||
|
||||
octal-digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
|
||||
hexadecimal-digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
| 8 | 9 | a | b | c | d | e | f
|
||||
| A | B | C | D | E | F
|
||||
\end{lstlisting}
|
||||
|
||||
%% ->-> String literals %%
|
||||
\subsubsection{String literals}
|
||||
\begin{lstlisting}[language=bnf]
|
||||
string-literal := " s-char-sequence "
|
||||
|
||||
s-char-sequence := s-char
|
||||
| s-char-sequence s-char
|
||||
|
||||
s-char := ` | ! | @ | # | $ | % | ^ | & | *
|
||||
| ( | ) | _ | - | + | = | { | } | [
|
||||
| ] | | | : | ; | ' | < | , | > | .
|
||||
| ? | / | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
| 8 | 9 | 0 | q | w | e | r | t | y
|
||||
| u | i | o | p | a | s | d | f | g
|
||||
| h | j | k | l | z | x | c | v | b
|
||||
| n | m | Q | W | E | R | T | Y | U
|
||||
| I | O | P | A | S | D | F | G | |
|
||||
| H | J | K | L | Z | X | C | V | B
|
||||
| N | M
|
||||
\end{lstlisting}
|
||||
\paragraph*{Description}
|
||||
A character string literal is a sequence of zero or more characters enclosed in
|
||||
double-quotes, as in "xyz".
|
||||
\linebreak
|
||||
|
||||
The same considerations apply to each element of the sequence in a character string
|
||||
literal as if it where an integer character constant, except that the single-quote
|
||||
' is representable either by itself or by the escape sequence \textbackslash', but
|
||||
the double-quote " shall be represented by the escape sequence \textbackslash".
|
||||
|
||||
\paragraph*{Semantics}
|
||||
In translation stage six, the character sequences specified by any sequence of adjacent
|
||||
character string literal tokens are concatenated into a single character sequence.
|
||||
|
||||
%% ->-> Punctuators %%
|
||||
\subsubsection{Punctuators}
|
||||
TODO: BNF
|
||||
|
||||
A punctuator is a symbol that has independent syntactic and semantic significance.
|
||||
Depending on context, it may specify an operation to be performed (which in turn
|
||||
may yield a value or a function designator, produce a side effect, or some combination
|
||||
thereof) in which case it is known as an operator (other forms of operator also exist
|
||||
in some contexts). An operand is an item on which an operator acts.
|
||||
\linebreak
|
||||
|
||||
TODO: Trigraphs \& Digraphs
|
||||
|
||||
\subsubsection{Header names}
|
||||
TODO
|
||||
\subsubsection{Preprocessing numbers}
|
||||
TODO
|
||||
\subsubsection{Comments}
|
||||
Except within a character constant, a string literal, or a comment, the characters /*
|
||||
introduce a comment. The contents of such a comment are examined only to identify
|
||||
characters and to find the characters */ that terminate it.
|
||||
\linebreak
|
||||
|
||||
Except within a character constant, a string literal, or a comment, the characters //
|
||||
introduce a comment that includes all characters up to, but not including, the next
|
||||
new-line character. The contents of such a comment are examined only to identify
|
||||
characters and to find the terminating new-line character.
|
||||
\linebreak
|
||||
|
||||
%% -> Expressions %%
|
||||
\subsection{Expressions}
|
||||
An expression is a sequence of operators and operands that specifies computation of a
|
||||
value, or that designates an object or function, or that generates side effects, or that
|
||||
performs a combination thereof.
|
||||
\linebreak
|
||||
|
||||
Between the previous and next sequence point an object shall have its stored value
|
||||
modified at most once by the evaluation of an expression. Furthermore, the prior value
|
||||
shall be read only to determine the value to be stored.
|
||||
\linebreak
|
||||
|
||||
The grouping of operators and operands is indicated by the syntax. Except as specified
|
||||
later (for the function call (), \&\&, \textbar\textbar ?:, and comma operators), the
|
||||
order of evaluation of sub-expressions and the order in which side effects take place
|
||||
are both unspecified.
|
||||
\linebreak
|
||||
|
||||
Some operators (the unary \textasciitilde operator, and the binary operators \textless
|
||||
\textless, \textgreater\textgreater, \&, \^, and \textbar, collectively describe bitwise
|
||||
operators) are required to have operands that are either integer, or floating point with
|
||||
zero points of decimal precision.
|
||||
\linebreak
|
||||
|
||||
If an exceptional condition occurs during the evaluation of an expression (that is, if
|
||||
the result is not mathematically defined or not in the range or representable values for
|
||||
its type), the behavior is undefined.
|
||||
|
||||
%% ->-> Primary expressions %%
|
||||
\subsubsection{Primary expressions}
|
||||
\paragraph*{Syntax}
|
||||
\begin{lstlisting}[language=bnf]
|
||||
primary-expression ::= identifier
|
||||
| constant
|
||||
| string-literal
|
||||
( expression )
|
||||
\end{lstlisting}
|
||||
\paragraph*{Semantics}
|
||||
An identifier is a primary expression, provided it has been declared as designating an
|
||||
object(in which case it is an lvalue) or a function(in which case it is a function
|
||||
designator).
|
||||
\linebreak
|
||||
|
||||
A constant is a primary expression. Its type depends on its form and value.
|
||||
\linebreak
|
||||
|
||||
A string literal is a primary expression. It is an lvalue.
|
||||
\linebreak
|
||||
|
||||
A parenthesized expression is a primary expression. Its type and value identical to
|
||||
those of the unparenthesized expression. It is an lvalue, a function designator, or a
|
||||
void expression if the unparenthesized expression is, respectively, an lvalue, a
|
||||
function designator, or a void expression.
|
||||
|
||||
%% ->-> Constant expressions %%
|
||||
\subsubsection{Constant expressions}
|
||||
\paragraph*{Syntax}
|
||||
\begin{lstlisting}[language=bnf]
|
||||
constant-expression ::= conditional-expression
|
||||
\end{lstlisting}
|
||||
\paragraph*{Description}
|
||||
A constant expression can be evaluated during translation rather than runtime, and
|
||||
accordingly may be used in any place that a constant may be.
|
||||
\paragraph*{Constraints}
|
||||
\begin{itemize}
|
||||
\item Constant expressions shall not contain assignment, increment, decrement,
|
||||
function-call, or comma operators, except when contained within a subexpression
|
||||
that is not evaluated.
|
||||
\item Each constant expression shall evaluate to a constant that is in range of
|
||||
representable values for its type.
|
||||
\end{itemize}
|
||||
\paragraph*{Semantics}
|
||||
An expression that evaluates to a constant is required in several contexts. If a floating
|
||||
point expression is evaluated in the translation environment, the arithmetic precision range
|
||||
shall be as great is if the expression were being evaluated in the execution environment.
|
||||
\linebreak
|
||||
|
||||
An integer constant expression shall have integer type and shall only have operands that
|
||||
are integer constants, enumeration constants, character constants, and floating constants
|
||||
that are the immediate operand of casts. Cast operators in an integer constant expression
|
||||
shall only convert arithmetic types to integer types.
|
||||
\linebreak
|
||||
|
||||
More latitude is permitted for constant expressions in initializers. Such a constant expression
|
||||
shall be, or evaluate to an arithmetic constant expression.
|
||||
\linebreak
|
||||
|
||||
An arithmetic constant expression shall have arithmetic type and shall only have operands that
|
||||
are integer constants, floating constants, enumeration constants, and character constants. Cast
|
||||
operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic
|
||||
types.
|
||||
\linebreak
|
||||
|
||||
An implementation may accept other forms of constant expressions.
|
||||
\linebreak
|
||||
|
||||
The semantic rules for the evaluation of a constant expression are the same as for nonconstant
|
||||
expressions.
|
||||
|
||||
|
||||
\bibliographystyle{abbrv}
|
||||
\bibliography{main}
|
||||
|
||||
\end{document}
|
File diff suppressed because it is too large
Load diff
121
fold.h
Normal file
121
fold.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
#ifndef GMQCC_FOLD_HDR
|
||||
#define GMQCC_FOLD_HDR
|
||||
#include "lexer.h"
|
||||
#include "gmqcc.h"
|
||||
|
||||
struct ir_builder;
|
||||
struct ir_value;
|
||||
|
||||
struct ast_function;
|
||||
struct ast_ifthen;
|
||||
struct ast_ternary;
|
||||
struct ast_expression;
|
||||
struct ast_value;
|
||||
|
||||
struct parser_t;
|
||||
|
||||
struct fold {
|
||||
fold();
|
||||
fold(parser_t *parser);
|
||||
~fold();
|
||||
|
||||
// Bitmask describing which branches of a conditional to take after folding.
|
||||
// Zero indicates all the branches can be removed.
|
||||
// ON_TRUE means ON_FALSE can be removed.
|
||||
// ON_FALSE means ON_TRUE can be removed.
|
||||
// ON_TRUE | ON_FALSE means nothing can be removed.
|
||||
enum {
|
||||
ON_TRUE = 1 << 0,
|
||||
ON_FALSE = 1 << 1,
|
||||
};
|
||||
|
||||
bool generate(ir_builder *ir);
|
||||
ast_expression *op(const oper_info *info, ast_expression **opexprs);
|
||||
ast_expression *intrinsic(const char *intrinsic, size_t n_args, ast_expression **args);
|
||||
|
||||
static uint32_t cond_ternary(ast_value *condval, ast_ternary *branch);
|
||||
static uint32_t cond_ifthen(ast_value *condval, ast_ifthen *branch);
|
||||
|
||||
static ast_expression *superfluous(ast_expression *left, ast_expression *right, int op);
|
||||
static ast_expression *binary(lex_ctx_t ctx, int op, ast_expression *left, ast_expression *right);
|
||||
|
||||
ast_expression *constgen_float(qcfloat_t value, bool inexact);
|
||||
ast_expression *constgen_vector(vec3_t value);
|
||||
ast_expression *constgen_string(const char *str, bool translate);
|
||||
ast_expression *constgen_string(const std::string &str, bool translate);
|
||||
|
||||
ast_value *imm_float(size_t index) const { return m_imm_float[index]; }
|
||||
ast_value *imm_vector(size_t index) const { return m_imm_vector[index]; }
|
||||
|
||||
protected:
|
||||
static qcfloat_t immvalue_float(ast_value *value);
|
||||
static vec3_t immvalue_vector(ast_value *value);
|
||||
static const char *immvalue_string(ast_value *value);
|
||||
|
||||
lex_ctx_t ctx();
|
||||
|
||||
bool immediate_true(ast_value *v);
|
||||
|
||||
bool check_except_float_impl(void (*callback)(void), ast_value *a, ast_value *b);
|
||||
bool check_inexact_float(ast_value *a, ast_value *b);
|
||||
|
||||
ast_expression *op_mul_vec(vec3_t vec, ast_value *sel, const char *set);
|
||||
ast_expression *op_neg(ast_value *a);
|
||||
ast_expression *op_not(ast_value *a);
|
||||
ast_expression *op_add(ast_value *a, ast_value *b);
|
||||
ast_expression *op_sub(ast_value *a, ast_value *b);
|
||||
ast_expression *op_mul(ast_value *a, ast_value *b);
|
||||
ast_expression *op_div(ast_value *a, ast_value *b);
|
||||
ast_expression *op_mod(ast_value *a, ast_value *b);
|
||||
ast_expression *op_bor(ast_value *a, ast_value *b);
|
||||
ast_expression *op_band(ast_value *a, ast_value *b);
|
||||
ast_expression *op_xor(ast_value *a, ast_value *b);
|
||||
ast_expression *op_lshift(ast_value *a, ast_value *b);
|
||||
ast_expression *op_rshift(ast_value *a, ast_value *b);
|
||||
ast_expression *op_andor(ast_value *a, ast_value *b, float expr);
|
||||
ast_expression *op_tern(ast_value *a, ast_value *b, ast_value *c);
|
||||
ast_expression *op_exp(ast_value *a, ast_value *b);
|
||||
ast_expression *op_lteqgt(ast_value *a, ast_value *b);
|
||||
ast_expression *op_ltgt(ast_value *a, ast_value *b, bool lt);
|
||||
ast_expression *op_cmp(ast_value *a, ast_value *b, bool ne);
|
||||
ast_expression *op_bnot(ast_value *a);
|
||||
ast_expression *op_cross(ast_value *a, ast_value *b);
|
||||
ast_expression *op_length(ast_value *a);
|
||||
|
||||
ast_expression *intrinsic_isfinite(ast_value *a);
|
||||
ast_expression *intrinsic_isinf(ast_value *a);
|
||||
ast_expression *intrinsic_isnan(ast_value *a);
|
||||
ast_expression *intrinsic_isnormal(ast_value *a);
|
||||
ast_expression *intrinsic_signbit(ast_value *a);
|
||||
ast_expression *intrinsic_acosh(ast_value *a);
|
||||
ast_expression *intrinsic_asinh(ast_value *a);
|
||||
ast_expression *intrinsic_atanh(ast_value *a);
|
||||
ast_expression *intrinsic_exp(ast_value *a);
|
||||
ast_expression *intrinsic_exp2(ast_value *a);
|
||||
ast_expression *intrinsic_expm1(ast_value *a);
|
||||
ast_expression *intrinsic_pow(ast_value *lhs, ast_value *rhs);
|
||||
ast_expression *intrinsic_mod(ast_value *lhs, ast_value *rhs);
|
||||
ast_expression *intrinsic_fabs(ast_value *a);
|
||||
|
||||
ast_expression* intrinsic_nan(void);
|
||||
ast_expression* intrinsic_epsilon(void);
|
||||
ast_expression* intrinsic_inf(void);
|
||||
|
||||
static qcfloat_t immvalue_float(ir_value *value);
|
||||
static vec3_t immvalue_vector(ir_value *value);
|
||||
|
||||
static uint32_t cond(ast_value *condval, ast_ifthen *branch);
|
||||
|
||||
private:
|
||||
friend struct intrin;
|
||||
|
||||
std::vector<ast_value*> m_imm_float;
|
||||
std::vector<ast_value*> m_imm_vector;
|
||||
std::vector<ast_value*> m_imm_string;
|
||||
hash_table_t *m_imm_string_untranslate; /* map<string, ast_value*> */
|
||||
hash_table_t *m_imm_string_dotranslate; /* map<string, ast_value*> */
|
||||
parser_t *m_parser;
|
||||
bool m_initialized;
|
||||
};
|
||||
|
||||
#endif
|
312
fs.c
312
fs.c
|
@ -1,312 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Dale Weiler
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "gmqcc.h"
|
||||
|
||||
/*
|
||||
* This is essentially a "wrapper" interface around standard C's IO
|
||||
* library. There is two reason we implement this, 1) visual studio
|
||||
* hearts for "secure" varations, as part of it's "Security Enhancements
|
||||
* in the CRT" (http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx).
|
||||
* 2) But one of the greater reasons is for the possibility of large file
|
||||
* support in the future. I don't expect to reach the 2GB limit any
|
||||
* time soon (mainly because that would be insane). But when it comes
|
||||
* to adding support for some other larger IO tasks (in the test-suite,
|
||||
* or even the QCVM we'll need it). There is also a third possibility of
|
||||
* building .dat files directly from zip files (which would be very cool
|
||||
* at least I think so).
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#include <crtdbg.h> /* _CrtSetReportMode, _CRT_ASSERT */
|
||||
/* {{{ */
|
||||
/*
|
||||
* Visual Studio has security CRT features which I actually want to support
|
||||
* if we ever port to Windows 8, and want GMQCC to be API safe.
|
||||
*
|
||||
* We handle them here, for all file-operations.
|
||||
*/
|
||||
|
||||
static void file_exception (
|
||||
const wchar_t *expression,
|
||||
const wchar_t *function,
|
||||
const wchar_t *file,
|
||||
unsigned int line,
|
||||
uintptr_t reserved
|
||||
) {
|
||||
wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression);
|
||||
wprintf(L"Aborting ...\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void file_init() {
|
||||
static bool init = false;
|
||||
|
||||
if (init)
|
||||
return;
|
||||
|
||||
_set_invalid_parameter_handler(&file_exception);
|
||||
|
||||
/*
|
||||
* Turnoff the message box for CRT asserations otherwise
|
||||
* we don't get the error reported to the console as we should
|
||||
* otherwise get.
|
||||
*/
|
||||
_CrtSetReportMode(_CRT_ASSERT, 0);
|
||||
init = !init;
|
||||
}
|
||||
|
||||
|
||||
FILE *fs_file_open(const char *filename, const char *mode) {
|
||||
FILE *handle = NULL;
|
||||
file_init();
|
||||
|
||||
return (fopen_s(&handle, filename, mode) != 0) ? NULL : handle;
|
||||
}
|
||||
|
||||
size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) {
|
||||
file_init();
|
||||
return fread_s(buffer, size*count, size, count, fp);
|
||||
}
|
||||
|
||||
int fs_file_printf(FILE *fp, const char *format, ...) {
|
||||
int rt;
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
|
||||
file_init();
|
||||
rt = vfprintf_s(fp, format, va);
|
||||
va_end (va);
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
#else
|
||||
/* {{{ */
|
||||
/*
|
||||
* All other compilers/platforms that don't restrict insane policies on
|
||||
* IO for no aparent reason.
|
||||
*/
|
||||
FILE *fs_file_open(const char *filename, const char *mode) {
|
||||
return fopen(filename, mode);
|
||||
}
|
||||
|
||||
size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) {
|
||||
return fread(buffer, size, count, fp);
|
||||
}
|
||||
|
||||
int fs_file_printf(FILE *fp, const char *format, ...) {
|
||||
int rt;
|
||||
va_list va;
|
||||
va_start(va, format);
|
||||
rt = vfprintf(fp, format, va);
|
||||
va_end (va);
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These are implemented as just generic wrappers to keep consistency in
|
||||
* the API. Not as macros though
|
||||
*/
|
||||
void fs_file_close(FILE *fp) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
size_t fs_file_write (
|
||||
const void *buffer,
|
||||
size_t size,
|
||||
size_t count,
|
||||
FILE *fp
|
||||
) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return fwrite(buffer, size, count, fp);
|
||||
}
|
||||
|
||||
int fs_file_error(FILE *fp) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return ferror(fp);
|
||||
}
|
||||
|
||||
int fs_file_getc(FILE *fp) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return fgetc(fp);
|
||||
}
|
||||
|
||||
int fs_file_puts(FILE *fp, const char *str) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return fputs(str, fp);
|
||||
}
|
||||
|
||||
int fs_file_seek(FILE *fp, long int off, int whence) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return fseek(fp, off, whence);
|
||||
}
|
||||
|
||||
long int fs_file_tell(FILE *fp) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return ftell(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements libc getline for systems that don't have it, which is
|
||||
* assmed all. This works the same as getline().
|
||||
*/
|
||||
int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
|
||||
int chr;
|
||||
int ret;
|
||||
char *pos;
|
||||
|
||||
if (!lineptr || !n || !stream)
|
||||
return -1;
|
||||
if (!*lineptr) {
|
||||
if (!(*lineptr = (char*)mem_a((*n=64))))
|
||||
return -1;
|
||||
}
|
||||
|
||||
chr = *n;
|
||||
pos = *lineptr;
|
||||
|
||||
for (;;) {
|
||||
int c = fs_file_getc(stream);
|
||||
|
||||
if (chr < 2) {
|
||||
*n += (*n > 16) ? *n : 64;
|
||||
chr = *n + *lineptr - pos;
|
||||
if (!(*lineptr = (char*)mem_r(*lineptr,*n)))
|
||||
return -1;
|
||||
pos = *n - chr + *lineptr;
|
||||
}
|
||||
|
||||
if (ferror(stream))
|
||||
return -1;
|
||||
if (c == EOF) {
|
||||
if (pos == *lineptr)
|
||||
return -1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
*pos++ = c;
|
||||
chr--;
|
||||
if (c == '\n')
|
||||
break;
|
||||
}
|
||||
*pos = '\0';
|
||||
return (ret = pos - *lineptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we implement some directory functionality. Windows lacks dirent.h
|
||||
* this is such a pisss off, we implement it here.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
DIR *fs_dir_open(const char *name) {
|
||||
DIR *dir = (DIR*)mem_a(sizeof(DIR) + strlen(name));
|
||||
if (!dir)
|
||||
return NULL;
|
||||
|
||||
util_strncpy(dir->dd_name, name, strlen(name));
|
||||
return dir;
|
||||
}
|
||||
|
||||
int fs_dir_close(DIR *dir) {
|
||||
FindClose((HANDLE)dir->dd_handle);
|
||||
mem_d ((void*)dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dirent *fs_dir_read(DIR *dir) {
|
||||
WIN32_FIND_DATA info;
|
||||
struct dirent *data;
|
||||
int rets;
|
||||
|
||||
if (!dir->dd_handle) {
|
||||
char *dirname;
|
||||
if (*dir->dd_name) {
|
||||
size_t n = strlen(dir->dd_name);
|
||||
if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) {
|
||||
util_strncpy(dirname, dir->dd_name, n);
|
||||
util_strncpy(dirname + n, "\\*.*", 4); /* 4 + 1 */
|
||||
}
|
||||
} else {
|
||||
if (!(dirname = util_strdup("\\*.*")))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dir->dd_handle = (long)FindFirstFile(dirname, &info);
|
||||
mem_d(dirname);
|
||||
rets = !(!dir->dd_handle);
|
||||
} else if (dir->dd_handle != -11) {
|
||||
rets = FindNextFile ((HANDLE)dir->dd_handle, &info);
|
||||
} else {
|
||||
rets = 0;
|
||||
}
|
||||
|
||||
if (!rets)
|
||||
return NULL;
|
||||
|
||||
if ((data = (struct dirent*)mem_a(sizeof(struct dirent)))) {
|
||||
util_strncpy(data->d_name, info.cFileName, FILENAME_MAX - 1);
|
||||
data->d_name[FILENAME_MAX - 1] = '\0'; /* terminate */
|
||||
data->d_namlen = strlen(data->d_name);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
int fs_dir_change(const char *path) {
|
||||
return !SetCurrentDirectory(path);
|
||||
}
|
||||
|
||||
int fs_dir_make(const char *path) {
|
||||
return !CreateDirectory(path, NULL);
|
||||
}
|
||||
#else
|
||||
# if !defined(__MINGW32__)
|
||||
# include <sys/stat.h> /* mkdir */
|
||||
|
||||
int fs_dir_make(const char *path) {
|
||||
return mkdir(path, 0700);
|
||||
}
|
||||
# else
|
||||
int fs_dir_make(const char *path) {
|
||||
return mkdir(path);
|
||||
}
|
||||
# endif /*! !defined(__MINGW32__) */
|
||||
|
||||
DIR *fs_dir_open(const char *name) {
|
||||
return opendir(name);
|
||||
}
|
||||
|
||||
int fs_dir_close(DIR *dir) {
|
||||
return closedir(dir);
|
||||
}
|
||||
|
||||
struct dirent *fs_dir_read(DIR *dir) {
|
||||
return readdir(dir);
|
||||
}
|
||||
|
||||
#endif /*! defined(_WIN32) && !defined(__MINGW32__) */
|
File diff suppressed because it is too large
Load diff
|
@ -1,267 +1,745 @@
|
|||
# This is an example INI file that can be used to set compiler options
|
||||
# without the rquirement for supplying them as arguments on the command
|
||||
# line, this can be coupled with progs.src. To utilize this file there
|
||||
# are two options availble, if it's named "gmqcc.ini" or "gmqcc.cfg" and
|
||||
# the file exists in the directory that GMQCC is invoked from, the compiler
|
||||
# will implicitally find and execute regardless. For more freedom you may
|
||||
# use -config=<file> from the command line to specify a more explicit
|
||||
# directory/name.ext for the configuration file.
|
||||
#This configuration file is similar to a regular .ini file. Comments start
|
||||
#with hashtags or semicolons, sections are written in square brackets and
|
||||
#in each section there can be arbitrary many key-value pairs.
|
||||
|
||||
#There are 3 sections currently: ‘flags’, ‘warnings’, ‘optimizations’.
|
||||
#They contain a list of boolean values of the form ‘VARNAME = true’ or
|
||||
#‘VARNAME = false’. The variable names are the same as for the corre‐
|
||||
#sponding -W, -f or -O flag written with only capital letters and dashes
|
||||
#replaced by underscores.
|
||||
|
||||
# These are common compiler flags usually represented via the -f prefix
|
||||
# from the command line.
|
||||
[flags]
|
||||
# Enabling this can potentially reduces code size by overlapping
|
||||
# locals where possible.
|
||||
OVERLAP_LOCALS = false
|
||||
#Add some additional characters to the string table in order to
|
||||
#compensate for a wrong boundcheck in some specific version of the
|
||||
#darkplaces engine.
|
||||
|
||||
# in some older versions of the Darkplaces engine the string table
|
||||
# size is computed wrong causing compiled progs.dat to fail to load
|
||||
# Enabling this works around the bug by writing a few additional
|
||||
# null bytes to the end of the string table to compensate.
|
||||
DARKPLACES_STRING_TABLE_BUG = false
|
||||
DARKPLACES_STRING_TABLE_BUG = true
|
||||
|
||||
# Enabling this corrects the assignment of vector field pointers via
|
||||
# subsituting STORE_FLD with STORE_V.
|
||||
ADJUST_VECTOR_FIELDS = true
|
||||
|
||||
# Enabling this allows the use of the FTEQ preprocessor, as well as
|
||||
# additional preprocessing directives such as #error and #warning.
|
||||
FTEPP = true
|
||||
#When assigning to field pointers of type .vector the common be‐
|
||||
#haviour in compilers like fteqcc is to only assign the x-compo‐
|
||||
#nent of the pointer. This means that you can use the vector as
|
||||
#such, but you cannot use its y and z components directly. This
|
||||
#flag fixes this behaviour. Before using it make sure your code
|
||||
#does not depend on the buggy behaviour.
|
||||
|
||||
# Enabling this relaxes switch statement semantics
|
||||
RELAXED_SWITCH = false
|
||||
ADJUST_VECTOR_FIELDS = true
|
||||
|
||||
# Enabling this allows short-circut evaluation and logic, opposed
|
||||
# to full evaluation.
|
||||
SHORT_LOGIC = false
|
||||
|
||||
# Enabling this allows perl-like evaluation/logic.
|
||||
PERL_LOGIC = true
|
||||
#Enable a partially fteqcc-compatible preprocessor. It supports
|
||||
#all the features used in the Xonotic codebase. If you need more,
|
||||
#write a ticket.
|
||||
|
||||
# Enabling this allows the use of the "translatable strings" extension
|
||||
# assisted by .po files.
|
||||
TRANSLATABLE_STRINGS = false
|
||||
FTEPP = true
|
||||
|
||||
# Enabling this prevents initializations from becoming constant unless
|
||||
# 'const' is specified as a type qualifier.
|
||||
INITIALIZED_NONCONSTANTS = false
|
||||
|
||||
# Enabling this allows function types to be assignable even if their
|
||||
# signatures are invariant of each other.
|
||||
ASSIGN_FUNCTION_TYPES = false
|
||||
#Enable some predefined macros. This only works in combination
|
||||
#with '-fftepp' and is currently not included by '-std=fteqcc'.
|
||||
#The following macros will be added:
|
||||
#
|
||||
# __LINE__
|
||||
# __FILE__
|
||||
# __COUNTER__
|
||||
# __COUNTER_LAST__
|
||||
# __RANDOM__
|
||||
# __RANDOM_LAST__
|
||||
# __DATE__
|
||||
# __TIME__
|
||||
# __FUNC__
|
||||
#
|
||||
#Note that __FUNC__ is not actually a preprocessor macro, but is
|
||||
#recognized by the parser even with the preprocessor disabled.
|
||||
#
|
||||
#Note that fteqcc also defines __NULL__ which becomes the first
|
||||
#global. Assigning it to a vector does not yield the same result
|
||||
#as in gmqcc where __NULL__ is defined to nil (See -funtyped-nil
|
||||
#), which will cause the vector to be zero in all components. With
|
||||
#fteqcc only the first component will be 0, while the other two
|
||||
#will become the first to of the global return value. This behav‐
|
||||
#ior is odd and relying on it should be discouraged, and thus is
|
||||
#not supported by gmqcc.
|
||||
|
||||
# Enabling this will allow the generation of .lno files for engine
|
||||
# virtual machine backtraces (this is enabled with -g as well).
|
||||
LNO = false
|
||||
FTEPP_PREDEFS = false
|
||||
|
||||
# Enabling this corrects ternary percedence bugs present in fteqcc.
|
||||
CORRECT_TERNARY = true
|
||||
|
||||
# Prevent the creation of _x, _y and _z progdefs for vectors
|
||||
SINGLE_VECTOR_DEFS = false
|
||||
#Enable math constant definitions. This only works in combination
|
||||
#with '-fftepp' and is currently not included by '-std=fteqcc'.
|
||||
#The following macros will be added:
|
||||
#
|
||||
# M_E
|
||||
# M_LOG2E
|
||||
# M_LOG10E
|
||||
# M_LN2
|
||||
# M_LN10
|
||||
# M_PI
|
||||
# M_PI_2
|
||||
# M_PI_4
|
||||
# M_1_PI
|
||||
# M_2_PI
|
||||
# M_2_SQRTPI
|
||||
# M_SQRT2
|
||||
# M_SQRT1_2
|
||||
# M_TAU
|
||||
|
||||
# Cast vectors to real booleans when used in logic expressions.
|
||||
# This is achieved by using NOT_V.
|
||||
CORRECT_LOGIC = false
|
||||
FTEPP_MATHDEFS = false
|
||||
|
||||
# Always treat empty strings as true. Usuall !"" yields true, because
|
||||
# the string-NOT instruction considers empty strings to be false, while
|
||||
# an empty string as condition for 'if' will be considered true, since
|
||||
# only the numerical value of the global is looked at.
|
||||
TRUE_EMPTY_STRINGS = false
|
||||
|
||||
# Opposite of the above, empty strings are always false. Similar to
|
||||
# CORRECT_LOGIC this will always use NOT_S to cast a string to a real
|
||||
# boolean value.
|
||||
FALSE_EMPTY_STRINGS = false
|
||||
#Enable indirect macro expansion. This only works in combination
|
||||
#with '-fftepp' and is currently not included by '-std=fteqcc'.
|
||||
#Enabling this behavior will allow the preprocessor to operate more
|
||||
#like the standard C preprocessor in that it will allow arguments
|
||||
#of macros which are macro-expanded to be substituted into the
|
||||
#definition of the macro. As an example:
|
||||
#
|
||||
# #define STR1(x) #x
|
||||
# #define STR2(x) STR1(x)
|
||||
# #define THE_ANSWER 42
|
||||
# #define THE_ANSWER_STR STR2(THE_ANSWER) /* "42" */
|
||||
#
|
||||
#With this enabled, an expansion of THE_ANSWER_STR will yield
|
||||
#the string "42". With this disabled an expansion of THE_ANSWER_STR
|
||||
#will yield "THE_ANSWER"
|
||||
|
||||
# Recognize utf-8 characters in character constants, and encode
|
||||
# codepoint escape sequences in strings as utf-8. This essentially allows
|
||||
# \{1234} escape sequences to be higher than 255.
|
||||
UTF8
|
||||
FTEPP_INDIRECT_EXPANSION = false
|
||||
|
||||
# When a warning is printed and it is set to be treated as error via
|
||||
# a -Werror switch, compilation will be stopped, unless this is false.
|
||||
# When this is false, the rest of the code will be compiled, and at the end
|
||||
# the file and line of the first warning will be shown.
|
||||
BAIL_ON_WERROR = true
|
||||
|
||||
# Allow loops and switches to be labeled and break and continue to take an
|
||||
# optional label to target a specific loop/switch.
|
||||
LOOP_LABELS = false
|
||||
#Allow switch cases to use non constant variables.
|
||||
|
||||
# Enable the 'nil' null constant, which has no type. It can be used as the
|
||||
# right hand of any assignment regardless of the required type.
|
||||
UNTYPED_NIL = false
|
||||
RELAXED_SWITCH = true
|
||||
|
||||
# Be "permissive". For instance, when -funtyped-nil is used, this allows local
|
||||
# variables with the name 'nil' to be declared.
|
||||
PREMISSIVE = false
|
||||
|
||||
# Allow vararg access from within QC of the form: ...(argnumber, type)
|
||||
VARIADIC_ARGS = true
|
||||
#Perform early out in logical AND and OR expressions. The final
|
||||
#result will be either a 0 or a 1, see the next flag for more pos‐
|
||||
#sibilities.
|
||||
|
||||
# Most Quake VMs, including the one from FTEQW or up till recently
|
||||
# Darkplaces, do not cope well with vector instructions with overlapping
|
||||
# input and output. This option will avoid producing such code.
|
||||
LEGACY_VECTOR_MATHS = true
|
||||
SHORT_LOGIC = true
|
||||
|
||||
# Builtin-numbers are usually just immediate constants.
|
||||
# The following allows whole expressions to be used, as long as they
|
||||
# are compile-time constant.
|
||||
EXPRESSIONS_FOR_BUILTINS = false
|
||||
|
||||
# These are all the warnings, usually present via the -W prefix from
|
||||
# the command line.
|
||||
#In many languages, logical expressions perform early out in a
|
||||
#special way: If the left operand of an AND yeilds true, or the
|
||||
#one of an OR yields false, the complete expression evaluates to
|
||||
#the right side. Thus ‘true && 5’ evaluates to 5 rather than 1.
|
||||
|
||||
PERL_LOGIC = false
|
||||
|
||||
|
||||
#Enable the underscore intrinsic: Using ‘_("A string constant")’
|
||||
#will cause the string immediate to get a name with a "dotrans‐
|
||||
#late_" prefix. The darkplaces engine recognizes these and trans‐
|
||||
#lates them in a way similar to how gettext works.
|
||||
|
||||
TRANSLATABLE_STRINGS = true
|
||||
|
||||
|
||||
#Don't implicitly convert initialized variables to constants. With
|
||||
#this flag, the const keyword is required to make a constant.
|
||||
|
||||
INITIALIZED_NONCONSTANTS = false
|
||||
|
||||
|
||||
#If this flag is not set, (and it is set by default in the qcc and
|
||||
#fteqcc standards), assigning function pointers of mismatching
|
||||
#signatures will result in an error rather than a warning.
|
||||
|
||||
ASSIGN_FUNCTION_TYPES = true
|
||||
|
||||
|
||||
#Produce a linenumber file along with the output .dat file.
|
||||
|
||||
LNO = false
|
||||
|
||||
|
||||
#Use C's operator precedence for ternary expressions. Unless your
|
||||
#code depends on fteqcc-compatible behaviour, you'll want to use
|
||||
#this option.
|
||||
|
||||
CORRECT_TERNARY = true
|
||||
|
||||
|
||||
#Normally vectors generate 4 defs, once for the vector, and once
|
||||
#for its components with _x, _y, _z suffixes. This option prevents
|
||||
#components from being listed.
|
||||
|
||||
|
||||
SINGLE_VECTOR_DEFS = true
|
||||
|
||||
|
||||
#Most QC compilers translate ‘if(a_vector)’ directly as an IF on
|
||||
#the vector, which means only the x-component is checked. This
|
||||
#option causes vectors to be cast to actual booleans via a NOT_V
|
||||
#and, if necessary, a NOT_F chained to it.
|
||||
#
|
||||
# if (a_vector) // becomes
|
||||
# if not(!a_vector)
|
||||
# // likewise
|
||||
# a = a_vector && a_float // becomes
|
||||
# a = !!a_vector && a_float
|
||||
|
||||
CORRECT_LOGIC = true
|
||||
|
||||
|
||||
#An empty string is considered to be true everywhere. The NOT_S
|
||||
#instruction usually considers an empty string to be false, this
|
||||
#option effectively causes the unary not in strings to use NOT_F
|
||||
#instead.
|
||||
|
||||
TRUE_EMPTY_STRINGS = false
|
||||
|
||||
|
||||
#An empty string is considered to be false everywhere. This means
|
||||
#loops and if statements which depend on a string will perform a
|
||||
#NOT_S instruction on the string before using it.
|
||||
|
||||
FALSE_EMPTY_STRINGS = true
|
||||
|
||||
|
||||
#Enable utf8 characters. This allows utf-8 encoded character con‐
|
||||
#stants, and escape sequence codepoints in the valid utf-8 range.
|
||||
#Effectively enabling escape sequences like '\{x2211}'.
|
||||
|
||||
UTF8 = true
|
||||
|
||||
|
||||
#When a warning is treated as an error, and this option is set
|
||||
#(which it is by default), it is like any other error and will
|
||||
#cause compilation to stop. When disabling this flag by using
|
||||
#-fno-bail-on-werror, compilation will continue until the end, but
|
||||
#no output is generated. Instead the first such error message's
|
||||
#context is shown.
|
||||
|
||||
BAIL_ON_WERROR = false
|
||||
|
||||
|
||||
#Allow loops to be labeled, and allow 'break' and 'continue' to
|
||||
#take an optional label to decide which loop to actually jump out
|
||||
#of or continue.
|
||||
#
|
||||
# for :outer (i = 0; i < n; ++i) {
|
||||
# while (inner) {
|
||||
# ...;
|
||||
# if (something)
|
||||
# continue outer;
|
||||
# }
|
||||
# }
|
||||
|
||||
LOOP_LABELS = true
|
||||
|
||||
|
||||
#Adds a global named 'nil' which is of no type and can be assigned
|
||||
#to anything. No typechecking will be performed on assignments.
|
||||
#Assigning to it is forbidden, using it in any other kind of
|
||||
#expression is also not allowed.
|
||||
#
|
||||
#Note that this is different from fteqcc's __NULL__: In fteqcc,
|
||||
#__NULL__ maps to the integer written as '0i'. It's can be
|
||||
#assigned to function pointers and integers, but it'll error about
|
||||
#invalid instructions when assigning it to floats without enabling
|
||||
#the FTE instruction set. There's also a bug which allows it to be
|
||||
#assigned to vectors, for which the source will be the global at
|
||||
#offset 0, meaning the vector's y and z components will contain
|
||||
#the OFS_RETURN x and y components.#
|
||||
#
|
||||
#In that gmqcc the nil global is an actual global filled with
|
||||
#zeroes, and can be assigned to anything including fields, vectors
|
||||
#or function pointers, and they end up becoming zeroed.
|
||||
|
||||
|
||||
UNTYPED_NIL = true
|
||||
|
||||
|
||||
#Various effects, usually to weaken some conditions.
|
||||
# with -funtyped-nil
|
||||
# Allow local variables named ‘nil’. (This will not
|
||||
# allow declaring a global of that name.)
|
||||
|
||||
PERMISSIVE = false
|
||||
|
||||
|
||||
#Allow variadic parameters to be accessed by QC code. This can be
|
||||
#achieved via the '...' function, which takes a parameter index
|
||||
#and a typename.
|
||||
#
|
||||
#Example:
|
||||
#
|
||||
# void vafunc(string...count) {
|
||||
# float i;
|
||||
# for (i = 0; i < count; ++i)
|
||||
# print(...(i, string), "\n");
|
||||
# }
|
||||
|
||||
VARIADIC_ARGS = true
|
||||
|
||||
|
||||
#Most Quake VMs, including the one from FTEQW or up till recently
|
||||
#Darkplaces, do not cope well with vector instructions with over‐
|
||||
#lapping input and output. This option will avoid producing such
|
||||
#code.
|
||||
|
||||
LEGACY_VECTOR_MATHS = false
|
||||
|
||||
|
||||
#Usually builtin-numbers are just immediate constants. With this
|
||||
#flag expressions can be used, as long as they are compile-time
|
||||
#constant.
|
||||
#
|
||||
#Example:
|
||||
#
|
||||
# void printA() = #1; // the usual way
|
||||
# void printB() = #2-1; // with a constant expression
|
||||
|
||||
EXPRESSIONS_FOR_BUILTINS = true
|
||||
|
||||
|
||||
#Enabiling this option will allow assigning values or expressions
|
||||
#to the return keyword as if it were a local variable of the same
|
||||
#type as the function's signature's return type.
|
||||
#
|
||||
#Example:
|
||||
#
|
||||
# float bar() { return 1024; }
|
||||
# float fun() {
|
||||
# return = bar();
|
||||
# return; // returns value of bar (this can be omitted)
|
||||
# }
|
||||
|
||||
RETURN_ASSIGNMENTS = true
|
||||
|
||||
|
||||
#When passing on varargs to a different functions, this turns some
|
||||
#static error cases into warnings. Like when the caller's varargs
|
||||
#are restricted to a different type than the callee's parameter.
|
||||
#Or a list of unrestricted varargs is passed into restricted
|
||||
#varargs.
|
||||
|
||||
UNSAFE_VARARGS = false
|
||||
|
||||
|
||||
#Always use STORE_F, LOAD_F, STOREP_F when accessing scalar variables.
|
||||
#This is somewhat incorrect assembly instruction use, but in all engines
|
||||
#they do exactly the same. This makes disassembly output harder to read,
|
||||
#breaks decompilers, but causes the output file to be better compressible.
|
||||
|
||||
TYPELESS_STORES = false
|
||||
|
||||
|
||||
#In commutative instructions, always put the lower-numbered operand first.
|
||||
#This shaves off 1 byte of entropy from all these instructions, reducing
|
||||
#compressed size of the output file.
|
||||
|
||||
SORT_OPERANDS = false
|
||||
|
||||
|
||||
#Emulate OP_STATE operations in code rather than using the instruction.
|
||||
#The desired fps can be set via -state-fps=NUM, defaults to 10.
|
||||
|
||||
EMULATE_STATE = false
|
||||
|
||||
|
||||
#Turn on arithmetic exception tests in the compiler. In constant expressions
|
||||
#which trigger exceptions like division by zero, overflow, underflow, etc,
|
||||
#the following flag will produce diagnostics for what triggered that
|
||||
#exception.
|
||||
ARITHMETIC_EXCEPTIONS = false
|
||||
|
||||
#Split vector-literals which are only used dirctly as function parameters
|
||||
#into 3 floats stored separately to reduce the number of globals at the
|
||||
#expense of additional instructions.
|
||||
SPLIT_VECTOR_PARAMETERS = false
|
||||
|
||||
#Force all expressions to be "eraseable" which permits the compiler
|
||||
#to remove unused functions, variables and statements. This is
|
||||
#equivlant to putting [[eraseable]] on all definitions. This is
|
||||
#dangerous as it breaks auto cvars, definitions for functions the
|
||||
#engine may be looking for and translatable strings. Instead, you
|
||||
#can mark a definition with [[noerase]] to prevent this from happening.
|
||||
DEFAULT_ERASEABLE = false
|
||||
|
||||
[warnings]
|
||||
# ?? Used for debugging ??
|
||||
DEBUG = false
|
||||
#Generate a warning about variables which are declared but never
|
||||
#used. This can be avoided by adding the ‘noref’ keyword in front
|
||||
#of the variable declaration. Additionally a complete section of
|
||||
#unreferenced variables can be opened using ‘#pragma noref 1’ and
|
||||
#closed via ‘#pragma noref 0’.
|
||||
|
||||
# Enables warnings about unused variables.
|
||||
UNUSED_VARIABLE = true
|
||||
UNUSED_VARIABLE = false
|
||||
|
||||
# Enables warnings about uninitialized variables.
|
||||
USED_UNINITIALIZED = true
|
||||
|
||||
# Enables warnings about the unknown control sequences in the source
|
||||
# stream.
|
||||
UNKNOWN_CONTROL_SEQUENCE = true
|
||||
#Generate a warning about vector variables which are declared but
|
||||
#components of it are never used.
|
||||
|
||||
# Enables warnings about the use of (an) extension(s).
|
||||
EXTENSIONS = true
|
||||
UNUSED_COMPONENT = false
|
||||
|
||||
# Enables warnings about redeclared fields.
|
||||
FIELD_REDECLARED = true
|
||||
#Generate a warning if it is possible that a variable can be used
|
||||
#without prior initialization. Note that this warning is not nec‐
|
||||
#essarily reliable if the initialization happens only under cer‐
|
||||
#tain conditions. The other way is not possible: that the warning
|
||||
#is not generated when uninitialized use is possible.
|
||||
|
||||
# Enables warnings about missing return values.
|
||||
MISSING_RETURN_VALUES = true
|
||||
USED_UNINITIALIZED = false
|
||||
|
||||
# Enables warnings about missing parameters for function calls.
|
||||
INVALID_PARAMETER_COUNT = true
|
||||
|
||||
# Enables warnings about locals shadowing parameters or other locals.
|
||||
LOCAL_SHADOWS = true
|
||||
#Generate an error when an unrecognized control sequence in a
|
||||
#string is used. Meaning: when there's a character after a back‐
|
||||
#slash in a string which has no known meaning.
|
||||
|
||||
# Enables warnings about constants specified as locals.
|
||||
LOCAL_CONSTANTS = true
|
||||
UNKNOWN_CONTROL_SEQUENCE = false
|
||||
|
||||
# Enables warnings about variables declared as type void.
|
||||
VOID_VARIABLES = true
|
||||
|
||||
# Enables warnings about implicitally declared function pointers.
|
||||
IMPLICIT_FUNCTION_POINTER = true
|
||||
#Warn when using special extensions which are not part of the
|
||||
#selected standard.
|
||||
|
||||
# Enables warnings for use of varadics for non-builtin functions.
|
||||
VARIADIC_FUNCTION = true
|
||||
EXTENSIONS = false
|
||||
|
||||
# Enables warnings about duplicated frame macros.
|
||||
FRAME_MACROS = true
|
||||
|
||||
# Enables warnings about effectivless statements.
|
||||
EFFECTLESS_STATEMENT = true
|
||||
#Generally QC compilers ignore redeclaration of fields. Here you
|
||||
#can optionally enable a warning.
|
||||
|
||||
# Enables warnings of "end_sys_fields" beiing declared a field.
|
||||
END_SYS_FIELDS = true
|
||||
FIELD_REDECLARED = false
|
||||
|
||||
# Enables warnings for infompatible function pointer signatures used
|
||||
# in assignment.
|
||||
ASSIGN_FUNCTION_TYPES = true
|
||||
|
||||
# Enables warnings about redefined macros in the preprocessor
|
||||
PREPROCESSOR = true
|
||||
#Functions which aren't of type void will warn if it possible to
|
||||
#reach the end without returning an actual value.
|
||||
|
||||
# Enables warnings about multi-file if statements
|
||||
MULTIFILE_IF = true
|
||||
MISSING_RETURN_VALUES = false
|
||||
|
||||
# Enables warnings about double declarations
|
||||
DOUBLE_DECLARATION = true
|
||||
|
||||
# Enables warnings about type qualifiers containing both 'var' and
|
||||
# 'const'
|
||||
CONST_VAR = true
|
||||
#Warn about a function call with an invalid number of parameters.
|
||||
|
||||
# Enables warnings about the use of multibytes characters / constants
|
||||
MULTIBYTE_CHARACTER = true
|
||||
INVALID_PARAMETER_COUNT = false
|
||||
|
||||
# Enables warnings about ternary expressions whos precedence may be
|
||||
# not what was initially expected.
|
||||
TERNARY_PRECEDENCE = true
|
||||
|
||||
# Enables warnings about unknown pragmas.
|
||||
UNKNOWN_PRAGMAS = true
|
||||
#Warn when a locally declared variable shadows variable.
|
||||
|
||||
# Enables warnings about unreachable code.
|
||||
UNREACHABLE_CODE = true
|
||||
LOCAL_SHADOWS = false
|
||||
|
||||
# Enables preprocessor "#warnings"
|
||||
CPP = true
|
||||
|
||||
# With the [[attribute]] syntax enabled, warn when an unknown
|
||||
# attribute is encountered. Its first token will be included in the
|
||||
# message.
|
||||
UNKNOWN_ATTRIBUTE = true
|
||||
#Warn when the initialization of a local variable turns the vari‐
|
||||
#able into a constant. This is default behaviour unless
|
||||
#-finitialized-nonconstants is used.
|
||||
|
||||
# Warn when declaring variables or fields with a reserved name like 'nil'
|
||||
RESERVED_NAMES = true
|
||||
LOCAL_CONSTANTS = false
|
||||
|
||||
# Warn about 'const'-qualified global variables with no initializing value.
|
||||
UNINITIALIZED_CONSTANT = true
|
||||
|
||||
# Warn about non-constant global variables with no initializing value.
|
||||
UNINITIALIZED_GLOBAL = true
|
||||
#There are only 2 known global variables of type void:
|
||||
#‘end_sys_globals’ and ‘end_sys_fields’. Any other void-variable
|
||||
#will warn.
|
||||
|
||||
# Redeclaring a 'const' as 'var' or the other way round.
|
||||
DIFFERENT_QUALIFIERS = true
|
||||
VOID_VARIABLES = false
|
||||
|
||||
# Redeclaring a function with different attributes such as
|
||||
# [[noreturn]]
|
||||
DIFFERENT_ATTRIBUTES = true
|
||||
|
||||
# Warn when a function is marked with the attribute
|
||||
# "[[deprecated]]". This flag enables a warning on calls to functions
|
||||
# marked as such.
|
||||
DEPRECATED = true
|
||||
#A global function which is not declared with the ‘var’ keyword is
|
||||
#expected to have an implementing body, or be a builtin. If nei‐
|
||||
#ther is the case, it implicitly becomes a function pointer, and a
|
||||
#warning is generated.
|
||||
|
||||
IMPLICIT_FUNCTION_POINTER = false
|
||||
|
||||
|
||||
#Currently there's no way for an in QC implemented function to
|
||||
#access variadic parameters. If a function with variadic parame‐
|
||||
#ters has an implementing body, a warning will be generated.
|
||||
|
||||
VARIADIC_FUNCTION = false
|
||||
|
||||
|
||||
#Generate warnings about ‘$frame’ commands, for instance about
|
||||
#duplicate frame definitions.
|
||||
|
||||
FRAME_MACROS = false
|
||||
|
||||
|
||||
#Warn about statements which have no effect. Any expression which
|
||||
#does not call a function or assigns a variable.
|
||||
|
||||
EFFECTLESS_STATEMENT = false
|
||||
|
||||
|
||||
#The ‘end_sys_fields’ variable is supposed to be a global variable
|
||||
#of type void. It is also recognized as a field but this will
|
||||
#generate a warning.
|
||||
|
||||
END_SYS_FIELDS = false
|
||||
|
||||
|
||||
#Warn when assigning to a function pointer with an unmatching sig‐
|
||||
#nature. This usually happens in cases like assigning the null
|
||||
#function to an entity's .think function pointer.
|
||||
|
||||
ASSIGN_FUNCTION_TYPES = false
|
||||
|
||||
|
||||
#Show warnings created using the preprocessor's '#warning' directive
|
||||
|
||||
CPP = true
|
||||
|
||||
|
||||
#Warn if there's a preprocessor #if spanning across several files.
|
||||
|
||||
MULTIFILE_IF = true
|
||||
|
||||
|
||||
#Warn about multiple declarations of globals. This seems pretty
|
||||
#common in QC code so you probably do not want this unless you
|
||||
#want to clean up your code.
|
||||
|
||||
DOUBLE_DECLARATION = false
|
||||
|
||||
|
||||
#The combination of const and var is not illegal, however differ‐
|
||||
#ent compilers may handle them differently. We were told, the
|
||||
#intention is to create a function-pointer which is not assigna‐
|
||||
#ble. This is exactly how we interpret it. However for this
|
||||
#interpretation the ‘var’ keyword is considered superfluous (and
|
||||
#philosophically wrong), so it is possible to generate a warning
|
||||
#about this.
|
||||
|
||||
CONST_VAR = true
|
||||
|
||||
|
||||
#Warn about multibyte character constants, they do not work right
|
||||
#now.
|
||||
|
||||
MULTIBYTE_CHARACTER = false
|
||||
|
||||
|
||||
#Warn if a ternary expression which contains a comma operator is
|
||||
#used without enclosing parenthesis, since this is most likely not
|
||||
#what you actually want. We recommend the -fcorrect-ternary
|
||||
#option.
|
||||
|
||||
TERNARY_PRECEDENCE = false
|
||||
|
||||
|
||||
#Warn when encountering an unrecognized ‘#pragma’ line.
|
||||
|
||||
UNKNOWN_PRAGMAS = true
|
||||
|
||||
|
||||
#Warn about unreachable code. That is: code after a return state‐
|
||||
#ment, or code after a call to a function marked as 'noreturn'.
|
||||
|
||||
UNREACHABLE_CODE = true
|
||||
|
||||
|
||||
#Enable some warnings added in order to help debugging in the com‐
|
||||
#piler. You won't need this.
|
||||
|
||||
DEBUG = false
|
||||
|
||||
|
||||
#Warn on an unknown attribute. The warning will inlclude only the
|
||||
#first token inside the enclosing attribute-brackets. This may
|
||||
#change when the actual attribute syntax is better defined.
|
||||
|
||||
UNKNOWN_ATTRIBUTE = true
|
||||
|
||||
|
||||
#Warn when using reserved names such as ‘nil’.
|
||||
|
||||
RESERVED_NAMES = true
|
||||
|
||||
|
||||
#Warn about global constants (using the ‘const’ keyword) with no
|
||||
#assigned value.
|
||||
|
||||
UNINITIALIZED_CONSTANT = true
|
||||
|
||||
|
||||
#Warn about global variables with no initializing value. This is
|
||||
#off by default, and is added mostly to help find null-values
|
||||
#which are supposed to be replaced by the untyped 'nil' constant.
|
||||
|
||||
UNINITIALIZED_GLOBAL = true
|
||||
|
||||
|
||||
#Warn when a variables is redeclared with a different qualifier.
|
||||
#For example when redeclaring a variable as 'var' which was previ‐
|
||||
#ously marked 'const'.
|
||||
|
||||
DIFFERENT_QUALIFIERS = true
|
||||
|
||||
|
||||
#Similar to the above but for attributes like ‘[[noreturn]]’.
|
||||
|
||||
DIFFERENT_ATTRIBUTES = true
|
||||
|
||||
|
||||
#Warn when a function is marked with the attribute "[[depre‐
|
||||
#cated]]". This flag enables a warning on calls to functions
|
||||
#marked as such.
|
||||
|
||||
DEPRECATED = true
|
||||
|
||||
|
||||
#Warn about possible mistakes caused by missing or wrong parenthe‐
|
||||
#sis, like an assignment in an 'if' condition when there's no
|
||||
#additional set of parens around the assignment.
|
||||
|
||||
PARENTHESIS = true
|
||||
|
||||
|
||||
#When passing variadic parameters via ...(N) it can happen that
|
||||
#incompatible types are passed to functions. This enables several
|
||||
#warnings when static typechecking cannot guarantee consistent
|
||||
#behavior.
|
||||
|
||||
UNSAFE_TYPES = true
|
||||
|
||||
|
||||
#When compiling original id1 QC there is a definition for `break`
|
||||
#which conflicts with the 'break' keyword in GMQCC. Enabling this
|
||||
#print a warning when the definition occurs. The definition is
|
||||
#ignored for both cases.
|
||||
|
||||
BREAKDEF = true
|
||||
|
||||
|
||||
#When compiling original QuakeWorld QC there are instances where
|
||||
#code overwrites constants. This is considered an error, however
|
||||
#for QuakeWorld to compile it needs to be treated as a warning
|
||||
#instead, as such this warning only works when -std=qcc.
|
||||
|
||||
CONST_OVERWRITE = true
|
||||
|
||||
|
||||
#Warn about the use of preprocessor directives inside macros.
|
||||
|
||||
DIRECTIVE_INMACRO = true
|
||||
|
||||
|
||||
#When using a function that is not explicitly defined, the compiler
|
||||
#will search its intrinsics table for something that matches that
|
||||
#function name by appending "__builtin_" to it. This behaviour may
|
||||
#be unexpected, so enabling this will produce a diagnostic when
|
||||
#such a function is resolved to a builtin.
|
||||
|
||||
BUILTINS = true
|
||||
|
||||
|
||||
#When comparing an inexact value such as `1.0/3.0' the result is
|
||||
#pathologically wrong. Enabling this will trigger a compiler warning
|
||||
#on such expressions.
|
||||
INEXACT_COMPARES = true
|
||||
|
||||
# Warn about possible problems from missing parenthesis, like an
|
||||
# assignment used as truth value without additional parens around.
|
||||
PARENTHESIS = true
|
||||
|
||||
# Finally these are all the optimizations, usually present via the -O
|
||||
# prefix from the command line.
|
||||
[optimizations]
|
||||
# Enables peephole optimizations.
|
||||
PEEPHOLE = true
|
||||
#Some general peephole optimizations. For instance the code `a = b
|
||||
#+ c` typically generates 2 instructions, an ADD and a STORE. This
|
||||
#optimization removes the STORE and lets the ADD write directly
|
||||
#into A.
|
||||
|
||||
# Enables localtemp omission optimizations.
|
||||
LOCALTEMPS = true
|
||||
PEEPHOLE = true
|
||||
|
||||
# Enables tail recrusion optimizationd.
|
||||
TAIL_RECURSION = true
|
||||
|
||||
# Enables tail-call optimizations. (Not implemented)
|
||||
TAIL_CALLS = true
|
||||
#Tail recursive function calls will be turned into loops to avoid
|
||||
#the overhead of the CALL and RETURN instructions.
|
||||
|
||||
# Every function where it is safe to do so will share its local data section
|
||||
# with the others. The criteria are currently that the function must not have
|
||||
# any possibly uninitialized locals, or local arrays regardless of how they
|
||||
# are initialized.
|
||||
OVERLAP_LOCALS = false
|
||||
TAIL_RECURSION = true
|
||||
|
||||
# Strip out the names of constants to save some space in the progs.dat
|
||||
STRIP_CONSTANT_NAMES = true
|
||||
|
||||
# Aggressivly reuse strings in the string-section
|
||||
OVERLAP_STRINGS = true
|
||||
#Make all functions which use neither local arrays nor have locals
|
||||
#which are seen as possibly uninitialized use the same local sec‐
|
||||
#tion. This should be pretty safe compared to other compilers
|
||||
#which do not check for uninitialized values properly. The problem
|
||||
#is that there's QC code out there which really doesn't initialize
|
||||
#some values. This is fine as long as this kind of optimization
|
||||
#isn't used, but also, only as long as the functions cannot be
|
||||
#called in a recursive manner. Since it's hard to know whether or
|
||||
#not an array is actually fully initialized, especially when ini‐
|
||||
#tializing it via a loop, we assume functions with arrays to be
|
||||
#too dangerous for this optimization.
|
||||
|
||||
# Have expressions which are used as function parameters evaluate directly
|
||||
# into the parameter-globals if possible.
|
||||
# This avoids a whole lot of copying.
|
||||
CALL_STORES = true
|
||||
OVERLAP_LOCALS = true
|
||||
|
||||
# Do not create a RETURN instruction at the end functions of return-type void.
|
||||
VOID_RETURN = true
|
||||
|
||||
# Turn extraction-multiplications such as (a_vector * '0 1 0')
|
||||
# into direct component accesses
|
||||
VECTOR_COMPONENTS = true
|
||||
#This promotes locally declared variables to "temps". Meaning when
|
||||
#a temporary result of an operation has to be stored somewhere, a
|
||||
#local variable which is not 'alive' at that point can be used to
|
||||
#keep the result. This can reduce the size of the global section.
|
||||
#This will not have declared variables overlap, even if it was
|
||||
#possible.
|
||||
|
||||
LOCAL_TEMPS = true
|
||||
|
||||
|
||||
#Causes temporary values which do not need to be backed up on a
|
||||
#CALL to not be stored in the function's locals-area. With this, a
|
||||
#CALL to a function may need to back up fewer values and thus exe‐
|
||||
#cute faster.
|
||||
|
||||
GLOBAL_TEMPS = true
|
||||
|
||||
|
||||
#Don't generate defs for immediate values or even declared con‐
|
||||
#stants. Meaning variables which are implicitly constant or qual‐
|
||||
#ified as such using the 'const' keyword.
|
||||
|
||||
STRIP_CONSTANT_NAMES = true
|
||||
|
||||
|
||||
#Aggressively reuse strings in the string section. When a string
|
||||
#should be added which is the trailing substring of an already
|
||||
#existing string, the existing string's tail will be returned
|
||||
#instead of the new string being added.
|
||||
#
|
||||
#For example the following code will only generate 1 string:
|
||||
#
|
||||
# print("Hello you!\n");
|
||||
# print("you!\n"); // trailing substring of "Hello you!\n"
|
||||
#
|
||||
#There's however one limitation. Strings are still processed in
|
||||
#order, so if the above print statements were reversed, this opti‐
|
||||
#mization would not happen.
|
||||
|
||||
OVERLAP_STRINGS = true
|
||||
|
||||
|
||||
#By default, all parameters of a CALL are copied into the parame‐
|
||||
#ter-globals right before the CALL instructions. This is the easi‐
|
||||
#est and safest way to translate calls, but also adds a lot of
|
||||
#unnecessary copying and unnecessary temporary values. This opti‐
|
||||
#mization makes operations which are used as a parameter evaluate
|
||||
#directly into the parameter-global if that is possible, which is
|
||||
#when there's no other CALL instruction in between.
|
||||
|
||||
CALL_STORES = true
|
||||
|
||||
|
||||
#Usually an empty RETURN instruction is added to the end of a void
|
||||
#typed function. However, additionally after every function a DONE
|
||||
#instruction is added for several reasons. (For example the qcvm's
|
||||
#disassemble switch uses it to know when the function ends.). This
|
||||
#optimization replaces that last RETURN with DONE rather than
|
||||
#adding the DONE additionally.
|
||||
|
||||
VOID_RETURN = true
|
||||
|
||||
|
||||
#Because traditional QC code doesn't allow you to access individ‐
|
||||
#ual vector components of a computed vector without storing it in
|
||||
#a local first, sometimes people multiply it by a constant like
|
||||
#‘'0 1 0'’ to get, in this case, the y component of a vector. This
|
||||
#optimization will turn such a multiplication into a direct compo‐
|
||||
#nent access. If the factor is anything other than 1, a float-mul‐
|
||||
#tiplication will be added, which is still faster than a vector
|
||||
#multiplication.
|
||||
|
||||
VECTOR_COMPONENTS = true
|
||||
|
||||
|
||||
#For constant expressions that result in dead code (such as a
|
||||
#branch whos condition can be evaluated at compile-time), this
|
||||
#will eliminate the branch and else body (if present) to produce
|
||||
#more optimal code.
|
||||
|
||||
CONST_FOLD_DCE = true
|
||||
|
||||
|
||||
#For constant expressions we can fold them to immediate values.
|
||||
#this option cannot be disabled or enabled, the compiler forces
|
||||
#it to stay enabled by ignoring the value entierly. There are
|
||||
#plans to enable some level of constant fold disabling, but right
|
||||
#now the language can't function without it. This is merley here
|
||||
#as an exercise to the reader.
|
||||
|
||||
CONST_FOLD = true
|
||||
|
|
2048
intrin.cpp
Normal file
2048
intrin.cpp
Normal file
File diff suppressed because it is too large
Load diff
478
intrin.h
478
intrin.h
|
@ -1,418 +1,74 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Dale Weiler
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef GMQCC_INTRIN_HDR
|
||||
#define GMQCC_INTRIN_HDR
|
||||
#include "gmqcc.h"
|
||||
|
||||
/*
|
||||
* Provides all the "intrinsics" / "builtins" for GMQCC. These can do
|
||||
* a few things, they can provide fall back implementations for math
|
||||
* functions if the definitions don't exist for some given engine. Or
|
||||
* then can determine definitions for existing builtins, and simply
|
||||
* wrap back to them instead. This is like a "portable" intrface that
|
||||
* is entered when -fintrin is used (causing all existing builtins to
|
||||
* be ignored by the compiler and instead interface through here.
|
||||
*/
|
||||
typedef struct {
|
||||
ast_expression *(*intrin)(parser_t *);
|
||||
const char *name;
|
||||
const char *alias;
|
||||
} intrin_t;
|
||||
struct fold;
|
||||
struct parser_t;
|
||||
|
||||
ht intrin_intrinsics() {
|
||||
static ht intrinsics = NULL;
|
||||
if (!intrinsics)
|
||||
intrinsics = util_htnew(PARSER_HT_SIZE);
|
||||
struct ast_function;
|
||||
struct ast_expression;
|
||||
struct ast_value;
|
||||
|
||||
return intrinsics;
|
||||
}
|
||||
struct intrin;
|
||||
|
||||
#define INTRIN_VAL(VALUE, NAME, FUNC, STYPE, VTYPE) \
|
||||
do { \
|
||||
(VALUE) = ast_value_new ( \
|
||||
parser_ctx(parser), \
|
||||
"__builtin_" NAME, \
|
||||
TYPE_FUNCTION \
|
||||
); \
|
||||
(VALUE)->expression.next = (ast_expression*)ast_value_new ( \
|
||||
parser_ctx(parser), \
|
||||
STYPE, \
|
||||
VTYPE \
|
||||
); \
|
||||
(FUNC) = ast_function_new ( \
|
||||
parser_ctx(parser), \
|
||||
"__builtin_" NAME, \
|
||||
(VALUE) \
|
||||
); \
|
||||
} while (0)
|
||||
|
||||
#define INTRIN_REG(FUNC, VALUE) \
|
||||
do { \
|
||||
vec_push(parser->functions, (FUNC)); \
|
||||
vec_push(parser->globals, (ast_expression*)(VALUE)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
ast_expression *intrin_func (parser_t *parser, const char *name);
|
||||
|
||||
#define QC_M_E 2.71828182845905
|
||||
|
||||
ast_expression *intrin_pow(parser_t *parser) {
|
||||
/*
|
||||
* float pow(float x, float y) {
|
||||
* float local = 1.0f;
|
||||
* while (y > 0) {
|
||||
* while (!(y & 1)) {
|
||||
* y >>= 2;
|
||||
* x *= x;
|
||||
* }
|
||||
* y--;
|
||||
* local *= x;
|
||||
* }
|
||||
* return local;
|
||||
* }
|
||||
*/
|
||||
static ast_value *value = NULL;
|
||||
|
||||
if (!value) {
|
||||
ast_value *arg1 = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT);
|
||||
ast_value *arg2 = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT);
|
||||
ast_value *local = ast_value_new(parser_ctx(parser), "local", TYPE_FLOAT);
|
||||
ast_block *body = ast_block_new(parser_ctx(parser));
|
||||
ast_block *l1b = ast_block_new(parser_ctx(parser)); /* loop 1 body */
|
||||
ast_block *l2b = ast_block_new(parser_ctx(parser)); /* looo 2 body */
|
||||
ast_loop *loop1 = NULL;
|
||||
ast_loop *loop2 = NULL;
|
||||
ast_function *func = NULL;
|
||||
|
||||
INTRIN_VAL(value, "pow", func, "<float>", TYPE_FLOAT);
|
||||
|
||||
/* arguments */
|
||||
vec_push(value->expression.params, arg1);
|
||||
vec_push(value->expression.params, arg2);
|
||||
|
||||
/* local */
|
||||
vec_push(body->locals, local);
|
||||
|
||||
/* assignment to local of value 1.0f */
|
||||
vec_push(body->exprs,
|
||||
(ast_expression*)ast_store_new (
|
||||
parser_ctx(parser),
|
||||
INSTR_STORE_F,
|
||||
(ast_expression*)local,
|
||||
(ast_expression*)parser_const_float_1(parser)
|
||||
)
|
||||
);
|
||||
|
||||
/* y >>= 2 */
|
||||
vec_push(l2b->exprs,
|
||||
(ast_expression*)ast_binstore_new (
|
||||
parser_ctx(parser),
|
||||
INSTR_STORE_F,
|
||||
INSTR_MUL_F,
|
||||
(ast_expression*)arg2,
|
||||
(ast_expression*)parser_const_float(parser, 0.25f)
|
||||
)
|
||||
);
|
||||
|
||||
/* x *= x */
|
||||
vec_push(l2b->exprs,
|
||||
(ast_expression*)ast_binstore_new (
|
||||
parser_ctx(parser),
|
||||
INSTR_STORE_F,
|
||||
INSTR_MUL_F,
|
||||
(ast_expression*)arg1,
|
||||
(ast_expression*)arg1
|
||||
)
|
||||
);
|
||||
|
||||
/* while (!(y&1)) */
|
||||
loop2 = ast_loop_new (
|
||||
parser_ctx(parser),
|
||||
NULL,
|
||||
(ast_expression*)ast_binary_new (
|
||||
parser_ctx(parser),
|
||||
INSTR_AND,
|
||||
(ast_expression*)arg2,
|
||||
(ast_expression*)parser_const_float_1(parser)
|
||||
),
|
||||
true, /* ! not */
|
||||
NULL,
|
||||
false,
|
||||
NULL,
|
||||
(ast_expression*)l2b
|
||||
);
|
||||
|
||||
/* push nested loop into loop expressions */
|
||||
vec_push(l1b->exprs, (ast_expression*)loop2);
|
||||
|
||||
/* y-- */
|
||||
vec_push(l1b->exprs,
|
||||
(ast_expression*)ast_binstore_new (
|
||||
parser_ctx(parser),
|
||||
INSTR_STORE_F,
|
||||
INSTR_SUB_F,
|
||||
(ast_expression*)arg2,
|
||||
(ast_expression*)parser_const_float_1(parser)
|
||||
)
|
||||
);
|
||||
/* local *= x */
|
||||
vec_push(l1b->exprs,
|
||||
(ast_expression*)ast_binstore_new (
|
||||
parser_ctx(parser),
|
||||
INSTR_STORE_F,
|
||||
INSTR_MUL_F,
|
||||
(ast_expression*)local,
|
||||
(ast_expression*)arg1
|
||||
)
|
||||
);
|
||||
|
||||
/* while (y > 0) */
|
||||
loop1 = ast_loop_new (
|
||||
parser_ctx(parser),
|
||||
NULL,
|
||||
(ast_expression*)ast_binary_new (
|
||||
parser_ctx(parser),
|
||||
INSTR_GT,
|
||||
(ast_expression*)arg2,
|
||||
(ast_expression*)parser_const_float_0(parser)
|
||||
),
|
||||
false,
|
||||
NULL,
|
||||
false,
|
||||
NULL,
|
||||
(ast_expression*)l1b
|
||||
);
|
||||
|
||||
/* push the loop1 into the body for the function */
|
||||
vec_push(body->exprs, (ast_expression*)loop1);
|
||||
|
||||
/* return local; */
|
||||
vec_push(body->exprs,
|
||||
(ast_expression*)ast_return_new (
|
||||
parser_ctx(parser),
|
||||
(ast_expression*)local
|
||||
)
|
||||
);
|
||||
|
||||
/* push block and register intrin for codegen */
|
||||
vec_push(func->blocks, body);
|
||||
|
||||
INTRIN_REG(func, value);
|
||||
}
|
||||
|
||||
return (ast_expression*)value;
|
||||
}
|
||||
|
||||
ast_expression *intrin_mod(parser_t *parser) {
|
||||
/*
|
||||
* float mod(float x, float y) {
|
||||
* return x - y * floor(x / y);
|
||||
* }
|
||||
*/
|
||||
static ast_value *value = NULL;
|
||||
|
||||
if (!value) {
|
||||
ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "floor"));
|
||||
ast_value *arg1 = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT);
|
||||
ast_value *arg2 = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT);
|
||||
ast_block *body = ast_block_new(parser_ctx(parser));
|
||||
ast_function *func = NULL;
|
||||
|
||||
INTRIN_VAL(value, "mod", func, "<float>", TYPE_FLOAT);
|
||||
|
||||
/* floor(x/y) */
|
||||
vec_push(call->params,
|
||||
(ast_expression*)ast_binary_new (
|
||||
parser_ctx(parser),
|
||||
INSTR_DIV_F,
|
||||
(ast_expression*)arg1,
|
||||
(ast_expression*)arg2
|
||||
)
|
||||
);
|
||||
|
||||
vec_push(body->exprs,
|
||||
(ast_expression*)ast_return_new(
|
||||
parser_ctx(parser),
|
||||
(ast_expression*)ast_binary_new(
|
||||
parser_ctx(parser),
|
||||
INSTR_SUB_F,
|
||||
(ast_expression*)arg1,
|
||||
(ast_expression*)ast_binary_new(
|
||||
parser_ctx(parser),
|
||||
INSTR_MUL_F,
|
||||
(ast_expression*)arg2,
|
||||
(ast_expression*)call
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
vec_push(value->expression.params, arg1); /* float x (for param) */
|
||||
vec_push(value->expression.params, arg2); /* float y (for param) */
|
||||
|
||||
vec_push(func->blocks, body); /* {{{ body }}} */
|
||||
|
||||
INTRIN_REG(func, value);
|
||||
}
|
||||
|
||||
return (ast_expression*)value;
|
||||
}
|
||||
|
||||
ast_expression *intrin_exp(parser_t *parser) {
|
||||
/*
|
||||
* float exp(float x) {
|
||||
* return pow(QC_M_E, x);
|
||||
* }
|
||||
*/
|
||||
static ast_value *value = NULL;
|
||||
|
||||
if (!value) {
|
||||
ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "pow"));
|
||||
ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT);
|
||||
ast_block *body = ast_block_new (parser_ctx(parser));
|
||||
ast_function *func = NULL;
|
||||
|
||||
INTRIN_VAL(value, "exp", func, "<float>", TYPE_FLOAT);
|
||||
|
||||
/* push arguments for params to call */
|
||||
vec_push(call->params, (ast_expression*)parser_const_float(parser, QC_M_E));
|
||||
vec_push(call->params, (ast_expression*)arg1);
|
||||
|
||||
/* return pow(QC_M_E, x) */
|
||||
vec_push(body->exprs,
|
||||
(ast_expression*)ast_return_new(
|
||||
parser_ctx(parser),
|
||||
(ast_expression*)call
|
||||
)
|
||||
);
|
||||
|
||||
vec_push(value->expression.params, arg1); /* float x (for param) */
|
||||
|
||||
vec_push(func->blocks, body); /* {{{ body }}} */
|
||||
|
||||
INTRIN_REG(func, value);
|
||||
}
|
||||
|
||||
return (ast_expression*)value;
|
||||
}
|
||||
|
||||
ast_expression *intrin_isnan(parser_t *parser) {
|
||||
/*
|
||||
* float isnan(float x) {
|
||||
* float local;
|
||||
* local = x;
|
||||
*
|
||||
* return (x != local);
|
||||
* }
|
||||
*/
|
||||
static ast_value *value = NULL;
|
||||
|
||||
if (!value) {
|
||||
ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT);
|
||||
ast_value *local = ast_value_new (parser_ctx(parser), "local", TYPE_FLOAT);
|
||||
ast_block *body = ast_block_new (parser_ctx(parser));
|
||||
ast_function *func = NULL;
|
||||
|
||||
INTRIN_VAL(value, "isnan", func, "<float>", TYPE_FLOAT);
|
||||
|
||||
vec_push(body->locals, local);
|
||||
vec_push(body->exprs,
|
||||
(ast_expression*)ast_store_new(
|
||||
parser_ctx(parser),
|
||||
INSTR_STORE_F,
|
||||
(ast_expression*)local,
|
||||
(ast_expression*)arg1
|
||||
)
|
||||
);
|
||||
|
||||
vec_push(body->exprs,
|
||||
(ast_expression*)ast_return_new(
|
||||
parser_ctx(parser),
|
||||
(ast_expression*)ast_binary_new(
|
||||
parser_ctx(parser),
|
||||
INSTR_NE_F,
|
||||
(ast_expression*)arg1,
|
||||
(ast_expression*)local
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
vec_push(value->expression.params, arg1);
|
||||
|
||||
vec_push(func->blocks, body);
|
||||
|
||||
INTRIN_REG(func, value);
|
||||
}
|
||||
|
||||
return (ast_expression*)value;
|
||||
}
|
||||
|
||||
static intrin_t intrinsics[] = {
|
||||
{&intrin_exp, "__builtin_exp", "exp"},
|
||||
{&intrin_mod, "__builtin_mod", "mod"},
|
||||
{&intrin_pow, "__builtin_pow", "pow"},
|
||||
{&intrin_isnan, "__builtin_isnan", "isnan"}
|
||||
struct intrin_func_t {
|
||||
ast_expression *(intrin::*function)();
|
||||
const char *name;
|
||||
const char *alias;
|
||||
size_t args;
|
||||
};
|
||||
|
||||
void intrin_intrinsics_destroy(parser_t *parser) {
|
||||
/*size_t i;*/
|
||||
(void)parser;
|
||||
util_htdel(intrin_intrinsics());
|
||||
#if 0
|
||||
for (i = 0; i < sizeof(intrinsics)/sizeof(intrin_t); i++)
|
||||
ast_value_delete( (ast_value*) intrinsics[i].intrin(parser));
|
||||
struct intrin {
|
||||
intrin() = default;
|
||||
intrin(parser_t *parser);
|
||||
|
||||
ast_expression *debug_typestring();
|
||||
ast_expression *do_fold(ast_value *val, ast_expression **exprs);
|
||||
ast_expression *func_try(size_t offset, const char *compare);
|
||||
ast_expression *func_self(const char *name, const char *from);
|
||||
ast_expression *func(const char *name);
|
||||
|
||||
protected:
|
||||
lex_ctx_t ctx() const;
|
||||
ast_function *value(ast_value **out, const char *name, qc_type vtype);
|
||||
void reg(ast_value *const value, ast_function *const func);
|
||||
|
||||
ast_expression *nullfunc();
|
||||
ast_expression *isfinite_();
|
||||
ast_expression *isinf_();
|
||||
ast_expression *isnan_();
|
||||
ast_expression *isnormal_();
|
||||
ast_expression *signbit_();
|
||||
ast_expression *acosh_();
|
||||
ast_expression *asinh_();
|
||||
ast_expression *atanh_();
|
||||
ast_expression *exp_();
|
||||
ast_expression *exp2_();
|
||||
ast_expression *expm1_();
|
||||
ast_expression *pow_();
|
||||
ast_expression *mod_();
|
||||
ast_expression *fabs_();
|
||||
ast_expression *epsilon_();
|
||||
ast_expression *nan_();
|
||||
ast_expression *inf_();
|
||||
ast_expression *ln_();
|
||||
ast_expression *log_variant(const char *name, float base);
|
||||
ast_expression *log_();
|
||||
ast_expression *log10_();
|
||||
ast_expression *log2_();
|
||||
ast_expression *logb_();
|
||||
ast_expression *shift_variant(const char *name, size_t instr);
|
||||
ast_expression *lshift();
|
||||
ast_expression *rshift();
|
||||
|
||||
void error(const char *fmt, ...);
|
||||
|
||||
private:
|
||||
parser_t *m_parser;
|
||||
fold *m_fold;
|
||||
std::vector<intrin_func_t> m_intrinsics;
|
||||
std::vector<ast_expression*> m_generated;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ast_expression *intrin_func(parser_t *parser, const char *name) {
|
||||
static bool init = false;
|
||||
size_t i = 0;
|
||||
void *find;
|
||||
|
||||
/* register the intrinsics in the hashtable for O(1) lookup */
|
||||
if (!init) {
|
||||
for (i = 0; i < sizeof(intrinsics)/sizeof(*intrinsics); i++)
|
||||
util_htset(intrin_intrinsics(), intrinsics[i].alias, &intrinsics[i]);
|
||||
|
||||
init = true; /* only once */
|
||||
}
|
||||
|
||||
/*
|
||||
* jesus fucking christ, Blub design something less fucking
|
||||
* impossible to use, like a ast_is_builtin(ast_expression *), also
|
||||
* use a hashtable :P
|
||||
*/
|
||||
if ((find = (void*)parser_find_global(parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION)
|
||||
for (i = 0; i < vec_size(parser->functions); ++i)
|
||||
if (((ast_value*)find)->name && !strcmp(parser->functions[i]->name, ((ast_value*)find)->name) && parser->functions[i]->builtin < 0)
|
||||
return (ast_expression*)find;
|
||||
|
||||
if ((find = util_htget(intrin_intrinsics(), name))) {
|
||||
/* intrinsic is in table. This will "generate the function" so
|
||||
* to speak (if it's not already generated).
|
||||
*/
|
||||
return ((intrin_t*)find)->intrin(parser);
|
||||
}
|
||||
|
||||
parseerror(parser, "need function: `%s` compiler depends on it", name);
|
||||
return NULL;
|
||||
}
|
||||
|
|
503
ir.h
503
ir.h
|
@ -1,365 +1,334 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Wolfgang Bumiller
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef GMQCC_IR_HDR
|
||||
#define GMQCC_IR_HDR
|
||||
#include "gmqcc.h"
|
||||
/* ir_value */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/*
|
||||
* Type large enough to hold all the possible IR flags. This should be
|
||||
* changed if the static assertion at the end of this file fails.
|
||||
*/
|
||||
typedef uint8_t ir_flag_t;
|
||||
|
||||
struct ir_value;
|
||||
struct ir_instr;
|
||||
struct ir_block;
|
||||
struct ir_function;
|
||||
struct ir_builder;
|
||||
|
||||
struct ir_life_entry_t {
|
||||
/* both inclusive */
|
||||
size_t start;
|
||||
size_t end;
|
||||
} ir_life_entry_t;
|
||||
};
|
||||
|
||||
struct ir_function_s;
|
||||
typedef struct ir_value_s {
|
||||
char *name;
|
||||
int vtype;
|
||||
int store;
|
||||
lex_ctx context;
|
||||
/* even the IR knows the subtype of a field */
|
||||
int fieldtype;
|
||||
/* and the output type of a function */
|
||||
int outtype;
|
||||
/* 'const' vs 'var' qualifier */
|
||||
int cvq;
|
||||
uint32_t flags;
|
||||
enum {
|
||||
IR_FLAG_HAS_ARRAYS = 1 << 0,
|
||||
IR_FLAG_HAS_UNINITIALIZED = 1 << 1,
|
||||
IR_FLAG_HAS_GOTO = 1 << 2,
|
||||
IR_FLAG_INCLUDE_DEF = 1 << 3,
|
||||
IR_FLAG_ERASABLE = 1 << 4,
|
||||
IR_FLAG_BLOCK_COVERAGE = 1 << 5,
|
||||
IR_FLAG_NOREF = 1 << 6,
|
||||
IR_FLAG_SPLIT_VECTOR = 1 << 7,
|
||||
|
||||
struct ir_instr_s **reads;
|
||||
struct ir_instr_s **writes;
|
||||
IR_FLAG_LAST,
|
||||
IR_FLAG_MASK_NO_OVERLAP = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED),
|
||||
IR_FLAG_MASK_NO_LOCAL_TEMPS = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
|
||||
};
|
||||
|
||||
/* constantvalues */
|
||||
bool hasvalue;
|
||||
struct ir_value {
|
||||
ir_value(std::string&& name, store_type storetype, qc_type vtype);
|
||||
ir_value(ir_function *owner, std::string&& name, store_type storetype, qc_type vtype);
|
||||
~ir_value();
|
||||
|
||||
ir_value *vectorMember(unsigned int member);
|
||||
|
||||
bool GMQCC_WARN setFloat(float);
|
||||
bool GMQCC_WARN setFunc(int);
|
||||
bool GMQCC_WARN setString(const char*);
|
||||
bool GMQCC_WARN setVector(vec3_t);
|
||||
bool GMQCC_WARN setField(ir_value*);
|
||||
#if 0
|
||||
bool GMQCC_WARN setInt(int);
|
||||
#endif
|
||||
|
||||
bool lives(size_t at);
|
||||
void dumpLife(int (*oprintf)(const char*, ...)) const;
|
||||
|
||||
void setCodeAddress(int32_t gaddr);
|
||||
int32_t codeAddress() const;
|
||||
|
||||
bool insertLife(size_t idx, ir_life_entry_t);
|
||||
bool setAlive(size_t position);
|
||||
bool mergeLife(const ir_value *other);
|
||||
|
||||
std::string m_name;
|
||||
|
||||
qc_type m_vtype;
|
||||
store_type m_store;
|
||||
lex_ctx_t m_context;
|
||||
qc_type m_fieldtype; // even the IR knows the subtype of a field
|
||||
qc_type m_outtype; // and the output type of a function
|
||||
int m_cvq; // 'const' vs 'var' qualifier
|
||||
ir_flag_t m_flags;
|
||||
|
||||
std::vector<ir_instr *> m_reads;
|
||||
std::vector<ir_instr *> m_writes;
|
||||
|
||||
// constant values
|
||||
bool m_hasvalue;
|
||||
union {
|
||||
float vfloat;
|
||||
int vint;
|
||||
vector vvec;
|
||||
int32_t ivec[3];
|
||||
char *vstring;
|
||||
struct ir_value_s *vpointer;
|
||||
struct ir_function_s *vfunc;
|
||||
} constval;
|
||||
qcfloat_t vfloat;
|
||||
int vint;
|
||||
vec3_t vvec;
|
||||
int32_t ivec[3];
|
||||
char *vstring;
|
||||
ir_value *vpointer;
|
||||
ir_function *vfunc;
|
||||
} m_constval;
|
||||
|
||||
struct {
|
||||
int32_t globaladdr;
|
||||
int32_t name;
|
||||
/* filled by the local-allocator */
|
||||
int32_t local;
|
||||
/* added for members */
|
||||
int32_t addroffset;
|
||||
/* to generate field-addresses early */
|
||||
int32_t fieldaddr;
|
||||
} code;
|
||||
int32_t local; // filled by the local-allocator
|
||||
int32_t addroffset; // added for members
|
||||
int32_t fieldaddr; // to generate field-addresses early
|
||||
} m_code;
|
||||
|
||||
/* for acessing vectors */
|
||||
struct ir_value_s *members[3];
|
||||
struct ir_value_s *memberof;
|
||||
// for accessing vectors
|
||||
ir_value *m_members[3];
|
||||
ir_value *m_memberof;
|
||||
|
||||
/* arrays will never overlap with temps */
|
||||
bool unique_life;
|
||||
/* temps living during a CALL must be locked */
|
||||
bool locked;
|
||||
bool callparam;
|
||||
bool m_unique_life; // arrays will never overlap with temps
|
||||
bool m_locked; // temps living during a CALL must be locked
|
||||
bool m_callparam;
|
||||
|
||||
/* For the temp allocator */
|
||||
ir_life_entry_t *life;
|
||||
} ir_value;
|
||||
std::vector<ir_life_entry_t> m_life; // For the temp allocator
|
||||
|
||||
int32_t ir_value_code_addr(const ir_value*);
|
||||
size_t size() const;
|
||||
|
||||
/* ir_value can be a variable, or created by an operation */
|
||||
ir_value* ir_value_var(const char *name, int st, int vtype);
|
||||
/* if a result of an operation: the function should store
|
||||
* it to remember to delete it / garbage collect it
|
||||
*/
|
||||
ir_value* ir_value_out(struct ir_function_s *owner, const char *name, int st, int vtype);
|
||||
void ir_value_delete(ir_value*);
|
||||
bool ir_value_set_name(ir_value*, const char *name);
|
||||
ir_value* ir_value_vector_member(ir_value*, unsigned int member);
|
||||
|
||||
bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx);
|
||||
|
||||
bool GMQCC_WARN ir_value_set_float(ir_value*, float f);
|
||||
bool GMQCC_WARN ir_value_set_func(ir_value*, int f);
|
||||
#if 0
|
||||
bool GMQCC_WARN ir_value_set_int(ir_value*, int i);
|
||||
#endif
|
||||
bool GMQCC_WARN ir_value_set_string(ir_value*, const char *s);
|
||||
bool GMQCC_WARN ir_value_set_vector(ir_value*, vector v);
|
||||
bool GMQCC_WARN ir_value_set_field(ir_value*, ir_value *fld);
|
||||
/*bool ir_value_set_pointer_v(ir_value*, ir_value* p); */
|
||||
/*bool ir_value_set_pointer_i(ir_value*, int i); */
|
||||
|
||||
/* merge an instruction into the life-range */
|
||||
/* returns false if the lifepoint was already known */
|
||||
bool ir_value_life_merge(ir_value*, size_t);
|
||||
bool ir_value_life_merge_into(ir_value*, const ir_value*);
|
||||
/* check if a value lives at a specific point */
|
||||
bool ir_value_lives(ir_value*, size_t);
|
||||
/* check if the life-range of 2 values overlaps */
|
||||
bool ir_values_overlap(const ir_value*, const ir_value*);
|
||||
|
||||
void ir_value_dump(ir_value*, int (*oprintf)(const char*,...));
|
||||
void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...));
|
||||
void dump(int (*oprintf)(const char*, ...)) const;
|
||||
};
|
||||
|
||||
/* PHI data */
|
||||
typedef struct ir_phi_entry_s
|
||||
{
|
||||
ir_value *value;
|
||||
struct ir_block_s *from;
|
||||
} ir_phi_entry_t;
|
||||
struct ir_phi_entry_t {
|
||||
ir_value *value;
|
||||
ir_block *from;
|
||||
};
|
||||
|
||||
/* instruction */
|
||||
typedef struct ir_instr_s
|
||||
{
|
||||
int opcode;
|
||||
lex_ctx context;
|
||||
ir_value* (_ops[3]);
|
||||
struct ir_block_s* (bops[2]);
|
||||
struct ir_instr {
|
||||
ir_instr(lex_ctx_t, ir_block *owner, int opcode);
|
||||
~ir_instr();
|
||||
|
||||
ir_phi_entry_t *phi;
|
||||
ir_value **params;
|
||||
int m_opcode;
|
||||
lex_ctx_t m_context;
|
||||
ir_value *(_m_ops[3]) = { nullptr, nullptr, nullptr };
|
||||
ir_block *(m_bops[2]) = { nullptr, nullptr };
|
||||
|
||||
/* For the temp-allocation */
|
||||
size_t eid;
|
||||
std::vector<ir_phi_entry_t> m_phi;
|
||||
std::vector<ir_value *> m_params;
|
||||
|
||||
/* For IFs */
|
||||
bool likely;
|
||||
// For the temp-allocation
|
||||
size_t m_eid = 0;
|
||||
|
||||
struct ir_block_s *owner;
|
||||
} ir_instr;
|
||||
// For IFs
|
||||
bool m_likely = true;
|
||||
|
||||
ir_instr* ir_instr_new(lex_ctx ctx, struct ir_block_s *owner, int opcode);
|
||||
void ir_instr_delete(ir_instr*);
|
||||
|
||||
bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx);
|
||||
|
||||
bool ir_instr_op(ir_instr*, int op, ir_value *value, bool writing);
|
||||
|
||||
void ir_instr_dump(ir_instr* in, char *ind, int (*oprintf)(const char*,...));
|
||||
ir_block *m_owner;
|
||||
};
|
||||
|
||||
/* block */
|
||||
typedef struct ir_block_s
|
||||
{
|
||||
char *label;
|
||||
lex_ctx context;
|
||||
bool final; /* once a jump is added we're done */
|
||||
struct ir_block {
|
||||
ir_block(ir_function *owner, const std::string& name);
|
||||
~ir_block();
|
||||
|
||||
ir_instr **instr;
|
||||
struct ir_block_s **entries;
|
||||
struct ir_block_s **exits;
|
||||
ir_value **living;
|
||||
ir_function *m_owner;
|
||||
std::string m_label;
|
||||
|
||||
lex_ctx_t m_context;
|
||||
bool m_final = false; /* once a jump is added we're done */
|
||||
|
||||
std::vector<ir_instr *> m_instr;
|
||||
std::vector<ir_block *> m_entries;
|
||||
std::vector<ir_block *> m_exits;
|
||||
std::vector<ir_value *> m_living;
|
||||
|
||||
/* For the temp-allocation */
|
||||
size_t entry_id;
|
||||
size_t eid;
|
||||
bool is_return;
|
||||
size_t m_entry_id = 0;
|
||||
size_t m_eid = 0;
|
||||
bool m_is_return = false;
|
||||
|
||||
struct ir_function_s *owner;
|
||||
bool m_generated = false;
|
||||
size_t m_code_start = 0;
|
||||
};
|
||||
|
||||
bool generated;
|
||||
size_t code_start;
|
||||
} ir_block;
|
||||
|
||||
ir_block* ir_block_new(struct ir_function_s *owner, const char *label);
|
||||
void ir_block_delete(ir_block*);
|
||||
|
||||
bool ir_block_set_label(ir_block*, const char *label);
|
||||
|
||||
ir_value* ir_block_create_binop(ir_block*, lex_ctx, const char *label, int op,
|
||||
ir_value *left, ir_value *right);
|
||||
ir_value* ir_block_create_unary(ir_block*, lex_ctx, const char *label, int op,
|
||||
ir_value *operand);
|
||||
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx, int op, ir_value *target, ir_value *what);
|
||||
bool GMQCC_WARN ir_block_create_store(ir_block*, lex_ctx, ir_value *target, ir_value *what);
|
||||
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx, ir_value *target, ir_value *what);
|
||||
|
||||
/* field must be of TYPE_FIELD */
|
||||
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx, const char *label, ir_value *ent, ir_value *field, int outype);
|
||||
|
||||
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx, const char *label, ir_value *entity, ir_value *field);
|
||||
ir_value* ir_block_create_binop(ir_block*, lex_ctx_t, const char *label, int op, ir_value *left, ir_value *right);
|
||||
ir_value* ir_block_create_unary(ir_block*, lex_ctx_t, const char *label, int op, ir_value *operand);
|
||||
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx_t, int op, ir_value *target, ir_value *what);
|
||||
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx_t, ir_value *target, ir_value *what);
|
||||
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx_t, const char *label, ir_value *ent, ir_value *field, qc_type outype);
|
||||
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx_t, const char *label, ir_value *entity, ir_value *field);
|
||||
bool GMQCC_WARN ir_block_create_state_op(ir_block*, lex_ctx_t, ir_value *frame, ir_value *think);
|
||||
|
||||
/* This is to create an instruction of the form
|
||||
* <outtype>%label := opcode a, b
|
||||
*/
|
||||
ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx, const char *label,
|
||||
int op, ir_value *a, ir_value *b, int outype);
|
||||
|
||||
ir_instr* ir_block_create_phi(ir_block*, lex_ctx, const char *label, int vtype);
|
||||
ir_instr* ir_block_create_phi(ir_block*, lex_ctx_t, const char *label, qc_type vtype);
|
||||
ir_value* ir_phi_value(ir_instr*);
|
||||
void ir_phi_add(ir_instr*, ir_block *b, ir_value *v);
|
||||
ir_instr* ir_block_create_call(ir_block*, lex_ctx, const char *label, ir_value *func, bool noreturn);
|
||||
ir_instr* ir_block_create_call(ir_block*, lex_ctx_t, const char *label, ir_value *func, bool noreturn);
|
||||
ir_value* ir_call_value(ir_instr*);
|
||||
void ir_call_param(ir_instr*, ir_value*);
|
||||
|
||||
bool GMQCC_WARN ir_block_create_return(ir_block*, lex_ctx, ir_value *opt_value);
|
||||
bool GMQCC_WARN ir_block_create_return(ir_block*, lex_ctx_t, ir_value *opt_value);
|
||||
|
||||
bool GMQCC_WARN ir_block_create_if(ir_block*, lex_ctx, ir_value *cond,
|
||||
bool GMQCC_WARN ir_block_create_if(ir_block*, lex_ctx_t, ir_value *cond,
|
||||
ir_block *ontrue, ir_block *onfalse);
|
||||
/* A 'goto' is an actual 'goto' coded in QC, whereas
|
||||
/*
|
||||
* A 'goto' is an actual 'goto' coded in QC, whereas
|
||||
* a 'jump' is a virtual construct which simply names the
|
||||
* next block to go to.
|
||||
* A goto usually becomes an OP_GOTO in the resulting code,
|
||||
* whereas a 'jump' usually doesn't add any actual instruction.
|
||||
*/
|
||||
bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx, ir_block *to);
|
||||
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx, ir_block *to);
|
||||
|
||||
void ir_block_dump(ir_block*, char *ind, int (*oprintf)(const char*,...));
|
||||
bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx_t, ir_block *to);
|
||||
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx_t, ir_block *to);
|
||||
|
||||
/* function */
|
||||
struct ir_function {
|
||||
ir_function(ir_builder *owner, qc_type returntype);
|
||||
~ir_function();
|
||||
|
||||
typedef struct ir_function_s
|
||||
{
|
||||
char *name;
|
||||
int outtype;
|
||||
int *params;
|
||||
ir_block **blocks;
|
||||
ir_builder *m_owner;
|
||||
|
||||
uint32_t flags;
|
||||
std::string m_name;
|
||||
qc_type m_outtype;
|
||||
std::vector<int> m_params;
|
||||
ir_flag_t m_flags = 0;
|
||||
int m_builtin = 0;
|
||||
|
||||
int builtin;
|
||||
std::vector<std::unique_ptr<ir_block>> m_blocks;
|
||||
|
||||
ir_value *value;
|
||||
|
||||
/* values generated from operations
|
||||
/*
|
||||
* values generated from operations
|
||||
* which might get optimized away, so anything
|
||||
* in there needs to be deleted in the dtor.
|
||||
*/
|
||||
ir_value **values;
|
||||
std::vector<std::unique_ptr<ir_value>> m_values;
|
||||
std::vector<std::unique_ptr<ir_value>> m_locals; /* locally defined variables */
|
||||
ir_value *m_value = nullptr;
|
||||
|
||||
/* locally defined variables */
|
||||
ir_value **locals;
|
||||
size_t m_allocated_locals = 0;
|
||||
size_t m_globaltemps = 0;
|
||||
|
||||
size_t allocated_locals;
|
||||
size_t globaltemps;
|
||||
ir_block* m_first = nullptr;
|
||||
ir_block* m_last = nullptr;
|
||||
|
||||
ir_block* first;
|
||||
ir_block* last;
|
||||
lex_ctx_t m_context;
|
||||
|
||||
lex_ctx context;
|
||||
|
||||
/* for prototypes - first we generate all the
|
||||
/*
|
||||
* for prototypes - first we generate all the
|
||||
* globals, and we remember teh function-defs
|
||||
* so we can later fill in the entry pos
|
||||
*
|
||||
* remember the ID:
|
||||
*/
|
||||
qcint code_function_def;
|
||||
qcint_t m_code_function_def = -1;
|
||||
|
||||
/* for temp allocation */
|
||||
size_t run_id;
|
||||
|
||||
struct ir_builder_s *owner;
|
||||
size_t m_run_id = 0;
|
||||
|
||||
/* vararg support: */
|
||||
size_t max_varargs;
|
||||
} ir_function;
|
||||
#define IR_FLAG_HAS_ARRAYS (1<<1)
|
||||
#define IR_FLAG_HAS_UNINITIALIZED (1<<2)
|
||||
#define IR_FLAG_HAS_GOTO (1<<3)
|
||||
#define IR_FLAG_INCLUDE_DEF (1<<4)
|
||||
#define IR_FLAG_MASK_NO_OVERLAP (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
|
||||
#define IR_FLAG_MASK_NO_LOCAL_TEMPS (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
|
||||
size_t m_max_varargs = 0;
|
||||
};
|
||||
|
||||
ir_function* ir_function_new(struct ir_builder_s *owner, int returntype);
|
||||
void ir_function_delete(ir_function*);
|
||||
|
||||
void ir_function_collect_value(ir_function*, ir_value *value);
|
||||
|
||||
bool ir_function_set_name(ir_function*, const char *name);
|
||||
|
||||
ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param);
|
||||
|
||||
ir_value* ir_function_create_local(ir_function *self, const std::string& name, qc_type vtype, bool param);
|
||||
bool GMQCC_WARN ir_function_finalize(ir_function*);
|
||||
/*
|
||||
bool ir_function_naive_phi(ir_function*);
|
||||
bool ir_function_enumerate(ir_function*);
|
||||
bool ir_function_calculate_liferanges(ir_function*);
|
||||
*/
|
||||
|
||||
ir_block* ir_function_create_block(lex_ctx ctx, ir_function*, const char *label);
|
||||
|
||||
void ir_function_dump(ir_function*, char *ind, int (*oprintf)(const char*,...));
|
||||
ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function*, const char *label);
|
||||
|
||||
/* builder */
|
||||
#define IR_HT_SIZE 1024
|
||||
typedef struct ir_builder_s
|
||||
{
|
||||
char *name;
|
||||
ir_function **functions;
|
||||
ir_value **globals;
|
||||
ir_value **fields;
|
||||
#define IR_HT_SIZE 1024
|
||||
#define IR_MAX_VINSTR_TEMPS 2
|
||||
|
||||
ht htfunctions;
|
||||
ht htglobals;
|
||||
ht htfields;
|
||||
struct ir_builder {
|
||||
ir_builder(const std::string& modulename);
|
||||
~ir_builder();
|
||||
|
||||
ir_value **extparams;
|
||||
ir_value **extparam_protos;
|
||||
ir_function *createFunction(const std::string &name, qc_type outtype);
|
||||
ir_value *createGlobal(const std::string &name, qc_type vtype);
|
||||
ir_value *createField(const std::string &name, qc_type vtype);
|
||||
ir_value *get_va_count();
|
||||
bool generate(const char *filename);
|
||||
void dump(int (*oprintf)(const char*, ...)) const;
|
||||
|
||||
/* the highest func->allocated_locals */
|
||||
size_t max_locals;
|
||||
size_t max_globaltemps;
|
||||
uint32_t first_common_local;
|
||||
uint32_t first_common_globaltemp;
|
||||
ir_value *generateExtparamProto();
|
||||
void generateExtparam();
|
||||
|
||||
const char **filenames;
|
||||
qcint *filestrings;
|
||||
/* we cache the #IMMEDIATE string here */
|
||||
qcint str_immediate;
|
||||
/* there should just be this one nil */
|
||||
ir_value *nil;
|
||||
ir_value *reserved_va_count;
|
||||
} ir_builder;
|
||||
ir_value *literalFloat(float value, bool add_to_list);
|
||||
|
||||
ir_builder* ir_builder_new(const char *modulename);
|
||||
void ir_builder_delete(ir_builder*);
|
||||
std::string m_name;
|
||||
std::vector<std::unique_ptr<ir_function>> m_functions;
|
||||
std::vector<std::unique_ptr<ir_value>> m_globals;
|
||||
std::vector<std::unique_ptr<ir_value>> m_fields;
|
||||
// for reusing them in vector-splits, TODO: sort this or use a radix-tree
|
||||
std::vector<ir_value*> m_const_floats;
|
||||
|
||||
bool ir_builder_set_name(ir_builder *self, const char *name);
|
||||
ht m_htfunctions;
|
||||
ht m_htglobals;
|
||||
ht m_htfields;
|
||||
|
||||
ir_function* ir_builder_get_function(ir_builder*, const char *fun);
|
||||
ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype);
|
||||
// extparams' ir_values reference the ones from extparam_protos
|
||||
std::vector<std::unique_ptr<ir_value>> m_extparam_protos;
|
||||
std::vector<ir_value*> m_extparams;
|
||||
|
||||
ir_value* ir_builder_get_global(ir_builder*, const char *fun);
|
||||
ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
|
||||
ir_value* ir_builder_get_field(ir_builder*, const char *fun);
|
||||
ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype);
|
||||
// the highest func->allocated_locals
|
||||
size_t m_max_locals = 0;
|
||||
size_t m_max_globaltemps = 0;
|
||||
uint32_t m_first_common_local = 0;
|
||||
uint32_t m_first_common_globaltemp = 0;
|
||||
|
||||
ir_value* ir_builder_get_va_count(ir_builder*);
|
||||
std::vector<const char*> m_filenames;
|
||||
std::vector<qcint_t> m_filestrings;
|
||||
|
||||
bool ir_builder_generate(code_t *, ir_builder *self, const char *filename);
|
||||
// we cache the #IMMEDIATE string here
|
||||
qcint_t m_str_immediate = 0;
|
||||
|
||||
void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
|
||||
// there should just be this one nil
|
||||
ir_value *m_nil;
|
||||
ir_value *m_reserved_va_count = nullptr;
|
||||
ir_value *m_coverage_func = nullptr;
|
||||
|
||||
/* some virtual instructions require temps, and their code is isolated
|
||||
* so that we don't need to keep track of their liveness.
|
||||
*/
|
||||
ir_value *m_vinstr_temp[IR_MAX_VINSTR_TEMPS];
|
||||
|
||||
/* code generator */
|
||||
std::unique_ptr<code_t> m_code;
|
||||
|
||||
private:
|
||||
qcint_t filestring(const char *filename);
|
||||
bool generateGlobal(ir_value*, bool is_local);
|
||||
bool generateGlobalFunction(ir_value*);
|
||||
bool generateGlobalFunctionCode(ir_value*);
|
||||
bool generateFunctionLocals(ir_value*);
|
||||
};
|
||||
|
||||
/*
|
||||
* This code assumes 32 bit floats while generating binary
|
||||
* Blub: don't use extern here, it's annoying and shows up in nm
|
||||
* for some reason :P
|
||||
* for some reason :P
|
||||
*/
|
||||
typedef int static_assert_is_32bit_float [(sizeof(int32_t) == 4)?1:-1];
|
||||
typedef int static_assert_is_32bit_integer[(sizeof(qcfloat) == 4)?1:-1];
|
||||
typedef int static_assert_is_32bit_float [(sizeof(int32_t) == 4) ? 1 : -1];
|
||||
typedef int static_assert_is_32bit_integer[(sizeof(qcfloat_t) == 4) ? 1 : -1];
|
||||
|
||||
/*
|
||||
* If the condition creates a situation where this becomes -1 size it means there are
|
||||
* more IR_FLAGs than the type ir_flag_t is capable of holding. So either eliminate
|
||||
* the IR flag count or change the ir_flag_t typedef to a type large enough to accomodate
|
||||
* all the flags.
|
||||
*/
|
||||
typedef int static_assert_is_ir_flag_safe [((IR_FLAG_LAST) <= (ir_flag_t)(-1)) ? 1 : -1];
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,29 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Wolfgang Bumiller
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gmqcc.h"
|
||||
#include "lexer.h"
|
||||
|
@ -40,8 +16,6 @@ static const char *keywords_qc[] = {
|
|||
"return",
|
||||
"const"
|
||||
};
|
||||
static size_t num_keywords_qc = sizeof(keywords_qc) / sizeof(keywords_qc[0]);
|
||||
|
||||
/* For fte/gmgqcc */
|
||||
static const char *keywords_fg[] = {
|
||||
"switch", "case", "default",
|
||||
|
@ -52,34 +26,33 @@ static const char *keywords_fg[] = {
|
|||
|
||||
"__builtin_debug_printtype"
|
||||
};
|
||||
static size_t num_keywords_fg = sizeof(keywords_fg) / sizeof(keywords_fg[0]);
|
||||
|
||||
/*
|
||||
* Lexer code
|
||||
*/
|
||||
|
||||
static char* *lex_filenames;
|
||||
|
||||
void lexerror(lex_file *lex, const char *fmt, ...)
|
||||
static void lexerror(lex_file *lex, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (lex)
|
||||
con_vprintmsg(LVL_ERROR, lex->name, lex->sline, "parse error", fmt, ap);
|
||||
con_vprintmsg(LVL_ERROR, lex->name, lex->sline, lex->column, "parse error", fmt, ap);
|
||||
else
|
||||
con_vprintmsg(LVL_ERROR, "", 0, "parse error", fmt, ap);
|
||||
con_vprintmsg(LVL_ERROR, "", 0, 0, "parse error", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
|
||||
static bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
|
||||
{
|
||||
bool r;
|
||||
lex_ctx ctx;
|
||||
va_list ap;
|
||||
bool r;
|
||||
lex_ctx_t ctx;
|
||||
va_list ap;
|
||||
|
||||
ctx.file = lex->name;
|
||||
ctx.line = lex->sline;
|
||||
ctx.file = lex->name;
|
||||
ctx.line = lex->sline;
|
||||
ctx.column = lex->column;
|
||||
|
||||
va_start(ap, fmt);
|
||||
r = vcompile_warning(ctx, warntype, fmt, ap);
|
||||
|
@ -87,125 +60,59 @@ bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
|
|||
return r;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
token* token_new()
|
||||
{
|
||||
token *tok = (token*)mem_a(sizeof(token));
|
||||
if (!tok)
|
||||
return NULL;
|
||||
memset(tok, 0, sizeof(*tok));
|
||||
return tok;
|
||||
}
|
||||
|
||||
void token_delete(token *self)
|
||||
{
|
||||
if (self->next && self->next->prev == self)
|
||||
self->next->prev = self->prev;
|
||||
if (self->prev && self->prev->next == self)
|
||||
self->prev->next = self->next;
|
||||
MEM_VECTOR_CLEAR(self, value);
|
||||
mem_d(self);
|
||||
}
|
||||
|
||||
token* token_copy(const token *cp)
|
||||
{
|
||||
token* self = token_new();
|
||||
if (!self)
|
||||
return NULL;
|
||||
/* copy the value */
|
||||
self->value_alloc = cp->value_count + 1;
|
||||
self->value_count = cp->value_count;
|
||||
self->value = (char*)mem_a(self->value_alloc);
|
||||
if (!self->value) {
|
||||
mem_d(self);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(self->value, cp->value, cp->value_count);
|
||||
self->value[self->value_alloc-1] = 0;
|
||||
|
||||
/* rest */
|
||||
self->ctx = cp->ctx;
|
||||
self->ttype = cp->ttype;
|
||||
memcpy(&self->constval, &cp->constval, sizeof(self->constval));
|
||||
return self;
|
||||
}
|
||||
|
||||
void token_delete_all(token *t)
|
||||
{
|
||||
token *n;
|
||||
|
||||
do {
|
||||
n = t->next;
|
||||
token_delete(t);
|
||||
t = n;
|
||||
} while(t);
|
||||
}
|
||||
|
||||
token* token_copy_all(const token *cp)
|
||||
{
|
||||
token *cur;
|
||||
token *out;
|
||||
|
||||
out = cur = token_copy(cp);
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
while (cp->next) {
|
||||
cp = cp->next;
|
||||
cur->next = token_copy(cp);
|
||||
if (!cur->next) {
|
||||
token_delete_all(out);
|
||||
return NULL;
|
||||
}
|
||||
cur->next->prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
#else
|
||||
static void lex_token_new(lex_file *lex)
|
||||
{
|
||||
#if 0
|
||||
if (lex->tok)
|
||||
token_delete(lex->tok);
|
||||
lex->tok = token_new();
|
||||
#else
|
||||
if (lex->tok.value)
|
||||
vec_shrinkto(lex->tok.value, 0);
|
||||
lex->tok.constval.t = 0;
|
||||
lex->tok.ctx.line = lex->sline;
|
||||
lex->tok.ctx.file = lex->name;
|
||||
#endif
|
||||
|
||||
lex->tok.constval.t = TYPE_VOID;
|
||||
lex->tok.ctx.line = lex->sline;
|
||||
lex->tok.ctx.file = lex->name;
|
||||
lex->tok.ctx.column = lex->column;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void lex_ungetch(lex_file *lex, int ch);
|
||||
static int lex_getch(lex_file *lex);
|
||||
|
||||
lex_file* lex_open(const char *file)
|
||||
{
|
||||
lex_file *lex;
|
||||
FILE *in = fs_file_open(file, "rb");
|
||||
lex_file *lex;
|
||||
FILE *in = fopen(file, "rb");
|
||||
uint32_t read;
|
||||
|
||||
if (!in) {
|
||||
lexerror(NULL, "open failed: '%s'\n", file);
|
||||
return NULL;
|
||||
lexerror(nullptr, "open failed: '%s'\n", file);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
lex = (lex_file*)mem_a(sizeof(*lex));
|
||||
if (!lex) {
|
||||
fs_file_close(in);
|
||||
lexerror(NULL, "out of memory\n");
|
||||
return NULL;
|
||||
fclose(in);
|
||||
lexerror(nullptr, "out of memory\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memset(lex, 0, sizeof(*lex));
|
||||
|
||||
lex->file = in;
|
||||
lex->name = util_strdup(file);
|
||||
lex->line = 1; /* we start counting at 1 */
|
||||
|
||||
lex->file = in;
|
||||
lex->name = util_strdup(file);
|
||||
lex->line = 1; /* we start counting at 1 */
|
||||
lex->column = 0;
|
||||
lex->peekpos = 0;
|
||||
lex->eof = false;
|
||||
lex->eof = false;
|
||||
|
||||
/* handle BOM */
|
||||
if ((read = (lex_getch(lex) << 16) | (lex_getch(lex) << 8) | lex_getch(lex)) != 0xEFBBBF) {
|
||||
lex_ungetch(lex, (read & 0x0000FF));
|
||||
lex_ungetch(lex, (read & 0x00FF00) >> 8);
|
||||
lex_ungetch(lex, (read & 0xFF0000) >> 16);
|
||||
} else {
|
||||
/*
|
||||
* otherwise the lexer has advanced 3 bytes for the BOM, we need
|
||||
* to set the column back to 0
|
||||
*/
|
||||
lex->column = 0;
|
||||
}
|
||||
|
||||
vec_push(lex_filenames, lex->name);
|
||||
return lex;
|
||||
|
@ -217,22 +124,22 @@ lex_file* lex_open_string(const char *str, size_t len, const char *name)
|
|||
|
||||
lex = (lex_file*)mem_a(sizeof(*lex));
|
||||
if (!lex) {
|
||||
lexerror(NULL, "out of memory\n");
|
||||
return NULL;
|
||||
lexerror(nullptr, "out of memory\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memset(lex, 0, sizeof(*lex));
|
||||
|
||||
lex->file = NULL;
|
||||
lex->file = nullptr;
|
||||
lex->open_string = str;
|
||||
lex->open_string_length = len;
|
||||
lex->open_string_pos = 0;
|
||||
|
||||
lex->name = util_strdup(name ? name : "<string-source>");
|
||||
lex->line = 1; /* we start counting at 1 */
|
||||
|
||||
lex->name = util_strdup(name ? name : "<string-source>");
|
||||
lex->line = 1; /* we start counting at 1 */
|
||||
lex->peekpos = 0;
|
||||
lex->eof = false;
|
||||
lex->eof = false;
|
||||
lex->column = 0;
|
||||
|
||||
vec_push(lex_filenames, lex->name);
|
||||
|
||||
|
@ -258,24 +165,26 @@ void lex_close(lex_file *lex)
|
|||
vec_free(lex->modelname);
|
||||
|
||||
if (lex->file)
|
||||
fs_file_close(lex->file);
|
||||
#if 0
|
||||
if (lex->tok)
|
||||
token_delete(lex->tok);
|
||||
#else
|
||||
fclose(lex->file);
|
||||
|
||||
vec_free(lex->tok.value);
|
||||
#endif
|
||||
|
||||
/* mem_d(lex->name); collected in lex_filenames */
|
||||
mem_d(lex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int lex_fgetc(lex_file *lex)
|
||||
{
|
||||
if (lex->file)
|
||||
return fs_file_getc(lex->file);
|
||||
if (lex->file) {
|
||||
lex->column++;
|
||||
return fgetc(lex->file);
|
||||
}
|
||||
if (lex->open_string) {
|
||||
if (lex->open_string_pos >= lex->open_string_length)
|
||||
return EOF;
|
||||
lex->column++;
|
||||
return lex->open_string[lex->open_string_pos++];
|
||||
}
|
||||
return EOF;
|
||||
|
@ -286,21 +195,26 @@ static int lex_fgetc(lex_file *lex)
|
|||
* are working on.
|
||||
* The are merely wrapping get/put in order to count line numbers.
|
||||
*/
|
||||
static void lex_ungetch(lex_file *lex, int ch);
|
||||
static int lex_try_trigraph(lex_file *lex, int old)
|
||||
{
|
||||
int c2, c3;
|
||||
c2 = lex_fgetc(lex);
|
||||
if (!lex->push_line && c2 == '\n')
|
||||
if (!lex->push_line && c2 == '\n') {
|
||||
lex->line++;
|
||||
lex->column = 0;
|
||||
}
|
||||
|
||||
if (c2 != '?') {
|
||||
lex_ungetch(lex, c2);
|
||||
return old;
|
||||
}
|
||||
|
||||
c3 = lex_fgetc(lex);
|
||||
if (!lex->push_line && c3 == '\n')
|
||||
if (!lex->push_line && c3 == '\n') {
|
||||
lex->line++;
|
||||
lex->column = 0;
|
||||
}
|
||||
|
||||
switch (c3) {
|
||||
case '=': return '#';
|
||||
case '/': return '\\';
|
||||
|
@ -347,14 +261,18 @@ static int lex_getch(lex_file *lex)
|
|||
|
||||
if (lex->peekpos) {
|
||||
lex->peekpos--;
|
||||
if (!lex->push_line && lex->peek[lex->peekpos] == '\n')
|
||||
if (!lex->push_line && lex->peek[lex->peekpos] == '\n') {
|
||||
lex->line++;
|
||||
lex->column = 0;
|
||||
}
|
||||
return lex->peek[lex->peekpos];
|
||||
}
|
||||
|
||||
ch = lex_fgetc(lex);
|
||||
if (!lex->push_line && ch == '\n')
|
||||
if (!lex->push_line && ch == '\n') {
|
||||
lex->line++;
|
||||
lex->column = 0;
|
||||
}
|
||||
else if (ch == '?')
|
||||
return lex_try_trigraph(lex, ch);
|
||||
else if (!lex->flags.nodigraphs && (ch == '<' || ch == ':' || ch == '%'))
|
||||
|
@ -365,8 +283,11 @@ static int lex_getch(lex_file *lex)
|
|||
static void lex_ungetch(lex_file *lex, int ch)
|
||||
{
|
||||
lex->peek[lex->peekpos++] = ch;
|
||||
if (!lex->push_line && ch == '\n')
|
||||
lex->column--;
|
||||
if (!lex->push_line && ch == '\n') {
|
||||
lex->line--;
|
||||
lex->column = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* classify characters
|
||||
|
@ -376,12 +297,12 @@ static void lex_ungetch(lex_file *lex, int ch)
|
|||
/* Idents are alphanumberic, but they start with alpha or _ */
|
||||
static bool isident_start(int ch)
|
||||
{
|
||||
return isalpha(ch) || ch == '_';
|
||||
return util_isalpha(ch) || ch == '_';
|
||||
}
|
||||
|
||||
static bool isident(int ch)
|
||||
static bool isident(int ch, bool allow_dot)
|
||||
{
|
||||
return isident_start(ch) || isdigit(ch);
|
||||
return isident_start(ch) || util_isdigit(ch) || (allow_dot && ch == '.');
|
||||
}
|
||||
|
||||
/* isxdigit_only is used when we already know it's not a digit
|
||||
|
@ -408,9 +329,9 @@ static void lex_endtoken(lex_file *lex)
|
|||
static bool lex_try_pragma(lex_file *lex)
|
||||
{
|
||||
int ch;
|
||||
char *pragma = NULL;
|
||||
char *command = NULL;
|
||||
char *param = NULL;
|
||||
char *pragma = nullptr;
|
||||
char *command = nullptr;
|
||||
char *param = nullptr;
|
||||
size_t line;
|
||||
|
||||
if (lex->flags.preprocessing)
|
||||
|
@ -471,11 +392,12 @@ static bool lex_try_pragma(lex_file *lex)
|
|||
goto unroll;
|
||||
}
|
||||
else if (!strcmp(command, "file")) {
|
||||
lex->framevalue = 0;
|
||||
lex->name = util_strdup(param);
|
||||
vec_push(lex_filenames, lex->name);
|
||||
}
|
||||
else if (!strcmp(command, "line")) {
|
||||
line = strtol(param, NULL, 0)-1;
|
||||
line = strtol(param, nullptr, 0)-1;
|
||||
}
|
||||
else
|
||||
goto unroll;
|
||||
|
@ -561,7 +483,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
|
|||
do
|
||||
{
|
||||
ch = lex_getch(lex);
|
||||
while (ch != EOF && isspace(ch)) {
|
||||
while (ch != EOF && util_isspace(ch)) {
|
||||
if (ch == '\n') {
|
||||
if (lex_try_pragma(lex))
|
||||
continue;
|
||||
|
@ -593,10 +515,6 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
|
|||
|
||||
if (lex->flags.preprocessing) {
|
||||
haswhite = true;
|
||||
/*
|
||||
lex_tokench(lex, '/');
|
||||
lex_tokench(lex, '/');
|
||||
*/
|
||||
lex_tokench(lex, ' ');
|
||||
lex_tokench(lex, ' ');
|
||||
}
|
||||
|
@ -618,10 +536,6 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
|
|||
/* multiline comment */
|
||||
if (lex->flags.preprocessing) {
|
||||
haswhite = true;
|
||||
/*
|
||||
lex_tokench(lex, '/');
|
||||
lex_tokench(lex, '*');
|
||||
*/
|
||||
lex_tokench(lex, ' ');
|
||||
lex_tokench(lex, ' ');
|
||||
}
|
||||
|
@ -633,10 +547,6 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
|
|||
ch = lex_getch(lex);
|
||||
if (ch == '/') {
|
||||
if (lex->flags.preprocessing) {
|
||||
/*
|
||||
lex_tokench(lex, '*');
|
||||
lex_tokench(lex, '/');
|
||||
*/
|
||||
lex_tokench(lex, ' ');
|
||||
lex_tokench(lex, ' ');
|
||||
}
|
||||
|
@ -648,7 +558,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
|
|||
if (ch == '\n')
|
||||
lex_tokench(lex, '\n');
|
||||
else
|
||||
lex_tokench(lex, ' '); /* ch); */
|
||||
lex_tokench(lex, ' ');
|
||||
}
|
||||
}
|
||||
ch = ' '; /* cause TRUE in the isspace check */
|
||||
|
@ -659,7 +569,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
|
|||
ch = '/';
|
||||
break;
|
||||
}
|
||||
} while (ch != EOF && isspace(ch));
|
||||
} while (ch != EOF && util_isspace(ch));
|
||||
|
||||
if (haswhite) {
|
||||
lex_endtoken(lex);
|
||||
|
@ -670,12 +580,12 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
|
|||
}
|
||||
|
||||
/* Get a token */
|
||||
static bool GMQCC_WARN lex_finish_ident(lex_file *lex)
|
||||
static bool GMQCC_WARN lex_finish_ident(lex_file *lex, bool allow_dot)
|
||||
{
|
||||
int ch;
|
||||
|
||||
ch = lex_getch(lex);
|
||||
while (ch != EOF && isident(ch))
|
||||
while (ch != EOF && isident(ch, allow_dot))
|
||||
{
|
||||
lex_tokench(lex, ch);
|
||||
ch = lex_getch(lex);
|
||||
|
@ -695,19 +605,19 @@ static int lex_parse_frame(lex_file *lex)
|
|||
lex_token_new(lex);
|
||||
|
||||
ch = lex_getch(lex);
|
||||
while (ch != EOF && ch != '\n' && isspace(ch))
|
||||
while (ch != EOF && ch != '\n' && util_isspace(ch))
|
||||
ch = lex_getch(lex);
|
||||
|
||||
if (ch == '\n')
|
||||
return 1;
|
||||
|
||||
if (!isident_start(ch)) {
|
||||
lexerror(lex, "invalid framename, must start with one of a-z or _, got %c", ch);
|
||||
lexerror(lex, "invalid framename, must start with one of a-z, or _, got %c", ch);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lex_tokench(lex, ch);
|
||||
if (!lex_finish_ident(lex))
|
||||
if (!lex_finish_ident(lex, true))
|
||||
return -1;
|
||||
lex_endtoken(lex);
|
||||
return 0;
|
||||
|
@ -749,10 +659,11 @@ static bool lex_finish_frames(lex_file *lex)
|
|||
|
||||
static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
||||
{
|
||||
uchar_t chr;
|
||||
int ch = 0;
|
||||
utf8ch_t chr = 0;
|
||||
int ch = 0, texttype = 0;
|
||||
int nextch;
|
||||
bool hex;
|
||||
bool oct;
|
||||
char u8buf[8]; /* way more than enough */
|
||||
int u8len, uc;
|
||||
|
||||
|
@ -784,13 +695,12 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
case '\\': break;
|
||||
case '\'': break;
|
||||
case '"': break;
|
||||
case 'a': ch = '\a'; break;
|
||||
case 'b': ch = '\b'; break;
|
||||
case 'r': ch = '\r'; break;
|
||||
case 'n': ch = '\n'; break;
|
||||
case 't': ch = '\t'; break;
|
||||
case 'f': ch = '\f'; break;
|
||||
case 'v': ch = '\v'; break;
|
||||
case 'a': ch = '\a'; break;
|
||||
case 'r': ch = '\r'; break;
|
||||
case 'n': ch = '\n'; break;
|
||||
case 't': ch = '\t'; break;
|
||||
case 'f': ch = '\f'; break;
|
||||
case 'v': ch = '\v'; break;
|
||||
case 'x':
|
||||
case 'X':
|
||||
/* same procedure as in fteqcc */
|
||||
|
@ -838,17 +748,18 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
chr = 0;
|
||||
nextch = lex_getch(lex);
|
||||
hex = (nextch == 'x');
|
||||
if (!hex)
|
||||
oct = (nextch == '0');
|
||||
if (!hex && !oct)
|
||||
lex_ungetch(lex, nextch);
|
||||
for (nextch = lex_getch(lex); nextch != '}'; nextch = lex_getch(lex)) {
|
||||
if (!hex) {
|
||||
if (!hex && !oct) {
|
||||
if (nextch >= '0' && nextch <= '9')
|
||||
chr = chr * 10 + nextch - '0';
|
||||
else {
|
||||
lexerror(lex, "bad character code");
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
}
|
||||
} else {
|
||||
} else if (!oct) {
|
||||
if (nextch >= '0' && nextch <= '9')
|
||||
chr = chr * 0x10 + nextch - '0';
|
||||
else if (nextch >= 'a' && nextch <= 'f')
|
||||
|
@ -859,6 +770,13 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
lexerror(lex, "bad character code");
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
}
|
||||
} else {
|
||||
if (nextch >= '0' && nextch <= '9')
|
||||
chr = chr * 8 + chr - '0';
|
||||
else {
|
||||
lexerror(lex, "bad character code");
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
}
|
||||
}
|
||||
if (chr > 0x10FFFF || (!OPTS_FLAG(UTF8) && chr > 255))
|
||||
{
|
||||
|
@ -867,14 +785,16 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
}
|
||||
}
|
||||
if (OPTS_FLAG(UTF8) && chr >= 128) {
|
||||
u8len = u8_fromchar(chr, u8buf, sizeof(u8buf));
|
||||
u8len = utf8_from(u8buf, chr);
|
||||
if (!u8len)
|
||||
ch = 0;
|
||||
else {
|
||||
--u8len;
|
||||
lex->column += u8len;
|
||||
for (uc = 0; uc < u8len; ++uc)
|
||||
lex_tokench(lex, u8buf[uc]);
|
||||
/* the last character will be inserted with the tokench() call
|
||||
/*
|
||||
* the last character will be inserted with the tokench() call
|
||||
* below the switch
|
||||
*/
|
||||
ch = u8buf[uc];
|
||||
|
@ -883,7 +803,15 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
else
|
||||
ch = chr;
|
||||
break;
|
||||
case '\n': ch = '\n'; break;
|
||||
|
||||
/* high bit text */
|
||||
case 'b': case 's':
|
||||
texttype ^= 128;
|
||||
continue;
|
||||
|
||||
case '\n':
|
||||
ch = '\n';
|
||||
break;
|
||||
|
||||
default:
|
||||
lexwarn(lex, WARN_UNKNOWN_CONTROL_SEQUENCE, "unrecognized control sequence: \\%c", ch);
|
||||
|
@ -891,7 +819,7 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
lex_tokench(lex, '\\');
|
||||
}
|
||||
/* add the character finally */
|
||||
lex_tokench(lex, ch);
|
||||
lex_tokench(lex, ch | texttype);
|
||||
}
|
||||
else
|
||||
lex_tokench(lex, ch);
|
||||
|
@ -904,6 +832,7 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
|
||||
{
|
||||
bool ishex = false;
|
||||
bool isoct = false;
|
||||
|
||||
int ch = lastch;
|
||||
|
||||
|
@ -916,7 +845,16 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
|
|||
lex_tokench(lex, ch);
|
||||
|
||||
ch = lex_getch(lex);
|
||||
if (ch != '.' && !isdigit(ch))
|
||||
|
||||
if (lastch == '0' && util_isdigit(ch)) {
|
||||
if (ch < '0' || ch > '7') {
|
||||
lexerror(lex, "invalid octal constant");
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
}
|
||||
isoct = true;
|
||||
}
|
||||
|
||||
if (!isoct && ch != '.' && !util_isdigit(ch))
|
||||
{
|
||||
if (lastch != '0' || ch != 'x')
|
||||
{
|
||||
|
@ -937,7 +875,7 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
|
|||
{
|
||||
lex_tokench(lex, ch);
|
||||
ch = lex_getch(lex);
|
||||
while (isdigit(ch) || (ishex && isxdigit_only(ch)))
|
||||
while (util_isdigit(ch) || (ishex && isxdigit_only(ch)))
|
||||
{
|
||||
lex_tokench(lex, ch);
|
||||
ch = lex_getch(lex);
|
||||
|
@ -952,7 +890,7 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
|
|||
|
||||
/* continue digits-only */
|
||||
ch = lex_getch(lex);
|
||||
while (isdigit(ch))
|
||||
while (util_isdigit(ch))
|
||||
{
|
||||
lex_tokench(lex, ch);
|
||||
ch = lex_getch(lex);
|
||||
|
@ -964,17 +902,22 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
|
|||
ch = lex_getch(lex);
|
||||
|
||||
/* generally we don't want words to follow numbers: */
|
||||
if (isident(ch)) {
|
||||
if (isident(ch, false)) {
|
||||
lexerror(lex, "unexpected trailing characters after number");
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
}
|
||||
lex_ungetch(lex, ch);
|
||||
|
||||
lex_endtoken(lex);
|
||||
if (lex->tok.ttype == TOKEN_FLOATCONST)
|
||||
lex->tok.constval.f = strtod(lex->tok.value, NULL);
|
||||
else
|
||||
lex->tok.constval.i = strtol(lex->tok.value, NULL, 0);
|
||||
if (lex->tok.ttype == TOKEN_FLOATCONST) {
|
||||
lex->tok.constval.f = strtod(lex->tok.value, nullptr);
|
||||
} else {
|
||||
/* determine base for strtol */
|
||||
int base = 10;
|
||||
if (ishex) base = 16;
|
||||
if (isoct) base = 8;
|
||||
lex->tok.constval.i = strtol(lex->tok.value, nullptr, base);
|
||||
}
|
||||
return lex->tok.ttype;
|
||||
}
|
||||
|
||||
|
@ -984,10 +927,6 @@ int lex_do(lex_file *lex)
|
|||
bool hadwhite = false;
|
||||
|
||||
lex_token_new(lex);
|
||||
#if 0
|
||||
if (!lex->tok)
|
||||
return TOKEN_FATAL;
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
ch = lex_skipwhite(lex, hadwhite);
|
||||
|
@ -1034,7 +973,7 @@ int lex_do(lex_file *lex)
|
|||
return lex_do(lex);
|
||||
}
|
||||
lex_tokench(lex, ch);
|
||||
if (!lex_finish_ident(lex))
|
||||
if (!lex_finish_ident(lex, true))
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
lex_endtoken(lex);
|
||||
/* skip the known commands */
|
||||
|
@ -1055,10 +994,10 @@ int lex_do(lex_file *lex)
|
|||
if (!strcmp(v, "framevalue"))
|
||||
{
|
||||
ch = lex_getch(lex);
|
||||
while (ch != EOF && isspace(ch) && ch != '\n')
|
||||
while (ch != EOF && util_isspace(ch) && ch != '\n')
|
||||
ch = lex_getch(lex);
|
||||
|
||||
if (!isdigit(ch)) {
|
||||
if (!util_isdigit(ch)) {
|
||||
lexerror(lex, "$framevalue requires an integer parameter");
|
||||
return lex_do(lex);
|
||||
}
|
||||
|
@ -1119,11 +1058,11 @@ int lex_do(lex_file *lex)
|
|||
frame_macro m;
|
||||
m.value = lex->framevalue;
|
||||
m.name = lex->modelname;
|
||||
lex->modelname = NULL;
|
||||
lex->modelname = nullptr;
|
||||
vec_push(lex->frames, m);
|
||||
}
|
||||
lex->modelname = lex->tok.value;
|
||||
lex->tok.value = NULL;
|
||||
lex->tok.value = nullptr;
|
||||
return lex_do(lex);
|
||||
}
|
||||
|
||||
|
@ -1216,7 +1155,7 @@ int lex_do(lex_file *lex)
|
|||
if (ch == '.') {
|
||||
nextch = lex_getch(lex);
|
||||
/* digits starting with a dot */
|
||||
if (isdigit(nextch)) {
|
||||
if (util_isdigit(nextch)) {
|
||||
lex_ungetch(lex, nextch);
|
||||
lex->tok.ttype = lex_finish_digit(lex, ch);
|
||||
lex_endtoken(lex);
|
||||
|
@ -1232,10 +1171,6 @@ int lex_do(lex_file *lex)
|
|||
*/
|
||||
switch (ch)
|
||||
{
|
||||
/*
|
||||
case '+':
|
||||
case '-':
|
||||
*/
|
||||
case '*':
|
||||
case '/':
|
||||
case '<':
|
||||
|
@ -1293,16 +1228,22 @@ int lex_do(lex_file *lex)
|
|||
}
|
||||
|
||||
if (ch == '+' || ch == '-' || /* ++, --, +=, -= and -> as well! */
|
||||
ch == '>' || ch == '<' || /* <<, >>, <=, >= */
|
||||
ch == '>' || ch == '<' || /* <<, >>, <=, >= and >< as well! */
|
||||
ch == '=' || ch == '!' || /* <=>, ==, != */
|
||||
ch == '&' || ch == '|' || /* &&, ||, &=, |= */
|
||||
ch == '~' /* ~=, ~ */
|
||||
ch == '~' || ch == '^' /* ~=, ~, ^ */
|
||||
) {
|
||||
lex_tokench(lex, ch);
|
||||
|
||||
nextch = lex_getch(lex);
|
||||
if ((nextch == '=' && ch != '<') || (nextch == ch && ch != '!')) {
|
||||
|
||||
if ((nextch == '=' && ch != '<') || (nextch == '<' && ch == '>'))
|
||||
lex_tokench(lex, nextch);
|
||||
else if (nextch == ch && ch != '!') {
|
||||
lex_tokench(lex, nextch);
|
||||
if ((thirdch = lex_getch(lex)) == '=')
|
||||
lex_tokench(lex, thirdch);
|
||||
else
|
||||
lex_ungetch(lex, thirdch);
|
||||
} else if (ch == '<' && nextch == '=') {
|
||||
lex_tokench(lex, nextch);
|
||||
if ((thirdch = lex_getch(lex)) == '>')
|
||||
|
@ -1324,7 +1265,7 @@ int lex_do(lex_file *lex)
|
|||
}
|
||||
}
|
||||
else if (lex->flags.preprocessing &&
|
||||
ch == '-' && isdigit(nextch))
|
||||
ch == '-' && util_isdigit(nextch))
|
||||
{
|
||||
lex->tok.ttype = lex_finish_digit(lex, nextch);
|
||||
if (lex->tok.ttype == TOKEN_INTCONST)
|
||||
|
@ -1341,15 +1282,6 @@ int lex_do(lex_file *lex)
|
|||
return (lex->tok.ttype = TOKEN_OPERATOR);
|
||||
}
|
||||
|
||||
/*
|
||||
if (ch == '^' || ch == '~' || ch == '!')
|
||||
{
|
||||
lex_tokench(lex, ch);
|
||||
lex_endtoken(lex);
|
||||
return (lex->tok.ttype = TOKEN_OPERATOR);
|
||||
}
|
||||
*/
|
||||
|
||||
if (ch == '*' || ch == '/') /* *=, /= */
|
||||
{
|
||||
lex_tokench(lex, ch);
|
||||
|
@ -1375,7 +1307,7 @@ int lex_do(lex_file *lex)
|
|||
const char *v;
|
||||
|
||||
lex_tokench(lex, ch);
|
||||
if (!lex_finish_ident(lex)) {
|
||||
if (!lex_finish_ident(lex, false)) {
|
||||
/* error? */
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
}
|
||||
|
@ -1401,14 +1333,16 @@ int lex_do(lex_file *lex)
|
|||
} else if (!strcmp(v, "vector")) {
|
||||
lex->tok.ttype = TOKEN_TYPENAME;
|
||||
lex->tok.constval.t = TYPE_VECTOR;
|
||||
} else if (!strcmp(v, "_length")) {
|
||||
lex->tok.ttype = TOKEN_OPERATOR;
|
||||
} else {
|
||||
size_t kw;
|
||||
for (kw = 0; kw < num_keywords_qc; ++kw) {
|
||||
for (kw = 0; kw < GMQCC_ARRAY_COUNT(keywords_qc); ++kw) {
|
||||
if (!strcmp(v, keywords_qc[kw]))
|
||||
return (lex->tok.ttype = TOKEN_KEYWORD);
|
||||
}
|
||||
if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) {
|
||||
for (kw = 0; kw < num_keywords_fg; ++kw) {
|
||||
for (kw = 0; kw < GMQCC_ARRAY_COUNT(keywords_fg); ++kw) {
|
||||
if (!strcmp(v, keywords_fg[kw]))
|
||||
return (lex->tok.ttype = TOKEN_KEYWORD);
|
||||
}
|
||||
|
@ -1457,14 +1391,10 @@ int lex_do(lex_file *lex)
|
|||
lex_endtoken(lex);
|
||||
|
||||
lex->tok.ttype = TOKEN_CHARCONST;
|
||||
/* It's a vector if we can successfully scan 3 floats */
|
||||
#ifdef _MSC_VER
|
||||
if (sscanf_s(lex->tok.value, " %f %f %f ",
|
||||
|
||||
/* It's a vector if we can successfully scan 3 floats */
|
||||
if (util_sscanf(lex->tok.value, " %f %f %f ",
|
||||
&lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
|
||||
#else
|
||||
if (sscanf(lex->tok.value, " %f %f %f ",
|
||||
&lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
|
||||
#endif
|
||||
|
||||
{
|
||||
lex->tok.ttype = TOKEN_VECTORCONST;
|
||||
|
@ -1472,9 +1402,9 @@ int lex_do(lex_file *lex)
|
|||
else
|
||||
{
|
||||
if (!lex->flags.preprocessing && strlen(lex->tok.value) > 1) {
|
||||
uchar_t u8char;
|
||||
utf8ch_t u8char;
|
||||
/* check for a valid utf8 character */
|
||||
if (!OPTS_FLAG(UTF8) || !u8_analyze(lex->tok.value, NULL, NULL, &u8char, 8)) {
|
||||
if (!OPTS_FLAG(UTF8) || !utf8_to(&u8char, (const unsigned char *)lex->tok.value, 8)) {
|
||||
if (lexwarn(lex, WARN_MULTIBYTE_CHARACTER,
|
||||
( OPTS_FLAG(UTF8) ? "invalid multibyte character sequence `%s`"
|
||||
: "multibyte character: `%s`" ),
|
||||
|
@ -1491,7 +1421,7 @@ int lex_do(lex_file *lex)
|
|||
return lex->tok.ttype;
|
||||
}
|
||||
|
||||
if (isdigit(ch))
|
||||
if (util_isdigit(ch))
|
||||
{
|
||||
lex->tok.ttype = lex_finish_digit(lex, ch);
|
||||
lex_endtoken(lex);
|
||||
|
@ -1504,6 +1434,6 @@ int lex_do(lex_file *lex)
|
|||
return (lex->tok.ttype = ch);
|
||||
}
|
||||
|
||||
lexerror(lex, "unknown token: `%s`", lex->tok.value);
|
||||
lexerror(lex, "unknown token: `%c`", ch);
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
}
|
333
lexer.h
333
lexer.h
|
@ -1,58 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Wolfgang Bumiller
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef GMQCC_LEXER_HDR
|
||||
#define GMQCC_LEXER_HDR
|
||||
#include "gmqcc.h"
|
||||
|
||||
typedef struct token_s token;
|
||||
|
||||
struct token_s {
|
||||
struct token {
|
||||
int ttype;
|
||||
|
||||
char *value;
|
||||
|
||||
union {
|
||||
vector v;
|
||||
int i;
|
||||
double f;
|
||||
int t; /* type */
|
||||
vec3_t v;
|
||||
int i;
|
||||
qcfloat_t f;
|
||||
qc_type t; /* type */
|
||||
} constval;
|
||||
|
||||
#if 0
|
||||
struct token_s *next;
|
||||
struct token_s *prev;
|
||||
#endif
|
||||
|
||||
lex_ctx ctx;
|
||||
lex_ctx_t ctx;
|
||||
};
|
||||
|
||||
#if 0
|
||||
token* token_new();
|
||||
void token_delete(token*);
|
||||
token* token_copy(const token *cp);
|
||||
void token_delete_all(token *t);
|
||||
token* token_copy_all(const token *cp);
|
||||
#endif
|
||||
|
||||
/* Lexer
|
||||
*
|
||||
*/
|
||||
|
@ -100,13 +61,13 @@ enum {
|
|||
TOKEN_FATAL /* internal error, eg out of memory */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct frame_macro {
|
||||
char *name;
|
||||
int value;
|
||||
} frame_macro;
|
||||
int value;
|
||||
};
|
||||
|
||||
typedef struct lex_file_s {
|
||||
FILE *file;
|
||||
struct lex_file {
|
||||
FILE *file;
|
||||
const char *open_string;
|
||||
size_t open_string_length;
|
||||
size_t open_string_pos;
|
||||
|
@ -114,6 +75,7 @@ typedef struct lex_file_s {
|
|||
char *name;
|
||||
size_t line;
|
||||
size_t sline; /* line at the start of a token */
|
||||
size_t column;
|
||||
|
||||
int peek[256];
|
||||
size_t peekpos;
|
||||
|
@ -123,18 +85,18 @@ typedef struct lex_file_s {
|
|||
token tok; /* not a pointer anymore */
|
||||
|
||||
struct {
|
||||
bool noops;
|
||||
bool nodigraphs; /* used when lexing string constants */
|
||||
bool preprocessing; /* whitespace and EOLs become actual tokens */
|
||||
bool mergelines; /* backslash at the end of a line escapes the newline */
|
||||
} flags;
|
||||
unsigned noops:1;
|
||||
unsigned nodigraphs:1; /* used when lexing string constants */
|
||||
unsigned preprocessing:1; /* whitespace and EOLs become actual tokens */
|
||||
unsigned mergelines:1; /* backslash at the end of a line escapes the newline */
|
||||
} flags; /* sizeof == 1 */
|
||||
|
||||
int framevalue;
|
||||
frame_macro *frames;
|
||||
char *modelname;
|
||||
|
||||
size_t push_line;
|
||||
} lex_file;
|
||||
};
|
||||
|
||||
lex_file* lex_open (const char *file);
|
||||
lex_file* lex_open_string(const char *str, size_t len, const char *name);
|
||||
|
@ -154,187 +116,188 @@ enum {
|
|||
#define OP_SUFFIX 1
|
||||
#define OP_PREFIX 2
|
||||
|
||||
typedef struct {
|
||||
struct oper_info {
|
||||
const char *op;
|
||||
unsigned int operands;
|
||||
unsigned int id;
|
||||
unsigned int assoc;
|
||||
signed int prec;
|
||||
unsigned int flags;
|
||||
} oper_info;
|
||||
bool folds;
|
||||
};
|
||||
|
||||
#define opid1(a) (a)
|
||||
#define opid2(a,b) ((a<<8)|b)
|
||||
#define opid3(a,b,c) ((a<<16)|(b<<8)|c)
|
||||
/*
|
||||
* Explicit uint8_t casts since the left operand of shift operator cannot
|
||||
* be negative, even though it won't happen, this supresses the future
|
||||
* possibility.
|
||||
*/
|
||||
#define opid1(a) ((uint8_t)a)
|
||||
#define opid2(a,b) (((uint8_t)a<<8) |(uint8_t)b)
|
||||
#define opid3(a,b,c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c)
|
||||
|
||||
static const oper_info c_operators[] = {
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
|
||||
{ "_length", 1, opid3('l','e','n'), ASSOC_RIGHT, 98, OP_PREFIX, true},
|
||||
|
||||
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX},
|
||||
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX},
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 17, 0 },
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 17, 0 }, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 17, 0 }, /* array subscript */
|
||||
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX, false},
|
||||
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX, false},
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 17, 0, false},
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 17, 0, false}, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 17, 0, false}, /* array subscript */
|
||||
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX },
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX },
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
|
||||
|
||||
{ "**", 2, opid2('*', '*'), ASSOC_RIGHT, 15, 0 },
|
||||
{ "**", 2, opid2('*','*'), ASSOC_RIGHT, 14, 0, true},
|
||||
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "~", 1, opid2('~','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
|
||||
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX, false}, */
|
||||
|
||||
{ "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "~", 1, opid2('~', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX }, */
|
||||
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "><", 2, opid2('>','<'), ASSOC_LEFT, 13, 0, true},
|
||||
|
||||
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
|
||||
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 0 },
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
|
||||
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
|
||||
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 },
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0 },
|
||||
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
|
||||
{ "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0, true},
|
||||
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
|
||||
|
||||
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
|
||||
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
|
||||
{ "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0 },
|
||||
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
|
||||
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0, true},
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0, true},
|
||||
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0 },
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0 },
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0, true},
|
||||
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0 },
|
||||
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0, true},
|
||||
|
||||
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0 },
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0, true},
|
||||
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0 },
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
|
||||
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0, true},
|
||||
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0 },
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0, true},
|
||||
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0 },
|
||||
{ "=", 2, opid1('='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0, false},
|
||||
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0, false},
|
||||
|
||||
{ "=", 2, opid1('='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 2, 0 },
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false},
|
||||
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 },
|
||||
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0 }
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0, false}
|
||||
};
|
||||
static const size_t c_operator_count = (sizeof(c_operators) / sizeof(c_operators[0]));
|
||||
|
||||
static const oper_info fte_operators[] = {
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
|
||||
|
||||
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 15, OP_SUFFIX},
|
||||
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX},
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */
|
||||
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 15, OP_SUFFIX, false},
|
||||
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX, false},
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0, false},
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0, false}, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0, false}, /* array subscript */
|
||||
|
||||
{ "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
|
||||
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
|
||||
|
||||
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0 },
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0 },
|
||||
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0, true},
|
||||
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
|
||||
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 },
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0 },
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
|
||||
|
||||
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
|
||||
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
|
||||
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
|
||||
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 },
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 },
|
||||
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
|
||||
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0, true},
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0, true},
|
||||
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0 },
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0, true},
|
||||
|
||||
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8, 0, false},
|
||||
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 },
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0, true},
|
||||
|
||||
/* Leave precedence 3 for : with -fcorrect-ternary */
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0 },
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 }
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0, false},
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false}
|
||||
};
|
||||
static const size_t fte_operator_count = (sizeof(fte_operators) / sizeof(fte_operators[0]));
|
||||
|
||||
static const oper_info qcc_operators[] = {
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
|
||||
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0, false},
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0, false}, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0, false}, /* array subscript */
|
||||
|
||||
{ "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
|
||||
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
|
||||
|
||||
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0 },
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0 },
|
||||
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0, true},
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0, true},
|
||||
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
|
||||
|
||||
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
|
||||
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
|
||||
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
|
||||
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 },
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 },
|
||||
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
|
||||
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
|
||||
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0, true},
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0, true},
|
||||
|
||||
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0 },
|
||||
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0, false},
|
||||
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0, false},
|
||||
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 },
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0, true},
|
||||
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0 },
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0, false},
|
||||
};
|
||||
static const size_t qcc_operator_count = (sizeof(qcc_operators) / sizeof(qcc_operators[0]));
|
||||
|
||||
extern const oper_info *operators;
|
||||
extern size_t operator_count;
|
||||
void lexerror(lex_file*, const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,41 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Dale Weiler
|
||||
* Wolfgang Bumiller
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gmqcc.h"
|
||||
#include "lexer.h"
|
||||
#include <time.h>
|
||||
#include "parser.h"
|
||||
|
||||
/* TODO: cleanup this whole file .. it's a fuckign mess */
|
||||
|
||||
/* set by the standard */
|
||||
const oper_info *operators = NULL;
|
||||
size_t operator_count = 0;
|
||||
static bool opts_output_wasset = false;
|
||||
|
||||
typedef struct { char *filename; int type; } argitem;
|
||||
typedef struct { char *name; char *value; } ppitem;
|
||||
static argitem *items = NULL;
|
||||
static ppitem *ppems = NULL;
|
||||
const oper_info *operators = nullptr;
|
||||
size_t operator_count = 0;
|
||||
static bool opts_output_wasset = false;
|
||||
struct argitem { char *filename; int type; };
|
||||
struct ppitem { char *name; char *value; };
|
||||
static argitem *items = nullptr;
|
||||
static ppitem *ppems = nullptr;
|
||||
|
||||
#define TYPE_QC 0
|
||||
#define TYPE_ASM 1
|
||||
|
@ -43,7 +22,7 @@ static ppitem *ppems = NULL;
|
|||
|
||||
static const char *app_name;
|
||||
|
||||
static void version() {
|
||||
static void version(void) {
|
||||
con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
|
||||
GMQCC_VERSION_MAJOR,
|
||||
GMQCC_VERSION_MINOR,
|
||||
|
@ -53,12 +32,11 @@ static void version() {
|
|||
);
|
||||
}
|
||||
|
||||
static int usage() {
|
||||
static int usage(void) {
|
||||
con_out("usage: %s [options] [files...]", app_name);
|
||||
con_out("options:\n"
|
||||
" -h, --help show this help message\n"
|
||||
" -debug turns on compiler debug messages\n"
|
||||
" -memchk turns on compiler memory leak check\n");
|
||||
" -debug turns on compiler debug messages\n");
|
||||
con_out(" -o, --output=file output file, defaults to progs.dat\n"
|
||||
" -s filename add a progs.src file to be used\n");
|
||||
con_out(" -E stop after preprocessing\n");
|
||||
|
@ -83,6 +61,8 @@ static int usage() {
|
|||
" -Ono-<name> disable specific optimization\n"
|
||||
" -Ohelp list optimizations\n");
|
||||
con_out(" -force-crc=num force a specific checksum into the header\n");
|
||||
con_out(" -state-fps=num emulate OP_STATE with the specified FPS\n");
|
||||
con_out(" -coverage add coverage support\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -137,19 +117,16 @@ static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, cha
|
|||
return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
|
||||
}
|
||||
|
||||
static bool options_parse(int argc, char **argv) {
|
||||
static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
||||
bool argend = false;
|
||||
size_t itr;
|
||||
char buffer[1024];
|
||||
char *redirout = NULL;
|
||||
char *redirerr = NULL;
|
||||
char *config = NULL;
|
||||
char *memdumpcols = NULL;
|
||||
char buffer[1024];
|
||||
char *config = nullptr;
|
||||
|
||||
while (!argend && argc > 1) {
|
||||
char *argarg;
|
||||
argitem item;
|
||||
ppitem macro;
|
||||
ppitem macro;
|
||||
|
||||
++argv;
|
||||
--argc;
|
||||
|
@ -161,6 +138,9 @@ static bool options_parse(int argc, char **argv) {
|
|||
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
|
||||
opts_set(opts.flags, CORRECT_LOGIC, true);
|
||||
opts_set(opts.flags, SHORT_LOGIC, true);
|
||||
opts_set(opts.flags, UNTYPED_NIL, true);
|
||||
opts_set(opts.flags, VARIADIC_ARGS, true);
|
||||
opts_set(opts.flags, FALSE_EMPTY_STRINGS, false);
|
||||
opts_set(opts.flags, TRUE_EMPTY_STRINGS, true);
|
||||
opts_set(opts.flags, LOOP_LABELS, true);
|
||||
|
@ -169,6 +149,8 @@ static bool options_parse(int argc, char **argv) {
|
|||
opts_set(opts.werror, WARN_INVALID_PARAMETER_COUNT, true);
|
||||
opts_set(opts.werror, WARN_MISSING_RETURN_VALUES, true);
|
||||
opts_set(opts.flags, EXPRESSIONS_FOR_BUILTINS, true);
|
||||
opts_set(opts.warn, WARN_BREAKDEF, true);
|
||||
|
||||
|
||||
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC;
|
||||
|
@ -188,6 +170,7 @@ static bool options_parse(int argc, char **argv) {
|
|||
opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
|
||||
opts_set(opts.flags, CORRECT_TERNARY, false);
|
||||
opts_set(opts.warn, WARN_TERNARY_PRECEDENCE, true);
|
||||
opts_set(opts.warn, WARN_BREAKDEF, true);
|
||||
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_FTEQCC;
|
||||
|
||||
|
@ -205,23 +188,21 @@ static bool options_parse(int argc, char **argv) {
|
|||
if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
|
||||
|
||||
OPTS_OPTION_BOOL(OPTION_FORCECRC) = true;
|
||||
OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, NULL, 0);
|
||||
OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, nullptr, 0);
|
||||
continue;
|
||||
}
|
||||
if (options_long_gcc("redirout", &argc, &argv, &redirout)) {
|
||||
con_change(redirout, redirerr);
|
||||
continue;
|
||||
}
|
||||
if (options_long_gcc("redirerr", &argc, &argv, &redirerr)) {
|
||||
con_change(redirout, redirerr);
|
||||
if (options_long_gcc("state-fps", &argc, &argv, &argarg)) {
|
||||
OPTS_OPTION_U32(OPTION_STATE_FPS) = strtol(argarg, nullptr, 0);
|
||||
opts_set(opts.flags, EMULATE_STATE, true);
|
||||
continue;
|
||||
}
|
||||
if (options_long_gcc("config", &argc, &argv, &argarg)) {
|
||||
config = argarg;
|
||||
continue;
|
||||
}
|
||||
if (options_long_gcc("memdumpcols", &argc, &argv, &memdumpcols)) {
|
||||
OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = (uint16_t)strtol(memdumpcols, NULL, 10);
|
||||
if (options_long_gcc("progsrc", &argc, &argv, &argarg)) {
|
||||
OPTS_OPTION_STR(OPTION_PROGSRC) = argarg;
|
||||
*has_progs_src = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -260,14 +241,14 @@ static bool options_parse(int argc, char **argv) {
|
|||
OPTS_OPTION_BOOL(OPTION_DUMPFIN) = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "memchk")) {
|
||||
OPTS_OPTION_BOOL(OPTION_MEMCHK) = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "nocolor")) {
|
||||
con_color(0);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "coverage")) {
|
||||
OPTS_OPTION_BOOL(OPTION_COVERAGE) = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (argv[0][1]) {
|
||||
/* -h, show usage but exit with 0 */
|
||||
|
@ -303,7 +284,7 @@ static bool options_parse(int argc, char **argv) {
|
|||
|
||||
if (!(argarg = strchr(argv[0] + 2, '='))) {
|
||||
macro.name = util_strdup(argv[0]+2);
|
||||
macro.value = NULL;
|
||||
macro.value = nullptr;
|
||||
} else {
|
||||
*argarg='\0'; /* terminate for name */
|
||||
macro.name = util_strdup(argv[0]+2);
|
||||
|
@ -349,7 +330,7 @@ static bool options_parse(int argc, char **argv) {
|
|||
else if (!strcmp(argv[0]+2, "NO_ERROR") ||
|
||||
!strcmp(argv[0]+2, "NO_ERROR_ALL"))
|
||||
{
|
||||
for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr)
|
||||
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
|
||||
opts.werror[itr] = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -357,19 +338,19 @@ static bool options_parse(int argc, char **argv) {
|
|||
!strcmp(argv[0]+2, "ERROR_ALL"))
|
||||
{
|
||||
opts_backup_non_Werror_all();
|
||||
for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr)
|
||||
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
|
||||
opts.werror[itr] = 0xFFFFFFFFL;
|
||||
opts_restore_non_Werror_all();
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "NONE")) {
|
||||
for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
|
||||
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
|
||||
opts.warn[itr] = 0;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "ALL")) {
|
||||
opts_backup_non_Wall();
|
||||
for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
|
||||
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
|
||||
opts.warn[itr] = 0xFFFFFFFFL;
|
||||
opts_restore_non_Wall();
|
||||
break;
|
||||
|
@ -403,8 +384,8 @@ static bool options_parse(int argc, char **argv) {
|
|||
con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
|
||||
return false;
|
||||
}
|
||||
if (isdigit(argarg[0])) {
|
||||
uint32_t val = (uint32_t)strtol(argarg, NULL, 10);
|
||||
if (util_isdigit(argarg[0])) {
|
||||
uint32_t val = (uint32_t)strtol(argarg, nullptr, 10);
|
||||
OPTS_OPTION_U32(OPTION_O) = val;
|
||||
opts_setoptimlevel(val);
|
||||
} else {
|
||||
|
@ -420,7 +401,8 @@ static bool options_parse(int argc, char **argv) {
|
|||
else if (!strcmp(argarg, "ALL"))
|
||||
opts_setoptimlevel(OPTS_OPTION_U32(OPTION_O) = 9999);
|
||||
else if (!strncmp(argarg, "NO_", 3)) {
|
||||
if (!opts_setoptim(argarg+3, false)) {
|
||||
/* constant folding cannot be turned off for obvious reasons */
|
||||
if (!strcmp(argarg, "NO_CONST_FOLD") || !opts_setoptim(argarg+3, false)) {
|
||||
con_out("unknown optimization: %s\n", argarg+3);
|
||||
return false;
|
||||
}
|
||||
|
@ -453,6 +435,9 @@ static bool options_parse(int argc, char **argv) {
|
|||
}
|
||||
item.filename = argarg;
|
||||
vec_push(items, item);
|
||||
if (item.type == TYPE_SRC) {
|
||||
*has_progs_src = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '-':
|
||||
|
@ -474,14 +459,6 @@ static bool options_parse(int argc, char **argv) {
|
|||
OPTS_OPTION_BOOL(OPTION_QUIET) = true;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "correct")) {
|
||||
OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "no-correct")) {
|
||||
OPTS_OPTION_BOOL(OPTION_CORRECTION) = false;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "add-info")) {
|
||||
OPTS_OPTION_BOOL(OPTION_ADD_INFO) = true;
|
||||
break;
|
||||
|
@ -516,21 +493,21 @@ static bool options_parse(int argc, char **argv) {
|
|||
}
|
||||
|
||||
/* returns the line number, or -1 on error */
|
||||
static bool progs_nextline(char **out, size_t *alen,FILE *src) {
|
||||
static bool progs_nextline(char **out, size_t *alen, FILE *src) {
|
||||
int len;
|
||||
char *line;
|
||||
char *start;
|
||||
char *end;
|
||||
|
||||
line = *out;
|
||||
len = fs_file_getline(&line, alen, src);
|
||||
len = util_getline(&line, alen, src);
|
||||
if (len == -1)
|
||||
return false;
|
||||
|
||||
/* start at first non-blank */
|
||||
for (start = line; isspace(*start); ++start) {}
|
||||
for (start = line; util_isspace(*start); ++start) {}
|
||||
/* end at the first non-blank */
|
||||
for (end = start; *end && !isspace(*end); ++end) {}
|
||||
for (end = start; *end && !util_isspace(*end); ++end) {}
|
||||
|
||||
*out = line;
|
||||
/* move the actual filename to the beginning */
|
||||
|
@ -542,22 +519,21 @@ static bool progs_nextline(char **out, size_t *alen,FILE *src) {
|
|||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
size_t itr;
|
||||
int retval = 0;
|
||||
bool opts_output_free = false;
|
||||
bool operators_free = false;
|
||||
bool progs_src = false;
|
||||
FILE *outfile = NULL;
|
||||
struct parser_s *parser = NULL;
|
||||
struct ftepp_s *ftepp = NULL;
|
||||
size_t itr;
|
||||
int retval = 0;
|
||||
bool operators_free = false;
|
||||
bool has_progs_src = false;
|
||||
FILE *outfile = nullptr;
|
||||
parser_t *parser = nullptr;
|
||||
ftepp_t *ftepp = nullptr;
|
||||
|
||||
app_name = argv[0];
|
||||
con_init ();
|
||||
opts_init("progs.dat", COMPILER_GMQCC, (1024 << 3));
|
||||
opts_init("progs.dat", COMPILER_QCC, (1024 << 3));
|
||||
|
||||
util_seed(time(0));
|
||||
|
||||
if (!options_parse(argc, argv)) {
|
||||
if (!options_parse(argc, argv, &has_progs_src)) {
|
||||
return usage();
|
||||
}
|
||||
|
||||
|
@ -569,13 +545,13 @@ int main(int argc, char **argv) {
|
|||
/* the standard decides which set of operators to use */
|
||||
if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
|
||||
operators = c_operators;
|
||||
operator_count = c_operator_count;
|
||||
operator_count = GMQCC_ARRAY_COUNT(c_operators);
|
||||
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
|
||||
operators = fte_operators;
|
||||
operator_count = fte_operator_count;
|
||||
operator_count = GMQCC_ARRAY_COUNT(fte_operators);
|
||||
} else {
|
||||
operators = qcc_operators;
|
||||
operator_count = qcc_operator_count;
|
||||
operator_count = GMQCC_ARRAY_COUNT(qcc_operators);
|
||||
}
|
||||
|
||||
if (operators == fte_operators) {
|
||||
|
@ -611,7 +587,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (opts_output_wasset) {
|
||||
outfile = fs_file_open(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
|
||||
outfile = fopen(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
|
||||
if (!outfile) {
|
||||
con_err("failed to open `%s` for writing\n", OPTS_OPTION_STR(OPTION_OUTPUT));
|
||||
retval = 1;
|
||||
|
@ -639,8 +615,6 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
util_debug("COM", "starting ...\n");
|
||||
|
||||
/* add macros */
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
|
||||
for (itr = 0; itr < vec_size(ppems); itr++) {
|
||||
|
@ -653,54 +627,55 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!vec_size(items)) {
|
||||
if (!vec_size(items) && !has_progs_src) {
|
||||
FILE *fp = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
|
||||
if (fp) {
|
||||
has_progs_src = true;
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_progs_src) {
|
||||
FILE *src;
|
||||
char *line;
|
||||
char *line = nullptr;
|
||||
size_t linelen = 0;
|
||||
bool has_first_line = false;
|
||||
|
||||
progs_src = true;
|
||||
|
||||
src = fs_file_open("progs.src", "rb");
|
||||
src = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
|
||||
if (!src) {
|
||||
con_err("failed to open `progs.src` for reading\n");
|
||||
con_err("failed to open `%s` for reading\n", OPTS_OPTION_STR(OPTION_PROGSRC));
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
line = NULL;
|
||||
if (!progs_nextline(&line, &linelen, src) || !line[0]) {
|
||||
con_err("illformatted progs.src file: expected output filename in first line\n");
|
||||
retval = 1;
|
||||
goto srcdone;
|
||||
}
|
||||
|
||||
if (!opts_output_wasset) {
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = util_strdup(line);
|
||||
opts_output_free = true;
|
||||
}
|
||||
|
||||
while (progs_nextline(&line, &linelen, src)) {
|
||||
argitem item;
|
||||
if (!line[0] || (line[0] == '/' && line[1] == '/'))
|
||||
|
||||
if (!line[0] || (line[0] == '/' && line[1] == '/')) {
|
||||
continue;
|
||||
item.filename = util_strdup(line);
|
||||
item.type = TYPE_QC;
|
||||
vec_push(items, item);
|
||||
}
|
||||
|
||||
if (has_first_line) {
|
||||
item.filename = util_strdup(line);
|
||||
item.type = TYPE_QC;
|
||||
vec_push(items, item);
|
||||
} else {
|
||||
if (!opts_output_wasset) {
|
||||
OPTS_OPTION_DUP(OPTION_OUTPUT) = util_strdup(line);
|
||||
}
|
||||
has_first_line = true;
|
||||
}
|
||||
}
|
||||
|
||||
srcdone:
|
||||
fs_file_close(src);
|
||||
fclose(src);
|
||||
mem_d(line);
|
||||
}
|
||||
|
||||
if (retval)
|
||||
goto cleanup;
|
||||
|
||||
if (vec_size(items)) {
|
||||
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
|
||||
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
{
|
||||
con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
|
||||
con_out("Mode: %s\n", (has_progs_src ? "progs.src" : "manual"));
|
||||
con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
|
||||
}
|
||||
|
||||
|
@ -716,6 +691,10 @@ srcdone:
|
|||
("unknown"))))));
|
||||
}
|
||||
|
||||
if (items[itr].type == TYPE_SRC) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
const char *out;
|
||||
if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
|
||||
|
@ -724,7 +703,7 @@ srcdone:
|
|||
}
|
||||
out = ftepp_get(ftepp);
|
||||
if (out)
|
||||
fs_file_printf(outfile, "%s", out);
|
||||
fprintf(outfile, "%s", out);
|
||||
ftepp_flush(ftepp);
|
||||
}
|
||||
else {
|
||||
|
@ -751,14 +730,14 @@ srcdone:
|
|||
}
|
||||
}
|
||||
|
||||
if (progs_src) {
|
||||
if (has_progs_src) {
|
||||
mem_d(items[itr].filename);
|
||||
items[itr].filename = NULL;
|
||||
items[itr].filename = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ftepp_finish(ftepp);
|
||||
ftepp = NULL;
|
||||
ftepp = nullptr;
|
||||
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) {
|
||||
retval = 1;
|
||||
|
@ -767,19 +746,7 @@ srcdone:
|
|||
}
|
||||
}
|
||||
|
||||
/* stuff */
|
||||
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
|
||||
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
{
|
||||
for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
|
||||
if (opts_optimizationcount[itr]) {
|
||||
con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)opts_optimizationcount[itr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
util_debug("COM", "cleaning ...\n");
|
||||
if (ftepp)
|
||||
ftepp_finish(ftepp);
|
||||
con_close();
|
||||
|
@ -787,13 +754,19 @@ cleanup:
|
|||
vec_free(ppems);
|
||||
|
||||
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
parser_cleanup(parser);
|
||||
if (opts_output_free)
|
||||
mem_d(OPTS_OPTION_STR(OPTION_OUTPUT));
|
||||
delete parser;
|
||||
|
||||
/* free allocated option strings */
|
||||
for (itr = 0; itr < OPTION_COUNT; itr++)
|
||||
if (OPTS_OPTION_DUPED(itr))
|
||||
mem_d(OPTS_OPTION_STR(itr));
|
||||
|
||||
if (operators_free)
|
||||
mem_d((void*)operators);
|
||||
|
||||
lex_cleanup();
|
||||
util_meminfo();
|
||||
|
||||
if (!retval && compile_errors)
|
||||
retval = 1;
|
||||
return retval;
|
||||
}
|
|
@ -41,3 +41,11 @@ check_opt() {
|
|||
check_opt FLAGS f
|
||||
check_opt WARNS W
|
||||
check_opt OPTIMIZATIONS O
|
||||
|
||||
# TODO: linux version
|
||||
if [ "$(uname -s)" != "Linux" ]; then
|
||||
for i in doc/*.1;
|
||||
do
|
||||
mandoc -Tlint -Wall "$i";
|
||||
done
|
||||
fi
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gmqcc", "gmqcc\gmqcc.vcxproj", "{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcvm", "qcvm\qcvm.vcxproj", "{DC980E20-C7A8-4112-A517-631DBDA788E7}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "pak\pak.vcxproj", "{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsuite", "testsuite\testsuite.vcxproj", "{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Release|Win32.Build.0 = Release|Win32
|
||||
{DC980E20-C7A8-4112-A517-631DBDA788E7}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{DC980E20-C7A8-4112-A517-631DBDA788E7}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{DC980E20-C7A8-4112-A517-631DBDA788E7}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{DC980E20-C7A8-4112-A517-631DBDA788E7}.Release|Win32.Build.0 = Release|Win32
|
||||
{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Release|Win32.Build.0 = Release|Win32
|
||||
{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,90 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}</ProjectGuid>
|
||||
<RootNamespace>gmqcc</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\ast.c" />
|
||||
<ClCompile Include="..\..\code.c" />
|
||||
<ClCompile Include="..\..\conout.c" />
|
||||
<ClCompile Include="..\..\correct.c" />
|
||||
<ClCompile Include="..\..\fs.c" />
|
||||
<ClCompile Include="..\..\ftepp.c" />
|
||||
<ClCompile Include="..\..\ir.c" />
|
||||
<ClCompile Include="..\..\lexer.c" />
|
||||
<ClCompile Include="..\..\main.c" />
|
||||
<ClCompile Include="..\..\opts.c" />
|
||||
<ClCompile Include="..\..\parser.c" />
|
||||
<ClCompile Include="..\..\utf8.c" />
|
||||
<ClCompile Include="..\..\util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\ast.h" />
|
||||
<ClInclude Include="..\..\gmqcc.h" />
|
||||
<ClInclude Include="..\..\intrin.h" />
|
||||
<ClInclude Include="..\..\ir.h" />
|
||||
<ClInclude Include="..\..\lexer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\opts.def" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\ast.c" />
|
||||
<ClCompile Include="..\..\code.c" />
|
||||
<ClCompile Include="..\..\conout.c" />
|
||||
<ClCompile Include="..\..\correct.c" />
|
||||
<ClCompile Include="..\..\fs.c" />
|
||||
<ClCompile Include="..\..\ftepp.c" />
|
||||
<ClCompile Include="..\..\ir.c" />
|
||||
<ClCompile Include="..\..\lexer.c" />
|
||||
<ClCompile Include="..\..\main.c" />
|
||||
<ClCompile Include="..\..\opts.c" />
|
||||
<ClCompile Include="..\..\parser.c" />
|
||||
<ClCompile Include="..\..\utf8.c" />
|
||||
<ClCompile Include="..\..\util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\ast.h" />
|
||||
<ClInclude Include="..\..\gmqcc.h" />
|
||||
<ClInclude Include="..\..\intrin.h" />
|
||||
<ClInclude Include="..\..\ir.h" />
|
||||
<ClInclude Include="..\..\lexer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\opts.def" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,78 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}</ProjectGuid>
|
||||
<RootNamespace>pak</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\conout.c" />
|
||||
<ClCompile Include="..\..\fs.c" />
|
||||
<ClCompile Include="..\..\opts.c" />
|
||||
<ClCompile Include="..\..\pak.c" />
|
||||
<ClCompile Include="..\..\util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\opts.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\gmqcc.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\conout.c" />
|
||||
<ClCompile Include="..\..\fs.c" />
|
||||
<ClCompile Include="..\..\opts.c" />
|
||||
<ClCompile Include="..\..\pak.c" />
|
||||
<ClCompile Include="..\..\util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\opts.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\gmqcc.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,76 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{DC980E20-C7A8-4112-A517-631DBDA788E7}</ProjectGuid>
|
||||
<RootNamespace>qcvm</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>QCVM_EXECUTOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>QCVM_EXECUTOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\conout.c" />
|
||||
<ClCompile Include="..\..\exec.c" />
|
||||
<ClCompile Include="..\..\fs.c" />
|
||||
<ClCompile Include="..\..\util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\gmqcc.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\conout.c" />
|
||||
<ClCompile Include="..\..\exec.c" />
|
||||
<ClCompile Include="..\..\fs.c" />
|
||||
<ClCompile Include="..\..\util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\gmqcc.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,74 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}</ProjectGuid>
|
||||
<RootNamespace>testsuite</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\conout.c" />
|
||||
<ClCompile Include="..\..\fs.c" />
|
||||
<ClCompile Include="..\..\test.c" />
|
||||
<ClCompile Include="..\..\util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\gmqcc.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\conout.c" />
|
||||
<ClCompile Include="..\..\fs.c" />
|
||||
<ClCompile Include="..\..\test.c" />
|
||||
<ClCompile Include="..\..\util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\gmqcc.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,33 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Wolfgang Bumiller
|
||||
* Dale Weiler
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "gmqcc.h"
|
||||
unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
|
||||
opts_cmd_t opts; /* command lien options */
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void opts_setdefault() {
|
||||
#include "gmqcc.h"
|
||||
|
||||
const unsigned int opts_opt_oflag[COUNT_OPTIMIZATIONS+1] = {
|
||||
# define GMQCC_TYPE_OPTIMIZATIONS
|
||||
# define GMQCC_DEFINE_FLAG(NAME, MIN_O) MIN_O,
|
||||
# include "opts.def"
|
||||
0
|
||||
};
|
||||
|
||||
const opts_flag_def_t opts_opt_list[COUNT_OPTIMIZATIONS+1] = {
|
||||
# define GMQCC_TYPE_OPTIMIZATIONS
|
||||
# define GMQCC_DEFINE_FLAG(NAME, MIN_O) { #NAME, LONGBIT(OPTIM_##NAME) },
|
||||
# include "opts.def"
|
||||
{ nullptr, LONGBIT(0) }
|
||||
};
|
||||
|
||||
const opts_flag_def_t opts_warn_list[COUNT_WARNINGS+1] = {
|
||||
# define GMQCC_TYPE_WARNS
|
||||
# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(WARN_##X) },
|
||||
# include "opts.def"
|
||||
{ nullptr, LONGBIT(0) }
|
||||
};
|
||||
|
||||
const opts_flag_def_t opts_flag_list[COUNT_FLAGS+1] = {
|
||||
# define GMQCC_TYPE_FLAGS
|
||||
# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) },
|
||||
# include "opts.def"
|
||||
{ nullptr, LONGBIT(0) }
|
||||
};
|
||||
|
||||
unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
|
||||
opts_cmd_t opts; /* command line options */
|
||||
|
||||
static void opts_setdefault(void) {
|
||||
memset(&opts, 0, sizeof(opts_cmd_t));
|
||||
OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
|
||||
OPTS_OPTION_STR(OPTION_PROGSRC) = "progs.src";
|
||||
|
||||
/* warnings */
|
||||
opts_set(opts.warn, WARN_UNUSED_VARIABLE, true);
|
||||
|
@ -57,6 +66,10 @@ static void opts_setdefault() {
|
|||
opts_set(opts.warn, WARN_UNINITIALIZED_CONSTANT, true);
|
||||
opts_set(opts.warn, WARN_DEPRECATED, true);
|
||||
opts_set(opts.warn, WARN_PARENTHESIS, true);
|
||||
opts_set(opts.warn, WARN_CONST_OVERWRITE, true);
|
||||
opts_set(opts.warn, WARN_DIRECTIVE_INMACRO, true);
|
||||
opts_set(opts.warn, WARN_BUILTINS, true);
|
||||
opts_set(opts.warn, WARN_INEXACT_COMPARES, true);
|
||||
|
||||
/* flags */
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
|
||||
|
@ -65,6 +78,8 @@ static void opts_setdefault() {
|
|||
opts_set(opts.flags, LEGACY_VECTOR_MATHS, true);
|
||||
opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true);
|
||||
|
||||
/* options */
|
||||
OPTS_OPTION_U32(OPTION_STATE_FPS) = 10;
|
||||
}
|
||||
|
||||
void opts_backup_non_Wall() {
|
||||
|
@ -94,13 +109,12 @@ void opts_restore_non_Werror_all() {
|
|||
void opts_init(const char *output, int standard, size_t arraysize) {
|
||||
opts_setdefault();
|
||||
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = (char*)output;
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = output;
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = standard;
|
||||
OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
|
||||
OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = 16;
|
||||
}
|
||||
|
||||
static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
|
||||
static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def_t *list, size_t listsize) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < listsize; ++i) {
|
||||
|
@ -135,9 +149,9 @@ void opts_set(uint32_t *flags, size_t idx, bool on) {
|
|||
LONGBIT_SET(lb, idx);
|
||||
|
||||
if (on)
|
||||
flags[lb.idx] |= (1<<(lb.bit));
|
||||
flags[lb.idx] |= (1u<<(lb.bit));
|
||||
else
|
||||
flags[lb.idx] &= ~(1<<(lb.bit));
|
||||
flags[lb.idx] &= ~(1u<<(lb.bit));
|
||||
}
|
||||
|
||||
void opts_setoptimlevel(unsigned int level) {
|
||||
|
@ -157,14 +171,14 @@ void opts_setoptimlevel(unsigned int level) {
|
|||
* from a progs.src.
|
||||
*/
|
||||
static char *opts_ini_rstrip(char *s) {
|
||||
char *p = s + strlen(s);
|
||||
while(p > s && isspace(*--p))
|
||||
*p = '\0';
|
||||
char *p = s + strlen(s) - 1;
|
||||
while (p > s && util_isspace(*p))
|
||||
*p = '\0', p--;
|
||||
return s;
|
||||
}
|
||||
|
||||
static char *opts_ini_lskip(const char *s) {
|
||||
while (*s && isspace(*s))
|
||||
while (*s && util_isspace(*s))
|
||||
s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
@ -172,20 +186,21 @@ static char *opts_ini_lskip(const char *s) {
|
|||
static char *opts_ini_next(const char *s, char c) {
|
||||
bool last = false;
|
||||
while (*s && *s != c && !(last && *s == ';'))
|
||||
last = !!isspace(*s), s++;
|
||||
last = !!util_isspace(*s), s++;
|
||||
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
static size_t opts_ini_parse (
|
||||
FILE *filehandle,
|
||||
char *(*loadhandle)(const char *, const char *, const char *),
|
||||
char **errorhandle
|
||||
FILE *filehandle,
|
||||
char *(*loadhandle)(const char *, const char *, const char *, char **),
|
||||
char **errorhandle,
|
||||
char **parse_file
|
||||
) {
|
||||
size_t linesize;
|
||||
size_t lineno = 1;
|
||||
size_t error = 0;
|
||||
char *line = NULL;
|
||||
char *line = nullptr;
|
||||
char section_data[2048] = "";
|
||||
char oldname_data[2048] = "";
|
||||
|
||||
|
@ -195,7 +210,7 @@ static size_t opts_ini_parse (
|
|||
char *read_name;
|
||||
char *read_value;
|
||||
|
||||
while (fs_file_getline(&line, &linesize, filehandle) != EOF) {
|
||||
while (util_getline(&line, &linesize, filehandle) != EOF) {
|
||||
parse_beg = line;
|
||||
|
||||
/* handle BOM */
|
||||
|
@ -226,7 +241,7 @@ static size_t opts_ini_parse (
|
|||
} else if (*parse_beg && *parse_beg != ';') {
|
||||
/* not a comment, must be a name value pair :) */
|
||||
if (*(parse_end = opts_ini_next(parse_beg, '=')) != '=')
|
||||
parse_end = opts_ini_next(parse_beg, ':');
|
||||
parse_end = opts_ini_next(parse_beg, ':');
|
||||
|
||||
if (*parse_end == '=' || *parse_end == ':') {
|
||||
*parse_end = '\0'; /* terminate bro */
|
||||
|
@ -240,8 +255,20 @@ static size_t opts_ini_parse (
|
|||
util_strncpy(oldname_data, read_name, sizeof(oldname_data));
|
||||
oldname_data[sizeof(oldname_data) - 1] ='\0';
|
||||
|
||||
if ((*errorhandle = loadhandle(section_data, read_name, read_value)) && !error)
|
||||
if ((*errorhandle = loadhandle(section_data, read_name, read_value, parse_file)) && !error)
|
||||
error = lineno;
|
||||
} else if (!strcmp(section_data, "includes")) {
|
||||
/* Includes are special */
|
||||
if (*(parse_end = opts_ini_next(parse_beg, '=')) == '='
|
||||
|| *(parse_end = opts_ini_next(parse_beg, ':')) == ':') {
|
||||
static const char *invalid_include = "invalid use of include";
|
||||
vec_append(*errorhandle, strlen(invalid_include), invalid_include);
|
||||
error = lineno;
|
||||
} else {
|
||||
read_name = opts_ini_rstrip(parse_beg);
|
||||
if ((*errorhandle = loadhandle(section_data, read_name, read_name, parse_file)) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
} else if (!error) {
|
||||
/* otherwise set error to the current line number */
|
||||
error = lineno;
|
||||
|
@ -251,6 +278,7 @@ static size_t opts_ini_parse (
|
|||
}
|
||||
mem_d(line);
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -259,11 +287,11 @@ static size_t opts_ini_parse (
|
|||
static bool opts_ini_bool(const char *value) {
|
||||
if (!strcmp(value, "true")) return true;
|
||||
if (!strcmp(value, "false")) return false;
|
||||
return !!strtol(value, NULL, 10);
|
||||
return !!strtol(value, nullptr, 10);
|
||||
}
|
||||
|
||||
static char *opts_ini_load(const char *section, const char *name, const char *value) {
|
||||
char *error = NULL;
|
||||
static char *opts_ini_load(const char *section, const char *name, const char *value, char **parse_file) {
|
||||
char *error = nullptr;
|
||||
bool found = false;
|
||||
|
||||
/*
|
||||
|
@ -274,6 +302,26 @@ static char *opts_ini_load(const char *section, const char *name, const char *va
|
|||
#undef GMQCC_TYPE_OPTIMIZATIONS
|
||||
#undef GMQCC_TYPE_WARNS
|
||||
|
||||
/* deal with includes */
|
||||
if (!strcmp(section, "includes")) {
|
||||
static const char *include_error_beg = "failed to open file `";
|
||||
static const char *include_error_end = "' for inclusion";
|
||||
FILE *file = fopen(value, "r");
|
||||
found = true;
|
||||
if (!file) {
|
||||
vec_append(error, strlen(include_error_beg), include_error_beg);
|
||||
vec_append(error, strlen(value), value);
|
||||
vec_append(error, strlen(include_error_end), include_error_end);
|
||||
} else {
|
||||
if (opts_ini_parse(file, &opts_ini_load, &error, parse_file) != 0)
|
||||
found = false;
|
||||
/* Change the file name */
|
||||
mem_d(*parse_file);
|
||||
*parse_file = util_strdup(value);
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
/* flags */
|
||||
#define GMQCC_TYPE_FLAGS
|
||||
#define GMQCC_DEFINE_FLAG(X) \
|
||||
|
@ -312,24 +360,29 @@ static char *opts_ini_load(const char *section, const char *name, const char *va
|
|||
|
||||
/* nothing was found ever! */
|
||||
if (!found) {
|
||||
if (strcmp(section, "flags") &&
|
||||
strcmp(section, "warnings") &&
|
||||
if (strcmp(section, "includes") &&
|
||||
strcmp(section, "flags") &&
|
||||
strcmp(section, "warnings") &&
|
||||
strcmp(section, "optimizations"))
|
||||
{
|
||||
vec_upload(error, "invalid section `", 17);
|
||||
vec_upload(error, section, strlen(section));
|
||||
vec_push (error, '`');
|
||||
vec_push (error, '\0');
|
||||
static const char *invalid_section = "invalid_section `";
|
||||
vec_append(error, strlen(invalid_section), invalid_section);
|
||||
vec_append(error, strlen(section), section);
|
||||
vec_push(error, '`');
|
||||
} else if (strcmp(section, "includes")) {
|
||||
static const char *invalid_variable = "invalid_variable `";
|
||||
static const char *in_section = "` in section: `";
|
||||
vec_append(error, strlen(invalid_variable), invalid_variable);
|
||||
vec_append(error, strlen(name), name);
|
||||
vec_append(error, strlen(in_section), in_section);
|
||||
vec_append(error, strlen(section), section);
|
||||
vec_push(error, '`');
|
||||
} else {
|
||||
vec_upload(error, "invalid variable `", 18);
|
||||
vec_upload(error, name, strlen(name));
|
||||
vec_push (error, '`');
|
||||
vec_upload(error, " in section: `", 14);
|
||||
vec_upload(error, section, strlen(section));
|
||||
vec_push (error, '`');
|
||||
vec_push (error, '\0');
|
||||
static const char *expected_something = "expected something";
|
||||
vec_append(error, strlen(expected_something), expected_something);
|
||||
}
|
||||
}
|
||||
vec_push(error, '\0');
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -343,27 +396,29 @@ void opts_ini_init(const char *file) {
|
|||
* gmqcc.ini
|
||||
* gmqcc.cfg
|
||||
*/
|
||||
char *error;
|
||||
char *error = nullptr;
|
||||
char *parse_file = nullptr;
|
||||
size_t line;
|
||||
FILE *ini;
|
||||
|
||||
FILE *ini;
|
||||
|
||||
if (!file) {
|
||||
/* try ini */
|
||||
if (!(ini = fs_file_open((file = "gmqcc.ini"), "r")))
|
||||
if (!(ini = fopen((file = "gmqcc.ini"), "r")))
|
||||
/* try cfg */
|
||||
if (!(ini = fs_file_open((file = "gmqcc.cfg"), "r")))
|
||||
if (!(ini = fopen((file = "gmqcc.cfg"), "r")))
|
||||
return;
|
||||
} else if (!(ini = fs_file_open(file, "r")))
|
||||
} else if (!(ini = fopen(file, "r")))
|
||||
return;
|
||||
|
||||
con_out("found ini file `%s`\n", file);
|
||||
|
||||
if ((line = opts_ini_parse(ini, &opts_ini_load, &error)) != 0) {
|
||||
parse_file = util_strdup(file);
|
||||
if ((line = opts_ini_parse(ini, &opts_ini_load, &error, &parse_file)) != 0) {
|
||||
/* there was a parse error with the ini file */
|
||||
con_printmsg(LVL_ERROR, file, line, "error", error);
|
||||
con_printmsg(LVL_ERROR, parse_file, line, 0 /*TODO: column for ini error*/, "error", error);
|
||||
vec_free(error);
|
||||
}
|
||||
mem_d(parse_file);
|
||||
|
||||
fs_file_close(ini);
|
||||
fclose(ini);
|
||||
}
|
48
opts.def
48
opts.def
|
@ -1,26 +1,3 @@
|
|||
/*
|
||||
* Copyright (C) 2012, 2013
|
||||
* Wolfgang Bumiller
|
||||
* Dale Weiler
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef GMQCC_DEFINE_FLAG
|
||||
# error "bad opts.def usage"
|
||||
#endif
|
||||
|
@ -31,6 +8,8 @@
|
|||
GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
|
||||
GMQCC_DEFINE_FLAG(FTEPP)
|
||||
GMQCC_DEFINE_FLAG(FTEPP_PREDEFS)
|
||||
GMQCC_DEFINE_FLAG(FTEPP_MATHDEFS)
|
||||
GMQCC_DEFINE_FLAG(FTEPP_INDIRECT_EXPANSION)
|
||||
GMQCC_DEFINE_FLAG(RELAXED_SWITCH)
|
||||
GMQCC_DEFINE_FLAG(SHORT_LOGIC)
|
||||
GMQCC_DEFINE_FLAG(PERL_LOGIC)
|
||||
|
@ -51,6 +30,14 @@
|
|||
GMQCC_DEFINE_FLAG(VARIADIC_ARGS)
|
||||
GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS)
|
||||
GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
|
||||
GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
|
||||
GMQCC_DEFINE_FLAG(UNSAFE_VARARGS)
|
||||
GMQCC_DEFINE_FLAG(TYPELESS_STORES)
|
||||
GMQCC_DEFINE_FLAG(SORT_OPERANDS)
|
||||
GMQCC_DEFINE_FLAG(EMULATE_STATE)
|
||||
GMQCC_DEFINE_FLAG(ARITHMETIC_EXCEPTIONS)
|
||||
GMQCC_DEFINE_FLAG(SPLIT_VECTOR_PARAMETERS)
|
||||
GMQCC_DEFINE_FLAG(DEFAULT_ERASEABLE)
|
||||
#endif
|
||||
|
||||
/* warning flags */
|
||||
|
@ -58,6 +45,7 @@
|
|||
GMQCC_DEFINE_FLAG(UNINITIALIZED_GLOBAL)
|
||||
GMQCC_DEFINE_FLAG(DEBUG)
|
||||
GMQCC_DEFINE_FLAG(UNUSED_VARIABLE)
|
||||
GMQCC_DEFINE_FLAG(UNUSED_COMPONENT)
|
||||
GMQCC_DEFINE_FLAG(USED_UNINITIALIZED)
|
||||
GMQCC_DEFINE_FLAG(UNKNOWN_CONTROL_SEQUENCE)
|
||||
GMQCC_DEFINE_FLAG(EXTENSIONS)
|
||||
|
@ -88,6 +76,12 @@
|
|||
GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES)
|
||||
GMQCC_DEFINE_FLAG(DEPRECATED)
|
||||
GMQCC_DEFINE_FLAG(PARENTHESIS)
|
||||
GMQCC_DEFINE_FLAG(UNSAFE_TYPES)
|
||||
GMQCC_DEFINE_FLAG(BREAKDEF)
|
||||
GMQCC_DEFINE_FLAG(CONST_OVERWRITE)
|
||||
GMQCC_DEFINE_FLAG(DIRECTIVE_INMACRO)
|
||||
GMQCC_DEFINE_FLAG(BUILTINS)
|
||||
GMQCC_DEFINE_FLAG(INEXACT_COMPARES)
|
||||
#endif
|
||||
|
||||
#ifdef GMQCC_TYPE_OPTIMIZATIONS
|
||||
|
@ -101,6 +95,8 @@
|
|||
GMQCC_DEFINE_FLAG(CALL_STORES, 3)
|
||||
GMQCC_DEFINE_FLAG(VOID_RETURN, 1)
|
||||
GMQCC_DEFINE_FLAG(VECTOR_COMPONENTS, 1)
|
||||
GMQCC_DEFINE_FLAG(CONST_FOLD_DCE, 2)
|
||||
GMQCC_DEFINE_FLAG(CONST_FOLD, 0) /* cannot be turned off */
|
||||
#endif
|
||||
|
||||
#ifdef GMQCC_TYPE_OPTIONS
|
||||
|
@ -110,8 +106,6 @@
|
|||
GMQCC_DEFINE_FLAG(G)
|
||||
GMQCC_DEFINE_FLAG(STANDARD)
|
||||
GMQCC_DEFINE_FLAG(DEBUG)
|
||||
GMQCC_DEFINE_FLAG(MEMDUMPCOLS)
|
||||
GMQCC_DEFINE_FLAG(MEMCHK)
|
||||
GMQCC_DEFINE_FLAG(DUMPFIN)
|
||||
GMQCC_DEFINE_FLAG(DUMP)
|
||||
GMQCC_DEFINE_FLAG(FORCECRC)
|
||||
|
@ -119,7 +113,9 @@
|
|||
GMQCC_DEFINE_FLAG(PP_ONLY)
|
||||
GMQCC_DEFINE_FLAG(MAX_ARRAY_SIZE)
|
||||
GMQCC_DEFINE_FLAG(ADD_INFO)
|
||||
GMQCC_DEFINE_FLAG(CORRECTION)
|
||||
GMQCC_DEFINE_FLAG(PROGSRC)
|
||||
GMQCC_DEFINE_FLAG(COVERAGE)
|
||||
GMQCC_DEFINE_FLAG(STATE_FPS)
|
||||
#endif
|
||||
|
||||
/* some cleanup so we don't have to */
|
||||
|
|
581
pak.c
581
pak.c
|
@ -1,581 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2013
|
||||
* Dale Weiler
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is furnished to do
|
||||
* so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#include "gmqcc.h"
|
||||
|
||||
/*
|
||||
* The PAK format uses a FOURCC concept for storing the magic ident within
|
||||
* the header as a uint32_t.
|
||||
*/
|
||||
#define PAK_FOURCC ((uint32_t)(('P' | ('A' << 8) | ('C' << 16) | ('K' << 24))))
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic; /* "PACK" */
|
||||
|
||||
/*
|
||||
* Offset to first directory entry in PAK file. It's often
|
||||
* best to store the directories at the end of the file opposed
|
||||
* to the front, since it allows easy insertion without having
|
||||
* to load the entire file into memory again.
|
||||
*/
|
||||
uint32_t diroff;
|
||||
uint32_t dirlen;
|
||||
} pak_header_t;
|
||||
|
||||
/*
|
||||
* A directory, is sort of a "file entry". The concept of
|
||||
* a directory in Quake world is a "file entry/record". This
|
||||
* describes a file (with directories/nested ones too in it's
|
||||
* file name). Hence it can be a file, file with directory, or
|
||||
* file with directories.
|
||||
*/
|
||||
typedef struct {
|
||||
char name[56];
|
||||
uint32_t pos;
|
||||
uint32_t len;
|
||||
} pak_directory_t;
|
||||
|
||||
/*
|
||||
* Used to get the next token from a string, where the
|
||||
* strings themselfs are seperated by chracters from
|
||||
* `sep`. This is essentially strsep.
|
||||
*/
|
||||
static char *pak_tree_sep(char **str, const char *sep) {
|
||||
char *beg = *str;
|
||||
char *end;
|
||||
|
||||
if (!beg)
|
||||
return NULL;
|
||||
|
||||
if (*(end = beg + strcspn(beg, sep)))
|
||||
* end++ = '\0'; /* null terminate */
|
||||
else
|
||||
end = 0;
|
||||
|
||||
*str = end;
|
||||
return beg;
|
||||
}
|
||||
|
||||
/*
|
||||
* When given a string like "a/b/c/d/e/file"
|
||||
* this function will handle the creation of
|
||||
* the directory structure, included nested
|
||||
* directories.
|
||||
*/
|
||||
static void pak_tree_build(const char *entry) {
|
||||
char *directory;
|
||||
char *elements[28];
|
||||
char *pathsplit;
|
||||
char *token;
|
||||
|
||||
size_t itr;
|
||||
size_t jtr;
|
||||
|
||||
pathsplit = (char *)mem_a(56);
|
||||
directory = (char *)mem_a(56);
|
||||
|
||||
memset(pathsplit, 0, 56);
|
||||
|
||||
util_strncpy(directory, entry, 56);
|
||||
for (itr = 0; (token = pak_tree_sep(&directory, "/")) != NULL; itr++) {
|
||||
elements[itr] = token;
|
||||
}
|
||||
|
||||
for (jtr = 0; jtr < itr - 1; jtr++) {
|
||||
util_strcat(pathsplit, elements[jtr]);
|
||||
util_strcat(pathsplit, "/");
|
||||
|
||||
if (fs_dir_make(pathsplit)) {
|
||||
mem_d(pathsplit);
|
||||
mem_d(directory);
|
||||
|
||||
/* TODO: undo on fail */
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mem_d(pathsplit);
|
||||
mem_d(directory);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
pak_directory_t *directories;
|
||||
pak_header_t header;
|
||||
FILE *handle;
|
||||
bool insert;
|
||||
} pak_file_t;
|
||||
|
||||
static pak_file_t *pak_open_read(const char *file) {
|
||||
pak_file_t *pak;
|
||||
size_t itr;
|
||||
|
||||
if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t))))
|
||||
return NULL;
|
||||
|
||||
if (!(pak->handle = fs_file_open(file, "rb"))) {
|
||||
mem_d(pak);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pak->directories = NULL;
|
||||
pak->insert = false; /* read doesn't allow insert */
|
||||
|
||||
memset (&pak->header, 0, sizeof(pak_header_t));
|
||||
fs_file_read (&pak->header, sizeof(pak_header_t), 1, pak->handle);
|
||||
util_endianswap(&pak->header, 1, sizeof(pak_header_t));
|
||||
|
||||
/*
|
||||
* Every PAK file has "PACK" stored as FOURCC data in the
|
||||
* header. If this data cannot compare (as checked here), it's
|
||||
* probably not a PAK file.
|
||||
*/
|
||||
if (pak->header.magic != PAK_FOURCC) {
|
||||
fs_file_close(pak->handle);
|
||||
mem_d (pak);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Time to read in the directory handles and prepare the directories
|
||||
* vector. We're going to be reading some the file inwards soon.
|
||||
*/
|
||||
fs_file_seek(pak->handle, pak->header.diroff, SEEK_SET);
|
||||
|
||||
/*
|
||||
* Read in all directories from the PAK file. These are considered
|
||||
* to be the "file entries".
|
||||
*/
|
||||
for (itr = 0; itr < pak->header.dirlen / 64; itr++) {
|
||||
pak_directory_t dir;
|
||||
fs_file_read (&dir, sizeof(pak_directory_t), 1, pak->handle);
|
||||
util_endianswap(&dir, 1, sizeof(pak_directory_t));
|
||||
|
||||
vec_push(pak->directories, dir);
|
||||
}
|
||||
return pak;
|
||||
}
|
||||
|
||||
static pak_file_t *pak_open_write(const char *file) {
|
||||
pak_file_t *pak;
|
||||
|
||||
if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t))))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Generate the required directory structure / tree for
|
||||
* writing this PAK file too.
|
||||
*/
|
||||
pak_tree_build(file);
|
||||
|
||||
if (!(pak->handle = fs_file_open(file, "wb"))) {
|
||||
/*
|
||||
* The directory tree that was created, needs to be
|
||||
* removed entierly if we failed to open a file.
|
||||
*/
|
||||
/* TODO backup directory clean */
|
||||
|
||||
mem_d(pak);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&(pak->header), 0, sizeof(pak_header_t));
|
||||
|
||||
/*
|
||||
* We're in "insert" mode, we need to do things like header
|
||||
* "patching" and writing the directories at the end of the
|
||||
* file.
|
||||
*/
|
||||
pak->insert = true;
|
||||
pak->header.magic = PAK_FOURCC;
|
||||
|
||||
/* on BE systems we need to swap the byte order of the FOURCC */
|
||||
util_endianswap(&pak->header.magic, 1, sizeof(uint32_t));
|
||||
|
||||
/*
|
||||
* We need to write out the header since files will be wrote out to
|
||||
* this even with directory entries, and that not wrote. The header
|
||||
* will need to be patched in later with a file_seek, and overwrite,
|
||||
* we could use offsets and other trickery. This is just easier.
|
||||
*/
|
||||
fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle);
|
||||
|
||||
return pak;
|
||||
}
|
||||
|
||||
pak_file_t *pak_open(const char *file, const char *mode) {
|
||||
if (!file || !mode)
|
||||
return NULL;
|
||||
|
||||
switch (*mode) {
|
||||
case 'r': return pak_open_read (file);
|
||||
case 'w': return pak_open_write(file);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
|
||||
size_t itr;
|
||||
|
||||
if (!pak || !file)
|
||||
return false;
|
||||
|
||||
for (itr = 0; itr < vec_size(pak->directories); itr++) {
|
||||
if (!strcmp(pak->directories[itr].name, file)) {
|
||||
/*
|
||||
* Store back a pointer to the directory that matches
|
||||
* the request if requested (NULL is not allowed).
|
||||
*/
|
||||
if (dir) {
|
||||
*dir = &(pak->directories[itr]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extraction abilities. These work as you expect them to.
|
||||
*/
|
||||
bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
|
||||
pak_directory_t *dir = NULL;
|
||||
unsigned char *dat = NULL;
|
||||
char *local = NULL;
|
||||
FILE *out;
|
||||
|
||||
if (!pak_exists(pak, file, &dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(dat = (unsigned char *)mem_a(dir->len))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate the directory structure / tree that will be required
|
||||
* to store the extracted file.
|
||||
*/
|
||||
pak_tree_build(file);
|
||||
|
||||
/* TODO portable path seperators */
|
||||
util_asprintf(&local, "%s/%s", outdir, file);
|
||||
|
||||
/*
|
||||
* Now create the file, if this operation fails. Then abort
|
||||
* It shouldn't fail though.
|
||||
*/
|
||||
if (!(out = fs_file_open(local, "wb"))) {
|
||||
mem_d(dat);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* free memory for directory string */
|
||||
mem_d(local);
|
||||
|
||||
/* read */
|
||||
fs_file_seek (pak->handle, dir->pos, SEEK_SET);
|
||||
fs_file_read (dat, 1, dir->len, pak->handle);
|
||||
|
||||
/* write */
|
||||
fs_file_write(dat, 1, dir->len, out);
|
||||
|
||||
/* close */
|
||||
fs_file_close(out);
|
||||
|
||||
/* free */
|
||||
mem_d(dat);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pak_extract_all(pak_file_t *pak, const char *dir) {
|
||||
size_t itr;
|
||||
|
||||
if (!fs_dir_make(dir))
|
||||
return false;
|
||||
|
||||
for (itr = 0; itr < vec_size(pak->directories); itr++) {
|
||||
if (!pak_extract_one(pak, pak->directories[itr].name, dir))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insertion functions (the opposite of extraction). Yes for generating
|
||||
* PAKs.
|
||||
*/
|
||||
bool pak_insert_one(pak_file_t *pak, const char *file) {
|
||||
pak_directory_t dir;
|
||||
unsigned char *dat;
|
||||
FILE *fp;
|
||||
|
||||
/*
|
||||
* We don't allow insertion on files that already exist within the
|
||||
* pak file. Weird shit can happen if we allow that ;). We also
|
||||
* don't allow insertion if the pak isn't opened in write mode.
|
||||
*/
|
||||
if (!pak || !file || !pak->insert || pak_exists(pak, file, NULL))
|
||||
return false;
|
||||
|
||||
if (!(fp = fs_file_open(file, "rb")))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Calculate the total file length, since it will be wrote to
|
||||
* the directory entry, and the actual contents of the file
|
||||
* to the PAK file itself.
|
||||
*/
|
||||
fs_file_seek(fp, 0, SEEK_END);
|
||||
dir.len = fs_file_tell(fp);
|
||||
fs_file_seek(fp, 0, SEEK_SET);
|
||||
|
||||
dir.pos = fs_file_tell(pak->handle);
|
||||
|
||||
/*
|
||||
* We're limited to 56 bytes for a file name string, that INCLUDES
|
||||
* the directory and '/' seperators.
|
||||
*/
|
||||
if (strlen(file) >= 56) {
|
||||
fs_file_close(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
util_strncpy(dir.name, file, strlen(file));
|
||||
|
||||
/*
|
||||
* Allocate some memory for loading in the data that will be
|
||||
* redirected into the PAK file.
|
||||
*/
|
||||
if (!(dat = (unsigned char *)mem_a(dir.len))) {
|
||||
fs_file_close(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
fs_file_read (dat, dir.len, 1, fp);
|
||||
fs_file_close(fp);
|
||||
fs_file_write(dat, dir.len, 1, pak->handle);
|
||||
|
||||
/*
|
||||
* Now add the directory to the directories vector, so pak_close
|
||||
* can actually write it.
|
||||
*/
|
||||
vec_push(pak->directories, dir);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Like pak_insert_one, except this collects files in all directories
|
||||
* from a root directory, and inserts them all.
|
||||
*/
|
||||
bool pak_insert_all(pak_file_t *pak, const char *dir) {
|
||||
DIR *dp;
|
||||
struct dirent *dirp;
|
||||
|
||||
if (!(pak->insert))
|
||||
return false;
|
||||
|
||||
if (!(dp = fs_dir_open(dir)))
|
||||
return false;
|
||||
|
||||
while ((dirp = fs_dir_read(dp))) {
|
||||
if (!(pak_insert_one(pak, dirp->d_name))) {
|
||||
fs_dir_close(dp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fs_dir_close(dp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pak_close(pak_file_t *pak) {
|
||||
size_t itr;
|
||||
|
||||
if (!pak)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* In insert mode we need to patch the header, and write
|
||||
* our directory entries at the end of the file.
|
||||
*/
|
||||
if (pak->insert) {
|
||||
pak->header.dirlen = vec_size(pak->directories) * 64;
|
||||
pak->header.diroff = ftell(pak->handle);
|
||||
|
||||
/* patch header */
|
||||
fs_file_seek (pak->handle, 0, SEEK_SET);
|
||||
fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle);
|
||||
|
||||
/* write directories */
|
||||
fs_file_seek (pak->handle, pak->header.diroff, SEEK_SET);
|
||||
|
||||
for (itr = 0; itr < vec_size(pak->directories); itr++) {
|
||||
fs_file_write(&(pak->directories[itr]), sizeof(pak_directory_t), 1, pak->handle);
|
||||
}
|
||||
}
|
||||
|
||||
vec_free (pak->directories);
|
||||
fs_file_close(pak->handle);
|
||||
mem_d (pak);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fancy GCC-like LONG parsing allows things like --opt=param with
|
||||
* assignment operator. This is used for redirecting stdout/stderr
|
||||
* console to specific files of your choice.
|
||||
*/
|
||||
static bool parsecmd(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
|
||||
int argc = *argc_;
|
||||
char **argv = *argv_;
|
||||
|
||||
size_t len = strlen(optname);
|
||||
|
||||
if (strncmp(argv[0]+ds, optname, len))
|
||||
return false;
|
||||
|
||||
/* it's --optname, check how the parameter is supplied */
|
||||
if (argv[0][ds+len] == '=') {
|
||||
*out = argv[0]+ds+len+1;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
|
||||
return false;
|
||||
|
||||
/* using --opt param */
|
||||
*out = argv[1];
|
||||
--*argc_;
|
||||
++*argv_;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
bool extract = true;
|
||||
char *redirout = (char*)stdout;
|
||||
char *redirerr = (char*)stderr;
|
||||
char *file = NULL;
|
||||
char **files = NULL;
|
||||
pak_file_t *pak = NULL;
|
||||
size_t iter = 0;
|
||||
|
||||
con_init();
|
||||
|
||||
/*
|
||||
* Command line option parsing commences now We only need to support
|
||||
* a few things in the test suite.
|
||||
*/
|
||||
while (argc > 1) {
|
||||
++argv;
|
||||
--argc;
|
||||
|
||||
if (argv[0][0] == '-') {
|
||||
if (parsecmd("redirout", &argc, &argv, &redirout, 1, false))
|
||||
continue;
|
||||
if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false))
|
||||
continue;
|
||||
if (parsecmd("file", &argc, &argv, &file, 1, false))
|
||||
continue;
|
||||
|
||||
con_change(redirout, redirerr);
|
||||
|
||||
switch (argv[0][1]) {
|
||||
case 'e': extract = true; continue;
|
||||
case 'c': extract = false; continue;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0]+1, "debug")) {
|
||||
OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "memchk")) {
|
||||
OPTS_OPTION_BOOL(OPTION_MEMCHK) = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "nocolor")) {
|
||||
con_color(0);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
vec_push(files, argv[0]);
|
||||
}
|
||||
con_change(redirout, redirerr);
|
||||
|
||||
|
||||
if (!file) {
|
||||
con_err("-file must be specified for output/input PAK file\n");
|
||||
vec_free(files);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (extract) {
|
||||
if (!(pak = pak_open(file, "r"))) {
|
||||
con_err("failed to open PAK file %s\n", file);
|
||||
vec_free(files);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!pak_extract_all(pak, "./")) {
|
||||
con_err("failed to extract PAK %s (files may be missing)\n", file);
|
||||
pak_close(pak);
|
||||
vec_free(files);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* not possible */
|
||||
pak_close(pak);
|
||||
vec_free(files);
|
||||
util_meminfo();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (!(pak = pak_open(file, "w"))) {
|
||||
con_err("failed to open PAK %s for writing\n", file);
|
||||
vec_free(files);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (iter = 0; iter < vec_size(files); iter++) {
|
||||
if (!(pak_insert_one(pak, files[iter]))) {
|
||||
con_err("failed inserting %s for PAK %s\n", files[iter], file);
|
||||
pak_close(pak);
|
||||
vec_free(files);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* not possible */
|
||||
pak_close(pak);
|
||||
vec_free(files);
|
||||
|
||||
|
||||
util_meminfo();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
File diff suppressed because it is too large
Load diff
84
parser.h
Normal file
84
parser.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
#ifndef GMQCC_PARSER_HDR
|
||||
#define GMQCC_PARSER_HDR
|
||||
#include "gmqcc.h"
|
||||
#include "lexer.h"
|
||||
#include "ast.h"
|
||||
|
||||
#include "intrin.h"
|
||||
#include "fold.h"
|
||||
|
||||
struct parser_t;
|
||||
|
||||
#define parser_ctx(p) ((p)->lex->tok.ctx)
|
||||
|
||||
struct parser_t {
|
||||
parser_t();
|
||||
~parser_t();
|
||||
|
||||
void remove_ast();
|
||||
|
||||
lex_file *lex;
|
||||
int tok;
|
||||
|
||||
bool ast_cleaned;
|
||||
|
||||
std::vector<ast_expression *> globals;
|
||||
std::vector<ast_expression *> fields;
|
||||
std::vector<ast_function *> functions;
|
||||
size_t translated;
|
||||
|
||||
/* must be deleted first, they reference immediates and values */
|
||||
std::vector<ast_value *> accessors;
|
||||
|
||||
ast_value *nil;
|
||||
ast_value *reserved_version;
|
||||
|
||||
size_t crc_globals;
|
||||
size_t crc_fields;
|
||||
|
||||
ast_function *function;
|
||||
ht aliases;
|
||||
|
||||
/* All the labels the function defined...
|
||||
* Should they be in ast_function instead?
|
||||
*/
|
||||
std::vector<ast_label*> labels;
|
||||
std::vector<ast_goto*> gotos;
|
||||
std::vector<const char *> breaks;
|
||||
std::vector<const char *> continues;
|
||||
|
||||
/* A list of hashtables for each scope */
|
||||
std::vector<ht> variables;
|
||||
ht htfields;
|
||||
ht htglobals;
|
||||
std::vector<ht> typedefs;
|
||||
|
||||
/* not to be used directly, we use the hash table */
|
||||
std::vector<ast_expression*> _locals;
|
||||
std::vector<size_t> _blocklocals;
|
||||
std::vector<std::unique_ptr<ast_value>> _typedefs;
|
||||
std::vector<size_t> _blocktypedefs;
|
||||
std::vector<lex_ctx_t> _block_ctx;
|
||||
|
||||
/* we store the '=' operator info */
|
||||
const oper_info *assign_op;
|
||||
|
||||
/* magic values */
|
||||
ast_value *const_vec[3];
|
||||
|
||||
/* pragma flags */
|
||||
bool noref;
|
||||
|
||||
/* collected information */
|
||||
size_t max_param_count;
|
||||
|
||||
fold m_fold;
|
||||
intrin m_intrin;
|
||||
};
|
||||
|
||||
|
||||
/* parser.c */
|
||||
char *parser_strdup (const char *str);
|
||||
ast_expression *parser_find_global(parser_t *parser, const char *name);
|
||||
|
||||
#endif
|
250
stat.cpp
Normal file
250
stat.cpp
Normal file
|
@ -0,0 +1,250 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gmqcc.h"
|
||||
|
||||
/*
|
||||
* strdup does it's own malloc, we need to track malloc. We don't want
|
||||
* to overwrite malloc though, infact, we can't really hook it at all
|
||||
* without library specific assumptions. So we re implement strdup.
|
||||
*/
|
||||
char *stat_mem_strdup(const char *src, bool empty) {
|
||||
size_t len = 0;
|
||||
char *ptr = nullptr;
|
||||
|
||||
if (!src)
|
||||
return nullptr;
|
||||
|
||||
len = strlen(src);
|
||||
if ((!empty ? len : true) && (ptr = (char*)mem_a(len + 1))) {
|
||||
memcpy(ptr, src, len);
|
||||
ptr[len] = '\0';
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* The reallocate function for resizing vectors.
|
||||
*/
|
||||
void _util_vec_grow(void **a, size_t i, size_t s) {
|
||||
vector_t *d = nullptr;
|
||||
size_t m = 0;
|
||||
void *p = nullptr;
|
||||
|
||||
if (*a) {
|
||||
d = vec_meta(*a);
|
||||
m = 2 * d->allocated + i;
|
||||
p = mem_r(d, s * m + sizeof(vector_t));
|
||||
} else {
|
||||
m = i + 1;
|
||||
p = mem_a(s * m + sizeof(vector_t));
|
||||
((vector_t*)p)->used = 0;
|
||||
}
|
||||
|
||||
d = (vector_t*)p;
|
||||
d->allocated = m;
|
||||
*a = d + 1;
|
||||
}
|
||||
|
||||
void _util_vec_delete(void *data) {
|
||||
mem_d(vec_meta(data));
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash table for generic data, based on dynamic memory allocations
|
||||
* all around. This is the internal interface, please look for
|
||||
* EXPOSED INTERFACE comment below
|
||||
*/
|
||||
struct hash_node_t {
|
||||
char *key; /* the key for this node in table */
|
||||
void *value; /* pointer to the data as void* */
|
||||
hash_node_t *next; /* next node (linked list) */
|
||||
};
|
||||
|
||||
size_t hash(const char *key);
|
||||
|
||||
size_t util_hthash(hash_table_t *ht, const char *key) {
|
||||
return hash(key) % ht->size;
|
||||
}
|
||||
|
||||
static hash_node_t *_util_htnewpair(const char *key, void *value) {
|
||||
hash_node_t *node;
|
||||
if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
|
||||
return nullptr;
|
||||
|
||||
if (!(node->key = util_strdupe(key))) {
|
||||
mem_d(node);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
node->value = value;
|
||||
node->next = nullptr;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/*
|
||||
* EXPOSED INTERFACE for the hashtable implementation
|
||||
* util_htnew(size) -- to make a new hashtable
|
||||
* util_htset(table, key, value, sizeof(value)) -- to set something in the table
|
||||
* util_htget(table, key) -- to get something from the table
|
||||
* util_htdel(table) -- to delete the table
|
||||
*/
|
||||
hash_table_t *util_htnew(size_t size) {
|
||||
hash_table_t *hashtable = nullptr;
|
||||
|
||||
if (size < 1)
|
||||
return nullptr;
|
||||
|
||||
if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t))))
|
||||
return nullptr;
|
||||
|
||||
if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) {
|
||||
mem_d(hashtable);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hashtable->size = size;
|
||||
memset(hashtable->table, 0, sizeof(hash_node_t*) * size);
|
||||
|
||||
return hashtable;
|
||||
}
|
||||
|
||||
void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) {
|
||||
hash_node_t *newnode = nullptr;
|
||||
hash_node_t *next = nullptr;
|
||||
hash_node_t *last = nullptr;
|
||||
|
||||
next = ht->table[bin];
|
||||
|
||||
while (next && next->key && strcmp(key, next->key) > 0)
|
||||
last = next, next = next->next;
|
||||
|
||||
/* already in table, do a replace */
|
||||
if (next && next->key && strcmp(key, next->key) == 0) {
|
||||
next->value = value;
|
||||
} else {
|
||||
/* not found, grow a pair man :P */
|
||||
newnode = _util_htnewpair(key, value);
|
||||
if (next == ht->table[bin]) {
|
||||
newnode->next = next;
|
||||
ht->table[bin] = newnode;
|
||||
} else if (!next) {
|
||||
last->next = newnode;
|
||||
} else {
|
||||
newnode->next = next;
|
||||
last->next = newnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void util_htset(hash_table_t *ht, const char *key, void *value) {
|
||||
util_htseth(ht, key, util_hthash(ht, key), value);
|
||||
}
|
||||
|
||||
void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) {
|
||||
hash_node_t *pair = ht->table[bin];
|
||||
|
||||
while (pair && pair->key && strcmp(key, pair->key) > 0)
|
||||
pair = pair->next;
|
||||
|
||||
if (!pair || !pair->key || strcmp(key, pair->key) != 0)
|
||||
return nullptr;
|
||||
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
void *util_htget(hash_table_t *ht, const char *key) {
|
||||
return util_htgeth(ht, key, util_hthash(ht, key));
|
||||
}
|
||||
|
||||
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
|
||||
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
|
||||
hash_node_t *pair;
|
||||
size_t len, keylen;
|
||||
int cmp;
|
||||
|
||||
keylen = strlen(key);
|
||||
|
||||
pair = ht->table[bin];
|
||||
while (pair && pair->key) {
|
||||
len = strlen(pair->key);
|
||||
if (len < keylen) {
|
||||
pair = pair->next;
|
||||
continue;
|
||||
}
|
||||
if (keylen == len) {
|
||||
cmp = strcmp(key, pair->key);
|
||||
if (cmp == 0)
|
||||
return pair->value;
|
||||
if (cmp < 0)
|
||||
return nullptr;
|
||||
pair = pair->next;
|
||||
continue;
|
||||
}
|
||||
cmp = strcmp(key, pair->key + len - keylen);
|
||||
if (cmp == 0) {
|
||||
uintptr_t up = (uintptr_t)pair->value;
|
||||
up += len - keylen;
|
||||
return (void*)up;
|
||||
}
|
||||
pair = pair->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all allocated data in a hashtable, this is quite the amount
|
||||
* of work.
|
||||
*/
|
||||
void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
|
||||
size_t i = 0;
|
||||
|
||||
for (; i < ht->size; ++i) {
|
||||
hash_node_t *n = ht->table[i];
|
||||
hash_node_t *p;
|
||||
|
||||
/* free in list */
|
||||
while (n) {
|
||||
if (n->key)
|
||||
mem_d(n->key);
|
||||
if (callback)
|
||||
callback(n->value);
|
||||
p = n;
|
||||
n = p->next;
|
||||
mem_d(p);
|
||||
}
|
||||
|
||||
}
|
||||
/* free table */
|
||||
mem_d(ht->table);
|
||||
mem_d(ht);
|
||||
}
|
||||
|
||||
void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
|
||||
hash_node_t **pair = &ht->table[bin];
|
||||
hash_node_t *tmp;
|
||||
|
||||
while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
|
||||
pair = &(*pair)->next;
|
||||
|
||||
tmp = *pair;
|
||||
if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
|
||||
return;
|
||||
|
||||
if (cb)
|
||||
(*cb)(tmp->value);
|
||||
|
||||
*pair = tmp->next;
|
||||
mem_d(tmp->key);
|
||||
mem_d(tmp);
|
||||
}
|
||||
|
||||
void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
|
||||
util_htrmh(ht, key, util_hthash(ht, key), cb);
|
||||
}
|
||||
|
||||
void util_htdel(hash_table_t *ht) {
|
||||
util_htrem(ht, nullptr);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
Here exists some syntax highlighting configuration files for various
|
||||
text editors. Inside each directory exists some documentaiton on how
|
||||
you can install the configuration file correctly.
|
||||
|
||||
Currently the supported text editors:
|
||||
geany
|
||||
kate
|
||||
kwrite - uses kate syntax highlighting
|
||||
kdevelop - uses kate syntax highlighting
|
||||
QtCreator - supports kate syntax highlighting
|
||||
gtksourceview - main source viewer in GNOME
|
||||
gedit - uses gtksourceview
|
||||
sandy - uses gtksourceview
|
||||
nano
|
||||
jedit
|
||||
|
||||
|
||||
Other text editors we plan to provide syntax highlighting configuration
|
||||
files for (but never got around to figuring out)
|
||||
vim
|
||||
emacs
|
||||
|
||||
If your text editor is not supported and you'd like to create syntax
|
||||
highlighting support for it, don't hesitate to share it with us.
|
|
@ -1,8 +0,0 @@
|
|||
To use the geany syntax highlighting install filetypes.qc to the syntax
|
||||
directory for geany.
|
||||
|
||||
# Can be installed globally to
|
||||
/usr/share/geany/
|
||||
|
||||
# Can be installed locally to
|
||||
~/.config/geany/filedefs/
|
|
@ -1,55 +0,0 @@
|
|||
[styling]
|
||||
default=default
|
||||
comment=comment
|
||||
commentline=comment_line
|
||||
commentdoc=comment_doc
|
||||
preprocessorcomment=comment
|
||||
number=number_1
|
||||
word=keyword_1
|
||||
word2=keyword_2
|
||||
string=string_1
|
||||
stringraw=string_2
|
||||
character=character
|
||||
uuid=other
|
||||
preprocessor=preprocessor
|
||||
operator=operator
|
||||
identifier=identifier_1
|
||||
stringeol=string_eol
|
||||
verbatim=string_2
|
||||
regex=regex
|
||||
commentlinedoc=comment_line_doc
|
||||
commentdockeyword=comment_doc_keyword
|
||||
commentdockeyworderror=comment_doc_keyword_error
|
||||
globalclass=class
|
||||
tripleverbatim=string_2
|
||||
hashquotedstring=string_2
|
||||
|
||||
[keywords]
|
||||
primary=break case const continue string default do else enum float for goto if return switch typedef void while false nil true
|
||||
secondary=
|
||||
docComment=
|
||||
|
||||
[lexer_properties]
|
||||
styling.within.preprocessor=1
|
||||
lexer.cpp.track.preprocessor=0
|
||||
preprocessor.symbol.$(file.patterns.cpp)=#
|
||||
preprocessor.start.$(file.patterns.cpp)=if ifdef ifndef
|
||||
preprocessor.middle.$(file.patterns.cpp)=else elif
|
||||
preprocessor.end.$(file.patterns.cpp)=endif
|
||||
|
||||
[settings]
|
||||
extension=qc
|
||||
comment_single=//
|
||||
comment_open=/*
|
||||
comment_close=*/
|
||||
comment_use_indent=true
|
||||
context_action_cmd=
|
||||
|
||||
[indentation]
|
||||
width=4
|
||||
type=0
|
||||
|
||||
[build_settings]
|
||||
compiler=gmqcc -Wall "%f" -o "%e"
|
||||
linker=
|
||||
run_cmd=qcvm "./%e"
|
|
@ -1,5 +0,0 @@
|
|||
To use the gtksourceview syntax highlighting install qc.lang to the syntax
|
||||
directory for gtksourceview
|
||||
|
||||
# Can be installed globally to
|
||||
/usr/share/gtksourceview-[version]/language-specs/
|
|
@ -1,173 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<language id="qc" _name="QuakeC" version="1.0" _section="Sources">
|
||||
<metadata>
|
||||
<property name="globs">*.qc</property>
|
||||
<property name="line-comment-start">//</property>
|
||||
<property name="block-comment-start">/*</property>
|
||||
<property name="block-comment-end">*/</property>
|
||||
</metadata>
|
||||
|
||||
<styles>
|
||||
<style id="comment" _name="Comment" map-to="def:comment"/>
|
||||
<style id="string" _name="String" map-to="def:string"/>
|
||||
<style id="preprocessor" _name="Preprocessor" map-to="def:preprocessor"/>
|
||||
<style id="common-defines" _name="Common Defines" map-to="def:special-constant"/>
|
||||
<style id="included-file" _name="Included File" map-to="def:string"/>
|
||||
<style id="keyword" _name="Keyword" map-to="def:keyword"/>
|
||||
<style id="type" _name="Data Type" map-to="def:type"/>
|
||||
<style id="escaped-character" _name="Escaped Character" map-to="def:special-char"/>
|
||||
<style id="floating-point" _name="Floating point number" map-to="def:floating-point"/>
|
||||
<style id="decimal" _name="Decimal number" map-to="def:decimal"/>
|
||||
<style id="hexadecimal" _name="Hexadecimal number" map-to="def:base-n-integer"/>
|
||||
<style id="boolean" _name="Boolean value" map-to="def:boolean"/>
|
||||
</styles>
|
||||
|
||||
<definitions>
|
||||
|
||||
<!--regexs-->
|
||||
<define-regex id="preproc-start">^\s*#\s*</define-regex>
|
||||
<define-regex id="escaped-character" extended="true">
|
||||
\\( # leading backslash
|
||||
[\\\"\'nrbtfav\?] | # escaped character
|
||||
[0-7]{1,3} | # one, two, or three octal digits
|
||||
x[0-9A-Fa-f]+ # 'x' followed by hex digits
|
||||
)
|
||||
</define-regex>
|
||||
|
||||
<!-- Preprocessor -->
|
||||
<context id="if0-comment" style-ref="comment">
|
||||
<start>\%{preproc-start}if\b\s*0\b</start>
|
||||
<end>\%{preproc-start}(endif|else|elif)\b</end>
|
||||
<include>
|
||||
<context id="if-in-if0">
|
||||
<start>\%{preproc-start}if(n?def)?\b</start>
|
||||
<end>\%{preproc-start}endif\b</end>
|
||||
<include>
|
||||
<context ref="if-in-if0"/>
|
||||
<context ref="def:in-comment"/>
|
||||
</include>
|
||||
</context>
|
||||
<context ref="def:in-comment"/>
|
||||
</include>
|
||||
</context>
|
||||
<context id="include" style-ref="preprocessor">
|
||||
<match extended="true">
|
||||
\%{preproc-start}
|
||||
(include|import)\s*
|
||||
(".*?"|<.*>)
|
||||
</match>
|
||||
<include>
|
||||
<context id="included-file" sub-pattern="2" style-ref="included-file"/>
|
||||
</include>
|
||||
</context>
|
||||
<context id="preprocessor" style-ref="preprocessor" end-at-line-end="true">
|
||||
<start extended="true">
|
||||
\%{preproc-start}
|
||||
(define|undef|error|pragma|ident|if(n?def)?|else|elif|endif|line|warning)
|
||||
\b
|
||||
</start>
|
||||
<include>
|
||||
<context ref="def:line-continue" ignore-style="true"/>
|
||||
<context ref="string" ignore-style="true"/>
|
||||
<context ref="def:qc-like-comment"/>
|
||||
<context ref="def:qc-like-comment-multiline"/>
|
||||
</include>
|
||||
</context>
|
||||
|
||||
<context id="float" style-ref="floating-point">
|
||||
<match extended="true">
|
||||
(?<![\w\.])
|
||||
((\.[0-9]+ | [0-9]+\.[0-9]*) ([Ee][+-]?[0-9]*)? |
|
||||
([0-9]+[Ee][+-]?[0-9]*))
|
||||
[fFlL]?
|
||||
(?![\w\.])
|
||||
</match>
|
||||
</context>
|
||||
|
||||
<context id="hexadecimal" style-ref="hexadecimal">
|
||||
<match extended="true">
|
||||
(?<![\w\.])
|
||||
0[xX][a-fA-F0-9]+[uUlL]*
|
||||
(?![\w\.])
|
||||
</match>
|
||||
</context>
|
||||
|
||||
<context id="invalid-hexadecimal" style-ref="error">
|
||||
<match extended="true">
|
||||
(?<![\w\.])
|
||||
0[xX][a-fA-F0-9]*[g-zG-Z][a-zA-Z0-9]*[uUlL]*
|
||||
(?![\w\.])
|
||||
</match>
|
||||
</context>
|
||||
|
||||
<context id="decimal" style-ref="decimal">
|
||||
<match extended="true">
|
||||
(?<![\w\.])
|
||||
(0|[1-9][0-9]*)[uUlL]*
|
||||
(?![\w\.])
|
||||
</match>
|
||||
</context>
|
||||
|
||||
<context id="keywords" style-ref="keyword">
|
||||
<keyword>break</keyword>
|
||||
<keyword>case</keyword>
|
||||
<keyword>continue</keyword>
|
||||
<keyword>default</keyword>
|
||||
<keyword>do</keyword>
|
||||
<keyword>else</keyword>
|
||||
<keyword>enum</keyword>
|
||||
<keyword>for</keyword>
|
||||
<keyword>goto</keyword>
|
||||
<keyword>if</keyword>
|
||||
<keyword>return</keyword>
|
||||
<keyword>switch</keyword>
|
||||
<keyword>typedef</keyword>
|
||||
<keyword>while</keyword>
|
||||
<keyword>nil</keyword>
|
||||
</context>
|
||||
|
||||
<context id="types" style-ref="type">
|
||||
<keyword>bool</keyword>
|
||||
<keyword>string</keyword>
|
||||
<keyword>vector</keyword>
|
||||
<keyword>float</keyword>
|
||||
<keyword>void</keyword>
|
||||
</context>
|
||||
|
||||
<context id="boolean" style-ref="boolean">
|
||||
<keyword>true</keyword>
|
||||
<keyword>false</keyword>
|
||||
</context>
|
||||
|
||||
<context id="common-defines" style-ref="common-defines">
|
||||
<keyword>__LINE__</keyword>
|
||||
<keyword>__FILE__</keyword>
|
||||
<keyword>__TIME__</keyword>
|
||||
<keyword>__RANDOM__</keyword>
|
||||
<keyword>__RANDOM_LAST__</keyword>
|
||||
<keyword>__COUNTER__</keyword>
|
||||
<keyword>__COUNTER_LAST__</keyword>
|
||||
<keyword>__DATE__</keyword>
|
||||
</context>
|
||||
|
||||
<context id="qc" class="no-spell-check">
|
||||
<include>
|
||||
<context ref="def:qc-like-comment"/>
|
||||
<context ref="def:qc-like-comment-multiline"/>
|
||||
<context ref="def:qc-like-close-comment-outside-comment"/>
|
||||
<context ref="if0-comment"/>
|
||||
<context ref="include"/>
|
||||
<context ref="preprocessor"/>
|
||||
<context ref="string"/>
|
||||
<context ref="float"/>
|
||||
<context ref="hexadecimal"/>
|
||||
<context ref="invalid-hexadecimal"/>
|
||||
<context ref="decimal"/>
|
||||
<context ref="keywords"/>
|
||||
<context ref="types"/>
|
||||
<context ref="boolean"/>
|
||||
<context ref="common-defines"/>
|
||||
</include>
|
||||
</context>
|
||||
</definitions>
|
||||
</language>
|
|
@ -1,26 +0,0 @@
|
|||
To use the jedit syntax highlighting install qc.xml to the syntax
|
||||
directory for jedit
|
||||
|
||||
# For Windows Users that directory is
|
||||
C:\Users\username\.jedit\modes
|
||||
|
||||
# For Linux users that directory is
|
||||
/home/username/.jedit/modes
|
||||
|
||||
# For Mac users that directory is
|
||||
/Users/username/Library/jEdit/modes
|
||||
|
||||
After the file is installed, a mode line needs to be added to
|
||||
a file caled catalog in that same directory.
|
||||
|
||||
Add the following line:
|
||||
<MODE NAME="QuakeC Code" FILE="qc.xml" FILE_NAME_GLOB="*.qc" />
|
||||
|
||||
inside the <MODES> block before the end tag </MODES>. If the file
|
||||
does not exist, you can simply make one and use the following:
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE MODES SYSTEM "catalog.dtd">
|
||||
<MODES>
|
||||
<MODE NAME="QuakeC Code" FILE="qc.xml" FILE_NAME_GLOB="*.qc" />
|
||||
</MODES>
|
|
@ -1,271 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!DOCTYPE MODE SYSTEM "xmode.dtd">
|
||||
|
||||
<MODE>
|
||||
<PROPS>
|
||||
<PROPERTY NAME="commentStart" VALUE="/*" />
|
||||
<PROPERTY NAME="commentEnd" VALUE="*/" />
|
||||
<PROPERTY NAME="lineComment" VALUE="//" />
|
||||
<PROPERTY NAME="wordBreakChars" VALUE=",+-=<>/?^&*" />
|
||||
|
||||
<!-- Auto indent -->
|
||||
<PROPERTY NAME="indentOpenBrackets" VALUE="{" />
|
||||
<PROPERTY NAME="indentCloseBrackets" VALUE="}" />
|
||||
<PROPERTY NAME="unalignedOpenBrackets" VALUE="(" />
|
||||
<PROPERTY NAME="unalignedCloseBrackets" VALUE=")" />
|
||||
<PROPERTY NAME="indentNextLine"
|
||||
VALUE="(?!^\s*(#|//)).*(\b(if|while|for)\s*\(.*\)|\b(else|do)\b)[^{;]*$" />
|
||||
<PROPERTY NAME="unindentThisLine"
|
||||
VALUE="^\s*((case\b.*|[\p{Alpha}_][\p{Alnum}_]*)\s*:(?!:)).*$" />
|
||||
<PROPERTY NAME="electricKeys" VALUE=":" />
|
||||
</PROPS>
|
||||
|
||||
<RULES
|
||||
IGNORE_CASE="FALSE"
|
||||
HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
|
||||
<EOL_SPAN TYPE="KEYWORD2" AT_WHITESPACE_END="TRUE" DELEGATE="CPP">#</EOL_SPAN>
|
||||
|
||||
<IMPORT DELEGATE="LEX"/>
|
||||
<IMPORT DELEGATE="CORE"/>
|
||||
</RULES>
|
||||
|
||||
<RULES SET="LEX" IGNORE_CASE="FALSE">
|
||||
<IMPORT DELEGATE="COMMENTS" />
|
||||
<IMPORT DELEGATE="C_LEXER" />
|
||||
</RULES>
|
||||
|
||||
<!-- Comments, Trigraph, Alternate-Tokens -->
|
||||
<RULES SET="C_LEXER"
|
||||
IGNORE_CASE="FALSE"
|
||||
HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
|
||||
|
||||
<SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
|
||||
<BEGIN>L"</BEGIN>
|
||||
<END>"</END>
|
||||
</SPAN>
|
||||
<SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
|
||||
<BEGIN>"</BEGIN>
|
||||
<END>"</END>
|
||||
</SPAN>
|
||||
<SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
|
||||
<BEGIN>L'</BEGIN>
|
||||
<END>'</END>
|
||||
</SPAN>
|
||||
<SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
|
||||
<BEGIN>'</BEGIN>
|
||||
<END>'</END>
|
||||
</SPAN>
|
||||
|
||||
<!-- Trigraphs -->
|
||||
<SEQ TYPE="LITERAL4">??(</SEQ>
|
||||
<SEQ TYPE="LITERAL4">??/</SEQ>
|
||||
<SEQ TYPE="LITERAL4">??)</SEQ>
|
||||
<SEQ TYPE="LITERAL4">??'</SEQ>
|
||||
<SEQ TYPE="LITERAL4">??<</SEQ>
|
||||
<SEQ TYPE="LITERAL4">??!</SEQ>
|
||||
<SEQ TYPE="LITERAL4">??></SEQ>
|
||||
<SEQ TYPE="LITERAL4">??-</SEQ>
|
||||
<SEQ TYPE="LITERAL4">??=</SEQ>
|
||||
|
||||
<!-- Alternate tokens -->
|
||||
<SEQ TYPE="LITERAL4"><:</SEQ>
|
||||
<SEQ TYPE="LITERAL4">:></SEQ>
|
||||
<SEQ TYPE="LITERAL4"><%</SEQ>
|
||||
<SEQ TYPE="LITERAL4">%></SEQ>
|
||||
<SEQ TYPE="LITERAL4">%:</SEQ>
|
||||
|
||||
<!-- Labels.
|
||||
This is a part of core language syntax, but must be here
|
||||
because it can't work after SEQ for ':'. -->
|
||||
<MARK_PREVIOUS AT_WHITESPACE_END="TRUE"
|
||||
MATCH_TYPE="OPERATOR"
|
||||
TYPE="LABEL">:</MARK_PREVIOUS>
|
||||
|
||||
<!-- Function-like macro or function calls.
|
||||
This can't work after SEQ for '('. -->
|
||||
<MARK_PREVIOUS
|
||||
TYPE="FUNCTION"
|
||||
MATCH_TYPE="OPERATOR">(</MARK_PREVIOUS>
|
||||
|
||||
<SEQ TYPE="OPERATOR">=</SEQ>
|
||||
<SEQ TYPE="OPERATOR">!</SEQ>
|
||||
<SEQ TYPE="OPERATOR">+</SEQ>
|
||||
<SEQ TYPE="OPERATOR">-</SEQ>
|
||||
<SEQ TYPE="OPERATOR">/</SEQ>
|
||||
<SEQ TYPE="OPERATOR">*</SEQ>
|
||||
<SEQ TYPE="OPERATOR">></SEQ>
|
||||
<SEQ TYPE="OPERATOR"><</SEQ>
|
||||
<SEQ TYPE="OPERATOR">%</SEQ>
|
||||
<SEQ TYPE="OPERATOR">&</SEQ>
|
||||
<SEQ TYPE="OPERATOR">|</SEQ>
|
||||
<SEQ TYPE="OPERATOR">^</SEQ>
|
||||
<SEQ TYPE="OPERATOR">~</SEQ>
|
||||
<SEQ TYPE="OPERATOR">?</SEQ>
|
||||
<SEQ TYPE="OPERATOR">:</SEQ>
|
||||
<SEQ TYPE="OPERATOR">.</SEQ>
|
||||
<SEQ TYPE="OPERATOR">,</SEQ>
|
||||
<SEQ TYPE="OPERATOR">[</SEQ>
|
||||
<SEQ TYPE="OPERATOR">]</SEQ>
|
||||
<SEQ TYPE="OPERATOR">)</SEQ>
|
||||
<SEQ TYPE="OPERATOR">}</SEQ>
|
||||
<SEQ TYPE="OPERATOR">{</SEQ>
|
||||
<SEQ TYPE="OPERATOR">;</SEQ>
|
||||
|
||||
<KEYWORDS>
|
||||
<LITERAL2>__FILE__</LITERAL2>
|
||||
<LITERAL2>__LINE__</LITERAL2>
|
||||
<LITERAL2>__DATE__</LITERAL2>
|
||||
<LITERAL2>__RANDOM__</LITERAL2>
|
||||
<LITERAL2>__RANDOM_LAST</LITERAL2>
|
||||
<LITERAL2>__COUNT__</LITERAL2>
|
||||
<LITERAL2>__COUNT_LAST</LITERAL2>
|
||||
</KEYWORDS>
|
||||
</RULES>
|
||||
|
||||
<!-- Core language -->
|
||||
<RULES SET="CORE"
|
||||
IGNORE_CASE="FALSE"
|
||||
HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
|
||||
<KEYWORDS>
|
||||
<!-- Types -->
|
||||
<KEYWORD3>float</KEYWORD3>
|
||||
<KEYWORD3>vector</KEYWORD3>
|
||||
<KEYWORD3>string</KEYWORD3>
|
||||
<KEYWORD3>entity</KEYWORD3>
|
||||
<KEYWORD3>enum</KEYWORD3>
|
||||
<KEYWORD3>.float</KEYWORD3>
|
||||
<KEYWORD3>.int</KEYWORD3>
|
||||
<KEYWORD3>.vector</KEYWORD3>
|
||||
<KEYWORD3>.string</KEYWORD3>
|
||||
<KEYWORD3>.entity</KEYWORD3>
|
||||
<KEYWORD3>.void</KEYWORD3>
|
||||
<KEYWORD3>typedef</KEYWORD3>
|
||||
|
||||
<KEYWORD1>break</KEYWORD1>
|
||||
<KEYWORD1>case</KEYWORD1>
|
||||
<KEYWORD1>continue</KEYWORD1>
|
||||
<KEYWORD1>default</KEYWORD1>
|
||||
<KEYWORD1>do</KEYWORD1>
|
||||
<KEYWORD1>else</KEYWORD1>
|
||||
<KEYWORD1>for</KEYWORD1>
|
||||
<KEYWORD1>goto</KEYWORD1>
|
||||
<KEYWORD1>if</KEYWORD1>
|
||||
<KEYWORD1>return</KEYWORD1>
|
||||
<KEYWORD1>switch</KEYWORD1>
|
||||
<KEYWORD1>void</KEYWORD1>
|
||||
<KEYWORD1>while</KEYWORD1>
|
||||
<KEYWORD1>nil</KEYWORD1>
|
||||
|
||||
<LITERAL2>FALSE</LITERAL2>
|
||||
<LITERAL2>TRUE</LITERAL2>
|
||||
<LITERAL2>...</LITERAL2>
|
||||
</KEYWORDS>
|
||||
</RULES>
|
||||
|
||||
<!-- Different comment styles. -->
|
||||
<RULES SET="COMMENTS">
|
||||
<!-- Doxygen comment, Javadoc style -->
|
||||
<SEQ TYPE="COMMENT1">/**/</SEQ>
|
||||
<SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
|
||||
<BEGIN>/**<</BEGIN>
|
||||
<END>*/</END>
|
||||
</SPAN>
|
||||
<SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
|
||||
<BEGIN>/**</BEGIN>
|
||||
<END>*/</END>
|
||||
</SPAN>
|
||||
<EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">///<</EOL_SPAN>
|
||||
<EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">///</EOL_SPAN>
|
||||
|
||||
<!-- Doxygen comment, Qt style -->
|
||||
<SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
|
||||
<BEGIN>/*!<</BEGIN>
|
||||
<END>*/</END>
|
||||
</SPAN>
|
||||
<SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
|
||||
<BEGIN>/*!</BEGIN>
|
||||
<END>*/</END>
|
||||
</SPAN>
|
||||
<EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">//!<</EOL_SPAN>
|
||||
<EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">//!</EOL_SPAN>
|
||||
|
||||
<!-- C style comment -->
|
||||
<SPAN TYPE="COMMENT1">
|
||||
<BEGIN>/*</BEGIN>
|
||||
<END>*/</END>
|
||||
</SPAN>
|
||||
<EOL_SPAN TYPE="COMMENT1">//</EOL_SPAN>
|
||||
</RULES>
|
||||
|
||||
<!-- Preprocessor specific rules -->
|
||||
<RULES SET="CPP"
|
||||
IGNORE_CASE="FALSE"
|
||||
HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
|
||||
|
||||
<EOL_SPAN_REGEXP HASH_CHAR="include" TYPE="MARKUP" DELEGATE="INCLUDE">include\b</EOL_SPAN_REGEXP>
|
||||
<EOL_SPAN_REGEXP HASH_CHAR="define" TYPE="MARKUP" DELEGATE="DEFINE">define\b</EOL_SPAN_REGEXP>
|
||||
<EOL_SPAN_REGEXP HASH_CHAR="endif" TYPE="MARKUP" DELEGATE="LEX">endif\b</EOL_SPAN_REGEXP>
|
||||
<EOL_SPAN_REGEXP HASH_CHAR="elif" TYPE="MARKUP" DELEGATE="CONDITION">elif\b</EOL_SPAN_REGEXP>
|
||||
<EOL_SPAN_REGEXP HASH_CHAR="if" TYPE="MARKUP" DELEGATE="CONDITION">if\b</EOL_SPAN_REGEXP>
|
||||
|
||||
<IMPORT DELEGATE="LEX"/>
|
||||
|
||||
<!-- Directives -->
|
||||
<KEYWORDS>
|
||||
<MARKUP>undef</MARKUP>
|
||||
<MARKUP>ifdef</MARKUP>
|
||||
<MARKUP>ifndef</MARKUP>
|
||||
<MARKUP>else</MARKUP>
|
||||
<MARKUP>error</MARKUP>
|
||||
<MARKUP>warning</MARKUP>
|
||||
<MARKUP>pragma</MARKUP>
|
||||
<MARKUP>$frame</MARKUP>
|
||||
<MARKUP>$model</MARKUP>
|
||||
</KEYWORDS>
|
||||
</RULES>
|
||||
|
||||
<!-- After #include directive -->
|
||||
<!-- "\"s are not escaped. -->
|
||||
<RULES SET="INCLUDE"
|
||||
IGNORE_CASE="FALSE"
|
||||
HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
|
||||
<SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE">
|
||||
<BEGIN><</BEGIN>
|
||||
<END>></END>
|
||||
</SPAN>
|
||||
<SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE">
|
||||
<BEGIN>"</BEGIN>
|
||||
<END>"</END>
|
||||
</SPAN>
|
||||
<IMPORT DELEGATE="LEX"/>
|
||||
</RULES>
|
||||
|
||||
<!-- After #define directive -->
|
||||
<!-- Almost same as the normal code,
|
||||
except two additional operators # and ##. -->
|
||||
<RULES SET="DEFINE"
|
||||
IGNORE_CASE="FALSE"
|
||||
HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
|
||||
<SEQ TYPE="OPERATOR">#</SEQ>
|
||||
<IMPORT DELEGATE="LEX"/>
|
||||
<IMPORT DELEGATE="CORE"/>
|
||||
</RULES>
|
||||
|
||||
<!-- After #if or #elif directive -->
|
||||
<!-- All constant expressions and a special operator
|
||||
'defined' is available. But the core language elements
|
||||
(such as operator 'sizeof', type casting, etc...) are not. -->
|
||||
<RULES SET="CONDITION"
|
||||
IGNORE_CASE="FALSE"
|
||||
HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
|
||||
<IMPORT DELEGATE="LEX"/>
|
||||
<KEYWORDS>
|
||||
<KEYWORD2>defined</KEYWORD2>
|
||||
<KEYWORD2>TRUE</KEYWORD2>
|
||||
<KEYWORD2>FALSE</KEYWORD2>
|
||||
<KEYWORD2>true</KEYWORD2>
|
||||
<KEYWORD2>false</KEYWORD2>
|
||||
</KEYWORDS>
|
||||
</RULES>
|
||||
</MODE>
|
|
@ -1,9 +0,0 @@
|
|||
To use the Kate syntax highlighting install qc.xml to the syntax
|
||||
directory for kate.
|
||||
|
||||
# Can be installed globally to
|
||||
$KDEDIR/share/apps/katepart/syntax
|
||||
|
||||
if $KDEDIR is unset you can lookup the folder directory with
|
||||
kde4-config --prefix if that doesn't work chances are KDEDIR is
|
||||
/usr
|
|
@ -1,155 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE language SYSTEM "language.dtd">
|
||||
<language name="QuakeC" section="Sources"
|
||||
version="1.00" kateversion="2.4"
|
||||
indenter="cstyle"
|
||||
extensions="*.qc;*.QC;*.qh"
|
||||
mimetype=""
|
||||
priority="5"
|
||||
author="Dale Weiler">
|
||||
<highlighting>
|
||||
<list name="keywords">
|
||||
<item> break </item>
|
||||
<item> case </item>
|
||||
<item> continue </item>
|
||||
<item> default </item>
|
||||
<item> do </item>
|
||||
<item> else </item>
|
||||
<item> enum </item>
|
||||
<item> for </item>
|
||||
<item> goto </item>
|
||||
<item> if </item>
|
||||
<item> return </item>
|
||||
<item> switch </item>
|
||||
<item> typedef </item>
|
||||
<item> while </item>
|
||||
<item> nil </item>
|
||||
</list>
|
||||
<list name="types">
|
||||
<item> const </item>
|
||||
<item> vector </item>
|
||||
<item> float </item>
|
||||
<item> void </item>
|
||||
<item> string </item>
|
||||
</list>
|
||||
<contexts>
|
||||
<context attribute="Normal Text" lineEndContext="#stay" name="Normal">
|
||||
<DetectSpaces />
|
||||
<RegExpr attribute="Preprocessor" context="Outscoped" String="#\s*if\s+0\s*$" beginRegion="PP" firstNonSpace="true" />
|
||||
<DetectChar context="AfterHash" char="#" firstNonSpace="true" lookAhead="true" />
|
||||
<StringDetect attribute="Region Marker" context="Region Marker" String="//BEGIN" beginRegion="Region1" firstNonSpace="true" />
|
||||
<StringDetect attribute="Region Marker" context="Region Marker" String="//END" endRegion="Region1" firstNonSpace="true" />
|
||||
<keyword attribute="Keyword" context="#stay" String="keywords"/>
|
||||
<keyword attribute="Data Type" context="#stay" String="types"/>
|
||||
<DetectIdentifier />
|
||||
<DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" />
|
||||
<DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" />
|
||||
<Float attribute="Float" context="#stay">
|
||||
<AnyChar String="fF" attribute="Float" context="#stay"/>
|
||||
</Float>
|
||||
<HlCHex attribute="Hex" context="#stay"/>
|
||||
<Int attribute="Decimal" context="#stay" >
|
||||
<StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
|
||||
<StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
|
||||
<StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
|
||||
<StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
|
||||
<StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
|
||||
<StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
|
||||
<StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
|
||||
<StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
|
||||
</Int>
|
||||
<HlCChar attribute="Char" context="#stay"/>
|
||||
<DetectChar attribute="String" context="String" char="""/>
|
||||
<Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
|
||||
<Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
|
||||
<AnyChar attribute="Symbol" context="#stay" String=":!%&()+,-/.*<=>?[]|~^;"/>
|
||||
</context>
|
||||
|
||||
<context attribute="String" lineEndContext="#pop" name="String">
|
||||
<LineContinue attribute="String" context="#stay"/>
|
||||
<HlCStringChar attribute="String Char" context="#stay"/>
|
||||
<DetectChar attribute="String" context="#pop" char="""/>
|
||||
</context>
|
||||
|
||||
<context attribute="Region Marker" lineEndContext="#pop" name="Region Marker">
|
||||
</context>
|
||||
|
||||
<context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
|
||||
<LineContinue attribute="Comment" context="#stay"/>
|
||||
<IncludeRules context="##Alerts" />
|
||||
</context>
|
||||
|
||||
<context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
|
||||
<Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
|
||||
<IncludeRules context="##Alerts" />
|
||||
</context>
|
||||
|
||||
<context attribute="Error" lineEndContext="#pop" name="AfterHash">
|
||||
<!-- define, elif, else, endif, error, if, ifdef, ifndef, include, include_next, line, pragma, undef, warning -->
|
||||
<RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*if(?:def|ndef)?(?=\s+\S)" insensitive="true" beginRegion="PP" firstNonSpace="true" />
|
||||
<RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*endif" insensitive="true" endRegion="PP" firstNonSpace="true" />
|
||||
<RegExpr attribute="Preprocessor" context="Define" String="#\s*define.*((?=\\))" insensitive="true" firstNonSpace="true" />
|
||||
<RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*(?:el(?:se|if)|include(?:_next)?|define|undef|line|error|warning|pragma)" insensitive="true" firstNonSpace="true" />
|
||||
<RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s+[0-9]+" insensitive="true" firstNonSpace="true" />
|
||||
</context>
|
||||
|
||||
<context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor">
|
||||
<LineContinue attribute="Preprocessor" context="#stay"/>
|
||||
<RangeDetect attribute="Prep. Lib" context="#stay" char=""" char1="""/>
|
||||
<RangeDetect attribute="Prep. Lib" context="#stay" char="<" char1=">"/>
|
||||
<IncludeRules context="##Doxygen" />
|
||||
<Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*" beginRegion="Comment2" />
|
||||
<Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/" />
|
||||
</context>
|
||||
|
||||
<context attribute="Preprocessor" lineEndContext="#pop" name="Define">
|
||||
<LineContinue attribute="Preprocessor" context="#stay"/>
|
||||
</context>
|
||||
|
||||
<context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor">
|
||||
<Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment2" />
|
||||
</context>
|
||||
|
||||
<context attribute="Comment" lineEndContext="#stay" name="Outscoped" >
|
||||
<DetectSpaces />
|
||||
<DetectIdentifier />
|
||||
<DetectChar attribute="String" context="String" char="""/>
|
||||
<Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
|
||||
<Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
|
||||
<RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="PP" firstNonSpace="true" />
|
||||
<RegExpr attribute="Preprocessor" context="#pop" String="#\s*el(?:se|if)" firstNonSpace="true" />
|
||||
<RegExpr attribute="Preprocessor" context="#pop" String="#\s*endif" endRegion="PP" firstNonSpace="true" />
|
||||
</context>
|
||||
|
||||
<context attribute="Comment" lineEndContext="#stay" name="Outscoped intern">
|
||||
<DetectSpaces />
|
||||
<DetectIdentifier />
|
||||
<DetectChar attribute="String" context="String" char="""/>
|
||||
<Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
|
||||
<Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
|
||||
<RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="PP" firstNonSpace="true" />
|
||||
<RegExpr attribute="Comment" context="#pop" String="#\s*endif" endRegion="PP" firstNonSpace="true" />
|
||||
</context>
|
||||
</contexts>
|
||||
<itemDatas>
|
||||
<itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
|
||||
<itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
|
||||
<itemData name="Data Type" defStyleNum="dsDataType" spellChecking="false"/>
|
||||
<itemData name="Decimal" defStyleNum="dsDecVal" spellChecking="false"/>
|
||||
<itemData name="Hex" defStyleNum="dsBaseN" spellChecking="false"/>
|
||||
<itemData name="Float" defStyleNum="dsFloat" spellChecking="false"/>
|
||||
<itemData name="String" defStyleNum="dsString"/>
|
||||
<itemData name="String Char" defStyleNum="dsChar"/>
|
||||
<itemData name="Comment" defStyleNum="dsComment"/>
|
||||
<itemData name="Symbol" defStyleNum="dsNormal" spellChecking="false"/>
|
||||
<itemData name="Preprocessor" defStyleNum="dsOthers" spellChecking="false"/>
|
||||
</itemDatas>
|
||||
</highlighting>
|
||||
<general>
|
||||
<comments>
|
||||
<comment name="singleLine" start="//" />
|
||||
<comment name="multiLine" start="/*" end="*/" />
|
||||
</comments>
|
||||
<keywords casesensitive="1" additionalDeliminator="'"" />
|
||||
</general>
|
||||
</language>
|
|
@ -1,12 +0,0 @@
|
|||
To use the nano syntax highlighting install qc.nanorc somewhere and
|
||||
add:
|
||||
|
||||
include /directory/qc.nanorc
|
||||
|
||||
to your nanorc file located at ~/.nanorc. If the file doesn't exist
|
||||
create it.
|
||||
|
||||
Optionally you can install it globally by installing qc.nanorc to
|
||||
/usr/share/nano
|
||||
|
||||
However you still need to provide the include to your ~/.nanorc
|
|
@ -1,22 +0,0 @@
|
|||
# Language: QuakeC
|
||||
# Maintainer: Dale Weiler
|
||||
|
||||
syntax "qc" "\.(qc|QC)$" "\.(qh|QH)$"
|
||||
color brightred "\<[A-Z_][0-9A-Z_]+\>"
|
||||
color green "\<(float|string|enum|void|const|typedef|nil)\>"
|
||||
color brightyellow "\<(for|if|while|do|else|case|default|switch)\>"
|
||||
color magenta "\<(goto|continue|break|return)\>"
|
||||
color brightcyan "^[[:space:]]*#[[:space:]]*(define|include|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma)"
|
||||
color brightmagenta "'([^'\]|(\\["'abfnrtv\\]))'" "'\\(([0-3]?[0-7]{1,2}))'" "'\\x[0-9A-Fa-f]{1,2}'"
|
||||
|
||||
color brightyellow "<[^= ]*>" ""(\\.|[^"])*""
|
||||
|
||||
## This string is VERY resource intensive!
|
||||
color brightyellow start=""(\\.|[^"])*\\[[:space:]]*$" end="^(\\.|[^"])*""
|
||||
|
||||
## Comment highlighting
|
||||
color brightblue "//.*"
|
||||
color brightblue start="/\*" end="\*/"
|
||||
|
||||
## Trailing whitespace
|
||||
color ,green "[[:space:]]+$"
|
1031
test.c → test.cpp
1031
test.c → test.cpp
File diff suppressed because it is too large
Load diff
18
tests/accumulate.qc
Normal file
18
tests/accumulate.qc
Normal file
|
@ -0,0 +1,18 @@
|
|||
#define ACCUMULATE_FUNCTION(FUNC) \
|
||||
[[accumulate]] void FUNC ()
|
||||
|
||||
ACCUMULATE_FUNCTION(foo) {
|
||||
print("hello ");
|
||||
}
|
||||
|
||||
ACCUMULATE_FUNCTION(foo) {
|
||||
print("accumulation ");
|
||||
}
|
||||
|
||||
ACCUMULATE_FUNCTION(foo) {
|
||||
print("world\n");
|
||||
}
|
||||
|
||||
void main() {
|
||||
foo();
|
||||
}
|
5
tests/accumulate.tmpl
Normal file
5
tests/accumulate.tmpl
Normal file
|
@ -0,0 +1,5 @@
|
|||
I: accumulate.qc
|
||||
D: test function accumulation
|
||||
T: -execute
|
||||
C: -std=gmqcc -fftepp
|
||||
M: hello accumulation world
|
13
tests/arithexcept.qc
Normal file
13
tests/arithexcept.qc
Normal file
|
@ -0,0 +1,13 @@
|
|||
const float huge = 340282346638528859811704183484516925440.000000; // FLT_MAX
|
||||
|
||||
#ifdef DIVBYZERO
|
||||
const float a = 1.0 / 0.0;
|
||||
#endif
|
||||
|
||||
#ifdef OVERFLOW
|
||||
const float a = huge * huge;
|
||||
#endif
|
||||
|
||||
#ifdef UNDERFLOW
|
||||
const float a = 1 / huge;
|
||||
#endif
|
4
tests/arithexcept.tmpl
Normal file
4
tests/arithexcept.tmpl
Normal file
|
@ -0,0 +1,4 @@
|
|||
I: arithexcept.qc
|
||||
D: arithmetic exceptions (divide by zero)
|
||||
T: -fail
|
||||
C: -std=fteqcc -farithmetic-exceptions -DDIVBYZERO
|
4
tests/arithexcept_of.tmpl
Normal file
4
tests/arithexcept_of.tmpl
Normal file
|
@ -0,0 +1,4 @@
|
|||
I: arithexcept.qc
|
||||
D: arithmetic exceptions (overflow)
|
||||
T: -fail
|
||||
C: -std=fteqcc -farithmetic-exceptions -DOVERFLOW
|
4
tests/arithexcept_uf.tmpl
Normal file
4
tests/arithexcept_uf.tmpl
Normal file
|
@ -0,0 +1,4 @@
|
|||
I: arithexcept.qc
|
||||
D: arithmetic exceptions (underflow)
|
||||
T: -fail
|
||||
C: -std=fteqcc -farithmetic-exceptions -DUNDERFLOW
|
|
@ -1,7 +1,7 @@
|
|||
I: arrays.qc
|
||||
D: array accessors and functionality
|
||||
I: arrays2.qc
|
||||
D: initialized arrays
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
M: 1001 1101 1201 1301 1401 1501
|
||||
M: 1001 1101 1201 1301 1401 1501 1601
|
||||
M: 1001 1101 1201 1301 1401 1501
|
||||
M: 10 20 30 40 50 60 70
|
||||
M: 100 200 300 400 500 600 0
|
||||
M: Hello World
|
||||
|
|
18
tests/arrays2.qc
Normal file
18
tests/arrays2.qc
Normal file
|
@ -0,0 +1,18 @@
|
|||
float glob1[7] = { 10, 20, 30, 40, 50, 60, 70 };
|
||||
float glob2[7] = { 100, 200, 300, 400, 500, 600 };
|
||||
string globs[] = { "Hello ", "World" };
|
||||
|
||||
void main() {
|
||||
float i;
|
||||
print(ftos(glob1[0]));
|
||||
for (i = 1; i != 7; ++i)
|
||||
print(" ", ftos(glob1[i]));
|
||||
print("\n");
|
||||
|
||||
print(ftos(glob2[0]));
|
||||
for (i = 1; i != 7; ++i)
|
||||
print(" ", ftos(glob2[i]));
|
||||
print("\n");
|
||||
|
||||
print(globs[0], globs[1], "\n");
|
||||
}
|
7
tests/arrays2.tmpl
Normal file
7
tests/arrays2.tmpl
Normal file
|
@ -0,0 +1,7 @@
|
|||
I: arrays.qc
|
||||
D: array accessors and functionality
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
M: 1001 1101 1201 1301 1401 1501
|
||||
M: 1001 1101 1201 1301 1401 1501 1601
|
||||
M: 1001 1101 1201 1301 1401 1501
|
|
@ -3,12 +3,35 @@ void main() {
|
|||
float b; b = 1;
|
||||
float c; c = 1;
|
||||
float d; d = 1;
|
||||
vector e; e = '1 1 1';
|
||||
vector f; f = '1 1 1';
|
||||
|
||||
#ifdef __STD_FTEQCC__
|
||||
a &~= 1; // 0
|
||||
#else
|
||||
a &= ~1; // 0
|
||||
#endif
|
||||
#ifdef __STD_GMQCC__
|
||||
b &= ~1; // 0
|
||||
c &= ~d; // 0
|
||||
#else
|
||||
b &~= 1; // 0
|
||||
c &~= 1; // 0
|
||||
#endif
|
||||
#ifdef __STD_FTEQCC__
|
||||
f &~= e; // '0 0 0'
|
||||
#else
|
||||
f &= ~e; // '0 0 0'
|
||||
#endif
|
||||
#ifdef __STD_GMQCC__
|
||||
e &= ~e; // '0 0 0'
|
||||
#else
|
||||
e &~= e; // '0 0 0'
|
||||
#endif
|
||||
|
||||
print("a: ", ftos(a), "\nb: ",
|
||||
ftos(b), "\nc: ",
|
||||
ftos(c), "\n");
|
||||
print("e: ", vtos(e), "\n");
|
||||
print("f: ", vtos(f), "\n");
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
# used to test the builtins
|
||||
I: bitnot.qc
|
||||
D: test bitwise not operators
|
||||
D: test bitwise not operators (fteqcc operators)
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
C: -std=fteqcc
|
||||
E: $null
|
||||
M: a: 0
|
||||
M: b: 0
|
||||
M: c: 0
|
||||
M: e: '0 0 0'
|
||||
M: f: '0 0 0'
|
||||
|
|
11
tests/bitnotgmqcc.tmpl
Normal file
11
tests/bitnotgmqcc.tmpl
Normal file
|
@ -0,0 +1,11 @@
|
|||
# used to test the builtins
|
||||
I: bitnot.qc
|
||||
D: test bitwise not operators (gmqcc operators)
|
||||
T: -execute
|
||||
C: -std=gmqcc -fftepp
|
||||
E: $null
|
||||
M: a: 0
|
||||
M: b: 0
|
||||
M: c: 0
|
||||
M: e: '0 0 0'
|
||||
M: f: '0 0 0'
|
|
@ -8,5 +8,5 @@ void(float a, float b, float c) main = {
|
|||
sum(sum(sum(a, b, c), b, sum(a, b, c)), b, sum(a, b, sum(a, b, c))),
|
||||
sum(sum(a, b, c), b, c));
|
||||
print(ftos(f), "\n");
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ I: correct-logic.qc
|
|||
D: vector logic flags
|
||||
T: -execute
|
||||
C: -std=fteqcc -fshort-logic
|
||||
M: ! & | i N
|
||||
M: ! & | i N
|
||||
M: 0, 0 -> 1 0 0 0 1
|
||||
M: 0, x -> 1 0 1 0 1
|
||||
M: x, 0 -> 0 0 1 1 0
|
||||
|
|
|
@ -2,7 +2,7 @@ I: correct-logic.qc
|
|||
D: vector logic flags
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
M: ! & | i N
|
||||
M: ! & | i N
|
||||
M: 0, 0 -> 1 0 0 0 1
|
||||
M: 0, x -> 1 0 1 0 1
|
||||
M: x, 0 -> 0 0 1 1 0
|
||||
|
|
|
@ -2,7 +2,7 @@ I: correct-logic.qc
|
|||
D: vector logic flags
|
||||
T: -execute
|
||||
C: -std=fteqcc -fcorrect-logic -fshort-logic
|
||||
M: ! & | i N
|
||||
M: ! & | i N
|
||||
M: 0, 0 -> 1 0 0 0 1
|
||||
M: 0, x -> 1 0 1 0 1
|
||||
M: x, 0 -> 0 0 1 1 0
|
||||
|
|
|
@ -2,7 +2,7 @@ I: correct-logic.qc
|
|||
D: vector logic flags
|
||||
T: -execute
|
||||
C: -std=fteqcc -fcorrect-logic
|
||||
M: ! & | i N
|
||||
M: ! & | i N
|
||||
M: 0, 0 -> 1 0 0 0 1
|
||||
M: 0, x -> 1 0 1 0 1
|
||||
M: x, 0 -> 0 0 1 1 0
|
||||
|
|
|
@ -3,17 +3,19 @@
|
|||
// builtins. I no event shall you even consider adding
|
||||
// these individually per test.
|
||||
|
||||
void (string, ...) print = #1;
|
||||
string (float) ftos = #2;
|
||||
entity () spawn = #3;
|
||||
void (entity) kill = #4;
|
||||
string (vector) vtos = #5;
|
||||
void (string) error = #6;
|
||||
float (vector) vlen = #7;
|
||||
string (entity) etos = #8;
|
||||
float (string) stof = #9;
|
||||
string (...) strcat = #10;
|
||||
float (string, string) strcmp = #11;
|
||||
vector (vector) normalize = #12;
|
||||
float (float) sqrt = #13;
|
||||
float (float) floor = #14;
|
||||
void (string str, ...) print = #1;
|
||||
string (float val) ftos = #2;
|
||||
entity () spawn = #3;
|
||||
void (entity ent) kill = #4;
|
||||
string (vector vec) vtos = #5;
|
||||
void (string str) error = #6;
|
||||
float (vector vec) vlen = #7;
|
||||
string (entity ent) etos = #8;
|
||||
float (string str) stof = #9;
|
||||
string (...) strcat = #10;
|
||||
float (string str1, string str2) strcmp = #11;
|
||||
vector (vector vec) normalize = #12;
|
||||
float (float val) sqrt = #13;
|
||||
float (float val) floor = #14;
|
||||
float (float val1, float val2) pow = #15;
|
||||
vector (string str) stov = #16;
|
||||
|
|
27
tests/dots.qc
Normal file
27
tests/dots.qc
Normal file
|
@ -0,0 +1,27 @@
|
|||
entity self;
|
||||
.float f;
|
||||
..float fp;
|
||||
...float fpp;
|
||||
|
||||
void try(entity e, ...float pp) {
|
||||
print("and: ", ftos( e.(e.(e.pp)) ), "\n");
|
||||
}
|
||||
|
||||
typedef float Float;
|
||||
|
||||
void try2(entity e, ...Float pp) {
|
||||
print("and: ", ftos( e.(e.(e.pp)) ), "\n");
|
||||
}
|
||||
|
||||
// whereas the varargs are tested in vararg tests
|
||||
|
||||
void main() {
|
||||
self = spawn();
|
||||
self.f = 123;
|
||||
self.fp = f;
|
||||
self.fpp = fp;
|
||||
print(ftos( self.(self.fp) ), "\n");
|
||||
print(ftos( self.(self.(self.fpp)) ), "\n");
|
||||
try(self, fpp);
|
||||
try2(self, fpp);
|
||||
}
|
8
tests/dots.tmpl
Normal file
8
tests/dots.tmpl
Normal file
|
@ -0,0 +1,8 @@
|
|||
I: dots.qc
|
||||
D: TOKEN_DOTS disambiguation
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
M: 123
|
||||
M: 123
|
||||
M: and: 123
|
||||
M: and: 123
|
|
@ -1,13 +1,13 @@
|
|||
float pow(float x, float y) {
|
||||
return __builtin_pow(x, y);
|
||||
}
|
||||
|
||||
void main() {
|
||||
float hundy = pow(10, 2); // 10^2 == 100
|
||||
float hundy = __builtin_pow(10, 2); // 10^2 == 100
|
||||
print(ftos(hundy), "\n"); // prints: 100
|
||||
|
||||
hundy = pow(10, 2);
|
||||
print(ftos(hundy), "\n");
|
||||
|
||||
hundy -= 90; // 100-90 = 10
|
||||
print(ftos(hundy ** 2), "\n"); // prints: 100
|
||||
print(ftos(pow(hundy, 2)), "\n"); // prints: 100
|
||||
|
||||
hundy = 10.0f;
|
||||
print(ftos(__builtin_exp(hundy)), "\n"); // prints: 22026.5
|
||||
|
|
|
@ -6,4 +6,6 @@ C: -std=gmqcc
|
|||
E: $null
|
||||
M: 100
|
||||
M: 100
|
||||
M: 100
|
||||
M: 100
|
||||
M: 22026.5
|
||||
|
|
13
tests/fieldfuncs.qc
Normal file
13
tests/fieldfuncs.qc
Normal file
|
@ -0,0 +1,13 @@
|
|||
.float field;
|
||||
|
||||
.float getfield() {
|
||||
return field;
|
||||
}
|
||||
|
||||
void() main = {
|
||||
entity e = spawn();
|
||||
e.field = 42;
|
||||
print(ftos(e.(getfield())), "\n");
|
||||
.float memptr = getfield();
|
||||
print(ftos(e.memptr), "\n");
|
||||
}
|
6
tests/fieldfuncs.tmpl
Normal file
6
tests/fieldfuncs.tmpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
I: fieldfuncs.qc
|
||||
D: test fields with functions
|
||||
T: -execute
|
||||
C: -std=fte
|
||||
M: 42
|
||||
M: 42
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue