mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-04-09 19:42:04 +00:00
Compare commits
2154 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 | ||
|
219508e478 | ||
|
75ceab8f51 | ||
|
5007fd7f71 | ||
|
50ff9e4fd0 | ||
|
7e0d6bdd87 | ||
|
9f8bee4bf1 | ||
|
b3e9ef3ad9 | ||
|
1077eb2061 | ||
|
d4f8e4a0dd | ||
|
35692c0b57 | ||
|
b9fb29d740 | ||
|
aed2b1031c | ||
|
68ca2c4962 | ||
|
ce73074d51 | ||
|
c3f4b7153b | ||
|
2eddc464d5 | ||
|
3d8e8cd80d | ||
|
9fee84f250 | ||
|
785ab7c072 | ||
|
73070395eb | ||
|
c6f2c8fc3d | ||
|
2a3376cb52 | ||
|
10dd7aacfe | ||
|
1b5504fb9e | ||
|
bfbb60bbed | ||
|
3a4aba0b31 | ||
|
1a915c7bf7 | ||
|
66305c676a | ||
|
8ffdfbfd97 | ||
|
5d88216f38 | ||
|
19c57c03cc | ||
|
a0f6b00a15 | ||
|
1e9d3e43cb | ||
|
dcd65f18f8 | ||
|
8bee9a6819 | ||
|
6284cc673d | ||
|
b3fc77efd6 | ||
|
35988b6191 | ||
|
dc8523c650 | ||
|
489ad486bc | ||
|
10738da2fb | ||
|
17996dee50 | ||
|
f6d554874b | ||
|
3d9a06591a | ||
|
d6f6241872 | ||
|
c86e778c76 | ||
|
217833dff7 | ||
|
1759388e3f | ||
|
9a1894f587 | ||
|
a879fc28a3 | ||
|
cc3b8dcfd6 | ||
|
53aac525d9 | ||
|
f8fc40c306 | ||
|
0f7634876f | ||
|
ef565adaa5 | ||
|
f9ed308aa8 | ||
|
685fa54daf | ||
|
160e7cf7ee | ||
|
934ff4a5eb | ||
|
2343e13c45 | ||
|
8c08897749 | ||
|
462c06d56b | ||
|
317559e5a5 | ||
|
4a1eb7f948 | ||
|
f3c04d0f37 | ||
|
3cecd5d066 | ||
|
7def17eed5 | ||
|
c362826dc6 | ||
|
0661002257 | ||
|
96c0fa983d | ||
|
6009e2b3ea | ||
|
07e335fdb8 | ||
|
e27eebe0f9 | ||
|
c53fa31a0a | ||
|
79282bfee0 | ||
|
ea0e2c47d5 | ||
|
57d2876021 | ||
|
485866552d | ||
|
b47e3ebccf | ||
|
46752af74b | ||
|
8f2a22b8c3 | ||
|
dc7e18ec73 | ||
|
0b0b6423ba | ||
|
4d394494b6 | ||
|
d7be99c9dd | ||
|
21c6079b7a | ||
|
1f0a1b8140 | ||
|
b82efcf0ce | ||
|
a61c9c6d89 | ||
|
992c6fbfb8 | ||
|
1c51fb80eb | ||
|
f2b21158d8 | ||
|
1a9c82483c | ||
|
1f667c4b35 | ||
|
d5e90a17f9 | ||
|
1caf1b3ebe | ||
|
c2b5a95961 | ||
|
ef528d6710 | ||
|
18fad16a7d | ||
|
7ba7fd5968 | ||
|
752409417f | ||
|
4591b3a4fa | ||
|
eb2a74f7e7 | ||
|
a25bd052d7 | ||
|
ccc2eb3298 | ||
|
3e7340c52c | ||
|
3f546df677 | ||
|
48d6375817 | ||
|
3f40fcb965 | ||
|
95172861f5 | ||
|
c62d88cb57 | ||
|
c74889f648 | ||
|
cb0169a784 | ||
|
7b3bbf05b9 | ||
|
f5ba2e825f | ||
|
5bb10260b3 | ||
|
47ebf7c84c | ||
|
c6bd5e6a2b | ||
|
a6ac90215b | ||
|
c01ad2bfcf | ||
|
50e8618b64 | ||
|
54c89e485a | ||
|
8e122d78be | ||
|
58a04a04e5 | ||
|
6900e50289 | ||
|
c42a853e0b | ||
|
1b02a5204a | ||
|
66b5cf29ba | ||
|
6a0f9d3cb2 | ||
|
11255058cf | ||
|
dfca3743f9 | ||
|
a798859a7a | ||
|
d4785ad6e3 | ||
|
965f8b9041 | ||
|
cbeac3e5f2 | ||
|
9bb586fb5a | ||
|
d8d78f61a7 | ||
|
73c085a461 | ||
|
662612d66f | ||
|
f19adcd1b3 | ||
|
b971ddec3a | ||
|
479f3b9343 | ||
|
12340a6bd0 | ||
|
0367a6175d | ||
|
db229b6f94 | ||
|
bd54fe03b9 | ||
|
f2380a2faa | ||
|
d3c7b6fb39 | ||
|
f59d557dce | ||
|
bedfee1fae | ||
|
bdbfc85243 | ||
|
2146e018f1 | ||
|
a32f59b256 | ||
|
9f06e0e017 | ||
|
b1d1aabbdd | ||
|
aebab8b68a | ||
|
d27c06ea7f | ||
|
80184b0d80 | ||
|
aa27249b88 | ||
|
3593a1b6b8 | ||
|
3b923a0183 | ||
|
3b630ec5dc | ||
|
c7e3e3e306 | ||
|
1dfd375738 | ||
|
3e3b8993aa | ||
|
0b154d69e7 | ||
|
c8959a2452 | ||
|
b5ead871ef | ||
|
5acde18ef1 | ||
|
b0c6dc5ab2 | ||
|
e4554892b0 | ||
|
e4075916f0 | ||
|
fb8fb9f0f7 | ||
|
ac8f510dac | ||
|
02230ca992 | ||
|
533b2144c5 | ||
|
eabb92f7b4 | ||
|
a568d43487 | ||
|
945aba6e10 | ||
|
09a1a37554 | ||
|
8d59d7029d | ||
|
ece64dc779 | ||
|
aef60b7ec1 | ||
|
8ecfc51799 | ||
|
1fa9fbb94b | ||
|
86df8c8a78 | ||
|
d1f4b108a0 | ||
|
ecd9e3be97 | ||
|
eb0e3c2898 | ||
|
9bc774dcd3 | ||
|
cb1a6a6a6b | ||
|
1e60bafcdc | ||
|
21e890602d | ||
|
6fc141733f | ||
|
060bc0be10 | ||
|
3dc8e9bb9c | ||
|
e464cc1ea6 | ||
|
52ffc6db10 | ||
|
9cff2f7b8a | ||
|
b3e87c3280 | ||
|
4b5afe3456 | ||
|
e2c424d607 | ||
|
8e87082e1d | ||
|
72106b0982 | ||
|
fed0f8b6b4 | ||
|
f860759758 | ||
|
15141897ae | ||
|
31ce4c2afa | ||
|
8e1cd4b1df | ||
|
442adee298 | ||
|
e11a17b408 | ||
|
e7aa24bd28 | ||
|
06ff6eb054 | ||
|
ce8b595ff2 | ||
|
46892daa3a | ||
|
e662efae61 | ||
|
530dbd85b2 | ||
|
0d1b40c9c1 | ||
|
c41ef65cc9 | ||
|
95dd0fcd9f | ||
|
2b51b35634 | ||
|
ddb0be6c0a | ||
|
2e07a7b2c1 | ||
|
7b96b0bd6d | ||
|
319783e873 | ||
|
4c0a836ca6 | ||
|
d45853f5fe | ||
|
9bf8f9c505 | ||
|
ccdfd4c65a | ||
|
394f1deade | ||
|
a3559548a8 | ||
|
c34f5d20ed | ||
|
03b933bc7a | ||
|
bf6eb9432e | ||
|
ea15abada5 | ||
|
622c55f254 | ||
|
d7a47eb5f9 | ||
|
a6ecc3cbc4 | ||
|
a1fac66587 | ||
|
9fffb3441c | ||
|
5f9f6c11b8 | ||
|
1916a6e339 | ||
|
b9f46f4ef7 | ||
|
18c317a787 | ||
|
8c6f4c3fc3 | ||
|
835659ac48 | ||
|
44c87bedfc | ||
|
98fd5cd429 | ||
|
ebd6a1ae95 | ||
|
9f5f2b6d74 | ||
|
e12d492d67 | ||
|
36c5722273 | ||
|
d201cfe6b4 | ||
|
6c4c373089 | ||
|
480b2fc7b6 | ||
|
f3c5c40104 | ||
|
76614b311e | ||
|
d61172598e | ||
|
3d35804a7d | ||
|
7ea1033a43 | ||
|
1597f38f45 | ||
|
b9fcd64a1f | ||
|
6fc5b32123 | ||
|
001d853f38 | ||
|
b4fbba5708 | ||
|
370b54251e | ||
|
95e8140555 | ||
|
3518646ad7 | ||
|
03f364a944 | ||
|
44e416e790 | ||
|
3e1c10628c | ||
|
531fbaf28c | ||
|
a94d2fee4e | ||
|
3cc97984bf | ||
|
74ba208a44 | ||
|
b4d3241e27 | ||
|
4500facb2b | ||
|
edfe340d09 | ||
|
b39b61e514 | ||
|
9afe41471b | ||
|
7fb3b97e73 | ||
|
48f8d0e0c3 | ||
|
ad7059156a | ||
|
9b51e7085b | ||
|
31f0c05ebc | ||
|
3c8ce7c866 | ||
|
69e29c3ef8 | ||
|
ef0de47954 | ||
|
32f1a5d2c8 | ||
|
62af1659eb | ||
|
dca9dd56d1 | ||
|
ba207cc04c | ||
|
496aededb1 | ||
|
5d9ad219cf | ||
|
99482a324e | ||
|
57102aea4a | ||
|
1d4c0bb388 | ||
|
de28df33ec | ||
|
a8d5504445 | ||
|
94a83ad969 | ||
|
06f53b1a28 | ||
|
8697fa59f3 | ||
|
1410840ef6 | ||
|
4c0a358e2b | ||
|
d5ba5a6e6a | ||
|
7d2b0e9638 | ||
|
b31e9b63eb | ||
|
81f3a6d186 | ||
|
00a9d2a9e3 | ||
|
e311dffcb3 | ||
|
0ba8455f6a | ||
|
dfad837f5b | ||
|
11450c11d1 | ||
|
dfa6d0c9ad | ||
|
f8f4ad8ee5 | ||
|
e695a5919d | ||
|
b311cd6163 | ||
|
73afc98699 | ||
|
150c6e21e7 | ||
|
c7ca5683b9 | ||
|
0f190026e8 | ||
|
ee7d45f492 | ||
|
b208c1a49f | ||
|
73c86127f7 | ||
|
bfbbe0c771 | ||
|
0962dfdd4d | ||
|
d12e6e15c9 | ||
|
d7f4e981a3 | ||
|
bec05f1bbb | ||
|
ab8cc64dfd | ||
|
a5029a510a | ||
|
3d62cb37f1 | ||
|
e0504ce482 | ||
|
12ffd83d4f | ||
|
8d4e395092 | ||
|
1d05cd28ee | ||
|
3ecae267ad | ||
|
3652a122ed | ||
|
d1def27dbb | ||
|
08200d4f77 | ||
|
7fc88cfe5b | ||
|
61b777f575 | ||
|
f440c3c614 | ||
|
127cc5215b | ||
|
4df6d1e028 | ||
|
47db74c699 | ||
|
7dd31ccf77 | ||
|
5887afae62 | ||
|
a01388ea7d | ||
|
57c0a09800 | ||
|
175623592b | ||
|
e149551744 | ||
|
cd543aad5e | ||
|
e8b5ad6625 | ||
|
2a4ea74a97 | ||
|
1861660585 | ||
|
c10d127fd7 | ||
|
d78997599a | ||
|
973122ed9b | ||
|
6dfdf69a8e | ||
|
c69ba2c734 | ||
|
87ff481abf | ||
|
6a248e2498 | ||
|
ca947d782c | ||
|
2f9db8972e | ||
|
3429e7d0cb | ||
|
30eac57199 | ||
|
5bb4e40c72 | ||
|
7bab7fc42e | ||
|
d6ffcbb8fc | ||
|
199ed6c31f | ||
|
5231279113 | ||
|
3436fa7d89 | ||
|
6df3c625b0 | ||
|
5ea710e317 | ||
|
88cfa53dc2 | ||
|
0d9144eeff | ||
|
f6197a965c | ||
|
4a063d3518 | ||
|
0da4626417 | ||
|
fa90d2d06e | ||
|
0616bb9468 | ||
|
9c2bc7f381 | ||
|
a474f20b84 | ||
|
aa8b9eb1ad | ||
|
a468d0b478 | ||
|
1c1ea1d0f7 | ||
|
101cb09e91 | ||
|
f69f2c2ece | ||
|
16ccaffc3f | ||
|
7a5a328c54 | ||
|
f06a59e3c7 | ||
|
ffba3429fb | ||
|
cf676443cb | ||
|
81cc4194dc | ||
|
428453a132 | ||
|
06101d48e4 | ||
|
d0efaa50d4 | ||
|
96126b435a | ||
|
10c7f4f838 | ||
|
6b0a522bb9 | ||
|
704ddf94f8 | ||
|
4f7113116d | ||
|
be6942d972 | ||
|
9eefe90d78 | ||
|
16e789419d | ||
|
7d4340469e | ||
|
a76ae11241 | ||
|
6ece523552 | ||
|
d6809ed331 | ||
|
4c05230cb3 | ||
|
3f3ed9b6f0 | ||
|
57c3d48711 | ||
|
debfe136a5 | ||
|
ae97ff7a80 | ||
|
63e5bc91de | ||
|
a395a20dba | ||
|
54c8801bec | ||
|
d8ae1cb3e9 | ||
|
cbb91c2026 | ||
|
99249c8fa3 | ||
|
f4d1ef4740 | ||
|
4d72c85f73 | ||
|
5134673461 | ||
|
5d1685b93d | ||
|
dcc511f4c3 | ||
|
e59d50ee5d | ||
|
2531a3695f | ||
|
985a177288 | ||
|
ecb83404ab | ||
|
8ac6bcdad7 | ||
|
4f873e1d9d | ||
|
7cc7e912e7 | ||
|
72f056a4d7 | ||
|
ac5cc49840 | ||
|
9a4e215179 | ||
|
9c8ddb3771 | ||
|
1e17b5f696 | ||
|
3fa771f51d | ||
|
4afe61060d | ||
|
0f4090402d | ||
|
cfdeaf4786 | ||
|
056779d3b8 | ||
|
6f5a20e76b | ||
|
a4c1e63637 | ||
|
cc7e1a3363 | ||
|
ee33b4e5cc | ||
|
0c59274c54 | ||
|
d98cc564b1 | ||
|
44a7154f58 | ||
|
106db76b9d | ||
|
cc8558025b | ||
|
9841240aab | ||
|
e2e4907b60 | ||
|
7e87c61d78 | ||
|
294870c5ba | ||
|
efecd160ca | ||
|
0fb089fbb7 | ||
|
682c43973e | ||
|
726d163631 | ||
|
7a5bb20778 | ||
|
caa44e82e0 | ||
|
41a01c08e1 | ||
|
8bc9d4b427 | ||
|
b8d238d76b | ||
|
0e3bb0e4f4 | ||
|
ec4cd0d258 | ||
|
793547a132 | ||
|
5377835f1e | ||
|
2d96b2a3ec | ||
|
36d02d010e | ||
|
18b9473cf8 | ||
|
d97e032fcf | ||
|
7ef051f58a | ||
|
ab64706cc3 | ||
|
2189dd4b85 | ||
|
861987e13e | ||
|
9edae7fa0a | ||
|
cf293bc669 | ||
|
7d14fdf530 | ||
|
9bda8f61f4 | ||
|
614fcb12f5 | ||
|
ae09831227 | ||
|
e146fd121c | ||
|
0ab15e9f22 | ||
|
a168c5efbe | ||
|
c1734d1b27 | ||
|
c750828632 | ||
|
a2f63aae33 | ||
|
908f6ded8a | ||
|
e08e4a9ce0 | ||
|
a421d9a33b | ||
|
6c076f99f6 | ||
|
467a4740da | ||
|
654eceb33b | ||
|
0d839bd138 | ||
|
558a091c08 | ||
|
14593ce789 | ||
|
359ba5f728 | ||
|
8331a2982d | ||
|
38a664ed72 | ||
|
3c212c8389 | ||
|
dce7a0c72b | ||
|
c7280fca52 | ||
|
686394654f | ||
|
29db4a44ed | ||
|
2f356f12e6 | ||
|
f4c3080824 | ||
|
1182ab55c5 | ||
|
2b1eaa6df3 | ||
|
5a5cb74db2 | ||
|
0c673dfebb | ||
|
12d87fba1b | ||
|
0920cb6ec4 | ||
|
0626bbef8e | ||
|
d8254cede0 | ||
|
17951ac92c | ||
|
d47da25b82 | ||
|
26d43e650f | ||
|
a170154927 | ||
|
2cf4b4e56d | ||
|
4d5153854b | ||
|
819ed10f29 | ||
|
5bb9351a3f | ||
|
2637f6f824 | ||
|
f698e796d2 | ||
|
3538b505da | ||
|
0c4806b4a0 | ||
|
d750d17be5 | ||
|
5a013c5b89 | ||
|
d9282ed988 | ||
|
af3ba03a5b | ||
|
46374e94ca | ||
|
048ef3e796 | ||
|
921877e8a4 | ||
|
755ee5462f | ||
|
432a29e4d9 | ||
|
d961cfb855 | ||
|
64ccc88a9e | ||
|
8bdd060112 | ||
|
d700bb66b2 | ||
|
842b0a1271 | ||
|
33be9d4559 | ||
|
fda4f4d027 | ||
|
3d67b29a7b | ||
|
73bc4cc3e4 | ||
|
c0eca32b92 | ||
|
3567abbd64 | ||
|
82f37c87fc | ||
|
ded1ab3da1 | ||
|
2ac2827256 | ||
|
e37adb8c47 | ||
|
14e954284e | ||
|
898e00f5b4 | ||
|
9315577be6 | ||
|
47baca22d9 | ||
|
dae1291b37 | ||
|
77d454725a | ||
|
2e0216b7c6 | ||
|
40b2a26e89 | ||
|
3f88b5fa14 | ||
|
8f34e9fa37 | ||
|
2ddb49f546 | ||
|
d6e7d8ca75 | ||
|
cb12460b95 | ||
|
3688dab048 | ||
|
a6f51264fd | ||
|
e2f9aa5027 | ||
|
e4b8df2dc6 | ||
|
6935561b41 | ||
|
f58b4a6cd9 | ||
|
f1fa8f6055 | ||
|
126b0fe324 | ||
|
566dda6ad7 | ||
|
a7724bffaa | ||
|
dbb7a87fd8 | ||
|
cfa7119fdb | ||
|
ba25970f27 | ||
|
fb30f11bc1 | ||
|
03c079cb8c | ||
|
a60d0182db | ||
|
c702970a0e | ||
|
ebb7cb2ae3 | ||
|
c6e7e80f23 | ||
|
4187332bb1 | ||
|
b1e0687373 | ||
|
8624af879a | ||
|
517b6fed81 | ||
|
24752b5fd1 | ||
|
5cfd97c344 | ||
|
24f3098418 | ||
|
babdebee79 | ||
|
3f8f2b3f48 | ||
|
d4483bfda6 | ||
|
93dbfa18f1 | ||
|
1cca992a8e | ||
|
3d9760473b | ||
|
00fc3a5686 | ||
|
b42328dbc6 | ||
|
24161543d9 | ||
|
1f867e3920 | ||
|
1ac110d426 | ||
|
56568d24ff | ||
|
de3d8747f7 | ||
|
9bf6b2e1bd | ||
|
c3a867f0c2 | ||
|
f832b86dcd | ||
|
33c7707f56 | ||
|
728a8e975a | ||
|
365fe400b0 | ||
|
b32bd22af3 | ||
|
a2504f6455 | ||
|
700628b2cb | ||
|
14889897da | ||
|
456e80a7d5 | ||
|
5779fea9b4 | ||
|
af5b552a7f | ||
|
47f7611ec9 | ||
|
aee7bf0de0 | ||
|
1172e2b8cc | ||
|
a7c3ef3e22 | ||
|
d7de5cb5ff | ||
|
710f580e15 | ||
|
35ba2dcaf9 | ||
|
307746dc35 | ||
|
fcdff3180a | ||
|
dc691c8a6e | ||
|
da927b5d41 | ||
|
2b468cb0ee | ||
|
c3dfe2c61c | ||
|
55dc45ec3e | ||
|
2d66431af4 | ||
|
f811a4e876 | ||
|
fa155f8a42 | ||
|
0a57c408c0 | ||
|
bd12429cd0 | ||
|
6a93b72ea5 | ||
|
d3568627e9 | ||
|
6a60368a1b | ||
|
1ac913877e | ||
|
63928e231c | ||
|
435dee935b | ||
|
3ef30e850d | ||
|
cff74493a7 | ||
|
fb5a65c51a | ||
|
46c2fe2443 | ||
|
890ca3c782 | ||
|
ee7051c5a4 | ||
|
275b6f777a | ||
|
3c9283cc41 | ||
|
2967dba7ad | ||
|
4f06daf7d0 | ||
|
c91b457054 | ||
|
b8c61f2f88 | ||
|
67bc99223d | ||
|
eabe23cc24 | ||
|
88a6437840 | ||
|
7998a98818 | ||
|
2382fdee50 | ||
|
1731a29c51 | ||
|
84fcd95d4e | ||
|
95965e9ff6 | ||
|
e72d141ec4 | ||
|
3e6c98d65d | ||
|
041f8a86d7 | ||
|
2164afb8cf | ||
|
ac0ca3de81 | ||
|
a707440e52 | ||
|
aec2284f45 | ||
|
fe3d8e44e6 | ||
|
da49a40b96 | ||
|
55117912ab | ||
|
d51a6ab3db | ||
|
5bc815c63f | ||
|
4580dcf1ea | ||
|
e0ea101838 | ||
|
f598fbeaea | ||
|
3b27107676 | ||
|
c3964cf29d | ||
|
d35d953a91 | ||
|
6951f3dfcc | ||
|
4224ac5ca0 | ||
|
d18656c2dc | ||
|
6bc29a1601 | ||
|
13003bf6af | ||
|
9d54ea6b0c | ||
|
aef7ff87fc | ||
|
77639bb21d | ||
|
eb952f1199 | ||
|
96ddc217da | ||
|
e8cd9411b0 | ||
|
7cf0ba5aa8 | ||
|
b38b3b08bd | ||
|
2c2bfab8bc | ||
|
26a80e0868 | ||
|
949cd9bb06 | ||
|
d858237010 | ||
|
1f070b740f | ||
|
0fe71af4ce | ||
|
7efc3a4c21 | ||
|
8459895fdd | ||
|
b4e38a8fed | ||
|
fa401b6f56 | ||
|
7d2a2f2ade | ||
|
322fbaf389 | ||
|
9ba655e74c | ||
|
313e442107 | ||
|
ed11fe130b | ||
|
7753e19ba8 | ||
|
88fee02228 | ||
|
43cf6e7cba | ||
|
48ba6a6189 | ||
|
06ea71d5be | ||
|
c9e3c29a25 | ||
|
3c003bc168 | ||
|
0be55824d4 | ||
|
0e40ef172a | ||
|
bf53f82d16 | ||
|
f78ab9061b | ||
|
36a90bb866 | ||
|
c7a62970a6 | ||
|
e1f0e40341 | ||
|
0bbc8a3350 | ||
|
e38bdecd21 | ||
|
5375400e85 | ||
|
1d3fdea432 | ||
|
8d86d7d1c1 | ||
|
93341dd009 | ||
|
a6547a15f3 | ||
|
8dc6696957 | ||
|
ccbccad994 | ||
|
712be84bba | ||
|
68c4070f62 | ||
|
0988b731b7 | ||
|
b595ec03f8 | ||
|
98b6772db9 | ||
|
4f9c7861ec | ||
|
a985a5cab1 | ||
|
d72cb42b08 | ||
|
db577fdf17 | ||
|
34063108a6 | ||
|
4319922b3c | ||
|
6b9eff19f1 | ||
|
b85441d6af | ||
|
d5cfe74d5d | ||
|
8f858a8f3e | ||
|
5b98011f5f | ||
|
6cd821d459 | ||
|
b360245b45 | ||
|
78b1105c10 | ||
|
fffa78c7a6 | ||
|
eb8dc60981 | ||
|
145c011247 | ||
|
915c002be4 | ||
|
4ef0e8c66e | ||
|
b02c4e4d10 | ||
|
5d51930adf | ||
|
bb356bffa5 | ||
|
0f0a458cc4 | ||
|
d76e6b103d | ||
|
3119a95a89 | ||
|
79f3f980e9 | ||
|
a274f8ec07 | ||
|
8adbb2f5ac | ||
|
7833efd6e1 | ||
|
a301796186 | ||
|
f1a662a422 | ||
|
2e57a952a7 | ||
|
42fc620466 | ||
|
475b3152ca | ||
|
148f7babcd | ||
|
199540e6ca | ||
|
8fda2233cb | ||
|
3e8435783c | ||
|
9f742271b1 | ||
|
d7585a810a | ||
|
0648835061 | ||
|
2bf2991ecb | ||
|
757617f89f | ||
|
afaa162aa1 | ||
|
57aaf57dfc | ||
|
2cc51b8eb0 | ||
|
664b6d6604 | ||
|
c8e163b0df | ||
|
ba434c8e22 | ||
|
7c5fc26081 | ||
|
bf267d0238 | ||
|
384446316a | ||
|
2a2c208884 | ||
|
5fc1cc2414 | ||
|
acf4da9385 | ||
|
8cd94192d8 | ||
|
dd6f415a04 | ||
|
2a61a65ce0 | ||
|
9b9fbb6e3b | ||
|
b337f630c8 | ||
|
259aaad324 | ||
|
9cb46789f0 | ||
|
e1c3e35422 | ||
|
6601e89029 | ||
|
70f676784d | ||
|
49c4da9670 | ||
|
201b3f9218 | ||
|
52b4c9f0c1 | ||
|
82e0073433 | ||
|
dccafd65ac | ||
|
19deac0707 | ||
|
66e6e6329f | ||
|
d9cca87aff | ||
|
d09ab90f24 | ||
|
116d744823 | ||
|
2a94fe9731 | ||
|
db5484bdef | ||
|
6b4c89700b | ||
|
26f3370ea6 | ||
|
83a41d13c0 | ||
|
a5e0542f95 | ||
|
2587aed697 | ||
|
ed4213d403 | ||
|
4b3e2571af | ||
|
0befe5da1c | ||
|
b7117e08ce | ||
|
2534076963 | ||
|
0f99ae3459 | ||
|
fe344cb5b2 | ||
|
6fa30fdaf4 | ||
|
8e0515de44 | ||
|
68e4fb87d8 | ||
|
c1a4c8102e | ||
|
f7bc1dca08 | ||
|
e72d656eb8 | ||
|
a473979cf5 | ||
|
340de8379a | ||
|
08302ff32b | ||
|
ae960efff4 | ||
|
ac18d58ffc | ||
|
f0687adbaa | ||
|
c2857a22e3 | ||
|
c1c7a93884 | ||
|
eab1050ba4 | ||
|
05ccb73e08 | ||
|
58f90d9f51 | ||
|
49e7f6d31d | ||
|
60807b677c | ||
|
c198c23df1 | ||
|
8858f265dd | ||
|
362d822e18 | ||
|
583c58f65b | ||
|
abfda3271b | ||
|
5e186bb434 | ||
|
9c5b95bbe3 | ||
|
f9b1d057b5 | ||
|
94560d1873 | ||
|
04a57d871e | ||
|
2eea7b03c0 | ||
|
37fbe7176f | ||
|
8acfd87039 | ||
|
5f0f5bedbc | ||
|
fe150aee72 | ||
|
8777624fe2 | ||
|
f140251e85 | ||
|
0c8bacd134 | ||
|
0dfd3136e5 | ||
|
8e077f378e | ||
|
36fe4e2729 | ||
|
0e4e81d94f | ||
|
88a4721de7 | ||
|
ea08d16c06 | ||
|
d43a6c50d4 | ||
|
16ae493a9a | ||
|
1ca3e72417 | ||
|
4d1514257e | ||
|
6b575372cc | ||
|
c926c4cb59 | ||
|
02ec45363e | ||
|
77ef7f516d | ||
|
668735e0f4 | ||
|
41881823b7 | ||
|
8aff0f76c0 | ||
|
b5a0fd7e42 | ||
|
628ed253b3 | ||
|
e8a1b7e71c | ||
|
79b8435877 | ||
|
95865035eb | ||
|
d39df54f8a | ||
|
37ccf19769 | ||
|
c371efb882 | ||
|
836935fba2 | ||
|
efe35ed02b | ||
|
07f4e6309d | ||
|
de837f2380 | ||
|
b7ce7370e8 | ||
|
dfd8f14116 | ||
|
79e0c5c4fa | ||
|
53fd019780 | ||
|
8188d0b9f7 | ||
|
ed4c513b1e | ||
|
624e95ee03 | ||
|
b966cd4f4d | ||
|
43c0343755 | ||
|
e102c1632b | ||
|
75ab0e6663 | ||
|
ac9d68387c | ||
|
d52879315d | ||
|
46942df6c0 | ||
|
d148cfdcd5 | ||
|
8d014cc6eb | ||
|
cda62cef20 | ||
|
2a2465c884 | ||
|
2a3e7c1cff | ||
|
316298650e | ||
|
1a264e5576 | ||
|
b3a9c4e8d9 | ||
|
e0a7f8a484 | ||
|
a9a41a786e | ||
|
6fc00e523d | ||
|
2f3612d905 | ||
|
bda5ff4213 | ||
|
696a8629b3 | ||
|
6acd159c9c | ||
|
9f1aa1490a | ||
|
44cbf40553 | ||
|
f1a36ca647 | ||
|
41cb923c0c | ||
|
0627e9e06e | ||
|
b78dab9d71 | ||
|
5ca4390a1f | ||
|
7f915c0f2a | ||
|
645bd212d7 | ||
|
f587e9cfb3 | ||
|
0252c0a47a | ||
|
535fd5744b | ||
|
207293bf74 | ||
|
321a1fe0d6 | ||
|
eec61af381 | ||
|
b476a3deca | ||
|
fefb51c592 | ||
|
70bda85629 | ||
|
600ecda860 | ||
|
5e23e8296d | ||
|
8e043c8447 | ||
|
39b8d8431c | ||
|
9841bc2f0d | ||
|
42376513e7 | ||
|
a19849099c | ||
|
a58061464e | ||
|
2ec0a96a21 | ||
|
03cb670096 | ||
|
0983125d2d | ||
|
17029ba695 | ||
|
e1fe6cff54 | ||
|
a890589031 | ||
|
fef9303381 | ||
|
3c1992fc38 | ||
|
8e1ce2ab5e | ||
|
e6928c6b78 | ||
|
08551eefba | ||
|
ca52ecc20a | ||
|
77272da718 | ||
|
a336fb62ff | ||
|
ec2ff09eff | ||
|
1c9de6763f | ||
|
bf244fc960 | ||
|
0564e701c0 | ||
|
23e0637e85 | ||
|
d70fcdec42 | ||
|
0af62801f4 | ||
|
7c241da548 | ||
|
38df8b5e40 | ||
|
0d3896ca54 | ||
|
b4a5517851 | ||
|
2f5a26a4de | ||
|
08ef8bd045 | ||
|
10f49a38ca | ||
|
089e490c69 | ||
|
42bd37a2e8 | ||
|
f1bbdb7d45 | ||
|
c62e0a93eb | ||
|
967254f7f2 | ||
|
797ceb9e04 | ||
|
2234090398 | ||
|
0301ea7818 | ||
|
e2602e6b87 | ||
|
2ec8ef4406 | ||
|
24f9b63475 | ||
|
4b71b74c6a | ||
|
f431b7a693 | ||
|
474d8bd6f0 | ||
|
336d91494f | ||
|
22173df7bd | ||
|
36cdffe1b3 | ||
|
3424b7227b | ||
|
4513fc4d0b | ||
|
1b72cb264a | ||
|
6cd432a909 | ||
|
779c974511 | ||
|
f2887f1208 | ||
|
b59401db1b | ||
|
353b1f945f | ||
|
39f572fe99 | ||
|
6e400ca81f | ||
|
55aa53beca | ||
|
ca033e5acd | ||
|
63d89f9f5a | ||
|
bf23ed948d | ||
|
2c0a9d78df | ||
|
66317aaef9 | ||
|
7838e772de | ||
|
0df394dcbc | ||
|
f65dbda659 | ||
|
e3acdcf0a9 | ||
|
b457bfec2a | ||
|
04347508e6 | ||
|
9a9ecd574e | ||
|
e22af32805 | ||
|
2884556fe3 | ||
|
b5d6f454a4 | ||
|
07fa97b8e7 | ||
|
2da37129d7 | ||
|
4b619ce1cd | ||
|
93ae1a65fb | ||
|
de2e2bd6d3 | ||
|
f132166f46 | ||
|
7cd6a477f8 | ||
|
f6a374c1d5 | ||
|
3c0c002e2f | ||
|
63d9e44f56 | ||
|
b851f542ac | ||
|
a1b50603e5 | ||
|
23d16b303d | ||
|
e7bf73455d | ||
|
7b56ad9405 | ||
|
3e6d173e31 | ||
|
d69b576d6b | ||
|
18b27d5cf6 | ||
|
060f995ca4 | ||
|
613e1e7247 | ||
|
9c0231a58e | ||
|
32c928ab6d | ||
|
5c0a62be96 | ||
|
f454a9f9cd | ||
|
8bce855f0b | ||
|
3dbe54f0cd | ||
|
4f00824104 | ||
|
d09ccba5d6 | ||
|
86f3ef2194 | ||
|
39b8b6a660 | ||
|
5897f0439c | ||
|
2bc99076cf | ||
|
1c7e9c5fde | ||
|
e3c1244687 | ||
|
6df6906ed2 | ||
|
ab55673c35 | ||
|
9bb80788d8 | ||
|
9d677bab7c | ||
|
74fce32233 | ||
|
6e047ec65b | ||
|
58c87ac328 | ||
|
85e98e9f53 | ||
|
37eaaa76f9 | ||
|
4a3921fcb9 | ||
|
f6b7ddf72b | ||
|
498cbf6fa5 | ||
|
0d33939b1b | ||
|
122e80cc4d | ||
|
553f3df5df | ||
|
5867167a70 | ||
|
448d4ebd75 | ||
|
1223e04e05 | ||
|
f22f1af891 | ||
|
4079835c7e | ||
|
38f5090778 | ||
|
9a43eb6370 | ||
|
3f805fdde9 | ||
|
e25e83b14c | ||
|
c748293ccb | ||
|
46fff99fad | ||
|
81bb87c49b | ||
|
f4edbe2165 | ||
|
9e3399df19 | ||
|
02a933b808 | ||
|
20d65da321 | ||
|
b5507b3127 | ||
|
af2324e922 | ||
|
a4198fc27e | ||
|
fafc755274 | ||
|
fcc57a1185 | ||
|
59e81eeb7f | ||
|
905ca8819b | ||
|
facd89b188 | ||
|
e6443496e7 | ||
|
ed53040907 | ||
|
baf69f3725 | ||
|
04665a9c94 | ||
|
6ea37089fa | ||
|
a8b31be328 | ||
|
dfde3a583c | ||
|
55915b5217 | ||
|
07d5686a47 | ||
|
b771695e0d | ||
|
a87bc6a575 | ||
|
f6129ea4e4 | ||
|
968397da61 | ||
|
2f672cce1d | ||
|
f719a81e2a | ||
|
014ae1425e | ||
|
c5f8fbb871 | ||
|
c0d46ba893 | ||
|
0500181327 | ||
|
fed86b73fd | ||
|
6a8494f21b | ||
|
79bac33a40 | ||
|
ba9217e576 | ||
|
6983142c4d | ||
|
42135f1322 | ||
|
2e3d572b53 | ||
|
ec50d8508e | ||
|
e7917d3225 | ||
|
1a5447ae8f | ||
|
e20184ae59 | ||
|
9c8dc43775 | ||
|
be52ca3879 | ||
|
0787038934 | ||
|
90bd91e294 | ||
|
5ec75a228e | ||
|
55c2148016 | ||
|
11f1af78d5 | ||
|
e0ddf32d2e | ||
|
8517221b1b | ||
|
a7fb45f102 | ||
|
268ad7db53 | ||
|
de5592dfc5 | ||
|
ebc6954bf5 | ||
|
21c2fcb929 | ||
|
bd1bc0dc59 | ||
|
5a6b2bceb2 | ||
|
b1425dfcf1 | ||
|
ca2b414c7c | ||
|
1dfb14fef0 | ||
|
390ac0d871 | ||
|
be8981a5c3 | ||
|
57e7303cf4 | ||
|
6bc72a0262 | ||
|
62ff522013 | ||
|
5778b6ffb6 | ||
|
f4043faf0b | ||
|
5c52e817ea | ||
|
986d58877f | ||
|
4649c12fa9 | ||
|
2a55dd7234 | ||
|
63969180cf | ||
|
06ba28f2a0 | ||
|
b72900214b | ||
|
8ac689c03a | ||
|
ea75003cf4 | ||
|
2d8bf20d43 | ||
|
6e888396b5 | ||
|
3391874ebf | ||
|
276346e63c | ||
|
61fa54318c | ||
|
091173341e | ||
|
245908f491 | ||
|
b1c27e3bf2 | ||
|
4e85dfa71e | ||
|
19e82883ea | ||
|
f1735d91b8 | ||
|
12fca5ef8f | ||
|
ed6189e655 | ||
|
149481b80c | ||
|
45cd90c777 | ||
|
f023004a67 | ||
|
e8d1e5dbc0 | ||
|
41235108c4 | ||
|
ccb9cf5347 | ||
|
2652353f50 | ||
|
2ab7d8d5c3 | ||
|
e4998e0798 | ||
|
43a72b2a63 | ||
|
a5dbfacf2f | ||
|
e66f2bcb33 | ||
|
d3861cea9f | ||
|
9898ab5316 | ||
|
79619fbf50 | ||
|
4c2e5d7ebf | ||
|
441a224435 | ||
|
edbe8f54cb | ||
|
a014480987 | ||
|
0330b082a2 | ||
|
f0d56d07fe | ||
|
cec58b53e4 | ||
|
f47a20aa53 | ||
|
d1373af7fc | ||
|
366557bbab | ||
|
2b65ea599f | ||
|
79abe3fb6d | ||
|
d1fd7d044b | ||
|
227c85ba4e | ||
|
852b2b91d2 | ||
|
990dcb106a | ||
|
55491bbc23 | ||
|
6333130537 | ||
|
33be9edec7 | ||
|
7b74a2bb94 | ||
|
d0c6e0a481 | ||
|
e062c847cb | ||
|
49cb2bcda2 | ||
|
56987b41eb | ||
|
bf3011a28d | ||
|
0dc4febb91 | ||
|
515cafe8bd | ||
|
990450bfe0 | ||
|
d237ff1736 | ||
|
0ad2b8b2c7 | ||
|
57ac55ad01 | ||
|
05a26333fc | ||
|
99dd208880 | ||
|
03f0e39f7f | ||
|
b8a5c87360 | ||
|
5b91c2af5e | ||
|
7e660951c2 | ||
|
d688820518 | ||
|
a56c224f9c | ||
|
6c0b4a46fe | ||
|
5f287fc476 | ||
|
0bd0a430d6 | ||
|
c48b9dbf61 | ||
|
52d7a5d7be | ||
|
053d8fb0e2 | ||
|
d4fe6ed82d | ||
|
34646cfca7 | ||
|
45a354d664 | ||
|
bdd8b8275e | ||
|
6ab09ef3bf | ||
|
bd5b2a8b58 | ||
|
0c75182aab | ||
|
0473fd84d1 | ||
|
cc1c197fc6 | ||
|
4bee1bdb6e | ||
|
07ca2e6407 | ||
|
9d57cf2774 | ||
|
d1640f177f | ||
|
4517072af4 | ||
|
3bd75546ec | ||
|
df61f3c182 | ||
|
fb30dd9a3f | ||
|
c583ae24f2 | ||
|
906f319673 | ||
|
b8fc2b7731 | ||
|
29336ef63d | ||
|
0b8da9eeb4 | ||
|
dcacaca2e8 | ||
|
69408fd58d | ||
|
2e84cc0b41 | ||
|
ac01e3b5d3 | ||
|
84088cb5f0 | ||
|
18deba1a51 | ||
|
f0678cfa5a | ||
|
db7a1784d4 | ||
|
f0750209b7 | ||
|
f3ebbf598f | ||
|
8f1f6160e3 | ||
|
e7d558dbd8 | ||
|
6204c8c956 | ||
|
51ba2c49d6 | ||
|
b1175eabfc | ||
|
e1c8e54863 | ||
|
b0326c66ed | ||
|
6044079528 | ||
|
28a6d943d9 | ||
|
b61ae53ecd | ||
|
d60a76abd1 | ||
|
dbdcdb059e | ||
|
0340a6a6e7 | ||
|
0224de02d5 | ||
|
89d86420ad | ||
|
3579e495be | ||
|
0de1ebbc21 | ||
|
f026ca2fc5 | ||
|
c18c89cd62 | ||
|
d959c71be6 | ||
|
baaa6407d5 | ||
|
01b619d53d | ||
|
66a503a900 | ||
|
9a42dd1c3a | ||
|
83438d9833 | ||
|
cdb7d06521 | ||
|
fc6bd54943 | ||
|
bde2175779 | ||
|
a0b6008c2e | ||
|
a78cdd5366 | ||
|
feec2d74ee | ||
|
22d6ff2309 | ||
|
511058c018 | ||
|
d5b95f0412 | ||
|
2edc7ce822 | ||
|
caf8946f99 | ||
|
2994d308a0 | ||
|
533f9f2cfc | ||
|
30d87ee837 | ||
|
1ab303c528 | ||
|
7d9568f806 | ||
|
9324a96f77 | ||
|
86e42ad123 | ||
|
df77e062dc | ||
|
25ad091866 | ||
|
fb264b90b9 | ||
|
1fa67577e0 | ||
|
a7d3a2d6ea | ||
|
62d062f860 | ||
|
a3c8605e18 | ||
|
cf1ea01de3 | ||
|
a84f9483e6 | ||
|
00a28e48ee | ||
|
2cb15e8c2d | ||
|
bc43c6de47 | ||
|
4977f605d4 | ||
|
1869440fd9 | ||
|
c87b056fdc | ||
|
8687aa9a3e | ||
|
42b9848375 | ||
|
a27750966f | ||
|
52daf02444 | ||
|
fe05d5afd9 | ||
|
76b21ad192 | ||
|
0bb90f284e | ||
|
0fc3a0c517 | ||
|
016257c44e | ||
|
b17fe4db0b | ||
|
7f4047381e | ||
|
e87fd30c48 | ||
|
8e30d7cb86 | ||
|
775f8ae005 | ||
|
6b12f4435c | ||
|
c155ef1582 | ||
|
df07531298 | ||
|
793ab14b87 | ||
|
01bfdd2ae5 | ||
|
852fa9d034 | ||
|
9ad35a2817 | ||
|
b5ea787044 | ||
|
09e18e1bbf | ||
|
04fd3d04d9 | ||
|
9141217481 | ||
|
68148dd0e8 | ||
|
e143db0cae | ||
|
5ff0013357 | ||
|
bf256abd66 | ||
|
5bef2f2870 | ||
|
b5970707c0 | ||
|
b219d4b468 | ||
|
caf4771ba1 | ||
|
a3791b3f51 | ||
|
a195e296a9 | ||
|
ab86b0470c | ||
|
2ef0203d17 | ||
|
b1b586d398 | ||
|
265e66b98b |
270 changed files with 32761 additions and 16078 deletions
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
*.o
|
||||
*.d
|
||||
*.orig
|
||||
*.rej
|
||||
*.patch
|
||||
*.diff
|
||||
*.exe
|
||||
|
||||
gmqcc
|
||||
gmqpak
|
||||
qcvm
|
||||
testsuite
|
||||
|
||||
build/
|
||||
.idea/
|
13
AUTHORS
13
AUTHORS
|
@ -1,3 +1,10 @@
|
|||
gmqcc brought to you by:
|
||||
Dale Weiler
|
||||
Wolfgang Bumiller
|
||||
Authors:
|
||||
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
|
||||
|
|
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)
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
Copyright (C) 2012, 2013, 2014, 2015
|
||||
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.
|
245
Makefile
245
Makefile
|
@ -1,63 +1,204 @@
|
|||
CC ?= clang
|
||||
CFLAGS += -Wall -I. -pedantic-errors
|
||||
# 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 ?= ./
|
||||
|
||||
#turn on tons of warnings if clang is present
|
||||
ifeq ($(CC), clang)
|
||||
CFLAGS += \
|
||||
-Weverything \
|
||||
-Wno-missing-prototypes \
|
||||
-Wno-unused-parameter \
|
||||
-Wno-sign-compare \
|
||||
-Wno-implicit-fallthrough \
|
||||
-Wno-sign-conversion \
|
||||
-Wno-conversion \
|
||||
-Wno-disabled-macro-expansion \
|
||||
-Wno-padded \
|
||||
-Wno-format-nonliteral
|
||||
|
||||
endif
|
||||
ifeq ($(track), no)
|
||||
CFLAGS += -DNOTRACK
|
||||
# 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
|
||||
GMQCC := gmqcc
|
||||
QCVM := qcvm
|
||||
TESTSUITE := testsuite
|
||||
endif
|
||||
|
||||
OBJ = \
|
||||
util.o \
|
||||
code.o \
|
||||
ast.o \
|
||||
ir.o \
|
||||
error.o
|
||||
OBJ_A = test/ast-test.o
|
||||
OBJ_I = test/ir-test.o
|
||||
OBJ_C = main.o lexer.o parser.o
|
||||
OBJ_X = exec-standalone.o util.o
|
||||
# C++ compiler
|
||||
CXX ?= clang++
|
||||
|
||||
#default is compiler only
|
||||
default: gmqcc
|
||||
%.o: %.c
|
||||
$(CC) -c $< -o $@ $(CFLAGS)
|
||||
# Build artifact directories
|
||||
OBJDIR := .build/objs
|
||||
DEPDIR := .build/deps
|
||||
|
||||
exec-standalone.o: exec.c
|
||||
$(CC) -c $< -o $@ $(CFLAGS) -DQCVM_EXECUTOR=1
|
||||
# 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
|
||||
|
||||
# test targets
|
||||
test_ast: $(OBJ_A) $(OBJ)
|
||||
$(CC) -o $@ $^ $(CFLAGS)
|
||||
test_ir: $(OBJ_I) $(OBJ)
|
||||
$(CC) -o $@ $^ $(CFLAGS)
|
||||
qcvm: $(OBJ_X)
|
||||
$(CC) -o $@ $^ $(CFLAGS) -lm
|
||||
exec.o: execloop.h
|
||||
exec-standalone.o: execloop.h
|
||||
test: test_ast test_ir
|
||||
# Collect all the source files for QCVM.
|
||||
QSRCS := exec.cpp
|
||||
QSRCS += stat.cpp
|
||||
QSRCS += util.cpp
|
||||
|
||||
# compiler target
|
||||
gmqcc: $(OBJ_C) $(OBJ)
|
||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||
# Collect all the source files for TESTSUITE.
|
||||
TSRCS := conout.cpp
|
||||
TSRCS += opts.cpp
|
||||
TSRCS += stat.cpp
|
||||
TSRCS += test.cpp
|
||||
TSRCS += util.cpp
|
||||
|
||||
#all target is test and all
|
||||
all: test gmqcc
|
||||
#
|
||||
# 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
|
||||
CXXFLAGS += -fomit-frame-pointer
|
||||
endif
|
||||
|
||||
# Highest optimization flag in release builds.
|
||||
CXXFLAGS += -O3
|
||||
endif
|
||||
|
||||
# Sanitizer selection
|
||||
ifeq ($(ASAN),1)
|
||||
CXXFLAGS += -fsanitize=address
|
||||
endif
|
||||
ifeq ($(UBSAN),1)
|
||||
CXXFLAGS += -fsanitize=undefined
|
||||
endif
|
||||
|
||||
#
|
||||
# Dependency flags
|
||||
#
|
||||
DEPFLAGS := -MMD
|
||||
DEPFLAGS += -MP
|
||||
|
||||
#
|
||||
# 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
|
||||
STRIP := strip
|
||||
endif
|
||||
|
||||
all: $(GMQCC) $(QCVM) $(TESTSUITE)
|
||||
|
||||
# Build artifact directories.
|
||||
$(DEPDIR):
|
||||
@mkdir -p $(DEPDIR)
|
||||
$(OBJDIR):
|
||||
@mkdir -p $(OBJDIR)
|
||||
|
||||
$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d | $(OBJDIR) $(DEPDIR)
|
||||
$(CXX) -MT $@ $(DEPFLAGS) -MF $(DEPDIR)/$*.Td $(CXXFLAGS) -c -o $@ $<
|
||||
@mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
|
||||
|
||||
$(GMQCC): $(filter %.o,$(GSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
$(QCVM): $(filter %.o,$(QSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
$(TESTSUITE): $(filter %.o,$(TSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
# Determine if the tests should be run.
|
||||
RUNTESTS := true
|
||||
ifdef TESTSUITE
|
||||
RUNTESTS := ./$(TESTSUITE)
|
||||
endif
|
||||
|
||||
test: $(QCVM) $(TESTSUITE)
|
||||
@$(RUNTESTS)
|
||||
|
||||
clean:
|
||||
rm -f *.o gmqcc qcvm test_ast test_ir test/*.o
|
||||
|
||||
rm -rf $(DEPDIR) $(OBJDIR)
|
||||
|
||||
.PHONY: test clean $(DEPDIR) $(OBJDIR)
|
||||
|
||||
# Dependencies
|
||||
$(filter %.d,$(GSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
|
||||
$(filter %.d,$(QSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
|
||||
$(filter %.d,$(TSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
|
|
240
README
240
README
|
@ -1,239 +1 @@
|
|||
This is a work in progress Quake C compiler. There are very few good QC
|
||||
compilers out there on the internet that can be used in the opensource
|
||||
community. There are a lot of mediocre compilers, but no one wants those.
|
||||
This is the solution for that, for once a proper Quake C compiler that is
|
||||
capable of doing proper optimization.
|
||||
|
||||
The compiler is intended to implement modern day compiler design princibles
|
||||
and support modifications through extensions that are provided for the
|
||||
user through a low-level syntax specific-language inside the language itself
|
||||
to implement language functionality.
|
||||
|
||||
The design goals of the compiler are very large, it's intended the compiler
|
||||
supports a multitude of things, these things along with the status of
|
||||
completeness is represented below in a table.
|
||||
|
||||
+-------------------+-----------------------------+------------------+
|
||||
| Feature | What's it for? | Complete Factor |
|
||||
+-------------------+-----------------------------+------------------+
|
||||
. Lexical analysis . Tokenization . 90% .
|
||||
.-------------------.-----------------------------.------------------.
|
||||
. Tokenization . Parsing . 90% .
|
||||
.-------------------.-----------------------------.------------------.
|
||||
. Parsing / SYA . AST Generation . 09% .
|
||||
.-------------------.-----------------------------.------------------.
|
||||
. AST Generation . IR Generation . ??% .
|
||||
.-------------------.-----------------------------.------------------.
|
||||
. IR Generation . Code Generation . ??% .
|
||||
.-------------------.-----------------------------.------------------.
|
||||
. Code Generation . Binary Generation . ??% .
|
||||
.-------------------.-----------------------------.------------------.
|
||||
. Binary Generation . Binary . 100% .
|
||||
+-------------------+-----------------------------+------------------+
|
||||
|
||||
Design tree:
|
||||
The compiler is intended to work in the following order:
|
||||
Lexical analysis ->
|
||||
Tokenization ->
|
||||
Parsing:
|
||||
Operator precedence:
|
||||
Shynting yard algorithm
|
||||
Inline assembly:
|
||||
Usage of the assembler subsystem:
|
||||
top-down parsing and assemblation no optimization
|
||||
Other parsing:
|
||||
recrusive decent
|
||||
->
|
||||
Abstract syntax tree generation ->
|
||||
Immediate representation (SSA):
|
||||
Optimizations:
|
||||
Constant propagation
|
||||
Value range propogation
|
||||
Sparse conditional constant propagation (possibly?)
|
||||
Dead code elimination
|
||||
Constant folding
|
||||
Global value numbering
|
||||
Partial redundancy elimination
|
||||
Strength reduction
|
||||
Common subexpression elimination
|
||||
Peephole optimizations
|
||||
Loop-invariant code motion
|
||||
Inline expansion
|
||||
Constant folding
|
||||
Induction variable recognition and elimination
|
||||
Dead store elimination
|
||||
Jump threading
|
||||
->
|
||||
Code Generation:
|
||||
Optimizations:
|
||||
Rematerialization
|
||||
Code Factoring
|
||||
Recrusion Elimination
|
||||
Loop unrolling
|
||||
Deforestation
|
||||
->
|
||||
Binary Generation
|
||||
|
||||
File tree and explination:
|
||||
gmqcc.h
|
||||
This is the common header with all definitions, structures, and
|
||||
constants for everything.
|
||||
|
||||
error.c
|
||||
This is the error subsystem, this handles the output of good detailed
|
||||
error messages (not currently, but will), with colors and such.
|
||||
|
||||
lex.c
|
||||
This is the lexer, a very small basic step-seek lexer that can be easily
|
||||
changed to add new tokens, very retargetable.
|
||||
|
||||
main.c
|
||||
This is the core compiler entry, handles switches (will) to toggle on
|
||||
and off certian compiler features.
|
||||
|
||||
parse.c
|
||||
This is the parser which goes over all tokens and generates a parse tree
|
||||
and check for syntax correctness.
|
||||
|
||||
typedef.c
|
||||
This is the typedef system, this is a seperate file because it's a lot more
|
||||
complicated than it sounds. This handles all typedefs, and even recrusive
|
||||
typedefs.
|
||||
|
||||
util.c
|
||||
These are utilities for the compiler, some things in here include a
|
||||
allocator used for debugging, and some string functions.
|
||||
|
||||
assembler.c
|
||||
This implements support for assembling Quake assembler (which doesn't
|
||||
actually exist untill now: documentation of the Quake assembler is below.
|
||||
This also implements (will) inline assembly for the C compiler.
|
||||
|
||||
README
|
||||
This is the file you're currently reading
|
||||
|
||||
Makefile
|
||||
The makefile, when sources are added you should add them to the SRC=
|
||||
line otherwise the build will not pick it up. Trivial stuff, small
|
||||
easy to manage makefile, no need to complicate it.
|
||||
Some targets:
|
||||
#make gmqcc
|
||||
Builds gmqcc, creating a `gmqcc` binary file in the current
|
||||
directory as the makefile.
|
||||
#make test
|
||||
Builds the ir and ast tests, creating a `test_ir` and `test_ast`
|
||||
binary file in the current directory as the makefile.
|
||||
#make test_ir
|
||||
Builds the ir test, creating a `test_ir` binary file in the
|
||||
current directory as the makefile.
|
||||
#make test_ast
|
||||
Builds the asr test, creating a `test_ast` binary file in the
|
||||
current directory as the makefile.
|
||||
#make clean
|
||||
Cleans the build files left behind by a previous build, as
|
||||
well as all the binary files.
|
||||
#make all
|
||||
Builds the tests and the compiler binary all in the current
|
||||
directory of the makefile.
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
///////////////////// Quake Assembler Documentation ////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
Quake assembler is quite simple: it's just an annotated version of the binary
|
||||
produced by any existing QuakeC compiler, but made cleaner to use, (so that
|
||||
the location of various globals or strings are not required to be known).
|
||||
|
||||
Constants:
|
||||
Using one of the following valid constant typenames, you can declare
|
||||
a constant {FLOAT,VECTOR,FUNCTION,FIELD,ENTITY}, all typenames are
|
||||
proceeded by a colon, and the name (white space doesn't matter).
|
||||
|
||||
Examples:
|
||||
FLOAT: foo 1
|
||||
VECTOR: bar 1 2 1
|
||||
STRING: hello "hello world"
|
||||
|
||||
Comments:
|
||||
Commenting assembly requires the use of either # or ; on the line
|
||||
that you'd like to be ignored by the assembler. You can only comment
|
||||
blank lines, and not lines assembly already exists on.
|
||||
|
||||
Examples:
|
||||
; this is allowed
|
||||
# as is this
|
||||
FLOAT: foo 1 ; this is not allowed
|
||||
FLOAT: bar 2 # neither is this
|
||||
|
||||
Functions:
|
||||
Creating functions is the same as declaring a constant, simply use
|
||||
FUNCTION followed by a colon, and the name (white space doesn't matter)
|
||||
and start the statements for that function on the line after it
|
||||
|
||||
Examples:
|
||||
FLOAT: foo 1
|
||||
FLOAT: bar 2
|
||||
FUNCTION: test1
|
||||
ADD foo, bar, OFS_RETURN
|
||||
RETURN
|
||||
|
||||
FUNCTION: test2
|
||||
CALL0 test1
|
||||
DONE
|
||||
|
||||
Internal:
|
||||
The Quake engine provides some internal functions such as print, to
|
||||
access these you first must declare them and their names. To do this
|
||||
you create a FUNCTION as you currently do. Adding a $ followed by the
|
||||
number of the engine builtin (negated).
|
||||
|
||||
Examples:
|
||||
FUNCTION: print $4
|
||||
FUNCTION: error $3
|
||||
|
||||
Misc:
|
||||
There are some rules as to what your identifiers can be for functions
|
||||
and constants. All indentifiers mustn't begin with a numeric digit,
|
||||
identifiers cannot include spaces, or tabs; they cannot contain symbols,
|
||||
and they cannot exceed 32768 characters. Identifiers cannot be all
|
||||
capitalized either, as all capatilized identifiers are reserved by the
|
||||
assembler.
|
||||
|
||||
Numeric constants cannot contain special notation such as `1-e10`, all
|
||||
numeric constants have to be numeric, they can contain decmial points
|
||||
and signs (+, -) however.
|
||||
|
||||
Constants cannot be assigned values of other constants, their value must
|
||||
be fully expressed inspot of the declartion.
|
||||
|
||||
No two identifiers can be the same name, this applies for variables allocated
|
||||
inside a function scope (despite it being considered local).
|
||||
|
||||
There exists one other keyword that is considered sugar, and that
|
||||
is AUTHOR, this keyword will allow you to speciy the AUTHOR(S) of
|
||||
the assembly being assembled. The string represented for each usage
|
||||
of AUTHOR is wrote to the end of the string table. Simaler to the
|
||||
usage of constants and functions the AUTHOR keyword must be proceeded
|
||||
by a colon.
|
||||
|
||||
Examples:
|
||||
AUTHOR: "Dale Weiler"
|
||||
AUTHOR: "Wolfgang Bumiller"
|
||||
|
||||
Colons exist for the sole reason of not having to use spaces after
|
||||
keyword usage (however spaces are allowed). To understand the
|
||||
following examples below are equivlent.
|
||||
|
||||
Example 1:
|
||||
FLOAT:foo 1
|
||||
Example 2:
|
||||
FLOAT: foo 1
|
||||
Example 3:
|
||||
FLOAT: foo 2
|
||||
|
||||
variable amounts of whitespace is allowed anywhere (as it should be).
|
||||
think of `:` as a delimiter (which is what it's used for during assembly).
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////// Quake C Documentation //////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
TODO ....
|
||||
An improved QuakeC compiler
|
||||
|
|
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
|
620
asm.c
620
asm.c
|
@ -1,620 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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"
|
||||
/*
|
||||
* Following parse states:
|
||||
* ASM_FUNCTION -- in a function accepting input statements
|
||||
* ....
|
||||
*/
|
||||
typedef enum {
|
||||
ASM_NULL,
|
||||
ASM_FUNCTION
|
||||
} asm_state;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char type; /* type, float, vector, string, function*/
|
||||
char elem; /* 0=x, 1=y, or 2=Z? */
|
||||
int offset; /* location in globals */
|
||||
bool isconst;
|
||||
} asm_sym;
|
||||
VECTOR_MAKE(asm_sym, asm_symbols);
|
||||
|
||||
/*
|
||||
* Assembly text processing: this handles the internal collection
|
||||
* of text to allow parsing and assemblation.
|
||||
*/
|
||||
static char* asm_getline(size_t *byte, FILE *fp) {
|
||||
char *line = NULL;
|
||||
size_t read = util_getline(&line, byte, fp);
|
||||
*byte = read;
|
||||
if (read == -1) {
|
||||
mem_d (line);
|
||||
return NULL;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
/*
|
||||
* Entire external interface for main.c - to perform actual assemblation
|
||||
* of assembly files.
|
||||
*/
|
||||
void asm_init(const char *file, FILE **fp) {
|
||||
*fp = fopen(file, "r");
|
||||
code_init();
|
||||
}
|
||||
void asm_close(FILE *fp) {
|
||||
fclose(fp);
|
||||
code_write("program.dat");
|
||||
}
|
||||
void asm_clear() {
|
||||
size_t i = 0;
|
||||
for (; i < asm_symbols_elements; i++)
|
||||
mem_d(asm_symbols_data[i].name);
|
||||
mem_d(asm_symbols_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dumps all values of all constants and assembly related
|
||||
* information obtained during the assembly procedure.
|
||||
*/
|
||||
void asm_dumps() {
|
||||
size_t i = 0;
|
||||
for (; i < asm_symbols_elements; i++) {
|
||||
asm_sym *g = &asm_symbols_data[i];
|
||||
if (!g->isconst) continue;
|
||||
switch (g->type) {
|
||||
case TYPE_VECTOR: {
|
||||
util_debug("ASM", "vector %s %c[%f]\n", g->name,
|
||||
(g->elem == 0) ? 'X' :(
|
||||
(g->elem == 1) ? 'Y' :
|
||||
(g->elem == 2) ? 'Z' :' '),
|
||||
INT2FLT(code_globals_data[g->offset])
|
||||
);
|
||||
break;
|
||||
}
|
||||
case TYPE_FUNCTION: {
|
||||
util_debug("ASM", "function %s\n", g->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a type, could be global or not depending on the
|
||||
* assembly state: global scope with assignments are constants.
|
||||
* globals with no assignments are globals. Function body types
|
||||
* are locals.
|
||||
*/
|
||||
static GMQCC_INLINE bool asm_parse_type(const char *skip, size_t line, asm_state *state) {
|
||||
if ((strstr(skip, "FLOAT:") != &skip[0]) &&
|
||||
(strstr(skip, "VECTOR:") != &skip[0]) &&
|
||||
(strstr(skip, "ENTITY:") != &skip[0]) &&
|
||||
(strstr(skip, "FIELD:") != &skip[0]) &&
|
||||
(strstr(skip, "STRING:") != &skip[0])) return false;
|
||||
|
||||
/* TODO: determine if constant, global, or local */
|
||||
switch (*skip) {
|
||||
/* VECTOR */ case 'V': {
|
||||
float val1;
|
||||
float val2;
|
||||
float val3;
|
||||
asm_sym sym;
|
||||
|
||||
char *find = (char*)skip + 7;
|
||||
char *name = (char*)skip + 7;
|
||||
while (*find == ' ' || *find == '\t') find++;
|
||||
|
||||
/* constant? */
|
||||
if (strchr(find, ',')) {
|
||||
/* strip name */
|
||||
*strchr((name = util_strdup(find)), ',')='\0';
|
||||
/* find data */
|
||||
find += strlen(name) + 1;
|
||||
while (*find == ' ' || *find == '\t') find++;
|
||||
/* valid name */
|
||||
if (util_strupper(name) || isdigit(*name)) {
|
||||
printf("invalid name for vector variable\n");
|
||||
mem_d(name);
|
||||
}
|
||||
/*
|
||||
* Parse all three elements of the vector. This will only
|
||||
* pass the first try if we hit a constant, otherwise it's
|
||||
* a global.
|
||||
*/
|
||||
#define PARSE_ELEMENT(X,Y,Z) \
|
||||
if (isdigit(*X) || *X == '-'||*X == '+') { \
|
||||
bool negated = (*X == '-'); \
|
||||
if (negated || *X == '+') { X++; } \
|
||||
Y = (negated)?-atof(X):atof(X); \
|
||||
X = strchr(X, ','); \
|
||||
Z \
|
||||
}
|
||||
|
||||
PARSE_ELEMENT(find, val1, { find ++; while (*find == ' ') { find ++; } });
|
||||
PARSE_ELEMENT(find, val2, { find ++; while (*find == ' ') { find ++; } });
|
||||
PARSE_ELEMENT(find, val3, { find ++; /* no need to do anything here */ });
|
||||
#undef PARSE_ELEMENT
|
||||
#define BUILD_ELEMENT(X,Y) \
|
||||
sym.type = TYPE_VECTOR; \
|
||||
sym.name = util_strdup(name); \
|
||||
sym.elem = (X); \
|
||||
sym.offset = code_globals_elements; \
|
||||
asm_symbols_add(sym); \
|
||||
code_globals_add(FLT2INT(Y))
|
||||
BUILD_ELEMENT(0, val1);
|
||||
BUILD_ELEMENT(1, val2);
|
||||
BUILD_ELEMENT(2, val3);
|
||||
#undef BUILD_ELEMENT
|
||||
mem_d(name);
|
||||
} else {
|
||||
/* TODO global not constant */
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* ENTITY */ case 'E': {
|
||||
const char *find = skip + 7;
|
||||
while (*find == ' ' || *find == '\t') find++;
|
||||
printf("found ENTITY %s\n", find);
|
||||
break;
|
||||
}
|
||||
/* STRING */ case 'S': {
|
||||
const char *find = skip + 7;
|
||||
while (*find == ' ' || *find == '\t') find++;
|
||||
printf("found STRING %s\n", find);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a function: trivial case, handles occurances of duplicated
|
||||
* names among other things. Ensures valid name as well, and even
|
||||
* internal engine function selection.
|
||||
*/
|
||||
static GMQCC_INLINE bool asm_parse_func(const char *skip, size_t line, asm_state *state) {
|
||||
if (*state == ASM_FUNCTION)
|
||||
return false;
|
||||
|
||||
if (strstr(skip, "FUNCTION:") == &skip[0]) {
|
||||
asm_sym sym;
|
||||
char *look = util_strdup(skip+10);
|
||||
char *copy = look;
|
||||
char *name = NULL;
|
||||
while (*copy == ' ' || *copy == '\t') copy++;
|
||||
|
||||
memset(&sym, 0, sizeof(asm_sym));
|
||||
|
||||
/*
|
||||
* Chop the function name out of the string, this allocates
|
||||
* a new string.
|
||||
*/
|
||||
name = util_strchp(copy, strchr(copy, '\0'));
|
||||
|
||||
/* TODO: failure system, missing name */
|
||||
if (!name) {
|
||||
printf("expected name on function\n");
|
||||
mem_d(copy);
|
||||
mem_d(name);
|
||||
return false;
|
||||
}
|
||||
/* TODO: failure system, invalid name */
|
||||
if (!isalpha(*name) || util_strupper(name)) {
|
||||
printf("invalid identifer for function name\n");
|
||||
mem_d(copy);
|
||||
mem_d(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function could be internal function, look for $
|
||||
* to determine this.
|
||||
*/
|
||||
if (strchr(name, ',')) {
|
||||
char *find = strchr(name, ',') + 1;
|
||||
prog_section_function function;
|
||||
prog_section_def def;
|
||||
memset(&function, 0, sizeof(prog_section_function));
|
||||
memset(&def, 0, sizeof(prog_section_def));
|
||||
|
||||
/* skip whitespace */
|
||||
while (*find == ' ' || *find == '\t')
|
||||
find++;
|
||||
|
||||
if (*find != '$') {
|
||||
printf("expected $ for internal function selection, got %s instead\n", find);
|
||||
mem_d(copy);
|
||||
mem_d(name);
|
||||
return false;
|
||||
}
|
||||
find ++;
|
||||
if (!isdigit(*find)) {
|
||||
printf("invalid internal identifier, expected valid number\n");
|
||||
mem_d(copy);
|
||||
mem_d(name);
|
||||
return false;
|
||||
}
|
||||
*strchr(name, ',')='\0';
|
||||
|
||||
/*
|
||||
* Now add the following items to the code system:
|
||||
* function
|
||||
* definition (optional)
|
||||
* global (optional)
|
||||
* name
|
||||
*/
|
||||
function.entry = -atoi(find);
|
||||
function.firstlocal = 0;
|
||||
function.locals = 0;
|
||||
function.profile = 0;
|
||||
function.name = code_chars_elements;
|
||||
function.file = 0;
|
||||
function.nargs = 0;
|
||||
def.type = TYPE_FUNCTION;
|
||||
def.offset = code_globals_elements;
|
||||
def.name = code_chars_elements;
|
||||
code_functions_add(function);
|
||||
code_defs_add (def);
|
||||
code_chars_put (name, strlen(name));
|
||||
code_chars_add ('\0');
|
||||
sym.type = TYPE_FUNCTION;
|
||||
sym.name = util_strdup(name);
|
||||
sym.offset = function.entry;
|
||||
asm_symbols_add(sym);
|
||||
|
||||
util_debug("ASM", "added internal function %s to function table\n", name);
|
||||
|
||||
/*
|
||||
* Sanatize the numerical constant used to select the
|
||||
* internal function. Must ensure it's all numeric, since
|
||||
* atoi can silently drop characters from a string and still
|
||||
* produce a valid constant that would lead to runtime problems.
|
||||
*/
|
||||
if (util_strdigit(find))
|
||||
util_debug("ASM", "found internal function %s, -%d\n", name, atoi(find));
|
||||
else
|
||||
printf("invalid internal function identifier, must be all numeric\n");
|
||||
|
||||
} else {
|
||||
/*
|
||||
* The function isn't an internal one. Determine the name and
|
||||
* amount of arguments the function accepts by searching for
|
||||
* the `#` (pound sign).
|
||||
*/
|
||||
int args = 0;
|
||||
int size = 0;
|
||||
char *find = strchr(name, '#');
|
||||
char *peek = find;
|
||||
|
||||
/*
|
||||
* Code structures for filling after determining the correct
|
||||
* information to add to the code write system.
|
||||
*/
|
||||
prog_section_function function;
|
||||
prog_section_def def;
|
||||
memset(&function, 0, sizeof(prog_section_function));
|
||||
memset(&def, 0, sizeof(prog_section_def));
|
||||
if (find) {
|
||||
find ++;
|
||||
|
||||
/* skip whitespace */
|
||||
if (*find == ' ' || *find == '\t')
|
||||
find++;
|
||||
|
||||
/*
|
||||
* If the input is larger than eight, it's considered
|
||||
* invalid and shouldn't be allowed. The QuakeC VM only
|
||||
* allows a maximum of eight arguments.
|
||||
*/
|
||||
if (*find == '9') {
|
||||
printf("invalid number of arguments, must be a valid number from 0-8\n");
|
||||
mem_d(copy);
|
||||
mem_d(name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*find != '0') {
|
||||
/*
|
||||
* if we made it this far we have a valid number for the
|
||||
* argument count, so fall through a switch statement and
|
||||
* do it.
|
||||
*/
|
||||
switch (*find) {
|
||||
case '8': args++; case '7': args++;
|
||||
case '6': args++; case '5': args++;
|
||||
case '4': args++; case '3': args++;
|
||||
case '2': args++; case '1': args++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We need to parse the argument size now by determining
|
||||
* the argument identifer list used after the amount of
|
||||
* arguments.
|
||||
*/
|
||||
memset(function.argsize, 0, sizeof(function.argsize));
|
||||
find ++; /* skip the number */
|
||||
while (*find == ' ' || *find == '\t') find++;
|
||||
while (size < args) {
|
||||
switch (*find) {
|
||||
case 'V': case 'v': function.argsize[size]=3; break;
|
||||
case 'S': case 's':
|
||||
case 'F': case 'f':
|
||||
case 'E': case 'e': function.argsize[size]=1; break;
|
||||
case '\0':
|
||||
printf("missing argument identifer, expected %d\n", args);
|
||||
return false;
|
||||
default:
|
||||
printf("error invalid function argument identifier\n");
|
||||
return false;
|
||||
}
|
||||
size++,find++;
|
||||
}
|
||||
while (*find == ' ' || *find == '\t') find++;
|
||||
if (*find != '\0') {
|
||||
printf("too many function argument identifers expected %d\n", args);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
printf("missing number of argument count in function %s\n", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we need to strip the name apart into it's exact size
|
||||
* by working in the peek buffer till we hit the name again.
|
||||
*/
|
||||
if (*peek == '#') {
|
||||
peek --; /* '#' */
|
||||
peek --; /* number */
|
||||
}
|
||||
while (*peek == ' ' || *peek == '\t') peek--;
|
||||
|
||||
/*
|
||||
* We're guranteed to be exactly where we need to be in the
|
||||
* peek buffer to null terminate and get our name from name
|
||||
* without any garbage before or after it.
|
||||
*/
|
||||
*++peek='\0';
|
||||
|
||||
/*
|
||||
* We got valid function structure information now. Lets add
|
||||
* the function to the code writer function table.
|
||||
*/
|
||||
function.entry = code_statements_elements;
|
||||
function.firstlocal = 0;
|
||||
function.locals = 0;
|
||||
function.profile = 0;
|
||||
function.name = code_chars_elements;
|
||||
function.file = 0;
|
||||
function.nargs = args;
|
||||
def.type = TYPE_FUNCTION;
|
||||
def.offset = code_globals_elements;
|
||||
def.name = code_chars_elements;
|
||||
code_functions_add(function);
|
||||
code_globals_add (code_statements_elements);
|
||||
code_chars_put (name, strlen(name));
|
||||
code_chars_add ('\0');
|
||||
sym.type = TYPE_FUNCTION;
|
||||
sym.name = util_strdup(name);
|
||||
sym.offset = function.entry;
|
||||
asm_symbols_add(sym);
|
||||
|
||||
/* update assembly state */
|
||||
|
||||
*state = ASM_FUNCTION;
|
||||
util_debug("ASM", "added context function %s to function table\n", name);
|
||||
}
|
||||
|
||||
mem_d(copy);
|
||||
mem_d(name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static GMQCC_INLINE bool asm_parse_stmt(const char *skip, size_t line, asm_state *state) {
|
||||
/*
|
||||
* This parses a valid statement in assembly and adds it to the code
|
||||
* table to be wrote. This needs to handle correct checking of all
|
||||
* statements to ensure the correct amount of operands are passed to
|
||||
* the menomic. This must also check for valid function calls (ensure
|
||||
* the names selected exist in the program scope) and ensure the correct
|
||||
* CALL* is used (depending on the amount of arguments the function
|
||||
* is expected to take)
|
||||
*/
|
||||
enum {
|
||||
EXPECT_FUNCTION = 1,
|
||||
EXPECT_VARIABLE = 2,
|
||||
EXPECT_VALUE = 3
|
||||
};
|
||||
|
||||
char *c = (char*)skip;
|
||||
size_t i = 0;
|
||||
char expect = 0;
|
||||
prog_section_statement s;
|
||||
memset(&s, 0, sizeof(prog_section_statement));
|
||||
|
||||
/*
|
||||
* statements are only allowed when inside a function body
|
||||
* otherwise the assembly is invalid.
|
||||
*/
|
||||
if (*state != ASM_FUNCTION)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Skip any possible whitespace, it's not wanted we're searching
|
||||
* for an instruction. TODO: recrusive decent parser skip on line
|
||||
* entry instead of pre-op.
|
||||
*/
|
||||
while (*skip == ' ' || *skip == '\t')
|
||||
skip++;
|
||||
|
||||
for (; i < sizeof(asm_instr)/sizeof(*asm_instr); i++) {
|
||||
/*
|
||||
* Iterate all possible instructions and check if the selected
|
||||
* instructure in the input stream `skip` is actually a valid
|
||||
* instruction.
|
||||
*/
|
||||
if (!strncmp(skip, asm_instr[i].m, asm_instr[i].l)) {
|
||||
|
||||
/*
|
||||
* We hit the end of a function scope, retarget the state
|
||||
* and add a DONE statement to the statment table.
|
||||
*/
|
||||
if (i == AINSTR_END) {
|
||||
s.opcode = i;
|
||||
code_statements_add(s);
|
||||
*state = ASM_NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the instruction type to see what sort of data
|
||||
* it's expected to have.
|
||||
*/
|
||||
if (i >= INSTR_CALL0 && i <= INSTR_CALL8)
|
||||
expect = EXPECT_FUNCTION;
|
||||
else
|
||||
expect = EXPECT_VARIABLE;
|
||||
|
||||
util_debug(
|
||||
"ASM",
|
||||
"found statement %s expecting: `%s` (%ld operand(s))\n",
|
||||
asm_instr[i].m,
|
||||
(expect == EXPECT_FUNCTION)?"function name":(
|
||||
(expect == EXPECT_VARIABLE)?"variable name":(
|
||||
(expect == EXPECT_VALUE ?"value" : "unknown"))),
|
||||
asm_instr[i].o
|
||||
);
|
||||
/*
|
||||
* Parse the operands for `i` (the instruction). The order
|
||||
* of asm_instr is in the order of the menomic encoding so
|
||||
* `i` == menomic encoding.
|
||||
*/
|
||||
s.opcode = i;
|
||||
switch (asm_instr[i].o) {
|
||||
/*
|
||||
* Each instruction can have from 0-3 operands; and can
|
||||
* be used with less or more operands depending on it's
|
||||
* selected use.
|
||||
*
|
||||
* DONE for example can use either 0 operands, or 1 (to
|
||||
* emulate the effect of RETURN)
|
||||
*
|
||||
* TODO: parse operands correctly figure out what it is
|
||||
* that the assembly is trying to do, i.e string table
|
||||
* lookup, function calls etc.
|
||||
*
|
||||
* This needs to have a fall state, we start from the
|
||||
* end of the string and work backwards.
|
||||
*/
|
||||
#define OPEATS(X,Y) X##Y
|
||||
#define OPCCAT(X,Y) OPEATS(X,Y)
|
||||
#define OPLOAD(X,Y) \
|
||||
do { \
|
||||
util_debug("ASM", "loading operand data ...\n"); \
|
||||
if (expect == EXPECT_VARIABLE) { \
|
||||
size_t f=0; \
|
||||
for (; f<asm_symbols_elements; f++) { \
|
||||
if (!strncmp(asm_symbols_data[f].name, (Y), strlen(Y)) && \
|
||||
asm_symbols_data[f].type != TYPE_FUNCTION) { \
|
||||
(X)=asm_symbols_data[f].offset; \
|
||||
goto OPCCAT(foundv, __LINE__); \
|
||||
} \
|
||||
} \
|
||||
printf("no variable named %s\n", (Y)); \
|
||||
break; \
|
||||
OPCCAT(foundv,__LINE__) : \
|
||||
printf("operand loaded for %s\n", (Y)); \
|
||||
} else if (expect == EXPECT_FUNCTION) { \
|
||||
/* \
|
||||
* It's a function call not a variable association with an instruction \
|
||||
* these are harder to handle. \
|
||||
*/ \
|
||||
size_t f=0; \
|
||||
if (strchr(Y, ' ')) { \
|
||||
*strchr(Y, ' ')='\0'; \
|
||||
} \
|
||||
for (; f<asm_symbols_elements; f++) { \
|
||||
if (!strncmp(asm_symbols_data[f].name, (Y), strlen(Y)) && \
|
||||
asm_symbols_data[f].type == TYPE_FUNCTION) { \
|
||||
(X)=asm_symbols_data[f].offset; \
|
||||
goto OPCCAT(foundf, __LINE__); \
|
||||
} \
|
||||
} \
|
||||
printf("no function named [%s]\n", (Y)); \
|
||||
break; \
|
||||
OPCCAT(foundf,__LINE__) : \
|
||||
printf("operand loaded for [%s]\n", (Y)); \
|
||||
} \
|
||||
} while (0)
|
||||
case 3: { OPLOAD(s.o3.s1,c); break; }
|
||||
case 2: { OPLOAD(s.o2.s1,c); break; }
|
||||
case 1: {
|
||||
while (*c == ' ' || *c == '\t') c++;
|
||||
c += asm_instr[i].l;
|
||||
while (*c == ' ' || *c == '\t') c++;
|
||||
OPLOAD(s.o1.s1, c);
|
||||
break;
|
||||
}
|
||||
#undef OPLOAD
|
||||
#undef OPCCAT
|
||||
}
|
||||
/* add the statement now */
|
||||
code_statements_add(s);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void asm_parse(FILE *fp) {
|
||||
char *data = NULL;
|
||||
long line = 1; /* current line */
|
||||
size_t size = 0; /* size of line */
|
||||
asm_state state = ASM_NULL;
|
||||
|
||||
#define asm_end(x) \
|
||||
do { \
|
||||
mem_d(data); \
|
||||
line ++; \
|
||||
util_debug("ASM", x); \
|
||||
} while (0); continue
|
||||
|
||||
while ((data = asm_getline (&size, fp)) != NULL) {
|
||||
char *copy = data;
|
||||
char *skip = copy;
|
||||
while (*copy == ' ' || *copy == '\t') copy++;
|
||||
while (*skip != '\n') skip++;
|
||||
*skip='\0';
|
||||
|
||||
if (asm_parse_type(copy, line, &state)){ asm_end("asm_parse_type\n"); }
|
||||
if (asm_parse_func(copy, line, &state)){ asm_end("asm_parse_func\n"); }
|
||||
if (asm_parse_stmt(copy, line, &state)){ asm_end("asm_parse_stmt\n"); }
|
||||
asm_end("asm_parse_white\n");
|
||||
}
|
||||
#undef asm_end
|
||||
asm_dumps();
|
||||
asm_clear();
|
||||
}
|
291
code.c
291
code.c
|
@ -1,291 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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"
|
||||
|
||||
/*
|
||||
* The macros below expand to a typesafe vector implementation, which
|
||||
* can be viewed in gmqcc.h
|
||||
*
|
||||
* code_statements_data -- raw prog_section_statement array
|
||||
* code_statements_elements -- number of elements
|
||||
* code_statements_allocated -- size of the array allocated
|
||||
* code_statements_add(T) -- add element (returns -1 on error)
|
||||
*
|
||||
* code_vars_data -- raw prog_section_var array
|
||||
* code_vars_elements -- number of elements
|
||||
* code_vars_allocated -- size of the array allocated
|
||||
* code_vars_add(T) -- add element (returns -1 on error)
|
||||
*
|
||||
* code_fields_data -- raw prog_section_field array
|
||||
* code_fields_elements -- number of elements
|
||||
* code_fields_allocated -- size of the array allocated
|
||||
* code_fields_add(T) -- add element (returns -1 on error)
|
||||
*
|
||||
* code_functions_data -- raw prog_section_function array
|
||||
* code_functions_elements -- number of elements
|
||||
* code_functions_allocated -- size of the array allocated
|
||||
* code_functions_add(T) -- add element (returns -1 on error)
|
||||
*
|
||||
* code_globals_data -- raw prog_section_def array
|
||||
* code_globals_elements -- number of elements
|
||||
* code_globals_allocated -- size of the array allocated
|
||||
* code_globals_add(T) -- add element (returns -1 on error)
|
||||
*
|
||||
* code_chars_data -- raw char* array
|
||||
* code_chars_elements -- number of elements
|
||||
* code_chars_allocated -- size of the array allocated
|
||||
* code_chars_add(T) -- add element (returns -1 on error)
|
||||
*/
|
||||
VECTOR_MAKE(prog_section_statement, code_statements);
|
||||
VECTOR_MAKE(prog_section_def, code_defs );
|
||||
VECTOR_MAKE(prog_section_field, code_fields );
|
||||
VECTOR_MAKE(prog_section_function, code_functions );
|
||||
VECTOR_MAKE(int, code_globals );
|
||||
VECTOR_MAKE(char, code_chars );
|
||||
|
||||
uint16_t code_crc;
|
||||
uint32_t code_entfields;
|
||||
|
||||
void code_init() {
|
||||
prog_section_function empty_function = {0,0,0,0,0,0,0,{0}};
|
||||
prog_section_statement empty_statement = {0,{0},{0},{0}};
|
||||
prog_section_def empty_def = {0, 0, 0};
|
||||
int i = 0;
|
||||
|
||||
code_entfields = 0;
|
||||
|
||||
/* omit creation of null code */
|
||||
if (OPTS_FLAG(OMIT_NULL_BYTES))
|
||||
return;
|
||||
|
||||
/*
|
||||
* 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++)
|
||||
code_globals_add(0);
|
||||
|
||||
code_chars_add ('\0');
|
||||
code_functions_add (empty_function);
|
||||
code_statements_add(empty_statement);
|
||||
code_defs_add (empty_def);
|
||||
code_fields_add (empty_def);
|
||||
}
|
||||
|
||||
uint32_t code_genstring(const char *str)
|
||||
{
|
||||
uint32_t off = code_chars_elements;
|
||||
while (*str) {
|
||||
code_chars_add(*str);
|
||||
++str;
|
||||
}
|
||||
code_chars_add(0);
|
||||
return off;
|
||||
}
|
||||
|
||||
uint32_t code_cachedstring(const char *str)
|
||||
{
|
||||
size_t s = 0;
|
||||
/* We could implement knuth-morris-pratt or something
|
||||
* and also take substrings, but I'm uncomfortable with
|
||||
* pointing to subparts of strings for the sake of clarity...
|
||||
*/
|
||||
while (s < code_chars_elements) {
|
||||
if (!strcmp(str, code_chars_data + s))
|
||||
return s;
|
||||
while (code_chars_data[s]) ++s;
|
||||
++s;
|
||||
}
|
||||
return code_genstring(str);
|
||||
}
|
||||
|
||||
void code_test() {
|
||||
prog_section_def d1 = { TYPE_VOID, 28, 1 };
|
||||
prog_section_def d2 = { TYPE_FUNCTION, 29, 8 };
|
||||
prog_section_def d3 = { TYPE_STRING, 30, 14};
|
||||
prog_section_function f1 = { 1, 0, 0, 0, 1, 0,0, {0}};
|
||||
prog_section_function f2 = {-4, 0, 0, 0, 8, 0,0, {0}};
|
||||
prog_section_function f3 = { 0, 0, 0, 0, 14+13, 0,0, {0}};
|
||||
prog_section_function f4 = { 0, 0, 0, 0, 14+13+10, 0,0, {0}};
|
||||
prog_section_function f5 = { 0, 0, 0, 0, 14+13+10+7, 0,0, {0}};
|
||||
prog_section_function f6 = { 0, 0, 0, 0, 14+13+10+7+9, 0,0, {0}};
|
||||
prog_section_statement s1 = { INSTR_STORE_F, {30}, {OFS_PARM0}, {0}};
|
||||
prog_section_statement s2 = { INSTR_CALL1, {29}, {0}, {0}};
|
||||
prog_section_statement s3 = { INSTR_RETURN, {0}, {0}, {0}};
|
||||
|
||||
code_chars_put("m_init", 0x6);
|
||||
code_chars_put("print", 0x5);
|
||||
code_chars_put("hello world\n", 0xC);
|
||||
code_chars_put("m_keydown", 0x9);
|
||||
code_chars_put("m_draw", 0x6);
|
||||
code_chars_put("m_toggle", 0x8);
|
||||
code_chars_put("m_shutdown", 0xA);
|
||||
|
||||
code_globals_add(1); /* m_init */
|
||||
code_globals_add(2); /* print */
|
||||
code_globals_add(14); /* hello world in string table */
|
||||
|
||||
/* now the defs */
|
||||
code_defs_add (d1); /* m_init */
|
||||
code_defs_add (d2); /* print */
|
||||
code_defs_add (d3); /*hello_world*/
|
||||
code_functions_add (f1); /* m_init */
|
||||
code_functions_add (f2); /* print */
|
||||
code_functions_add (f3); /* m_keydown */
|
||||
code_functions_add (f4);
|
||||
code_functions_add (f5);
|
||||
code_functions_add (f6);
|
||||
code_statements_add(s1);
|
||||
code_statements_add(s2);
|
||||
code_statements_add(s3);
|
||||
}
|
||||
|
||||
qcint code_alloc_field (size_t qcsize)
|
||||
{
|
||||
qcint pos = (qcint)code_entfields;
|
||||
code_entfields += qcsize;
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool code_write(const char *filename) {
|
||||
prog_header code_header;
|
||||
FILE *fp = NULL;
|
||||
size_t it = 2;
|
||||
|
||||
/* see proposal.txt */
|
||||
if (OPTS_FLAG(OMIT_NULL_BYTES)) {}
|
||||
code_header.statements.offset = sizeof(prog_header);
|
||||
code_header.statements.length = code_statements_elements;
|
||||
code_header.defs.offset = code_header.statements.offset + (sizeof(prog_section_statement) * code_statements_elements);
|
||||
code_header.defs.length = code_defs_elements;
|
||||
code_header.fields.offset = code_header.defs.offset + (sizeof(prog_section_def) * code_defs_elements);
|
||||
code_header.fields.length = code_fields_elements;
|
||||
code_header.functions.offset = code_header.fields.offset + (sizeof(prog_section_field) * code_fields_elements);
|
||||
code_header.functions.length = code_functions_elements;
|
||||
code_header.globals.offset = code_header.functions.offset + (sizeof(prog_section_function) * code_functions_elements);
|
||||
code_header.globals.length = code_globals_elements;
|
||||
code_header.strings.offset = code_header.globals.offset + (sizeof(int32_t) * code_globals_elements);
|
||||
code_header.strings.length = code_chars_elements;
|
||||
code_header.version = 6;
|
||||
if (opts_forcecrc)
|
||||
code_header.crc16 = opts_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 */
|
||||
code_chars_add('\0'); /* > */
|
||||
code_chars_add('\0'); /* = */
|
||||
code_chars_add('\0'); /* P */
|
||||
}
|
||||
|
||||
/* ensure all data is in LE format */
|
||||
util_endianswap(&code_header, 1, sizeof(prog_header));
|
||||
util_endianswap(code_statements_data, code_statements_elements, sizeof(prog_section_statement));
|
||||
util_endianswap(code_defs_data, code_defs_elements, sizeof(prog_section_def));
|
||||
util_endianswap(code_fields_data, code_fields_elements, sizeof(prog_section_field));
|
||||
util_endianswap(code_functions_data, code_functions_elements, sizeof(prog_section_function));
|
||||
util_endianswap(code_globals_data, code_globals_elements, sizeof(int32_t));
|
||||
|
||||
fp = util_fopen(filename, "wb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
if (1 != fwrite(&code_header, sizeof(prog_header), 1, fp) ||
|
||||
code_statements_elements != fwrite(code_statements_data, sizeof(prog_section_statement), code_statements_elements, fp) ||
|
||||
code_defs_elements != fwrite(code_defs_data, sizeof(prog_section_def) , code_defs_elements , fp) ||
|
||||
code_fields_elements != fwrite(code_fields_data, sizeof(prog_section_field) , code_fields_elements , fp) ||
|
||||
code_functions_elements != fwrite(code_functions_data, sizeof(prog_section_function) , code_functions_elements , fp) ||
|
||||
code_globals_elements != fwrite(code_globals_data, sizeof(int32_t) , code_globals_elements , fp) ||
|
||||
code_chars_elements != fwrite(code_chars_data, 1 , code_chars_elements , fp))
|
||||
{
|
||||
fclose(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 < code_functions_elements; it++) {
|
||||
size_t j = code_functions_data[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_data[it].entry,
|
||||
code_functions_data[it].firstlocal,
|
||||
code_functions_data[it].locals,
|
||||
code_functions_data[it].profile,
|
||||
code_functions_data[it].name,
|
||||
code_functions_data[it].file,
|
||||
code_functions_data[it].nargs,
|
||||
code_functions_data[it].argsize[0],
|
||||
code_functions_data[it].argsize[1],
|
||||
code_functions_data[it].argsize[2],
|
||||
code_functions_data[it].argsize[3],
|
||||
code_functions_data[it].argsize[4],
|
||||
code_functions_data[it].argsize[5],
|
||||
code_functions_data[it].argsize[6],
|
||||
code_functions_data[it].argsize[7]
|
||||
|
||||
);
|
||||
util_debug("GEN", " NAME: %s\n", &code_chars_data[code_functions_data[it].name]);
|
||||
/* Internal functions have no code */
|
||||
if (code_functions_data[it].entry >= 0) {
|
||||
util_debug("GEN", " CODE:\n");
|
||||
for (;;) {
|
||||
if (code_statements_data[j].opcode != AINSTR_END)
|
||||
util_debug("GEN", " %-12s {% 5i,% 5i,% 5i}\n",
|
||||
asm_instr[code_statements_data[j].opcode].m,
|
||||
code_statements_data[j].o1.s1,
|
||||
code_statements_data[j].o2.s1,
|
||||
code_statements_data[j].o3.s1
|
||||
);
|
||||
else {
|
||||
util_debug("GEN", " DONE {0x00000,0x00000,0x00000}\n");
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mem_d(code_statements_data);
|
||||
mem_d(code_defs_data);
|
||||
mem_d(code_fields_data);
|
||||
mem_d(code_functions_data);
|
||||
mem_d(code_globals_data);
|
||||
mem_d(code_chars_data);
|
||||
fclose(fp);
|
||||
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;
|
||||
}
|
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;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
float constant_1 = 1;
|
||||
string constant_2 = "hello world";
|
||||
vector constant_3 = { -0, +0, 0 };
|
||||
entity constant_4 = 0
|
|
@ -1,15 +0,0 @@
|
|||
/* this is the WIP test for the parser...
|
||||
* constantly adding stuff here to see if things break
|
||||
*/
|
||||
void(string) print = #1;
|
||||
void(string,string) print2 = #1;
|
||||
void(string,string,string) print3 = #1;
|
||||
string(float) ftos = #2;
|
||||
|
||||
float CONST_1 = 1;
|
||||
float CONST_2 = CONST_1 + 1;
|
||||
|
||||
void() main = {
|
||||
print3("CONST_1 = ", ftos(CONST_1), "\n");
|
||||
print3("CONST_2 = ", ftos(CONST_2), "\n");
|
||||
};
|
|
@ -1,48 +0,0 @@
|
|||
/* this is the WIP test for the parser...
|
||||
* constantly adding stuff here to see if things break
|
||||
*/
|
||||
void(string) print = #1;
|
||||
void(string,string) print2 = #1;
|
||||
void(string,string,string) print3 = #1;
|
||||
string(float) ftos = #2;
|
||||
entity() spawn = #3;
|
||||
void(entity) kill = #4;
|
||||
|
||||
.float mema;
|
||||
.float memb;
|
||||
.vector memv;
|
||||
|
||||
.void() fun;
|
||||
|
||||
void(entity a, .float f) printfield = {
|
||||
print3("The field is ", ftos(a.f), "\n");
|
||||
};
|
||||
|
||||
void() funny = {
|
||||
print("FUNNY\n");
|
||||
};
|
||||
|
||||
void() main = {
|
||||
local entity pawn;
|
||||
|
||||
pawn = spawn();
|
||||
|
||||
pawn.mema = 9;
|
||||
pawn.memv = '1 2 3';
|
||||
pawn.memb = 10;
|
||||
|
||||
print3("x = ", ftos(pawn.memv_x), "\n");
|
||||
print3("y = ", ftos(pawn.memv_y), "\n");
|
||||
print3("z = ", ftos(pawn.memv_z), "\n");
|
||||
print3("a = ", ftos(pawn.mema), "\n");
|
||||
print3("b = ", ftos(pawn.memb), "\n");
|
||||
pawn.memv_y += 3;
|
||||
print3("x = ", ftos(pawn.memv_x), "\n");
|
||||
print3("y = ", ftos(pawn.memv_y), "\n");
|
||||
print3("z = ", ftos(pawn.memv_z), "\n");
|
||||
printfield(pawn, memv_z);
|
||||
|
||||
pawn.fun = funny;
|
||||
|
||||
pawn.fun();
|
||||
};
|
|
@ -1,56 +0,0 @@
|
|||
/* this is the WIP test for the parser...
|
||||
* constantly adding stuff here to see if things break
|
||||
*/
|
||||
void(string) print = #1;
|
||||
void(string,string) print2 = #1;
|
||||
void(string,string,string) print3 = #1;
|
||||
string(float) ftos = #2;
|
||||
entity() spawn = #3;
|
||||
void(entity) kill = #4;
|
||||
|
||||
$frame stand1 stand2 standX
|
||||
.float frame;
|
||||
.float nextthink;
|
||||
.void() think;
|
||||
|
||||
entity self;
|
||||
float time;
|
||||
|
||||
// void() stand2; this is auto-prototyped
|
||||
void() stand1 = [ 0, stand2 ] {
|
||||
// expands to:
|
||||
//self.frame = 0;
|
||||
//self.nextthink = time + 0.1;
|
||||
//self.think = stand2
|
||||
print("In stand 1...\n");
|
||||
print3("--> self.frame should be 0, is ", ftos(self.frame), "\n");
|
||||
};
|
||||
|
||||
void() stand2 = [ 1, stand1 ] {
|
||||
print("In stand 2...\n");
|
||||
print3("--> self.frame should be 1, is ", ftos(self.frame), "\n");
|
||||
};
|
||||
|
||||
void() standm = {
|
||||
local string bar;
|
||||
bar = ftos(self);
|
||||
print3("Foo ", ftos(self), "\n");
|
||||
self.frame = 0;
|
||||
self.nextthink = time + 0.1;
|
||||
};
|
||||
|
||||
void() main = {
|
||||
self = spawn();
|
||||
|
||||
time = 10;
|
||||
|
||||
print("Setting think\n");
|
||||
self.think = stand1;
|
||||
|
||||
print("Running think\n");
|
||||
standm();
|
||||
print("Running from 'self'\n");
|
||||
self.think();
|
||||
self.think();
|
||||
self.think();
|
||||
};
|
|
@ -1,43 +0,0 @@
|
|||
/* this is the WIP test for the parser...
|
||||
* constantly adding stuff here to see if things break
|
||||
*/
|
||||
void(string) print = #1;
|
||||
void(string,string) print2 = #1;
|
||||
void(string,string,string) print3 = #1;
|
||||
string(float) ftos = #2;
|
||||
entity() spawn = #3;
|
||||
void(entity) kill = #4;
|
||||
|
||||
.float vis;
|
||||
.entity other;
|
||||
|
||||
float(entity targ) visible = {
|
||||
return targ.vis;
|
||||
};
|
||||
|
||||
void() printworking = {
|
||||
print("Working\n");
|
||||
};
|
||||
|
||||
void(void() callback) testcallback = {
|
||||
callback();
|
||||
};
|
||||
|
||||
void(float) has1param = {};
|
||||
|
||||
void() main = {
|
||||
local entity pawn, pawn2;
|
||||
|
||||
pawn = spawn();
|
||||
pawn2 = spawn();
|
||||
|
||||
pawn.other = pawn2;
|
||||
pawn.other.vis = 0;
|
||||
|
||||
if (!visible(pawn.other))
|
||||
print("Yes\n");
|
||||
|
||||
testcallback(printworking);
|
||||
|
||||
has1param();
|
||||
};
|
|
@ -1,5 +0,0 @@
|
|||
/*
|
||||
* all of these includes should work. No matter what the spacing
|
||||
* is, we rely on it.
|
||||
*/
|
||||
#include "test/include2.qc"
|
|
@ -1 +0,0 @@
|
|||
float foo;
|
|
@ -1,22 +0,0 @@
|
|||
/* this is the WIP test for the parser...
|
||||
* constantly adding stuff here to see if things break
|
||||
*/
|
||||
void(string) print = #1;
|
||||
void(string,string) print2 = #1;
|
||||
void(string,string,string) print3 = #1;
|
||||
string(float) ftos = #2;
|
||||
|
||||
void() main = {
|
||||
float a, b, c;
|
||||
a = 3 + 4 + 5;
|
||||
b = (5 * 2) + 1;
|
||||
c = 3 & 1;
|
||||
c = 1 | 2;
|
||||
a = 3 && 4;
|
||||
b = 0 && 4;
|
||||
c = 4 && 0;
|
||||
a = 1 || 1;
|
||||
b = 1 || 0;
|
||||
c = 0 || 1;
|
||||
a = 0 || 0;
|
||||
};
|
|
@ -1,77 +0,0 @@
|
|||
/* this is the WIP test for the parser...
|
||||
* constantly adding stuff here to see if things break
|
||||
*/
|
||||
void(string,...) print = #1;
|
||||
string(float) ftos = #2;
|
||||
entity() spawn = #3;
|
||||
void(entity) kill = #4;
|
||||
|
||||
.float mema;
|
||||
.float memb;
|
||||
|
||||
$framevalue 0
|
||||
$frame stand1 stand2 standX
|
||||
$framerestore stand2
|
||||
$frame stand3
|
||||
$modelname foobar
|
||||
$modelname foobar3
|
||||
|
||||
void(string a, ...) hasvaria = {
|
||||
};
|
||||
|
||||
void() main = {
|
||||
entity pawn;
|
||||
vector vec;
|
||||
float a;
|
||||
|
||||
vec = '3 4 5';
|
||||
vec_z = 5;
|
||||
|
||||
a = 5;
|
||||
|
||||
if (a) {
|
||||
print("a != 0\n");
|
||||
} else {
|
||||
print("not a\n");
|
||||
}
|
||||
|
||||
a = 19;
|
||||
print("Hello, World\n");
|
||||
|
||||
pawn = spawn();
|
||||
pawn.mema = 3;
|
||||
pawn.memb = 5;
|
||||
print(ftos(pawn.mema), "\n");
|
||||
print(ftos(pawn.memb), "\n");
|
||||
|
||||
print("SECOND TEST\n");
|
||||
for (a = 0; a < 3; a = a + 1) {
|
||||
print("LOOP ", ftos(a), "\n");
|
||||
}
|
||||
|
||||
print("DO-WHILE test\n");
|
||||
a = 2;
|
||||
do {
|
||||
print("Foo\n");
|
||||
a = a - 1;
|
||||
} while (a);
|
||||
|
||||
float b;
|
||||
a = 5;
|
||||
print("a = ", ftos(a), "\n");
|
||||
b = a += 7;
|
||||
print("adding\n");
|
||||
print("a = ", ftos(a), "\n");
|
||||
print("b = ", ftos(b), "\n");
|
||||
|
||||
print("memb = ", ftos(pawn.memb), "\n");
|
||||
pawn.memb += -1;
|
||||
print("memb = ", ftos(pawn.memb), "\n");
|
||||
print("Frame stand3 is ", ftos($stand3), " wooh\n");
|
||||
};
|
||||
|
||||
float() exprtest = {
|
||||
local float x;
|
||||
x = 3;
|
||||
return (x); /* parens */
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
void test_parth() {
|
||||
if (1) { }
|
||||
if (2) { }
|
||||
if (3) { }
|
||||
if (4) { }
|
||||
if (5) { }
|
||||
if (6) { }
|
||||
if (7) { }
|
||||
if (8) { }
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
void(string) print = #1;
|
||||
|
||||
void() correct;
|
||||
void(string) incorrect;
|
||||
|
||||
void() correct = {
|
||||
print("Hello\n");
|
||||
}
|
||||
|
||||
void() incorrect = {
|
||||
printf("The compiler should error about this function having a wrong type\n");
|
||||
}
|
103
data/test.qs
103
data/test.qs
|
@ -1,103 +0,0 @@
|
|||
; these are builtin functions
|
||||
FUNCTION: makevectors, $1
|
||||
FUNCTION: setorigin, $2
|
||||
FUNCTION: setmodel, $3
|
||||
FUNCTION: setsize, $4
|
||||
|
||||
FUNCTION: break, $6
|
||||
FUNCTION: random, $7
|
||||
FUNCTION: sound, $8
|
||||
FUNCTION: normalize, $9
|
||||
FUNCTION: error, $10
|
||||
FUNCTION: objerror, $11
|
||||
FUNCTION: vlen, $12
|
||||
FUNCTION: vectoyaw, $13
|
||||
FUNCTION: spawn, $14
|
||||
FUNCTION: remove, $15
|
||||
FUNCTION: traceline, $16
|
||||
|
||||
FUNCTION: find, $18
|
||||
FUNCTION: precache_sound, $19
|
||||
FUNCTION: precache_model, $20
|
||||
|
||||
FUNCTION: findradius, $22
|
||||
|
||||
FUNCTION: dprint, $25
|
||||
FUNCTION: ftos, $26
|
||||
FUNCTION: vtos, $27
|
||||
FUNCTION: coredump, $28
|
||||
FUNCTION: traceon, $29
|
||||
FUNCTION: traceoff, $30
|
||||
FUNCTION: eprint, $31
|
||||
FUNCTION: walkmove, $32
|
||||
|
||||
FUNCTION: droptofloor, $34
|
||||
FUNCTION: lightstyle, $35
|
||||
FUNCTION: rint, $36
|
||||
FUNCTION: floor, $37
|
||||
FUNCTION: ceil, $38
|
||||
|
||||
FUNCTION: checkbottom, $40
|
||||
FUNCTION: pointcontents, $41
|
||||
|
||||
FUNCTION: fabs, $43
|
||||
|
||||
FUNCTION: cvar, $45
|
||||
FUNCTION: localcmd, $46
|
||||
FUNCTION: nextent, $47
|
||||
FUNCTION: particle, $48
|
||||
FUNCTION: ChangeYaw, $49
|
||||
|
||||
FUNCTION: vectoangles, $51
|
||||
FUNCTION: vectoangles2, $51
|
||||
|
||||
FUNCTION: sin, $60
|
||||
FUNCTION: cos, $61
|
||||
FUNCTION: sqrt, $62
|
||||
FUNCTION: changepitch, $63
|
||||
FUNCTION: tracetoss, $64
|
||||
FUNCTION: etos, $65
|
||||
|
||||
FUNCTION: precache_file, $68
|
||||
FUNCTION: makestatic, $69
|
||||
|
||||
FUNCTION: cvar_set, $72
|
||||
|
||||
FUNCTION: ambientsound, $74
|
||||
FUNCTION: precache_model2,$75
|
||||
FUNCTION: precache_sound2,$76
|
||||
FUNCTION: precache_file2, $77
|
||||
|
||||
FUNCTION: stof, $81
|
||||
|
||||
FUNCTION: tracebox, $90
|
||||
FUNCTION: randomvec, $91
|
||||
FUNCTION: getlight, $92
|
||||
FUNCTION: getlight2, $92
|
||||
FUNCTION: registercvar, $93
|
||||
FUNCTION: min, $94
|
||||
FUNCTION: max, $95
|
||||
FUNCTION: bound, $96
|
||||
FUNCTION: pow, $97
|
||||
FUNCTION: findfloat, $98
|
||||
FUNCTION: checkextension, $99
|
||||
|
||||
FUNCTION: test #0
|
||||
FLOAT: x, 100
|
||||
FLOAT: y, 200
|
||||
|
||||
FLOAT: r_mul
|
||||
FLOAT: r_div
|
||||
FLOAT: r_add
|
||||
FLOAT: r_sub
|
||||
|
||||
MUL_V x,y,r_mul
|
||||
DIV_V x,y,r_div
|
||||
ADD_V x,y,r_add
|
||||
SUV_V x,y,r_sub
|
||||
|
||||
STORE_V r_mul
|
||||
CALL0 dprint
|
||||
|
||||
STORE_V
|
||||
END
|
|
@ -1,11 +0,0 @@
|
|||
typedef float my_float;
|
||||
typedef vector my_vector;
|
||||
typedef string my_string;
|
||||
typedef entity my_entity;
|
||||
typedef void my_void;
|
||||
|
||||
my_float type_float;
|
||||
my_vector type_vector;
|
||||
my_string type_string;
|
||||
my_entity type_entity;
|
||||
my_void type_void;
|
|
@ -1,5 +0,0 @@
|
|||
float typef;
|
||||
vector typev;
|
||||
string types;
|
||||
entity typee;
|
||||
void typev;
|
|
@ -1,19 +0,0 @@
|
|||
void(string) print = #1;
|
||||
void(float) ftos = #2;
|
||||
void() main = {
|
||||
local float uninit, unused, setonly;
|
||||
local vector invec;
|
||||
print("foo\n");
|
||||
|
||||
setonly = 3;
|
||||
invec = '1 2 3';
|
||||
|
||||
if (0)
|
||||
uninit = 3;
|
||||
ftos(uninit);
|
||||
ftos(invec_x);
|
||||
};
|
||||
|
||||
void(float par) partest = {
|
||||
ftos(par);
|
||||
};
|
43
data/vars.qc
43
data/vars.qc
|
@ -1,43 +0,0 @@
|
|||
/* this is the WIP test for the parser...
|
||||
* constantly adding stuff here to see if things break
|
||||
*/
|
||||
void(string) print = #1;
|
||||
void(string,string) print2 = #1;
|
||||
void(string,string,string) print3 = #1;
|
||||
string(float) ftos = #2;
|
||||
entity() spawn = #3;
|
||||
void(entity) kill = #4;
|
||||
|
||||
float multi, decla, ration;
|
||||
.vector memvec;
|
||||
|
||||
.void(string x) printit;
|
||||
|
||||
float(vector different_name, vector b) dot;
|
||||
|
||||
float(vector a, vector b) dot = {
|
||||
return a * b;
|
||||
};
|
||||
|
||||
void(string x) myprintit = {
|
||||
print3("-> ", x, "\n");
|
||||
};
|
||||
|
||||
void(vector par) vecpar = {
|
||||
// vector-parameters need _x, _y, _z as well
|
||||
print3("par_y should be 5... = ", ftos(par_y), "\n");
|
||||
};
|
||||
|
||||
void() main = {
|
||||
local entity pawn;
|
||||
local vector foovec;
|
||||
print3("should be 1: ", ftos(dot('1 1 0', '1 0 0')), "\n");
|
||||
|
||||
foovec = '3 4 5';
|
||||
foovec_y = 9;
|
||||
pawn = spawn();
|
||||
pawn.printit = myprintit;
|
||||
pawn.printit("Hello");
|
||||
|
||||
vecpar('1 5 9');
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
vector vec1 = {-0 +0 0 };
|
||||
vector vec2 = {.0 .0 .0 };
|
||||
vector vec3 = {-.0 +.0 +0.1 };
|
||||
vector vec4 = {1.1 2.2 3.3 };
|
||||
vector vec5 = {2. 3. 4. };
|
||||
vector vec6 = {-2. +3. -4. };
|
||||
/*
|
||||
* These are just comments: Ideally there is still some broken things
|
||||
* for the vector yet. which sort of sucks.
|
||||
*/
|
808
doc/gmqcc.1
808
doc/gmqcc.1
|
@ -1,49 +1,771 @@
|
|||
.\" Process with groff -man -Tascii file.3
|
||||
.TH GMQCC 1 2012-07-12 "" "gmqcc Manual"
|
||||
.SH NAME
|
||||
gmqcc \- A Quake C compiler which tries to reduce suckiness.
|
||||
.SH SYNOPSIS
|
||||
.B gmqcc
|
||||
[\fIOPTIONS\fR] [\fIfiles...\fR]
|
||||
.SH DESCRIPTION
|
||||
Traditionally, a QC compiler reads the file \fIprogs.src\fR which
|
||||
in its first line contains the output filename, and the rest is a
|
||||
.\"mdoc
|
||||
.Dd January 24, 2013
|
||||
.Dt GMQCC 1 PRM
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm gmqcc
|
||||
.Nd A Quake C compiler built from the NIH realm of sarcastic wit
|
||||
.Sh SYNOPSIS
|
||||
.Nm gmqcc
|
||||
.Op Cm options
|
||||
.Op Ar files...
|
||||
.Sh DESCRIPTION
|
||||
Traditionally, a QC compiler reads the file
|
||||
.Pa progs.src
|
||||
which in its first line contains the output filename, and the rest is a
|
||||
list of QC source files that are to be compiled in order.
|
||||
\fBgmqcc\fR optionally takes options to specify the output and
|
||||
.Nm gmqcc
|
||||
optionally takes options to specify the output and
|
||||
input files on the commandline, and also accepts assembly files.
|
||||
.SH OPTIONS
|
||||
\fBgmqcc\fR mostly tries to mimick gcc's commandline handling, though
|
||||
.Sh OPTIONS
|
||||
.Nm gmqcc
|
||||
mostly tries to mimic gcc's commandline handling, though
|
||||
there are also traditional long-options available.
|
||||
.TP
|
||||
.B "-h, --help"
|
||||
.Bl -tag -width Ds
|
||||
.It Fl h , Fl -help
|
||||
Show a usage message and exit.
|
||||
.TP
|
||||
.BI "-o, --output=" filename
|
||||
.It Fl o , Fl -output= Ns Ar filename
|
||||
Specify the output filename. Defaults to progs.dat. This will overwrite
|
||||
the output file listed in a \fIprogs.src\fR file in case such a file is used.
|
||||
.TP
|
||||
.BI "-O" n
|
||||
Specify the optimization level, similar to gcc.
|
||||
.TP
|
||||
.BI "-a" filename
|
||||
Append the specified files to the list of files to assemble using the QC-Assembler.
|
||||
.TP
|
||||
.BI "-s" filename
|
||||
Append the specified file which is to be interpreted as a \fIprogs.src\fR file.
|
||||
.TP
|
||||
.BI "-std=" standard
|
||||
Use the specified standard for parsing QC code. The following standards are available:
|
||||
.IR gmqcc , qcc , fteqcc
|
||||
.TP
|
||||
.BI -W warning "\fR, " "" -Wno- warning
|
||||
the output file listed in a
|
||||
.Pa progs.src
|
||||
file in case such a file is used.
|
||||
.Bl -tag -width indent
|
||||
.It Fl O Ns Ar number
|
||||
Specify the optimization level
|
||||
.It Ar 3
|
||||
Highest optimization level
|
||||
.It Ar 2
|
||||
Default optimization level
|
||||
.It Ar 1
|
||||
Minimal optimization level
|
||||
.It Ar 0
|
||||
Disable optimization entirely
|
||||
.El
|
||||
.Pp
|
||||
.It Fl O Ns Ar name , Fl Ono- Ns Ar name
|
||||
Enable or disable a specific optimization. Note that these options
|
||||
must be used after setting the optimization level, otherwise they'll
|
||||
be overwritten.
|
||||
.It Fl O Ns Cm help
|
||||
List all possible optimizations and the optimization level they're
|
||||
activated at.
|
||||
.It Fl q , Fl -quiet
|
||||
Be less verbose. In particular removes the messages about which files
|
||||
are being processed, and which compilation mode is being used, and
|
||||
some others. Warnings and errors will of course still be displayed.
|
||||
.It Fl D Ns Ar macroname , Fl D Ns Ar macroname Ns = Ns Ar value
|
||||
Predefine a macro, optionally with a optional value.
|
||||
.It Fl E
|
||||
Run only the preprocessor as if
|
||||
.Fl f Ns Cm ftepp
|
||||
was used and print the preprocessed code to stdout.
|
||||
.It Fl W Ns Ar warning , Fl Wno- Ns Ar warning
|
||||
Enable or disable a warning.
|
||||
.TP
|
||||
.B -Wall
|
||||
Enable all warnings. Overrides preceding -W parameters.
|
||||
.TP
|
||||
.B -fdarkplaces-string-table-bug
|
||||
Patch the output file to work around a string-table bug in certain darkplaces versions.
|
||||
.TP
|
||||
.B -fomit-nullbytes
|
||||
Changes the output format to be more efficient. Requires a patched engine. See the
|
||||
proposal for a better file structure in the gmqcc source tree.
|
||||
.It Fl W Ns Cm all
|
||||
Enable almost all warnings. Overrides preceding
|
||||
.Fl W
|
||||
parameters.
|
||||
.Pp
|
||||
The following warnings will
|
||||
.Em not
|
||||
be enabled:
|
||||
.Bl -tag -width indent -offset indent
|
||||
.It Fl W Ns Cm uninitialized-global
|
||||
.El
|
||||
.It Fl W Ns Cm error , Fl Wno- Ns Cm error
|
||||
Controls whether or not all warnings should be treated as errors.
|
||||
.It Fl Werror- Ns Ar warning , Fl Wno-error- Ns Ar warning
|
||||
Controls whether a specific warning should be an error.
|
||||
.It Fl W Ns Cm help
|
||||
List all possible warn flags.
|
||||
.It Fl f Ns Ar flag , Fl fno- Ns Ar flag
|
||||
Enable or disable a specific compile flag. See the list of flags
|
||||
below.
|
||||
.It Fl f Ns Cm help
|
||||
List all possible compile flags.
|
||||
.It Fl nocolor
|
||||
Disables colored output
|
||||
.It Fl config= Ns Ar file
|
||||
Use an ini file to read all the
|
||||
.Fl O , Fl W
|
||||
and
|
||||
.Fl f
|
||||
flag from. See the
|
||||
.It Fl "debug"
|
||||
Turn on some compiler debugging mechanisms.
|
||||
.It Fl memchk
|
||||
Turn on compiler mem-check. (Shows allocations and checks for leaks.)
|
||||
.It Fl -memdumpcols Ns Ar columns
|
||||
Changes the number of columns to use for the debug memory dump, defaults to 16.
|
||||
.Sx CONFIG
|
||||
section about the file format.
|
||||
.It Fl redirout= Ns Ar file
|
||||
Redirects standard output to a
|
||||
.Ar file
|
||||
.It Fl redirerr= Ns Ar file
|
||||
Redirects standard error to a
|
||||
.Ar file
|
||||
.It Fl std= Ns Ar standard
|
||||
Use the specified standard for parsing QC code. The following standards
|
||||
are available:
|
||||
.Ar gmqcc , Ar qcc , Ar fteqcc
|
||||
Selecting a standard also implies some
|
||||
.Fl f
|
||||
options and behaves as if
|
||||
those options have been written right after the
|
||||
.Fl std
|
||||
option, meaning
|
||||
if you changed them before the
|
||||
.Fl -std
|
||||
option, you're now overwriting them.
|
||||
.Pp
|
||||
.Fl std= Ns Cm gmqcc No includes:
|
||||
.Bl -tag -width indent -compact -offset Ds
|
||||
.It Fl f Ns Cm adjust-vector-fields
|
||||
.It Fl f Ns Cm correct-logic
|
||||
.It Fl f Ns Cm true-empty-strings
|
||||
.It Fl f Ns Cm loop-labels
|
||||
.It Fl f Ns Cm initialized-nonconstants
|
||||
.It Fl f Ns Cm translatable-strings
|
||||
.It Fl fno- Ns Cm false-empty-strings
|
||||
.It Fl W Ns Cm invalid-parameter-count
|
||||
.It Fl W Ns Cm missing-returnvalues
|
||||
.It Fl f Ns Cm correct-ternary Li (cannot be turned off)
|
||||
.El
|
||||
.Pp
|
||||
.Fl std= Ns Cm qcc No includes:
|
||||
.Bl -tag -width indent -compact -offset Ds
|
||||
.It Fl f Ns Cm assign-function-types
|
||||
.It Fl fIno- Ns Cm adjust-vector-fields
|
||||
.El
|
||||
.Pp
|
||||
.Fl std= Ns Cm fteqcc No includes:
|
||||
.Bl -tag -width indent -compact -offset Ds
|
||||
.It Fl f Ns Cm ftepp
|
||||
.It Fl f Ns Cm translatable-strings
|
||||
.It Fl f Ns Cm assign-function-types
|
||||
.It Fl W Ns Cm ternary-precedence
|
||||
.It Fl fno- Ns Cm adjust-vector-fields
|
||||
.It Fl fno- Ns Cm correct-ternary
|
||||
.El
|
||||
.It Fl -add-info
|
||||
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
|
||||
parameter.
|
||||
.El
|
||||
.It Fl -correct , Fl -no-correct
|
||||
When enabled, errors about undefined values try to suggest an existing
|
||||
value via spell checking.
|
||||
.It Fl dump
|
||||
DEBUG OPTION. Print the code's intermediate representation before the
|
||||
optimization and finalization passes to stdout before generating the
|
||||
binary.
|
||||
.It Fl dumpfin
|
||||
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
|
||||
.It Fl W Ns Cm unused-variable
|
||||
Generate a warning about variables which are declared but never used.
|
||||
This can be avoided by adding the
|
||||
.Ql noref
|
||||
keyword in front of the
|
||||
variable declaration. Additionally a complete section of unreferenced
|
||||
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
|
||||
necessarily reliable if the initialization happens only under certain
|
||||
conditions. The other way is
|
||||
.Em not
|
||||
possible: that the warning is
|
||||
.Em not
|
||||
generated when uninitialized use
|
||||
.Em is
|
||||
possible.
|
||||
.It Fl W Ns Cm unknown-control-sequence
|
||||
Generate an error when an unrecognized control sequence in a string is
|
||||
used. Meaning: when there's a character after a backslash in a string
|
||||
which has no known meaning.
|
||||
.It Fl W Ns Cm extensions
|
||||
Warn when using special extensions which are not part of the selected
|
||||
standard.
|
||||
.It Fl W Ns Cm field-redeclared
|
||||
Generally QC compilers ignore redeclaration of fields. Here you can
|
||||
optionally enable a warning.
|
||||
.It Fl W Ns Cm missing-return-values
|
||||
Functions which aren't of type
|
||||
.Ft void
|
||||
will warn if it possible to
|
||||
reach the end without returning an actual value.
|
||||
.It Fl W Ns Cm invalid-parameter-count
|
||||
Warn about a function call with an invalid number of parameters.
|
||||
.It Fl W Ns Cm local-shadows
|
||||
Warn when a locally declared variable shadows variable.
|
||||
.It Fl W Ns Cm local-constants
|
||||
Warn when the initialization of a local variable turns the variable
|
||||
into a constant. This is default behaviour unless
|
||||
.Fl f Ns Cm initialized-nonconstants
|
||||
is used.
|
||||
.It Fl W Ns Cm void-variables
|
||||
There are only 2 known global variables of type void:
|
||||
.Ql end_sys_globals
|
||||
and
|
||||
.Ql end_sys_fields Ns .
|
||||
Any other void-variable will warn.
|
||||
.It Fl W Ns Cm implicit-function-pointer
|
||||
A global function which is not declared with the
|
||||
.Ql var
|
||||
keyword is
|
||||
expected to have an implementing body, or be a builtin. If neither is
|
||||
the case, it implicitly becomes a function pointer, and a warning is
|
||||
generated.
|
||||
.It Fl W Ns Cm variadic-function
|
||||
Currently there's no way for an in QC implemented function to access
|
||||
variadic parameters. If a function with variadic parameters has an
|
||||
implementing body, a warning will be generated.
|
||||
.It Fl W Ns Cm frame-macros
|
||||
Generate warnings about
|
||||
.Ql $frame
|
||||
commands, for instance about
|
||||
duplicate frame definitions.
|
||||
.It Fl W Ns Cm effectless-statement
|
||||
Warn about statements which have no effect. Any expression which does
|
||||
not call a function or assigns a variable.
|
||||
.It Fl W Ns Cm end-sys-fields
|
||||
The
|
||||
.Ql end_sys_fields
|
||||
variable is supposed to be a global variable
|
||||
of type
|
||||
.Ft void Ns .
|
||||
It is also recognized as a \fIfield\fR but this
|
||||
will generate a warning.
|
||||
.It Fl W Ns Cm assign-function-types
|
||||
Warn when assigning to a function pointer with an unmatching
|
||||
signature. This usually happens in cases like assigning the null
|
||||
function to an entity's .think function pointer.
|
||||
.It Fl W Ns Cm cpp
|
||||
Show warnings created using the preprocessor's '#warning' directive.
|
||||
.It Fl W Ns Cm multifile-if
|
||||
Warn if there's a preprocessor \fI#if\fR spanning across several
|
||||
files.
|
||||
.It Fl W Ns Cm double-declaration
|
||||
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.
|
||||
.It Fl W Ns Cm const-var
|
||||
The combination of \fIconst\fR and \fIvar\fR is not illegal, however
|
||||
different compilers may handle them differently. We were told, the
|
||||
intention is to create a function-pointer which is not assignable.
|
||||
This is exactly how we interpret it. However for this interpretation
|
||||
the
|
||||
.Ql var
|
||||
keyword is considered superfluous (and philosophically
|
||||
wrong), so it is possible to generate a warning about this.
|
||||
.It Fl W Ns Cm multibyte-character
|
||||
Warn about multibyte character constants, they do not work right now.
|
||||
.It Fl W Ns Cm ternary-precedence
|
||||
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
|
||||
.Fl f Ns Cm correct-ternary
|
||||
option.
|
||||
.It Fl W Ns Cm unknown-pragmas
|
||||
Warn when encountering an unrecognized
|
||||
.Ql #pragma
|
||||
line.
|
||||
.It Fl W Ns Cm unreachable-code
|
||||
Warn about unreachable code. That is: code after a return statement,
|
||||
or code after a call to a function marked as 'noreturn'.
|
||||
.It Fl W Ns Cm debug
|
||||
Enable some warnings added in order to help debugging in the compiler.
|
||||
You won't need this.
|
||||
.It Fl W Ns Cm unknown-attribute
|
||||
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.
|
||||
.It Fl W Ns Cm reserved-names
|
||||
Warn when using reserved names such as
|
||||
.Ql nil Ns .
|
||||
.It Fl W Ns Cm uninitialized-constant
|
||||
Warn about global constants (using the
|
||||
.Ql const
|
||||
keyword) with no
|
||||
assigned value.
|
||||
.It Fl W Ns Cm uninitialized-global
|
||||
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.
|
||||
.It Fl W Ns Cm different-qualifiers
|
||||
Warn when a variables is redeclared with a different qualifier. For
|
||||
example when redeclaring a variable as \'var\' which was previously
|
||||
marked \'const\'.
|
||||
.It Fl W Ns Cm different-attributes
|
||||
Similar to the above but for attributes like
|
||||
.Ql [[noreturn]] Ns .
|
||||
.It Fl W Ns Cm deprecated
|
||||
Warn when a function is marked with the attribute
|
||||
"[[deprecated]]". This flag enables a warning on calls to functions
|
||||
marked as such.
|
||||
.It Fl W Ns Cm parenthesis
|
||||
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
|
||||
.It Fl f Ns Cm darkplaces-string-table-bug
|
||||
Add some additional characters to the string table in order to
|
||||
compensate for a wrong boundcheck in some specific version of the
|
||||
darkplaces engine.
|
||||
.It Fl f Ns Cm adjust-vector-fields
|
||||
When assigning to field pointers of type \fI.vector\fR the common
|
||||
behaviour in compilers like \fIfteqcc\fR is to only assign the
|
||||
x-component 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.
|
||||
.It Fl f Ns Cm ftepp
|
||||
Enable a partially fteqcc-compatible preprocessor. It supports all the
|
||||
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
|
||||
following macros will be added:
|
||||
.Bd -literal -offset indent
|
||||
__LINE__
|
||||
__FILE__
|
||||
__COUNTER__
|
||||
__COUNTER_LAST__
|
||||
__RANDOM__
|
||||
__RANDOM_LAST__
|
||||
__DATE__
|
||||
__TIME__
|
||||
__FUNC__
|
||||
.Ed
|
||||
.Pp
|
||||
Note that
|
||||
.Li __FUNC__
|
||||
is not actually a preprocessor macro, but is recognized by the parser
|
||||
even with the preprocessor disabled.
|
||||
.Pp
|
||||
Note that fteqcc also defines
|
||||
.Li __NULL__
|
||||
which becomes the first global. Assigning it to a vector does not
|
||||
yield the same result as in gmqcc where
|
||||
.Li __NULL__
|
||||
is defined to
|
||||
.Li nil
|
||||
(See
|
||||
.Fl f Ns Cm untyped-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 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
|
||||
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 possibilities.
|
||||
.It Fl f Ns Cm perl-logic
|
||||
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
|
||||
.Ql true && 5
|
||||
evaluates to 5 rather than 1.
|
||||
.It Fl f Ns Cm translatable-strings
|
||||
Enable the underscore intrinsic: Using
|
||||
.Ql _("A string constant")
|
||||
will cause the string immediate to get a name with a "dotranslate_"
|
||||
prefix. The darkplaces engine recognizes these and translates them in
|
||||
a way similar to how gettext works.
|
||||
.It Fl f Ns Cm initialized-nonconstants
|
||||
Don't implicitly convert initialized variables to constants. With this
|
||||
flag, the \fIconst\fR keyword is required to make a constant.
|
||||
.It Fl f Ns Cm assign-function-types
|
||||
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.
|
||||
.It Fl f Ns Cm lno
|
||||
Produce a linenumber file along with the output .dat file.
|
||||
.It Fl f Ns Cm correct-ternary
|
||||
Use C's operator precedence for ternary expressions. Unless your code
|
||||
depends on fteqcc-compatible behaviour, you'll want to use thi
|
||||
soption.
|
||||
.It Fl f Ns Cm single-vector-defs
|
||||
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.
|
||||
.It Fl f Ns Cm correct-logic
|
||||
Most QC compilers translate
|
||||
.Ql 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.
|
||||
.Bd -literal -offset indent
|
||||
if (a_vector) // becomes
|
||||
if not(!a_vector)
|
||||
// likewise
|
||||
a = a_vector && a_float // becomes
|
||||
a = !!a_vector && a_float
|
||||
.Ed
|
||||
.It Fl f Ns Cm true-empty-strings
|
||||
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.
|
||||
.It Fl f Ns Cm false-empty-strings
|
||||
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.
|
||||
.It Fl f Ns Cm utf8
|
||||
Enable utf8 characters. This allows utf-8 encoded character constants,
|
||||
and escape sequence codepoints in the valid utf-8 range. Effectively
|
||||
enabling escape sequences like '\\{x2211}'.
|
||||
.It Fl f Ns Cm bail-on-werror
|
||||
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.
|
||||
.It Fl f Ns Cm loop-labels
|
||||
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.
|
||||
.Bd -literal -offset indent
|
||||
for :outer (i = 0; i < n; ++i) {
|
||||
while (inner) {
|
||||
...;
|
||||
if (something)
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
.Ed
|
||||
.It Fl f Ns Cm untyped-nil
|
||||
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.
|
||||
.sp
|
||||
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.
|
||||
.sp
|
||||
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.
|
||||
.It Fl f Ns Cm permissive
|
||||
Various effects, usually to weaken some conditions.
|
||||
.Bl -tag -width indent -offset indent
|
||||
.It with Fl f Ns Cm untyped-nil
|
||||
Allow local variables named
|
||||
.Ql nil Ns .
|
||||
(This will not allow declaring a global of that name.)
|
||||
.El
|
||||
.It Fl f Ns Cm variadic-args
|
||||
Allow variadic parameters to be accessed by QC code. This can be
|
||||
achieved via the '...' function, which takes a parameter index and a
|
||||
typename.
|
||||
.Pp
|
||||
Example:
|
||||
.Bd -literal -offset indent
|
||||
void vafunc(string...count) {
|
||||
float i;
|
||||
for (i = 0; i < count; ++i)
|
||||
print(...(i, string), "\\n");
|
||||
}
|
||||
.Ed
|
||||
.It Fl f Ns Cm legacy-vector-maths
|
||||
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.
|
||||
.It Fl f Ns Cm expressions-for-builtins
|
||||
Usually builtin-numbers are just immediate constants. With this flag
|
||||
expressions can be used, as long as they are compile-time constant.
|
||||
.Pp
|
||||
Example:
|
||||
.Bd -literal -offset indent
|
||||
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
|
||||
.It Fl O Ns Cm peephole
|
||||
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.
|
||||
.It Fl O Ns Cm tail-recursion
|
||||
Tail recursive function calls will be turned into loops to avoid the
|
||||
overhead of the CALL and RETURN instructions.
|
||||
.It Fl O Ns Cm overlap-locals
|
||||
Make all functions which use neither local arrays nor have locals
|
||||
which are seen as possibly uninitialized use the same local section.
|
||||
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 initializing it via a loop, we assume
|
||||
functions with arrays to be too dangerous for this optimization.
|
||||
.It Fl O Ns Cm local-temps
|
||||
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.
|
||||
.It Fl O Ns Cm global-temps
|
||||
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 execute faster.
|
||||
.It Fl O Ns Cm strip-constant-names
|
||||
Don't generate defs for immediate values or even declared constants.
|
||||
Meaning variables which are implicitly constant or qualified as such
|
||||
using the 'const' keyword.
|
||||
.It Fl O Ns Cm overlap-strings
|
||||
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.
|
||||
.Pp
|
||||
For example the following code will only generate 1 string:
|
||||
.Bd -literal -offset indent
|
||||
print("Hello you!\\n");
|
||||
print("you!\\n"); // trailing substring of "Hello you!\\n"
|
||||
.Ed
|
||||
.Pp
|
||||
There's however one limitation. Strings are still processed in order,
|
||||
so if the above print statements were reversed, this optimization
|
||||
would not happen.
|
||||
.It Fl O Ns Cm call-stores
|
||||
By default, all parameters of a CALL are copied into the
|
||||
parameter-globals right before the CALL instructions. This is the
|
||||
easiest and safest way to translate calls, but also adds a lot of
|
||||
unnecessary copying and unnecessary temporary values. This
|
||||
optimization 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.
|
||||
.It Fl O Ns Cm void-return
|
||||
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.
|
||||
.It Fl O Ns Cm vector-components
|
||||
Because traditional QC code doesn't allow you to access individual
|
||||
vector components of a computed vector without storing it in a local
|
||||
first, sometimes people multiply it by a constant like
|
||||
.Ql '0 1 0'
|
||||
to get,
|
||||
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
|
||||
start with hashtags or semicolons, sections are written in square
|
||||
brackets and in each section there can be arbitrary many key-value
|
||||
pairs.
|
||||
.Pp
|
||||
There are 3 sections currently:
|
||||
.Ql flags Ns ,
|
||||
.Ql warnings Ns ,
|
||||
.Ql optimizations Ns .
|
||||
They contain a list of boolean values of the form
|
||||
.Ql VARNAME = true
|
||||
or
|
||||
.Ql VARNAME = false Ns .
|
||||
The variable names are the same as for the
|
||||
corresponding
|
||||
.Fl W , Fl f
|
||||
or
|
||||
.Fl O
|
||||
flag written with only capital letters and
|
||||
dashes replaced by underscores.
|
||||
.Pp
|
||||
Here's an example:
|
||||
.Bd -literal -offset indent
|
||||
# a GMQCC configuration file
|
||||
[flags]
|
||||
FTEPP = true
|
||||
ADJUST_VECTOR_FIELDS = false
|
||||
LNO = true
|
||||
|
||||
[warnings]
|
||||
UNUSED_VARIABLE = false
|
||||
USED_UNINITIALIZED = true
|
||||
|
||||
[optimizations]
|
||||
PEEPHOLE = true
|
||||
TAIL_RECURSION = true
|
||||
.Ed
|
||||
.Sh FILES
|
||||
.Bl -tag -width Ds
|
||||
.It gmqcc.ini.example
|
||||
A documented example for a gmqcc.ini file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr qcvm 1
|
||||
.Sh AUTHOR
|
||||
See <http://graphitemaster.github.com/gmqcc>.
|
||||
.Sh BUGS
|
||||
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>,
|
||||
or see <http://graphitemaster.github.com/gmqcc> on how to contact us.
|
||||
|
|
107
doc/qcvm.1
Normal file
107
doc/qcvm.1
Normal file
|
@ -0,0 +1,107 @@
|
|||
.\" qcvm mdoc manpage
|
||||
.Dd January 31, 2013
|
||||
.Dt QCVM 1 PRM
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm qcvm
|
||||
.Nd A standalone QuakeC VM binary executor
|
||||
.Sh SYNOPSIS
|
||||
.Nm qcvm
|
||||
.Op Cm options
|
||||
.Op Cm parameters
|
||||
.Ar program-file
|
||||
.Sh DESCRIPTION
|
||||
.Nm qcvm
|
||||
is an executor for QuakeC VM binary files created using a QC
|
||||
compiler such as gmqcc(1) or fteqcc. It provides a small set of
|
||||
builtin functions, and by default executes the
|
||||
.Fn main
|
||||
function if there is one. Some options useful for debugging are
|
||||
available as well.
|
||||
.Sh OPTIONS
|
||||
There are 2 types of options. Options for the executor, and parameter
|
||||
options used to add parameters which are passed to the main function
|
||||
on execution.
|
||||
.Bl -tag -width Ds
|
||||
.It Fl h , Fl -help
|
||||
Show a usage message and exit.
|
||||
.It Fl trace
|
||||
Trace the execution. Each instruction will be printed to stdout before
|
||||
executing it.
|
||||
.It Fl profile
|
||||
Perform some profiling. This is currently not really implemented, the
|
||||
option is available nonetheless.
|
||||
.It Fl info
|
||||
Print information from the program's header instead of executing.
|
||||
.It Fl disasm
|
||||
Disassemble the program by function instead of executing.
|
||||
.It Fl disasm-func Ar function
|
||||
Search for and disassemble the given function.
|
||||
.It Fl printdefs
|
||||
List all entries from the program's defs-section. Effectively
|
||||
listing all the global variables of the program.
|
||||
This option disables execution.
|
||||
.It Fl printfields
|
||||
List all entries from the program's fields-section. Listing all
|
||||
entity-fields declared in the program.
|
||||
This option disables execution.
|
||||
.It Fl printfuns
|
||||
List functions and some information about their parameters.
|
||||
This option disables execution. With a verbosity level of 1, builtin
|
||||
numbers are printed. With a verbosity of 2, the function's sizes are
|
||||
printed as well. This takes a little longer since the size is found by
|
||||
searching for a
|
||||
.Ql DONE
|
||||
instruction in the code.
|
||||
.It Fl v
|
||||
Increase verbosity level, can be used multiple times.
|
||||
.It Fl vector Ar 'x y z'
|
||||
Append a vector parameter to be passed to
|
||||
.Fn main Ns .
|
||||
.It Fl float Ar number
|
||||
Append a float parameter to be passed to
|
||||
.Fn main Ns .
|
||||
.It Fl string Ar 'text'
|
||||
Append a string parameter to be passed to
|
||||
.Fn main Ns .
|
||||
.El
|
||||
.Sh BUILTINS
|
||||
The following builtin functions are available:
|
||||
.Bl -ohang
|
||||
.It Li 1) void print(string...) = #1;
|
||||
.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;
|
||||
.D1 Spawn an entity.
|
||||
.It Li 4) void remove(entity) = #4;
|
||||
.D1 Remove an entity.
|
||||
.It Li 5) string vtos(vector) = #5;
|
||||
.D1 Convert a vector to a string.
|
||||
.It Li 6) void error(string...) = #6;
|
||||
.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;
|
||||
.D1 Get the entity ID as string.
|
||||
.It Li 9) float stof(string) = #9;
|
||||
.D1 Convert a string to a float.
|
||||
.It Li 10) string strcat(string, string) = #10;
|
||||
.D1 Concatenate two strings, returning a tempstring.
|
||||
.It Li 11) float strcmp(string, string) = #11;
|
||||
.Li 12) float strncmp(string, string, float) = #11;
|
||||
.D1 Compare two strings. Returns the same as the corresponding C functions.
|
||||
.It Li 12) vector normalize(vector) = #12;
|
||||
.D1 Normalize a vector so its length is 1.
|
||||
.It Li 13) float sqrt(float) = #13;
|
||||
.D1 Get a value's square root.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr gmqcc 1
|
||||
.Sh AUTHOR
|
||||
See <http://graphitemaster.github.com/gmqcc>.
|
||||
.Sh BUGS
|
||||
Please report bugs on <http://github.com/graphitemaster/gmqcc/issues>,
|
||||
or see <http://graphitemaster.github.com/gmqcc> on how to contact us.
|
70
error.c
70
error.c
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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"
|
||||
|
||||
/*
|
||||
* Compiler error system, this handles the error printing, and managing
|
||||
* such as after so many errors just stop the compilation, and other
|
||||
* intereting like colors for the console.
|
||||
*/
|
||||
|
||||
#ifndef WIN32
|
||||
int levelcolor[] = {
|
||||
CON_WHITE,
|
||||
CON_CYAN,
|
||||
CON_RED
|
||||
};
|
||||
#endif
|
||||
|
||||
void vprintmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap)
|
||||
{
|
||||
#ifndef WIN32
|
||||
fprintf (stderr, "\033[0;%dm%s:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, levelcolor[level], msgtype);
|
||||
#else
|
||||
fprintf (stderr, "%s:%d: %s: ", name, line, msgtype);
|
||||
#endif
|
||||
vfprintf(stderr, msg, ap);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
|
||||
void printmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
vprintmsg(level, name, line, msgtype, msg, va);
|
||||
va_end (va);
|
||||
}
|
||||
|
||||
void cvprintmsg(lex_ctx ctx, int lvl, const char *msgtype, const char *msg, va_list ap)
|
||||
{
|
||||
vprintmsg(lvl, ctx.file, ctx.line, msgtype, msg, ap);
|
||||
}
|
||||
|
||||
void cprintmsg (lex_ctx ctx, int lvl, const char *msgtype, const char *msg, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, msg);
|
||||
cvprintmsg(ctx, lvl, msgtype, msg, va);
|
||||
va_end (va);
|
||||
}
|
947
exec.c
947
exec.c
|
@ -1,947 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "gmqcc.h"
|
||||
|
||||
MEM_VEC_FUNCTIONS(qc_program, prog_section_statement, code)
|
||||
MEM_VEC_FUNCTIONS(qc_program, prog_section_def, defs)
|
||||
MEM_VEC_FUNCTIONS(qc_program, prog_section_def, fields)
|
||||
MEM_VEC_FUNCTIONS(qc_program, prog_section_function, functions)
|
||||
MEM_VEC_FUNCTIONS(qc_program, char, strings)
|
||||
MEM_VEC_FUN_APPEND(qc_program, char, strings)
|
||||
MEM_VEC_FUN_RESIZE(qc_program, char, strings)
|
||||
MEM_VEC_FUNCTIONS(qc_program, qcint, globals)
|
||||
MEM_VEC_FUNCTIONS(qc_program, qcint, entitydata)
|
||||
MEM_VEC_FUNCTIONS(qc_program, bool, entitypool)
|
||||
|
||||
MEM_VEC_FUNCTIONS(qc_program, qcint, localstack)
|
||||
MEM_VEC_FUN_APPEND(qc_program, qcint, localstack)
|
||||
MEM_VEC_FUN_RESIZE(qc_program, qcint, localstack)
|
||||
MEM_VEC_FUNCTIONS(qc_program, qc_exec_stack, stack)
|
||||
|
||||
MEM_VEC_FUNCTIONS(qc_program, size_t, profile)
|
||||
MEM_VEC_FUN_RESIZE(qc_program, size_t, profile)
|
||||
|
||||
MEM_VEC_FUNCTIONS(qc_program, prog_builtin, builtins)
|
||||
|
||||
static void loaderror(const char *fmt, ...)
|
||||
{
|
||||
int err = errno;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
printf(": %s\n", strerror(err));
|
||||
}
|
||||
|
||||
static void qcvmerror(qc_program *prog, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
prog->vmerror++;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
qc_program* prog_load(const char *filename)
|
||||
{
|
||||
qc_program *prog;
|
||||
prog_header header;
|
||||
size_t i;
|
||||
FILE *file;
|
||||
|
||||
file = util_fopen(filename, "rb");
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
if (fread(&header, sizeof(header), 1, file) != 1) {
|
||||
loaderror("failed to read header from '%s'", filename);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (header.version != 6) {
|
||||
loaderror("header says this is a version %i progs, we need version 6\n", header.version);
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prog = (qc_program*)mem_a(sizeof(qc_program));
|
||||
if (!prog) {
|
||||
fclose(file);
|
||||
printf("failed to allocate program data\n");
|
||||
return NULL;
|
||||
}
|
||||
memset(prog, 0, sizeof(*prog));
|
||||
|
||||
prog->entityfields = header.entfield;
|
||||
prog->crc16 = header.crc16;
|
||||
|
||||
prog->filename = util_strdup(filename);
|
||||
if (!prog->filename) {
|
||||
loaderror("failed to store program name");
|
||||
goto error;
|
||||
}
|
||||
|
||||
#define read_data(hdrvar, progvar, type) \
|
||||
if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) { \
|
||||
loaderror("seek failed"); \
|
||||
goto error; \
|
||||
} \
|
||||
prog->progvar##_alloc = header.hdrvar.length; \
|
||||
prog->progvar##_count = header.hdrvar.length; \
|
||||
prog->progvar = (type*)mem_a(header.hdrvar.length * sizeof(*prog->progvar)); \
|
||||
if (!prog->progvar) \
|
||||
goto error; \
|
||||
if (fread(prog->progvar, sizeof(*prog->progvar), header.hdrvar.length, file) \
|
||||
!= header.hdrvar.length) { \
|
||||
loaderror("read failed"); \
|
||||
goto error; \
|
||||
}
|
||||
#define read_data1(x, y) read_data(x, x, y)
|
||||
|
||||
read_data (statements, code, prog_section_statement);
|
||||
read_data1(defs, prog_section_def);
|
||||
read_data1(fields, prog_section_def);
|
||||
read_data1(functions, prog_section_function);
|
||||
read_data1(strings, char);
|
||||
read_data1(globals, qcint);
|
||||
|
||||
fclose(file);
|
||||
|
||||
/* profile counters */
|
||||
if (!qc_program_profile_resize(prog, prog->code_count))
|
||||
goto error;
|
||||
|
||||
/* Add tempstring area */
|
||||
prog->tempstring_start = prog->strings_count;
|
||||
prog->tempstring_at = prog->strings_count;
|
||||
if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
|
||||
goto error;
|
||||
|
||||
/* spawn the world entity */
|
||||
if (!qc_program_entitypool_add(prog, true)) {
|
||||
loaderror("failed to allocate world entity\n");
|
||||
goto error;
|
||||
}
|
||||
for (i = 0; i < prog->entityfields; ++i) {
|
||||
if (!qc_program_entitydata_add(prog, 0)) {
|
||||
loaderror("failed to allocate world data\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
prog->entities = 1;
|
||||
|
||||
return prog;
|
||||
|
||||
error:
|
||||
if (prog->filename) mem_d(prog->filename);
|
||||
if (prog->code) mem_d(prog->code);
|
||||
if (prog->defs) mem_d(prog->defs);
|
||||
if (prog->fields) mem_d(prog->fields);
|
||||
if (prog->functions) mem_d(prog->functions);
|
||||
if (prog->strings) mem_d(prog->strings);
|
||||
if (prog->globals) mem_d(prog->globals);
|
||||
if (prog->entitydata) mem_d(prog->entitydata);
|
||||
if (prog->entitypool) mem_d(prog->entitypool);
|
||||
mem_d(prog);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void prog_delete(qc_program *prog)
|
||||
{
|
||||
if (prog->filename) mem_d(prog->filename);
|
||||
MEM_VECTOR_CLEAR(prog, code);
|
||||
MEM_VECTOR_CLEAR(prog, defs);
|
||||
MEM_VECTOR_CLEAR(prog, fields);
|
||||
MEM_VECTOR_CLEAR(prog, functions);
|
||||
MEM_VECTOR_CLEAR(prog, strings);
|
||||
MEM_VECTOR_CLEAR(prog, globals);
|
||||
MEM_VECTOR_CLEAR(prog, entitydata);
|
||||
MEM_VECTOR_CLEAR(prog, entitypool);
|
||||
MEM_VECTOR_CLEAR(prog, localstack);
|
||||
MEM_VECTOR_CLEAR(prog, stack);
|
||||
MEM_VECTOR_CLEAR(prog, profile);
|
||||
|
||||
if (prog->builtins_alloc) {
|
||||
MEM_VECTOR_CLEAR(prog, builtins);
|
||||
}
|
||||
/* otherwise the builtins were statically allocated */
|
||||
mem_d(prog);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* VM code
|
||||
*/
|
||||
|
||||
char* prog_getstring(qc_program *prog, qcint str)
|
||||
{
|
||||
if (str < 0 || str >= prog->strings_count)
|
||||
return "<<<invalid string>>>";
|
||||
return prog->strings + str;
|
||||
}
|
||||
|
||||
prog_section_def* prog_entfield(qc_program *prog, qcint off)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < prog->fields_count; ++i) {
|
||||
if (prog->fields[i].offset == off)
|
||||
return (prog->fields + i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prog_section_def* prog_getdef(qc_program *prog, qcint off)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < prog->defs_count; ++i) {
|
||||
if (prog->defs[i].offset == off)
|
||||
return (prog->defs + i);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qcany* prog_getedict(qc_program *prog, qcint e)
|
||||
{
|
||||
if (e >= prog->entitypool_count) {
|
||||
prog->vmerror++;
|
||||
printf("Accessing out of bounds edict %i\n", (int)e);
|
||||
e = 0;
|
||||
}
|
||||
return (qcany*)(prog->entitydata + (prog->entityfields * e));
|
||||
}
|
||||
|
||||
qcint prog_spawn_entity(qc_program *prog)
|
||||
{
|
||||
char *data;
|
||||
size_t i;
|
||||
qcint e;
|
||||
for (e = 0; e < (qcint)prog->entitypool_count; ++e) {
|
||||
if (!prog->entitypool[e]) {
|
||||
data = (char*)(prog->entitydata + (prog->entityfields * e));
|
||||
memset(data, 0, prog->entityfields * sizeof(qcint));
|
||||
return e;
|
||||
}
|
||||
}
|
||||
if (!qc_program_entitypool_add(prog, true)) {
|
||||
prog->vmerror++;
|
||||
printf("Failed to allocate entity\n");
|
||||
return 0;
|
||||
}
|
||||
prog->entities++;
|
||||
for (i = 0; i < prog->entityfields; ++i) {
|
||||
if (!qc_program_entitydata_add(prog, 0)) {
|
||||
printf("Failed to allocate entity\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
data = (char*)(prog->entitydata + (prog->entityfields * e));
|
||||
memset(data, 0, prog->entityfields * sizeof(qcint));
|
||||
return e;
|
||||
}
|
||||
|
||||
void prog_free_entity(qc_program *prog, qcint e)
|
||||
{
|
||||
if (!e) {
|
||||
prog->vmerror++;
|
||||
printf("Trying to free world entity\n");
|
||||
return;
|
||||
}
|
||||
if (e >= prog->entitypool_count) {
|
||||
prog->vmerror++;
|
||||
printf("Trying to free out of bounds entity\n");
|
||||
return;
|
||||
}
|
||||
if (!prog->entitypool[e]) {
|
||||
prog->vmerror++;
|
||||
printf("Double free on entity\n");
|
||||
return;
|
||||
}
|
||||
prog->entitypool[e] = false;
|
||||
}
|
||||
|
||||
qcint prog_tempstring(qc_program *prog, const char *_str)
|
||||
{
|
||||
/* we don't access it, but the macro-generated functions don't use
|
||||
* const
|
||||
*/
|
||||
char *str = (char*)_str;
|
||||
|
||||
size_t len = strlen(str);
|
||||
size_t at = prog->tempstring_at;
|
||||
|
||||
/* when we reach the end we start over */
|
||||
if (at + len >= prog->strings_count)
|
||||
at = prog->tempstring_start;
|
||||
|
||||
/* when it doesn't fit, reallocate */
|
||||
if (at + len >= prog->strings_count)
|
||||
{
|
||||
prog->strings_count = at;
|
||||
if (!qc_program_strings_append(prog, str, len+1)) {
|
||||
prog->vmerror = VMERR_TEMPSTRING_ALLOC;
|
||||
return 0;
|
||||
}
|
||||
return at;
|
||||
}
|
||||
|
||||
/* when it fits, just copy */
|
||||
memcpy(prog->strings + at, str, len+1);
|
||||
prog->tempstring_at += len+1;
|
||||
return at;
|
||||
}
|
||||
|
||||
static int print_escaped_string(const char *str, size_t maxlen)
|
||||
{
|
||||
int len = 2;
|
||||
putchar('"');
|
||||
--maxlen; /* because we're lazy and have escape sequences */
|
||||
while (*str) {
|
||||
if (len >= maxlen) {
|
||||
putchar('.');
|
||||
putchar('.');
|
||||
putchar('.');
|
||||
len += 3;
|
||||
break;
|
||||
}
|
||||
switch (*str) {
|
||||
case '\a': len += 2; putchar('\\'); putchar('a'); break;
|
||||
case '\b': len += 2; putchar('\\'); putchar('b'); break;
|
||||
case '\r': len += 2; putchar('\\'); putchar('r'); break;
|
||||
case '\n': len += 2; putchar('\\'); putchar('n'); break;
|
||||
case '\t': len += 2; putchar('\\'); putchar('t'); break;
|
||||
case '\f': len += 2; putchar('\\'); putchar('f'); break;
|
||||
case '\v': len += 2; putchar('\\'); putchar('v'); break;
|
||||
case '\\': len += 2; putchar('\\'); putchar('\\'); break;
|
||||
case '"': len += 2; putchar('\\'); putchar('"'); break;
|
||||
default:
|
||||
++len;
|
||||
putchar(*str);
|
||||
break;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
putchar('"');
|
||||
return len;
|
||||
}
|
||||
|
||||
static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
|
||||
{
|
||||
static char spaces[28+1] = " ";
|
||||
prog_section_def *def;
|
||||
qcany *value;
|
||||
int len;
|
||||
|
||||
if (!glob) {
|
||||
len = printf("<null>,");
|
||||
goto done;
|
||||
}
|
||||
|
||||
def = prog_getdef(prog, glob);
|
||||
value = (qcany*)(&prog->globals[glob]);
|
||||
|
||||
if (def) {
|
||||
const char *name = prog_getstring(prog, def->name);
|
||||
if (name[0] == '#')
|
||||
len = printf("$");
|
||||
else
|
||||
len = printf("%s ", name);
|
||||
vtype = def->type;
|
||||
}
|
||||
else
|
||||
len = printf("[@%u] ", glob);
|
||||
|
||||
switch (vtype) {
|
||||
case TYPE_VOID:
|
||||
case TYPE_ENTITY:
|
||||
case TYPE_FIELD:
|
||||
case TYPE_FUNCTION:
|
||||
case TYPE_POINTER:
|
||||
len += printf("(%i),", value->_int);
|
||||
break;
|
||||
case TYPE_VECTOR:
|
||||
len += printf("'%g %g %g',", value->vector[0],
|
||||
value->vector[1],
|
||||
value->vector[2]);
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
len += print_escaped_string(prog_getstring(prog, value->string), sizeof(spaces)-len-5);
|
||||
len += printf(",");
|
||||
/* len += printf("\"%s\",", prog_getstring(prog, value->string)); */
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
default:
|
||||
len += printf("%g,", value->_float);
|
||||
break;
|
||||
}
|
||||
done:
|
||||
if (len < sizeof(spaces)-1) {
|
||||
spaces[sizeof(spaces)-1-len] = 0;
|
||||
printf(spaces);
|
||||
spaces[sizeof(spaces)-1-len] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
static void prog_print_statement(qc_program *prog, prog_section_statement *st)
|
||||
{
|
||||
if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
|
||||
printf("<illegal instruction %d>\n", st->opcode);
|
||||
return;
|
||||
}
|
||||
printf(" <> %-12s", asm_instr[st->opcode].m);
|
||||
if (st->opcode >= INSTR_IF &&
|
||||
st->opcode <= INSTR_IFNOT)
|
||||
{
|
||||
trace_print_global(prog, st->o1.u1, TYPE_FLOAT);
|
||||
printf("%d\n", st->o2.s1);
|
||||
}
|
||||
else if (st->opcode >= INSTR_CALL0 &&
|
||||
st->opcode <= INSTR_CALL8)
|
||||
{
|
||||
printf("\n");
|
||||
}
|
||||
else if (st->opcode == INSTR_GOTO)
|
||||
{
|
||||
printf("%i\n", st->o1.s1);
|
||||
}
|
||||
else
|
||||
{
|
||||
int t[3] = { TYPE_FLOAT, TYPE_FLOAT, TYPE_FLOAT };
|
||||
switch (st->opcode)
|
||||
{
|
||||
case INSTR_MUL_FV:
|
||||
t[1] = t[2] = TYPE_VECTOR;
|
||||
break;
|
||||
case INSTR_MUL_VF:
|
||||
t[0] = t[2] = TYPE_VECTOR;
|
||||
break;
|
||||
case INSTR_MUL_V:
|
||||
t[0] = t[1] = TYPE_VECTOR;
|
||||
break;
|
||||
case INSTR_ADD_V:
|
||||
case INSTR_SUB_V:
|
||||
case INSTR_EQ_V:
|
||||
case INSTR_NE_V:
|
||||
t[0] = t[1] = t[2] = TYPE_VECTOR;
|
||||
break;
|
||||
case INSTR_EQ_S:
|
||||
case INSTR_NE_S:
|
||||
t[0] = t[1] = TYPE_STRING;
|
||||
break;
|
||||
case INSTR_STORE_F:
|
||||
case INSTR_STOREP_F:
|
||||
t[2] = -1;
|
||||
break;
|
||||
case INSTR_STORE_V:
|
||||
t[0] = t[1] = TYPE_VECTOR; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STORE_S:
|
||||
t[0] = t[1] = TYPE_STRING; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STORE_ENT:
|
||||
t[0] = t[1] = TYPE_ENTITY; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STORE_FLD:
|
||||
t[0] = t[1] = TYPE_FIELD; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STORE_FNC:
|
||||
t[0] = t[1] = TYPE_FUNCTION; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STOREP_V:
|
||||
t[0] = TYPE_VECTOR; t[1] = TYPE_ENTITY; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STOREP_S:
|
||||
t[0] = TYPE_STRING; t[1] = TYPE_ENTITY; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STOREP_ENT:
|
||||
t[0] = TYPE_ENTITY; t[1] = TYPE_ENTITY; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STOREP_FLD:
|
||||
t[0] = TYPE_FIELD; t[1] = TYPE_ENTITY; t[2] = -1;
|
||||
break;
|
||||
case INSTR_STOREP_FNC:
|
||||
t[0] = TYPE_FUNCTION; t[1] = TYPE_ENTITY; t[2] = -1;
|
||||
break;
|
||||
}
|
||||
if (t[0] >= 0) trace_print_global(prog, st->o1.u1, t[0]);
|
||||
else printf("(none), ");
|
||||
if (t[1] >= 0) trace_print_global(prog, st->o2.u1, t[1]);
|
||||
else printf("(none), ");
|
||||
if (t[2] >= 0) trace_print_global(prog, st->o3.u1, t[2]);
|
||||
else printf("(none)");
|
||||
printf("\n");
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
|
||||
{
|
||||
qc_exec_stack st;
|
||||
size_t p, parampos;
|
||||
|
||||
/* back up locals */
|
||||
st.localsp = prog->localstack_count;
|
||||
st.stmt = prog->statement;
|
||||
st.function = func;
|
||||
|
||||
#ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
|
||||
if (prog->stack_count)
|
||||
{
|
||||
prog_section_function *cur;
|
||||
cur = prog->stack[prog->stack_count-1].function;
|
||||
if (cur)
|
||||
{
|
||||
qcint *globals = prog->globals + cur->firstlocal;
|
||||
if (!qc_program_localstack_append(prog, globals, cur->locals))
|
||||
{
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
qcint *globals = prog->globals + func->firstlocal;
|
||||
if (!qc_program_localstack_append(prog, globals, func->locals))
|
||||
{
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* copy parameters */
|
||||
parampos = func->firstlocal;
|
||||
for (p = 0; p < func->nargs; ++p)
|
||||
{
|
||||
size_t s;
|
||||
for (s = 0; s < func->argsize[p]; ++s) {
|
||||
prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s];
|
||||
++parampos;
|
||||
}
|
||||
}
|
||||
|
||||
if (!qc_program_stack_add(prog, st)) {
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return func->entry;
|
||||
}
|
||||
|
||||
static qcint prog_leavefunction(qc_program *prog)
|
||||
{
|
||||
prog_section_function *prev = NULL;
|
||||
size_t oldsp;
|
||||
|
||||
qc_exec_stack st = prog->stack[prog->stack_count-1];
|
||||
|
||||
#ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS
|
||||
if (prog->stack_count > 1) {
|
||||
prev = prog->stack[prog->stack_count-2].function;
|
||||
oldsp = prog->stack[prog->stack_count-2].localsp;
|
||||
}
|
||||
#else
|
||||
prev = prog->stack[prog->stack_count-1].function;
|
||||
oldsp = prog->stack[prog->stack_count-1].localsp;
|
||||
#endif
|
||||
if (prev) {
|
||||
qcint *globals = prog->globals + prev->firstlocal;
|
||||
memcpy(globals, prog->localstack + oldsp, prev->locals);
|
||||
if (!qc_program_localstack_resize(prog, oldsp)) {
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!qc_program_stack_remove(prog, prog->stack_count-1)) {
|
||||
printf("out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return st.stmt - 1; /* offset the ++st */
|
||||
}
|
||||
|
||||
bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps)
|
||||
{
|
||||
long jumpcount = 0;
|
||||
size_t oldxflags = prog->xflags;
|
||||
prog_section_statement *st;
|
||||
|
||||
prog->vmerror = 0;
|
||||
prog->xflags = flags;
|
||||
|
||||
st = prog->code + prog_enterfunction(prog, func);
|
||||
--st;
|
||||
switch (flags)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
{
|
||||
#define QCVM_PROFILE 0
|
||||
#define QCVM_TRACE 0
|
||||
# include "execloop.h"
|
||||
break;
|
||||
}
|
||||
case (VMXF_TRACE):
|
||||
{
|
||||
#define QCVM_PROFILE 0
|
||||
#define QCVM_TRACE 1
|
||||
# include "execloop.h"
|
||||
break;
|
||||
}
|
||||
case (VMXF_PROFILE):
|
||||
{
|
||||
#define QCVM_PROFILE 1
|
||||
#define QCVM_TRACE 0
|
||||
# include "execloop.h"
|
||||
break;
|
||||
}
|
||||
case (VMXF_TRACE|VMXF_PROFILE):
|
||||
{
|
||||
#define QCVM_PROFILE 1
|
||||
#define QCVM_TRACE 1
|
||||
# include "execloop.h"
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
cleanup:
|
||||
prog->xflags = oldxflags;
|
||||
prog->localstack_count = 0;
|
||||
prog->stack_count = 0;
|
||||
if (prog->vmerror)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* main for when building the standalone executor
|
||||
*/
|
||||
|
||||
#if defined(QCVM_EXECUTOR)
|
||||
#include <math.h>
|
||||
|
||||
const char *type_name[TYPE_COUNT] = {
|
||||
"void",
|
||||
"string",
|
||||
"float",
|
||||
"vector",
|
||||
"entity",
|
||||
"field",
|
||||
"function",
|
||||
"pointer",
|
||||
#if 0
|
||||
"integer",
|
||||
#endif
|
||||
"variant"
|
||||
};
|
||||
|
||||
bool opts_debug = false;
|
||||
bool opts_memchk = false;
|
||||
|
||||
typedef struct {
|
||||
int vtype;
|
||||
const char *value;
|
||||
} qcvm_parameter;
|
||||
|
||||
VECTOR_MAKE(qcvm_parameter, main_params);
|
||||
|
||||
#define CheckArgs(num) do { \
|
||||
if (prog->argc != (num)) { \
|
||||
prog->vmerror++; \
|
||||
printf("ERROR: invalid number of arguments for %s: %i, expected %i\n", \
|
||||
__FUNCTION__, prog->argc, (num)); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define GetGlobal(idx) ((qcany*)(prog->globals + (idx)))
|
||||
#define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
|
||||
#define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
|
||||
|
||||
static int qc_print(qc_program *prog)
|
||||
{
|
||||
size_t i;
|
||||
const char *laststr = NULL;
|
||||
for (i = 0; i < prog->argc; ++i) {
|
||||
qcany *str = (qcany*)(prog->globals + OFS_PARM0 + 3*i);
|
||||
printf("%s", (laststr = prog_getstring(prog, str->string)));
|
||||
}
|
||||
if (laststr && (prog->xflags & VMXF_TRACE)) {
|
||||
size_t len = strlen(laststr);
|
||||
if (!len || laststr[len-1] != '\n')
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qc_error(qc_program *prog)
|
||||
{
|
||||
printf("*** VM raised an error:\n");
|
||||
qc_print(prog);
|
||||
prog->vmerror++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int qc_ftos(qc_program *prog)
|
||||
{
|
||||
char buffer[512];
|
||||
qcany *num;
|
||||
qcany str;
|
||||
CheckArgs(1);
|
||||
num = GetArg(0);
|
||||
snprintf(buffer, sizeof(buffer), "%g", num->_float);
|
||||
str.string = prog_tempstring(prog, buffer);
|
||||
Return(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qc_vtos(qc_program *prog)
|
||||
{
|
||||
char buffer[512];
|
||||
qcany *num;
|
||||
qcany str;
|
||||
CheckArgs(1);
|
||||
num = GetArg(0);
|
||||
snprintf(buffer, sizeof(buffer), "'%g %g %g'", num->vector[0], num->vector[1], num->vector[2]);
|
||||
str.string = prog_tempstring(prog, buffer);
|
||||
Return(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qc_etos(qc_program *prog)
|
||||
{
|
||||
char buffer[512];
|
||||
qcany *num;
|
||||
qcany str;
|
||||
CheckArgs(1);
|
||||
num = GetArg(0);
|
||||
snprintf(buffer, sizeof(buffer), "%i", num->_int);
|
||||
str.string = prog_tempstring(prog, buffer);
|
||||
Return(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qc_spawn(qc_program *prog)
|
||||
{
|
||||
qcany ent;
|
||||
CheckArgs(0);
|
||||
ent.edict = prog_spawn_entity(prog);
|
||||
Return(ent);
|
||||
return (ent.edict ? 0 : -1);
|
||||
}
|
||||
|
||||
static int qc_kill(qc_program *prog)
|
||||
{
|
||||
qcany *ent;
|
||||
CheckArgs(1);
|
||||
ent = GetArg(0);
|
||||
prog_free_entity(prog, ent->edict);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qc_vlen(qc_program *prog)
|
||||
{
|
||||
qcany *vec, len;
|
||||
CheckArgs(1);
|
||||
vec = GetArg(0);
|
||||
len._float = sqrt(vec->vector[0] * vec->vector[0] +
|
||||
vec->vector[1] * vec->vector[1] +
|
||||
vec->vector[2] * vec->vector[2]);
|
||||
Return(len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static prog_builtin qc_builtins[] = {
|
||||
NULL,
|
||||
&qc_print, /* 1 */
|
||||
&qc_ftos, /* 2 */
|
||||
&qc_spawn, /* 3 */
|
||||
&qc_kill, /* 4 */
|
||||
&qc_vtos, /* 5 */
|
||||
&qc_error, /* 6 */
|
||||
&qc_vlen, /* 7 */
|
||||
&qc_etos /* 8 */
|
||||
};
|
||||
static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
|
||||
|
||||
static const char *arg0 = NULL;
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("usage: [-debug] %s file\n", arg0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void prog_main_setparams(qc_program *prog)
|
||||
{
|
||||
size_t i;
|
||||
qcany *arg;
|
||||
|
||||
for (i = 0; i < main_params_elements; ++i) {
|
||||
arg = GetGlobal(OFS_PARM0 + 3*i);
|
||||
arg->vector[0] = 0;
|
||||
arg->vector[1] = 0;
|
||||
arg->vector[2] = 0;
|
||||
switch (main_params_data[i].vtype) {
|
||||
case TYPE_VECTOR:
|
||||
#ifdef WIN32
|
||||
(void)sscanf_s(main_params_data[i].value, " %f %f %f ",
|
||||
&arg->vector[0],
|
||||
&arg->vector[1],
|
||||
&arg->vector[2]);
|
||||
#else
|
||||
(void)sscanf(main_params_data[i].value, " %f %f %f ",
|
||||
&arg->vector[0],
|
||||
&arg->vector[1],
|
||||
&arg->vector[2]);
|
||||
#endif
|
||||
break;
|
||||
case TYPE_FLOAT:
|
||||
arg->_float = atof(main_params_data[i].value);
|
||||
break;
|
||||
case TYPE_STRING:
|
||||
arg->string = prog_tempstring(prog, main_params_data[i].value);
|
||||
break;
|
||||
default:
|
||||
printf("error: unhandled parameter type: %i\n", main_params_data[i].vtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
size_t i;
|
||||
qcint fnmain = -1;
|
||||
qc_program *prog;
|
||||
size_t xflags = VMXF_DEFAULT;
|
||||
bool opts_printfields = false;
|
||||
bool opts_printdefs = false;
|
||||
bool opts_info = false;
|
||||
|
||||
arg0 = argv[0];
|
||||
|
||||
if (argc < 2)
|
||||
usage();
|
||||
|
||||
while (argc > 2) {
|
||||
if (!strcmp(argv[1], "-trace")) {
|
||||
--argc;
|
||||
++argv;
|
||||
xflags |= VMXF_TRACE;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-profile")) {
|
||||
--argc;
|
||||
++argv;
|
||||
xflags |= VMXF_PROFILE;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-info")) {
|
||||
--argc;
|
||||
++argv;
|
||||
opts_info = true;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-printdefs")) {
|
||||
--argc;
|
||||
++argv;
|
||||
opts_printdefs = true;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-printfields")) {
|
||||
--argc;
|
||||
++argv;
|
||||
opts_printfields = true;
|
||||
}
|
||||
else if (!strcmp(argv[1], "-vector") ||
|
||||
!strcmp(argv[1], "-string") ||
|
||||
!strcmp(argv[1], "-float") )
|
||||
{
|
||||
qcvm_parameter p;
|
||||
if (argv[1][1] == 'f')
|
||||
p.vtype = TYPE_FLOAT;
|
||||
else if (argv[1][1] == 's')
|
||||
p.vtype = TYPE_STRING;
|
||||
else if (argv[1][1] == 'v')
|
||||
p.vtype = TYPE_VECTOR;
|
||||
|
||||
--argc;
|
||||
++argv;
|
||||
if (argc < 3)
|
||||
usage();
|
||||
p.value = argv[1];
|
||||
|
||||
if (main_params_add(p) < 0) {
|
||||
if (main_params_data)
|
||||
mem_d(main_params_data);
|
||||
printf("cannot add parameter\n");
|
||||
exit(1);
|
||||
}
|
||||
--argc;
|
||||
++argv;
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
|
||||
|
||||
prog = prog_load(argv[1]);
|
||||
if (!prog) {
|
||||
printf("failed to load program '%s'\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
prog->builtins = qc_builtins;
|
||||
prog->builtins_count = qc_builtins_count;
|
||||
prog->builtins_alloc = 0;
|
||||
|
||||
if (opts_info) {
|
||||
printf("Program's system-checksum = 0x%04x\n", (int)prog->crc16);
|
||||
printf("Entity field space: %i\n", (int)prog->entityfields);
|
||||
}
|
||||
|
||||
for (i = 1; i < prog->functions_count; ++i) {
|
||||
const char *name = prog_getstring(prog, prog->functions[i].name);
|
||||
/* printf("Found function: %s\n", name); */
|
||||
if (!strcmp(name, "main"))
|
||||
fnmain = (qcint)i;
|
||||
}
|
||||
if (opts_info) {
|
||||
prog_delete(prog);
|
||||
return 0;
|
||||
}
|
||||
if (opts_printdefs) {
|
||||
for (i = 0; i < prog->defs_count; ++i) {
|
||||
printf("Global: %8s %-16s at %u\n",
|
||||
type_name[prog->defs[i].type & DEF_TYPEMASK],
|
||||
prog_getstring(prog, prog->defs[i].name),
|
||||
(unsigned int)prog->defs[i].offset);
|
||||
}
|
||||
}
|
||||
else if (opts_printfields) {
|
||||
for (i = 0; i < prog->fields_count; ++i) {
|
||||
printf("Field: %8s %-16s at %u\n",
|
||||
type_name[prog->fields[i].type],
|
||||
prog_getstring(prog, prog->fields[i].name),
|
||||
(unsigned int)prog->fields[i].offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fnmain > 0)
|
||||
{
|
||||
prog_main_setparams(prog);
|
||||
prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
|
||||
}
|
||||
else
|
||||
printf("No main function found\n");
|
||||
}
|
||||
|
||||
prog_delete(prog);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
349
execloop.h
349
execloop.h
|
@ -1,349 +0,0 @@
|
|||
#if 0
|
||||
/* Expected variables */
|
||||
qc_program *prog;
|
||||
#endif
|
||||
|
||||
#define OPA ( (qcany*) (prog->globals + st->o1.u1) )
|
||||
#define OPB ( (qcany*) (prog->globals + st->o2.u1) )
|
||||
#define OPC ( (qcany*) (prog->globals + st->o3.u1) )
|
||||
|
||||
#define GLOBAL(x) ( (qcany*) (prog->globals + (x)) )
|
||||
|
||||
/* to be consistent with current darkplaces behaviour */
|
||||
#if !defined(FLOAT_IS_TRUE_FOR_INT)
|
||||
# define FLOAT_IS_TRUE_FOR_INT(x) ( (x) & 0x7FFFFFFF )
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
prog_section_function *newf;
|
||||
qcany *ed;
|
||||
qcany *ptr;
|
||||
|
||||
++st;
|
||||
|
||||
#if QCVM_PROFILE
|
||||
prog->profile[st - prog->code]++;
|
||||
#endif
|
||||
|
||||
#if QCVM_TRACE
|
||||
prog_print_statement(prog, st);
|
||||
#endif
|
||||
|
||||
switch (st->opcode)
|
||||
{
|
||||
default:
|
||||
qcvmerror(prog, "Illegal instruction in %s\n", prog->filename);
|
||||
goto cleanup;
|
||||
|
||||
case INSTR_DONE:
|
||||
case INSTR_RETURN:
|
||||
/* TODO: add instruction count to function profile count */
|
||||
GLOBAL(OFS_RETURN)->ivector[0] = OPA->ivector[0];
|
||||
GLOBAL(OFS_RETURN)->ivector[1] = OPA->ivector[1];
|
||||
GLOBAL(OFS_RETURN)->ivector[2] = OPA->ivector[2];
|
||||
|
||||
st = prog->code + prog_leavefunction(prog);
|
||||
if (!prog->stack_count)
|
||||
goto cleanup;
|
||||
|
||||
break;
|
||||
|
||||
case INSTR_MUL_F:
|
||||
OPC->_float = OPA->_float * OPB->_float;
|
||||
break;
|
||||
case INSTR_MUL_V:
|
||||
OPC->_float = OPA->vector[0]*OPB->vector[0] +
|
||||
OPA->vector[1]*OPB->vector[1] +
|
||||
OPA->vector[2]*OPB->vector[2];
|
||||
break;
|
||||
case INSTR_MUL_FV:
|
||||
OPC->vector[0] = OPA->_float * OPB->vector[0];
|
||||
OPC->vector[1] = OPA->_float * OPB->vector[1];
|
||||
OPC->vector[2] = OPA->_float * OPB->vector[2];
|
||||
break;
|
||||
case INSTR_MUL_VF:
|
||||
OPC->vector[0] = OPB->_float * OPA->vector[0];
|
||||
OPC->vector[1] = OPB->_float * OPA->vector[1];
|
||||
OPC->vector[2] = OPB->_float * OPA->vector[2];
|
||||
break;
|
||||
case INSTR_DIV_F:
|
||||
if (OPB->_float != 0.0f)
|
||||
OPC->_float = OPA->_float / OPB->_float;
|
||||
else
|
||||
OPC->_float = 0;
|
||||
break;
|
||||
|
||||
case INSTR_ADD_F:
|
||||
OPC->_float = OPA->_float + OPB->_float;
|
||||
break;
|
||||
case INSTR_ADD_V:
|
||||
OPC->vector[0] = OPA->vector[0] + OPB->vector[0];
|
||||
OPC->vector[1] = OPA->vector[1] + OPB->vector[1];
|
||||
OPC->vector[2] = OPA->vector[2] + OPB->vector[2];
|
||||
break;
|
||||
case INSTR_SUB_F:
|
||||
OPC->_float = OPA->_float - OPB->_float;
|
||||
break;
|
||||
case INSTR_SUB_V:
|
||||
OPC->vector[0] = OPA->vector[0] - OPB->vector[0];
|
||||
OPC->vector[1] = OPA->vector[1] - OPB->vector[1];
|
||||
OPC->vector[2] = OPA->vector[2] - OPB->vector[2];
|
||||
break;
|
||||
|
||||
case INSTR_EQ_F:
|
||||
OPC->_float = (OPA->_float == OPB->_float);
|
||||
break;
|
||||
case INSTR_EQ_V:
|
||||
OPC->_float = ((OPA->vector[0] == OPB->vector[0]) &&
|
||||
(OPA->vector[1] == OPB->vector[1]) &&
|
||||
(OPA->vector[2] == OPB->vector[2]) );
|
||||
break;
|
||||
case INSTR_EQ_S:
|
||||
OPC->_float = !strcmp(prog_getstring(prog, OPA->string),
|
||||
prog_getstring(prog, OPB->string));
|
||||
break;
|
||||
case INSTR_EQ_E:
|
||||
OPC->_float = (OPA->_int == OPB->_int);
|
||||
break;
|
||||
case INSTR_EQ_FNC:
|
||||
OPC->_float = (OPA->function == OPB->function);
|
||||
break;
|
||||
case INSTR_NE_F:
|
||||
OPC->_float = (OPA->_float != OPB->_float);
|
||||
break;
|
||||
case INSTR_NE_V:
|
||||
OPC->_float = ((OPA->vector[0] != OPB->vector[0]) ||
|
||||
(OPA->vector[1] != OPB->vector[1]) ||
|
||||
(OPA->vector[2] != OPB->vector[2]) );
|
||||
break;
|
||||
case INSTR_NE_S:
|
||||
OPC->_float = !!strcmp(prog_getstring(prog, OPA->string),
|
||||
prog_getstring(prog, OPB->string));
|
||||
break;
|
||||
case INSTR_NE_E:
|
||||
OPC->_float = (OPA->_int != OPB->_int);
|
||||
break;
|
||||
case INSTR_NE_FNC:
|
||||
OPC->_float = (OPA->function != OPB->function);
|
||||
break;
|
||||
|
||||
case INSTR_LE:
|
||||
OPC->_float = (OPA->_float <= OPB->_float);
|
||||
break;
|
||||
case INSTR_GE:
|
||||
OPC->_float = (OPA->_float >= OPB->_float);
|
||||
break;
|
||||
case INSTR_LT:
|
||||
OPC->_float = (OPA->_float < OPB->_float);
|
||||
break;
|
||||
case INSTR_GT:
|
||||
OPC->_float = (OPA->_float > OPB->_float);
|
||||
break;
|
||||
|
||||
case INSTR_LOAD_F:
|
||||
case INSTR_LOAD_S:
|
||||
case INSTR_LOAD_FLD:
|
||||
case INSTR_LOAD_ENT:
|
||||
case INSTR_LOAD_FNC:
|
||||
if (OPA->edict < 0 || OPA->edict >= prog->entities) {
|
||||
qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
|
||||
goto cleanup;
|
||||
}
|
||||
if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields)) {
|
||||
qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
|
||||
prog->filename,
|
||||
OPB->_int);
|
||||
goto cleanup;
|
||||
}
|
||||
ed = prog_getedict(prog, OPA->edict);
|
||||
OPC->_int = ((qcany*)( ((qcint*)ed) + OPB->_int ))->_int;
|
||||
break;
|
||||
case INSTR_LOAD_V:
|
||||
if (OPA->edict < 0 || OPA->edict >= prog->entities) {
|
||||
qcvmerror(prog, "progs `%s` attempted to read an out of bounds entity", prog->filename);
|
||||
goto cleanup;
|
||||
}
|
||||
if (OPB->_int < 0 || OPB->_int + 3 > prog->entityfields)
|
||||
{
|
||||
qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
|
||||
prog->filename,
|
||||
OPB->_int + 2);
|
||||
goto cleanup;
|
||||
}
|
||||
ed = prog_getedict(prog, OPA->edict);
|
||||
OPC->ivector[0] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[0];
|
||||
OPC->ivector[1] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[1];
|
||||
OPC->ivector[2] = ((qcany*)( ((qcint*)ed) + OPB->_int ))->ivector[2];
|
||||
break;
|
||||
|
||||
case INSTR_ADDRESS:
|
||||
if (OPA->edict < 0 || OPA->edict >= prog->entities) {
|
||||
qcvmerror(prog, "prog `%s` attempted to address an out of bounds entity %i", prog->filename, OPA->edict);
|
||||
goto cleanup;
|
||||
}
|
||||
if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
|
||||
{
|
||||
qcvmerror(prog, "prog `%s` attempted to read an invalid field from entity (%i)",
|
||||
prog->filename,
|
||||
OPB->_int);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ed = prog_getedict(prog, OPA->edict);
|
||||
OPC->_int = ((qcint*)ed) - prog->entitydata;
|
||||
OPC->_int += OPB->_int;
|
||||
break;
|
||||
|
||||
case INSTR_STORE_F:
|
||||
case INSTR_STORE_S:
|
||||
case INSTR_STORE_ENT:
|
||||
case INSTR_STORE_FLD:
|
||||
case INSTR_STORE_FNC:
|
||||
OPB->_int = OPA->_int;
|
||||
break;
|
||||
case INSTR_STORE_V:
|
||||
OPB->ivector[0] = OPA->ivector[0];
|
||||
OPB->ivector[1] = OPA->ivector[1];
|
||||
OPB->ivector[2] = OPA->ivector[2];
|
||||
break;
|
||||
|
||||
case INSTR_STOREP_F:
|
||||
case INSTR_STOREP_S:
|
||||
case INSTR_STOREP_ENT:
|
||||
case INSTR_STOREP_FLD:
|
||||
case INSTR_STOREP_FNC:
|
||||
if (OPB->_int < 0 || OPB->_int >= prog->entitydata_count) {
|
||||
qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
|
||||
goto cleanup;
|
||||
}
|
||||
if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
|
||||
qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
|
||||
prog->filename,
|
||||
prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
|
||||
OPB->_int);
|
||||
ptr = (qcany*)(prog->entitydata + OPB->_int);
|
||||
ptr->_int = OPA->_int;
|
||||
break;
|
||||
case INSTR_STOREP_V:
|
||||
if (OPB->_int < 0 || OPB->_int + 2 >= prog->entitydata_count) {
|
||||
qcvmerror(prog, "`%s` attempted to write to an out of bounds edict (%i)", prog->filename, OPB->_int);
|
||||
goto cleanup;
|
||||
}
|
||||
if (OPB->_int < prog->entityfields && !prog->allowworldwrites)
|
||||
qcvmerror(prog, "`%s` tried to assign to world.%s (field %i)\n",
|
||||
prog->filename,
|
||||
prog_getstring(prog, prog_entfield(prog, OPB->_int)->name),
|
||||
OPB->_int);
|
||||
ptr = (qcany*)(prog->entitydata + OPB->_int);
|
||||
ptr->ivector[0] = OPA->ivector[0];
|
||||
ptr->ivector[1] = OPA->ivector[1];
|
||||
ptr->ivector[2] = OPA->ivector[2];
|
||||
break;
|
||||
|
||||
case INSTR_NOT_F:
|
||||
OPC->_float = !FLOAT_IS_TRUE_FOR_INT(OPA->_int);
|
||||
break;
|
||||
case INSTR_NOT_V:
|
||||
OPC->_float = !OPA->vector[0] &&
|
||||
!OPA->vector[1] &&
|
||||
!OPA->vector[2];
|
||||
break;
|
||||
case INSTR_NOT_S:
|
||||
OPC->_float = !OPA->string ||
|
||||
!*prog_getstring(prog, OPA->string);
|
||||
break;
|
||||
case INSTR_NOT_ENT:
|
||||
OPC->_float = (OPA->edict == 0);
|
||||
break;
|
||||
case INSTR_NOT_FNC:
|
||||
OPC->_float = !OPA->function;
|
||||
break;
|
||||
|
||||
case INSTR_IF:
|
||||
/* this is consistent with darkplaces' behaviour */
|
||||
if(FLOAT_IS_TRUE_FOR_INT(OPA->_int))
|
||||
{
|
||||
st += st->o2.s1 - 1; /* offset the s++ */
|
||||
if (++jumpcount >= maxjumps)
|
||||
qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
|
||||
}
|
||||
break;
|
||||
case INSTR_IFNOT:
|
||||
if(!FLOAT_IS_TRUE_FOR_INT(OPA->_int))
|
||||
{
|
||||
st += st->o2.s1 - 1; /* offset the s++ */
|
||||
if (++jumpcount >= maxjumps)
|
||||
qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
|
||||
}
|
||||
break;
|
||||
|
||||
case INSTR_CALL0:
|
||||
case INSTR_CALL1:
|
||||
case INSTR_CALL2:
|
||||
case INSTR_CALL3:
|
||||
case INSTR_CALL4:
|
||||
case INSTR_CALL5:
|
||||
case INSTR_CALL6:
|
||||
case INSTR_CALL7:
|
||||
case INSTR_CALL8:
|
||||
prog->argc = st->opcode - INSTR_CALL0;
|
||||
if (!OPA->function)
|
||||
qcvmerror(prog, "NULL function in `%s`", prog->filename);
|
||||
|
||||
if(!OPA->function || OPA->function >= (unsigned int)prog->functions_count)
|
||||
{
|
||||
qcvmerror(prog, "CALL outside the program in `%s`", prog->filename);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
newf = &prog->functions[OPA->function];
|
||||
newf->profile++;
|
||||
|
||||
prog->statement = (st - prog->code) + 1;
|
||||
|
||||
if (newf->entry < 0)
|
||||
{
|
||||
/* negative statements are built in functions */
|
||||
int builtinnumber = -newf->entry;
|
||||
if (builtinnumber < prog->builtins_count && prog->builtins[builtinnumber])
|
||||
prog->builtins[builtinnumber](prog);
|
||||
else
|
||||
qcvmerror(prog, "No such builtin #%i in %s! Try updating your gmqcc sources",
|
||||
builtinnumber, prog->filename);
|
||||
}
|
||||
else
|
||||
st = prog->code + prog_enterfunction(prog, newf) - 1; /* offset st++ */
|
||||
if (prog->vmerror)
|
||||
goto cleanup;
|
||||
break;
|
||||
|
||||
case INSTR_STATE:
|
||||
qcvmerror(prog, "`%s` tried to execute a STATE operation", prog->filename);
|
||||
break;
|
||||
|
||||
case INSTR_GOTO:
|
||||
st += st->o1.s1 - 1; /* offset the s++ */
|
||||
if (++jumpcount == 10000000)
|
||||
qcvmerror(prog, "`%s` hit the runaway loop counter limit of %li jumps", prog->filename, jumpcount);
|
||||
break;
|
||||
|
||||
case INSTR_AND:
|
||||
OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) &&
|
||||
FLOAT_IS_TRUE_FOR_INT(OPB->_int);
|
||||
break;
|
||||
case INSTR_OR:
|
||||
OPC->_float = FLOAT_IS_TRUE_FOR_INT(OPA->_int) ||
|
||||
FLOAT_IS_TRUE_FOR_INT(OPB->_int);
|
||||
break;
|
||||
|
||||
case INSTR_BITAND:
|
||||
OPC->_float = ((int)OPA->_float) & ((int)OPB->_float);
|
||||
break;
|
||||
case INSTR_BITOR:
|
||||
OPC->_float = ((int)OPA->_float) | ((int)OPB->_float);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef QCVM_PROFILE
|
||||
#undef QCVM_TRACE
|
|
@ -1,8 +0,0 @@
|
|||
#ifndef GMQCC_DEFINE_FLAG
|
||||
#define GMQCC_DEFINE_FLAG(x)
|
||||
#endif
|
||||
|
||||
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS)
|
||||
GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
|
||||
GMQCC_DEFINE_FLAG(OMIT_NULL_BYTES)
|
||||
GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
|
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
|
745
gmqcc.ini.example
Normal file
745
gmqcc.ini.example
Normal file
|
@ -0,0 +1,745 @@
|
|||
#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.
|
||||
|
||||
[flags]
|
||||
#Add some additional characters to the string table in order to
|
||||
#compensate for a wrong boundcheck in some specific version of the
|
||||
#darkplaces engine.
|
||||
|
||||
DARKPLACES_STRING_TABLE_BUG = 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.
|
||||
|
||||
ADJUST_VECTOR_FIELDS = true
|
||||
|
||||
|
||||
#Enable a partially fteqcc-compatible preprocessor. It supports
|
||||
#all the features used in the Xonotic codebase. If you need more,
|
||||
#write a ticket.
|
||||
|
||||
FTEPP = true
|
||||
|
||||
|
||||
#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.
|
||||
|
||||
FTEPP_PREDEFS = 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
|
||||
|
||||
FTEPP_MATHDEFS = 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"
|
||||
|
||||
FTEPP_INDIRECT_EXPANSION = false
|
||||
|
||||
|
||||
#Allow switch cases to use non constant variables.
|
||||
|
||||
RELAXED_SWITCH = 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.
|
||||
|
||||
SHORT_LOGIC = true
|
||||
|
||||
|
||||
#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]
|
||||
#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’.
|
||||
|
||||
UNUSED_VARIABLE = false
|
||||
|
||||
|
||||
#Generate a warning about vector variables which are declared but
|
||||
#components of it are never used.
|
||||
|
||||
UNUSED_COMPONENT = false
|
||||
|
||||
#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.
|
||||
|
||||
USED_UNINITIALIZED = false
|
||||
|
||||
|
||||
#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.
|
||||
|
||||
UNKNOWN_CONTROL_SEQUENCE = false
|
||||
|
||||
|
||||
#Warn when using special extensions which are not part of the
|
||||
#selected standard.
|
||||
|
||||
EXTENSIONS = false
|
||||
|
||||
|
||||
#Generally QC compilers ignore redeclaration of fields. Here you
|
||||
#can optionally enable a warning.
|
||||
|
||||
FIELD_REDECLARED = false
|
||||
|
||||
|
||||
#Functions which aren't of type void will warn if it possible to
|
||||
#reach the end without returning an actual value.
|
||||
|
||||
MISSING_RETURN_VALUES = false
|
||||
|
||||
|
||||
#Warn about a function call with an invalid number of parameters.
|
||||
|
||||
INVALID_PARAMETER_COUNT = false
|
||||
|
||||
|
||||
#Warn when a locally declared variable shadows variable.
|
||||
|
||||
LOCAL_SHADOWS = false
|
||||
|
||||
|
||||
#Warn when the initialization of a local variable turns the vari‐
|
||||
#able into a constant. This is default behaviour unless
|
||||
#-finitialized-nonconstants is used.
|
||||
|
||||
LOCAL_CONSTANTS = false
|
||||
|
||||
|
||||
#There are only 2 known global variables of type void:
|
||||
#‘end_sys_globals’ and ‘end_sys_fields’. Any other void-variable
|
||||
#will warn.
|
||||
|
||||
VOID_VARIABLES = false
|
||||
|
||||
|
||||
#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
|
||||
|
||||
|
||||
[optimizations]
|
||||
#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.
|
||||
|
||||
PEEPHOLE = true
|
||||
|
||||
|
||||
#Tail recursive function calls will be turned into loops to avoid
|
||||
#the overhead of the CALL and RETURN instructions.
|
||||
|
||||
TAIL_RECURSION = 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.
|
||||
|
||||
OVERLAP_LOCALS = 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
|
131
gmqcc.vcxproj
131
gmqcc.vcxproj
|
@ -1,131 +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>{85C266A8-7938-4AE6-AB64-428DC32B1ACD}</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>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</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 Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<OutDir>.\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IntDir>.\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetName>gmqcc</TargetName>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<OutDir>.</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IntDir>.</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetName>gmqcc</TargetName>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>.\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>
|
||||
</PrecompiledHeaderOutputFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ManifestFile>
|
||||
</ManifestFile>
|
||||
<ProgramDatabaseFile>$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>.\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<FloatingPointModel>Fast</FloatingPointModel>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>
|
||||
</PrecompiledHeaderOutputFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<ManifestFile>
|
||||
</ManifestFile>
|
||||
<ProgramDatabaseFile>$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="asm.c" />
|
||||
<ClCompile Include="ast.c" />
|
||||
<ClCompile Include="code.c" />
|
||||
<ClCompile Include="error.c" />
|
||||
<ClCompile Include="exec.c" />
|
||||
<ClCompile Include="ir.c" />
|
||||
<ClCompile Include="lexer.c" />
|
||||
<ClCompile Include="main.c" />
|
||||
<ClCompile Include="parser.c" />
|
||||
<ClCompile Include="util.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ast.h" />
|
||||
<ClInclude Include="execloop.h" />
|
||||
<ClInclude Include="gmqcc.h" />
|
||||
<ClInclude Include="ir.h" />
|
||||
<ClInclude Include="lexer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="flags.def" />
|
||||
<None Include="warns.def" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
2048
intrin.cpp
Normal file
2048
intrin.cpp
Normal file
File diff suppressed because it is too large
Load diff
74
intrin.h
Normal file
74
intrin.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#ifndef GMQCC_INTRIN_HDR
|
||||
#define GMQCC_INTRIN_HDR
|
||||
#include "gmqcc.h"
|
||||
|
||||
struct fold;
|
||||
struct parser_t;
|
||||
|
||||
struct ast_function;
|
||||
struct ast_expression;
|
||||
struct ast_value;
|
||||
|
||||
struct intrin;
|
||||
|
||||
struct intrin_func_t {
|
||||
ast_expression *(intrin::*function)();
|
||||
const char *name;
|
||||
const char *alias;
|
||||
size_t args;
|
||||
};
|
||||
|
||||
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
|
502
ir.h
502
ir.h
|
@ -1,342 +1,334 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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 */
|
||||
/*
|
||||
* 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;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
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;
|
||||
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,
|
||||
|
||||
MEM_VECTOR_MAKE(struct ir_instr_s*, reads);
|
||||
MEM_VECTOR_MAKE(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 isconst;
|
||||
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;
|
||||
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;
|
||||
} 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;
|
||||
|
||||
/* For the temp allocator */
|
||||
MEM_VECTOR_MAKE(ir_life_entry_t, life);
|
||||
} ir_value;
|
||||
bool m_unique_life; // arrays will never overlap with temps
|
||||
bool m_locked; // temps living during a CALL must be locked
|
||||
bool m_callparam;
|
||||
|
||||
int32_t ir_value_code_addr(const ir_value*);
|
||||
std::vector<ir_life_entry_t> m_life; // For the temp allocator
|
||||
|
||||
/* 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);
|
||||
size_t size() const;
|
||||
|
||||
MEM_VECTOR_PROTO_ALL(ir_value, struct ir_instr_s*, reads);
|
||||
MEM_VECTOR_PROTO_ALL(ir_value, struct ir_instr_s*, writes);
|
||||
|
||||
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); */
|
||||
|
||||
MEM_VECTOR_PROTO(ir_value, ir_life_entry_t, life);
|
||||
/* 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*,...));
|
||||
|
||||
/* A vector of IR values */
|
||||
typedef struct {
|
||||
MEM_VECTOR_MAKE(ir_value*, v);
|
||||
} ir_value_vector;
|
||||
MEM_VECTOR_PROTO(ir_value_vector, ir_value*, v);
|
||||
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();
|
||||
|
||||
MEM_VECTOR_MAKE(ir_phi_entry_t, phi);
|
||||
MEM_VECTOR_MAKE(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;
|
||||
|
||||
struct ir_block_s *owner;
|
||||
} ir_instr;
|
||||
// For the temp-allocation
|
||||
size_t m_eid = 0;
|
||||
|
||||
ir_instr* ir_instr_new(struct ir_block_s *owner, int opcode);
|
||||
void ir_instr_delete(ir_instr*);
|
||||
// For IFs
|
||||
bool m_likely = true;
|
||||
|
||||
MEM_VECTOR_PROTO(ir_value, ir_phi_entry_t, phi);
|
||||
bool GMQCC_WARN ir_instr_op(ir_instr*, int op, ir_value *value, bool writing);
|
||||
|
||||
MEM_VECTOR_PROTO(ir_value, ir_value*, params);
|
||||
|
||||
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();
|
||||
|
||||
MEM_VECTOR_MAKE(ir_instr*, instr);
|
||||
MEM_VECTOR_MAKE(struct ir_block_s*, entries);
|
||||
MEM_VECTOR_MAKE(struct ir_block_s*, exits);
|
||||
MEM_VECTOR_MAKE(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 eid;
|
||||
bool is_return;
|
||||
size_t run_id;
|
||||
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);
|
||||
|
||||
MEM_VECTOR_PROTO(ir_block, ir_instr*, instr);
|
||||
MEM_VECTOR_PROTO_ALL(ir_block, ir_block*, exits);
|
||||
MEM_VECTOR_PROTO_ALL(ir_block, ir_block*, entries);
|
||||
|
||||
ir_value* ir_block_create_binop(ir_block*, const char *label, int op,
|
||||
ir_value *left, ir_value *right);
|
||||
ir_value* ir_block_create_unary(ir_block*, const char *label, int op,
|
||||
ir_value *operand);
|
||||
bool GMQCC_WARN ir_block_create_store_op(ir_block*, int op, ir_value *target, ir_value *what);
|
||||
bool GMQCC_WARN ir_block_create_store(ir_block*, ir_value *target, ir_value *what);
|
||||
bool GMQCC_WARN ir_block_create_storep(ir_block*, ir_value *target, ir_value *what);
|
||||
|
||||
/* field must be of TYPE_FIELD */
|
||||
ir_value* ir_block_create_load_from_ent(ir_block*, const char *label, ir_value *ent, ir_value *field, int outype);
|
||||
|
||||
ir_value* ir_block_create_fieldaddress(ir_block*, 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, const char *label,
|
||||
int op, ir_value *a, ir_value *b, int outype);
|
||||
|
||||
ir_value* ir_block_create_add(ir_block*, const char *label, ir_value *l, ir_value *r);
|
||||
ir_value* ir_block_create_sub(ir_block*, const char *label, ir_value *l, ir_value *r);
|
||||
ir_value* ir_block_create_mul(ir_block*, const char *label, ir_value *l, ir_value *r);
|
||||
ir_value* ir_block_create_div(ir_block*, const char *label, ir_value *l, ir_value *r);
|
||||
ir_instr* ir_block_create_phi(ir_block*, 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*);
|
||||
bool GMQCC_WARN ir_phi_add(ir_instr*, ir_block *b, ir_value *v);
|
||||
ir_instr* ir_block_create_call(ir_block*, const char *label, ir_value *func);
|
||||
void ir_phi_add(ir_instr*, ir_block *b, ir_value *v);
|
||||
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*);
|
||||
bool GMQCC_WARN ir_call_param(ir_instr*, ir_value*);
|
||||
void ir_call_param(ir_instr*, ir_value*);
|
||||
|
||||
bool GMQCC_WARN ir_block_create_return(ir_block*, 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*, 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*, ir_block *to);
|
||||
bool GMQCC_WARN ir_block_create_goto(ir_block*, ir_block *to);
|
||||
|
||||
MEM_VECTOR_PROTO_ALL(ir_block, ir_value*, living);
|
||||
|
||||
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;
|
||||
MEM_VECTOR_MAKE(int, params);
|
||||
MEM_VECTOR_MAKE(ir_block*, blocks);
|
||||
ir_builder *m_owner;
|
||||
|
||||
int builtin;
|
||||
std::string m_name;
|
||||
qc_type m_outtype;
|
||||
std::vector<int> m_params;
|
||||
ir_flag_t m_flags = 0;
|
||||
int m_builtin = 0;
|
||||
|
||||
ir_value *value;
|
||||
std::vector<std::unique_ptr<ir_block>> m_blocks;
|
||||
|
||||
/* values generated from operations
|
||||
/*
|
||||
* values generated from operations
|
||||
* which might get optimized away, so anything
|
||||
* in there needs to be deleted in the dtor.
|
||||
*/
|
||||
MEM_VECTOR_MAKE(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 */
|
||||
MEM_VECTOR_MAKE(ir_value*, locals);
|
||||
size_t m_allocated_locals = 0;
|
||||
size_t m_globaltemps = 0;
|
||||
|
||||
size_t allocated_locals;
|
||||
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;
|
||||
size_t m_run_id = 0;
|
||||
|
||||
struct ir_builder_s *owner;
|
||||
} ir_function;
|
||||
/* vararg support: */
|
||||
size_t m_max_varargs = 0;
|
||||
};
|
||||
|
||||
ir_function* ir_function_new(struct ir_builder_s *owner, int returntype);
|
||||
void ir_function_delete(ir_function*);
|
||||
|
||||
bool GMQCC_WARN ir_function_collect_value(ir_function*, ir_value *value);
|
||||
|
||||
bool ir_function_set_name(ir_function*, const char *name);
|
||||
MEM_VECTOR_PROTO(ir_function, int, params);
|
||||
MEM_VECTOR_PROTO(ir_function, ir_block*, blocks);
|
||||
|
||||
ir_value* ir_function_get_local(ir_function *self, 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(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 */
|
||||
typedef struct ir_builder_s
|
||||
{
|
||||
char *name;
|
||||
MEM_VECTOR_MAKE(ir_function*, functions);
|
||||
MEM_VECTOR_MAKE(ir_value*, globals);
|
||||
MEM_VECTOR_MAKE(ir_value*, fields);
|
||||
#define IR_HT_SIZE 1024
|
||||
#define IR_MAX_VINSTR_TEMPS 2
|
||||
|
||||
MEM_VECTOR_MAKE(const char*, filenames);
|
||||
MEM_VECTOR_MAKE(qcint, filestrings);
|
||||
/* we cache the #IMMEDIATE string here */
|
||||
qcint str_immediate;
|
||||
} ir_builder;
|
||||
struct ir_builder {
|
||||
ir_builder(const std::string& modulename);
|
||||
~ir_builder();
|
||||
|
||||
ir_builder* ir_builder_new(const char *modulename);
|
||||
void ir_builder_delete(ir_builder*);
|
||||
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;
|
||||
|
||||
bool ir_builder_set_name(ir_builder *self, const char *name);
|
||||
ir_value *generateExtparamProto();
|
||||
void generateExtparam();
|
||||
|
||||
MEM_VECTOR_PROTO(ir_builder, ir_function*, functions);
|
||||
MEM_VECTOR_PROTO(ir_builder, ir_value*, globals);
|
||||
MEM_VECTOR_PROTO(ir_builder, ir_value*, fields);
|
||||
MEM_VECTOR_PROTO(ir_builder, const char*, filenames);
|
||||
MEM_VECTOR_PROTO(ir_builder, qcint, filestrings);
|
||||
ir_value *literalFloat(float value, bool add_to_list);
|
||||
|
||||
ir_function* ir_builder_get_function(ir_builder*, const char *fun);
|
||||
ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype);
|
||||
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;
|
||||
|
||||
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);
|
||||
ht m_htfunctions;
|
||||
ht m_htglobals;
|
||||
ht m_htfields;
|
||||
|
||||
bool ir_builder_generate(ir_builder *self, const char *filename);
|
||||
// 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;
|
||||
|
||||
void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
|
||||
// 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;
|
||||
|
||||
/* This code assumes 32 bit floats while generating binary */
|
||||
extern int check_int_and_float_size
|
||||
[ (sizeof(int32_t) == sizeof(qcfloat)) ? 1 : -1 ];
|
||||
std::vector<const char*> m_filenames;
|
||||
std::vector<qcint_t> m_filestrings;
|
||||
|
||||
// we cache the #IMMEDIATE string here
|
||||
qcint_t m_str_immediate = 0;
|
||||
|
||||
// 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
|
||||
*/
|
||||
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
|
||||
|
|
358
lexer.h
358
lexer.h
|
@ -1,38 +1,19 @@
|
|||
#ifndef GMQCC_LEXER_HDR_
|
||||
#define GMQCC_LEXER_HDR_
|
||||
#ifndef GMQCC_LEXER_HDR
|
||||
#define GMQCC_LEXER_HDR
|
||||
#include "gmqcc.h"
|
||||
|
||||
typedef struct token_s token;
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
struct token_s {
|
||||
int ttype;
|
||||
|
||||
MEM_VECTOR_MAKE(char, value);
|
||||
|
||||
union {
|
||||
vector v;
|
||||
int i;
|
||||
double f;
|
||||
int t; /* type */
|
||||
} constval;
|
||||
|
||||
#if 0
|
||||
struct token_s *next;
|
||||
struct token_s *prev;
|
||||
#endif
|
||||
|
||||
lex_ctx ctx;
|
||||
struct token {
|
||||
int ttype;
|
||||
char *value;
|
||||
union {
|
||||
vec3_t v;
|
||||
int i;
|
||||
qcfloat_t f;
|
||||
qc_type t; /* type */
|
||||
} constval;
|
||||
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
|
||||
*
|
||||
*/
|
||||
|
@ -51,13 +32,27 @@ enum {
|
|||
|
||||
TOKEN_DOTS, /* 3 dots, ... */
|
||||
|
||||
TOKEN_ATTRIBUTE_OPEN, /* [[ */
|
||||
TOKEN_ATTRIBUTE_CLOSE, /* ]] */
|
||||
|
||||
TOKEN_VA_ARGS, /* for the ftepp only */
|
||||
TOKEN_VA_ARGS_ARRAY, /* for the ftepp only */
|
||||
TOKEN_VA_COUNT, /* to get the count of vaargs */
|
||||
|
||||
TOKEN_STRINGCONST, /* not the typename but an actual "string" */
|
||||
TOKEN_CHARCONST,
|
||||
TOKEN_VECTORCONST,
|
||||
TOKEN_INTCONST,
|
||||
TOKEN_FLOATCONST,
|
||||
|
||||
TOKEN_EOF,
|
||||
TOKEN_WHITE,
|
||||
TOKEN_EOL,
|
||||
|
||||
/* if we add additional tokens before this, the exposed API
|
||||
* should not be broken anyway, but EOF/ERROR/... should
|
||||
* still be at the bottom
|
||||
*/
|
||||
TOKEN_EOF = 1024,
|
||||
|
||||
/* We use '< TOKEN_ERROR', so TOKEN_FATAL must come after it and any
|
||||
* other error related tokens as well
|
||||
|
@ -66,59 +61,45 @@ enum {
|
|||
TOKEN_FATAL /* internal error, eg out of memory */
|
||||
};
|
||||
|
||||
static const char *_tokennames[] = {
|
||||
"TOKEN_START",
|
||||
"TOKEN_IDENT",
|
||||
"TOKEN_TYPENAME",
|
||||
"TOKEN_OPERATOR",
|
||||
"TOKEN_KEYWORD",
|
||||
"TOKEN_DOTS",
|
||||
"TOKEN_STRINGCONST",
|
||||
"TOKEN_CHARCONST",
|
||||
"TOKEN_VECTORCONST",
|
||||
"TOKEN_INTCONST",
|
||||
"TOKEN_FLOATCONST",
|
||||
"TOKEN_EOF",
|
||||
"TOKEN_ERROR",
|
||||
"TOKEN_FATAL",
|
||||
};
|
||||
typedef int
|
||||
_all_tokennames_added_[
|
||||
((TOKEN_FATAL - TOKEN_START + 1) ==
|
||||
(sizeof(_tokennames)/sizeof(_tokennames[0])))
|
||||
? 1 : -1];
|
||||
|
||||
typedef struct {
|
||||
struct frame_macro {
|
||||
char *name;
|
||||
int value;
|
||||
} frame_macro;
|
||||
int value;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
FILE *file;
|
||||
char *name;
|
||||
size_t line;
|
||||
size_t sline; /* line at the start of a token */
|
||||
struct lex_file {
|
||||
FILE *file;
|
||||
const char *open_string;
|
||||
size_t open_string_length;
|
||||
size_t open_string_pos;
|
||||
|
||||
char peek[256];
|
||||
size_t peekpos;
|
||||
char *name;
|
||||
size_t line;
|
||||
size_t sline; /* line at the start of a token */
|
||||
size_t column;
|
||||
|
||||
bool eof;
|
||||
int peek[256];
|
||||
size_t peekpos;
|
||||
|
||||
token tok; /* not a pointer anymore */
|
||||
bool eof;
|
||||
|
||||
struct {
|
||||
bool noops;
|
||||
bool nodigraphs; /* used when lexing string constants */
|
||||
} flags;
|
||||
token tok; /* not a pointer anymore */
|
||||
|
||||
struct {
|
||||
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;
|
||||
MEM_VECTOR_MAKE(frame_macro, frames);
|
||||
char *modelname;
|
||||
} lex_file;
|
||||
frame_macro *frames;
|
||||
char *modelname;
|
||||
|
||||
MEM_VECTOR_PROTO(lex_file, char, token);
|
||||
size_t push_line;
|
||||
};
|
||||
|
||||
lex_file* lex_open (const char *file);
|
||||
lex_file* lex_open_string(const char *str, size_t len, const char *name);
|
||||
void lex_close(lex_file *lex);
|
||||
int lex_do (lex_file *lex);
|
||||
void lex_cleanup(void);
|
||||
|
@ -135,125 +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;
|
||||
unsigned int prec;
|
||||
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, 16, OP_SUFFIX},
|
||||
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 16, OP_SUFFIX},
|
||||
{ "++", 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 */
|
||||
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, 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, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX }, */
|
||||
{ "**", 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}, */
|
||||
|
||||
{ "*", 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, opid2('>','<'), 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, 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, opid2('=','='), ASSOC_LEFT, 9, 0 },
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0 },
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0, true},
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0, true},
|
||||
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0 },
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0, true},
|
||||
|
||||
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0 },
|
||||
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0, true},
|
||||
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0 },
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0, true},
|
||||
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
|
||||
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0 },
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0, true},
|
||||
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0 },
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0, true},
|
||||
|
||||
{ "=", 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, 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_LEFT, 1, 0 }
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false},
|
||||
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0, false}
|
||||
};
|
||||
|
||||
static const oper_info fte_operators[] = {
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
|
||||
|
||||
{ "++", 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, 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, 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, true},
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
|
||||
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
|
||||
|
||||
{ "<", 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, true},
|
||||
|
||||
{ "=", 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, true},
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0, true},
|
||||
|
||||
/* Leave precedence 3 for : with -fcorrect-ternary */
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0, false},
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false}
|
||||
};
|
||||
static const size_t c_operator_count = (sizeof(c_operators) / sizeof(c_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, 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, 1, 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
|
||||
|
|
524
main.c
524
main.c
|
@ -1,524 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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 "lexer.h"
|
||||
|
||||
uint32_t opts_flags[1 + (COUNT_FLAGS / 32)];
|
||||
uint32_t opts_warn [1 + (COUNT_WARNINGS / 32)];
|
||||
|
||||
uint32_t opts_O = 1;
|
||||
const char *opts_output = "progs.dat";
|
||||
int opts_standard = COMPILER_GMQCC;
|
||||
bool opts_debug = false;
|
||||
bool opts_memchk = false;
|
||||
bool opts_dump = false;
|
||||
bool opts_werror = false;
|
||||
bool opts_forcecrc = false;
|
||||
|
||||
uint16_t opts_forced_crc;
|
||||
|
||||
static bool opts_output_wasset = false;
|
||||
|
||||
/* set by the standard */
|
||||
const oper_info *operators = NULL;
|
||||
size_t operator_count = 0;
|
||||
|
||||
typedef struct { char *filename; int type; } argitem;
|
||||
VECTOR_MAKE(argitem, items);
|
||||
|
||||
#define TYPE_QC 0
|
||||
#define TYPE_ASM 1
|
||||
#define TYPE_SRC 2
|
||||
|
||||
static const char *app_name;
|
||||
|
||||
static int usage() {
|
||||
printf("usage: %s [options] [files...]", app_name);
|
||||
printf("options:\n"
|
||||
" -h, --help show this help message\n"
|
||||
" -debug turns on compiler debug messages\n"
|
||||
" -memchk turns on compiler memory leak check\n");
|
||||
printf(" -o, --output=file output file, defaults to progs.dat\n"
|
||||
" -a filename add an asm file to be assembled\n"
|
||||
" -s filename add a progs.src file to be used\n");
|
||||
printf(" -f<flag> enable a flag\n"
|
||||
" -fno-<flag> disable a flag\n"
|
||||
" -std standard select one of the following standards\n"
|
||||
" -std=qcc original QuakeC\n"
|
||||
" -std=fteqcc fteqcc QuakeC\n"
|
||||
" -std=gmqcc this compiler (default)\n");
|
||||
printf(" -W<warning> enable a warning\n"
|
||||
" -Wno-<warning> disable a warning\n"
|
||||
" -Wall enable all warnings\n"
|
||||
" -Werror treat warnings as errors\n");
|
||||
printf(" -force-crc=num force a specific checksum into the header\n");
|
||||
printf("\n");
|
||||
printf("flags:\n"
|
||||
" -fadjust-vector-fields\n"
|
||||
" when assigning a vector field, its _y and _z fields also get assigned\n"
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool options_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < listsize; ++i) {
|
||||
if (!strcmp(name, list[i].name)) {
|
||||
longbit lb = list[i].bit;
|
||||
#if 0
|
||||
if (on)
|
||||
flags[lb.idx] |= (1<<(lb.bit));
|
||||
else
|
||||
flags[lb.idx] &= ~(1<<(lb.bit));
|
||||
#else
|
||||
if (on)
|
||||
flags[0] |= (1<<lb);
|
||||
else
|
||||
flags[0] &= ~(1<<(lb));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool options_setflag(const char *name, bool on) {
|
||||
return options_setflag_all(name, on, opts_flags, opts_flag_list, COUNT_FLAGS);
|
||||
}
|
||||
static bool options_setwarn(const char *name, bool on) {
|
||||
return options_setflag_all(name, on, opts_warn, opts_warn_list, COUNT_WARNINGS);
|
||||
}
|
||||
|
||||
static bool options_witharg(int *argc_, char ***argv_, char **out) {
|
||||
int argc = *argc_;
|
||||
char **argv = *argv_;
|
||||
|
||||
if (argv[0][2]) {
|
||||
*out = argv[0]+2;
|
||||
return true;
|
||||
}
|
||||
/* eat up the next */
|
||||
if (argc < 2) /* no parameter was provided */
|
||||
return false;
|
||||
|
||||
*out = argv[1];
|
||||
--*argc_;
|
||||
++*argv_;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool options_long_witharg_all(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] == '=') {
|
||||
/* using --opt=param */
|
||||
*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;
|
||||
}
|
||||
static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) {
|
||||
return options_long_witharg_all(optname, argc_, argv_, out, 2, true);
|
||||
}
|
||||
static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) {
|
||||
return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
|
||||
}
|
||||
|
||||
static bool options_parse(int argc, char **argv) {
|
||||
bool argend = false;
|
||||
size_t itr;
|
||||
char buffer[1024];
|
||||
while (!argend && argc > 1) {
|
||||
char *argarg;
|
||||
argitem item;
|
||||
|
||||
++argv;
|
||||
--argc;
|
||||
|
||||
if (argv[0][0] == '-') {
|
||||
/* All gcc-type long options */
|
||||
if (options_long_gcc("std", &argc, &argv, &argarg)) {
|
||||
if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default"))
|
||||
opts_standard = COMPILER_GMQCC;
|
||||
else if (!strcmp(argarg, "qcc"))
|
||||
opts_standard = COMPILER_QCC;
|
||||
else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc"))
|
||||
opts_standard = COMPILER_FTEQCC;
|
||||
else if (!strcmp(argarg, "qccx"))
|
||||
opts_standard = COMPILER_QCCX;
|
||||
else {
|
||||
printf("Unknown standard: %s\n", argarg);
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
|
||||
opts_forcecrc = true;
|
||||
opts_forced_crc = strtol(argarg, NULL, 0);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "debug")) {
|
||||
opts_debug = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "dump")) {
|
||||
opts_dump = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "memchk")) {
|
||||
opts_memchk = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (argv[0][1]) {
|
||||
/* -h, show usage but exit with 0 */
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
/* handle all -fflags */
|
||||
case 'f':
|
||||
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
|
||||
if (!strcmp(argv[0]+2, "HELP")) {
|
||||
printf("Possible flags:\n");
|
||||
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
|
||||
util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
|
||||
printf(" -f%s\n", buffer);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
else if (!strncmp(argv[0]+2, "NO_", 3)) {
|
||||
if (!options_setflag(argv[0]+5, false)) {
|
||||
printf("unknown flag: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!options_setflag(argv[0]+2, true)) {
|
||||
printf("unknown flag: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
|
||||
if (!strcmp(argv[0]+2, "HELP")) {
|
||||
printf("Possible warnings:\n");
|
||||
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
|
||||
util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
|
||||
printf(" -W%s\n", buffer);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "NO_ERROR")) {
|
||||
opts_werror = false;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "ERROR")) {
|
||||
opts_werror = true;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "NONE")) {
|
||||
for (itr = 0; itr < sizeof(opts_warn)/sizeof(opts_warn[0]); ++itr)
|
||||
opts_warn[itr] = 0;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "ALL")) {
|
||||
for (itr = 0; itr < sizeof(opts_warn)/sizeof(opts_warn[0]); ++itr)
|
||||
opts_warn[itr] = 0xFFFFFFFFL;
|
||||
break;
|
||||
}
|
||||
if (!strncmp(argv[0]+2, "NO_", 3)) {
|
||||
if (!options_setwarn(argv[0]+5, false)) {
|
||||
printf("unknown warning: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!options_setwarn(argv[0]+2, true)) {
|
||||
printf("unknown warning: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if (!options_witharg(&argc, &argv, &argarg)) {
|
||||
printf("option -O requires a numerical argument\n");
|
||||
return false;
|
||||
}
|
||||
opts_O = atoi(argarg);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
if (!options_witharg(&argc, &argv, &argarg)) {
|
||||
printf("option -o requires an argument: the output file name\n");
|
||||
return false;
|
||||
}
|
||||
opts_output = argarg;
|
||||
opts_output_wasset = true;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 's':
|
||||
item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
|
||||
if (!options_witharg(&argc, &argv, &argarg)) {
|
||||
printf("option -a requires a filename %s\n",
|
||||
(argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
|
||||
return false;
|
||||
}
|
||||
item.filename = argarg;
|
||||
items_add(item);
|
||||
break;
|
||||
|
||||
case '-':
|
||||
if (!argv[0][2]) {
|
||||
/* anything following -- is considered a non-option argument */
|
||||
argend = true;
|
||||
break;
|
||||
}
|
||||
/* All long options without arguments */
|
||||
else if (!strcmp(argv[0]+2, "help")) {
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
else {
|
||||
/* All long options with arguments */
|
||||
if (options_long_witharg("output", &argc, &argv, &argarg)) {
|
||||
opts_output = argarg;
|
||||
opts_output_wasset = true;
|
||||
} else {
|
||||
printf("Unknown parameter: %s\n", argv[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown parameter: %s\n", argv[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* it's a QC filename */
|
||||
argitem item;
|
||||
item.filename = argv[0];
|
||||
item.type = TYPE_QC;
|
||||
items_add(item);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void options_set(uint32_t *flags, size_t idx, bool on)
|
||||
{
|
||||
longbit lb = LONGBIT(idx);
|
||||
#if 0
|
||||
if (on)
|
||||
flags[lb.idx] |= (1<<(lb.bit));
|
||||
else
|
||||
flags[lb.idx] &= ~(1<<(lb.bit));
|
||||
#else
|
||||
if (on)
|
||||
flags[0] |= (1<<(lb));
|
||||
else
|
||||
flags[0] &= ~(1<<(lb));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* returns the line number, or -1 on error */
|
||||
static bool progs_nextline(char **out, size_t *alen,FILE *src)
|
||||
{
|
||||
int len;
|
||||
char *line;
|
||||
char *start;
|
||||
char *end;
|
||||
|
||||
line = *out;
|
||||
len = util_getline(&line, alen, src);
|
||||
if (len == -1)
|
||||
return false;
|
||||
|
||||
/* start at first non-blank */
|
||||
for (start = line; isspace(*start); ++start) {}
|
||||
/* end at the first non-blank */
|
||||
for (end = start; *end && !isspace(*end); ++end) {}
|
||||
|
||||
*out = line;
|
||||
/* move the actual filename to the beginning */
|
||||
while (start != end) {
|
||||
*line++ = *start++;
|
||||
}
|
||||
*line = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
size_t itr;
|
||||
int retval = 0;
|
||||
bool opts_output_free = false;
|
||||
|
||||
app_name = argv[0];
|
||||
|
||||
/* default options / warn flags */
|
||||
options_set(opts_warn, WARN_UNKNOWN_CONTROL_SEQUENCE, true);
|
||||
options_set(opts_warn, WARN_EXTENSIONS, true);
|
||||
options_set(opts_warn, WARN_FIELD_REDECLARED, true);
|
||||
options_set(opts_warn, WARN_TOO_FEW_PARAMETERS, true);
|
||||
options_set(opts_warn, WARN_MISSING_RETURN_VALUES, true);
|
||||
options_set(opts_warn, WARN_USED_UNINITIALIZED, true);
|
||||
options_set(opts_warn, WARN_LOCAL_CONSTANTS, true);
|
||||
options_set(opts_warn, WARN_VOID_VARIABLES, true);
|
||||
options_set(opts_warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
|
||||
options_set(opts_warn, WARN_VARIADIC_FUNCTION, true);
|
||||
options_set(opts_warn, WARN_FRAME_MACROS, true);
|
||||
options_set(opts_warn, WARN_UNUSED_VARIABLE, true);
|
||||
options_set(opts_warn, WARN_EFFECTLESS_STATEMENT, true);
|
||||
options_set(opts_warn, WARN_END_SYS_FIELDS, true);
|
||||
options_set(opts_warn, WARN_ASSIGN_FUNCTION_TYPES, true);
|
||||
|
||||
if (!options_parse(argc, argv)) {
|
||||
return usage();
|
||||
}
|
||||
|
||||
/* the standard decides which set of operators to use */
|
||||
if (opts_standard == COMPILER_GMQCC) {
|
||||
operators = c_operators;
|
||||
operator_count = c_operator_count;
|
||||
} else {
|
||||
operators = qcc_operators;
|
||||
operator_count = qcc_operator_count;
|
||||
}
|
||||
|
||||
if (opts_dump) {
|
||||
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
|
||||
printf("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
|
||||
}
|
||||
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
|
||||
printf("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
|
||||
}
|
||||
printf("output = %s\n", opts_output);
|
||||
printf("optimization level = %i\n", (int)opts_O);
|
||||
printf("standard = %i\n", opts_standard);
|
||||
}
|
||||
|
||||
if (!parser_init()) {
|
||||
printf("failed to initialize parser\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
util_debug("COM", "starting ...\n");
|
||||
|
||||
if (items_elements) {
|
||||
printf("Mode: manual\n");
|
||||
printf("There are %lu items to compile:\n", (unsigned long)items_elements);
|
||||
for (itr = 0; itr < items_elements; ++itr) {
|
||||
printf(" item: %s (%s)\n",
|
||||
items_data[itr].filename,
|
||||
( (items_data[itr].type == TYPE_QC ? "qc" :
|
||||
(items_data[itr].type == TYPE_ASM ? "asm" :
|
||||
(items_data[itr].type == TYPE_SRC ? "progs.src" :
|
||||
("unknown"))))));
|
||||
|
||||
if (!parser_compile(items_data[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parser_finish(opts_output)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
} else {
|
||||
FILE *src;
|
||||
char *line;
|
||||
size_t linelen = 0;
|
||||
|
||||
printf("Mode: progs.src\n");
|
||||
src = util_fopen("progs.src", "rb");
|
||||
if (!src) {
|
||||
printf("failed to open `progs.src` for reading\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
line = NULL;
|
||||
if (!progs_nextline(&line, &linelen, src) || !line[0]) {
|
||||
printf("illformatted progs.src file: expected output filename in first line\n");
|
||||
retval = 1;
|
||||
goto srcdone;
|
||||
}
|
||||
|
||||
if (!opts_output_wasset) {
|
||||
opts_output = util_strdup(line);
|
||||
opts_output_free = true;
|
||||
}
|
||||
|
||||
while (progs_nextline(&line, &linelen, src)) {
|
||||
if (!line[0] || (line[0] == '/' && line[1] == '/'))
|
||||
continue;
|
||||
printf(" src: %s\n", line);
|
||||
if (!parser_compile(line)) {
|
||||
retval = 1;
|
||||
goto srcdone;
|
||||
}
|
||||
}
|
||||
|
||||
parser_finish(opts_output);
|
||||
|
||||
srcdone:
|
||||
fclose(src);
|
||||
mem_d(line);
|
||||
}
|
||||
|
||||
/* stuff */
|
||||
|
||||
cleanup:
|
||||
util_debug("COM", "cleaning ...\n");
|
||||
|
||||
mem_d(items_data);
|
||||
|
||||
parser_cleanup();
|
||||
if (opts_output_free)
|
||||
mem_d((char*)opts_output);
|
||||
|
||||
lex_cleanup();
|
||||
util_meminfo();
|
||||
return retval;
|
||||
}
|
772
main.cpp
Normal file
772
main.cpp
Normal file
|
@ -0,0 +1,772 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "gmqcc.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
|
||||
/* TODO: cleanup this whole file .. it's a fuckign mess */
|
||||
|
||||
/* set by the standard */
|
||||
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
|
||||
#define TYPE_SRC 2
|
||||
|
||||
static const char *app_name;
|
||||
|
||||
static void version(void) {
|
||||
con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
|
||||
GMQCC_VERSION_MAJOR,
|
||||
GMQCC_VERSION_MINOR,
|
||||
GMQCC_VERSION_PATCH,
|
||||
__DATE__,
|
||||
__TIME__
|
||||
);
|
||||
}
|
||||
|
||||
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");
|
||||
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");
|
||||
con_out(" -q, --quiet be less verbose\n");
|
||||
con_out(" -config file use the specified ini file\n");
|
||||
con_out(" -std=standard select one of the following standards\n"
|
||||
" -std=qcc original QuakeC\n"
|
||||
" -std=fteqcc fteqcc QuakeC\n"
|
||||
" -std=gmqcc this compiler (default)\n");
|
||||
con_out(" -f<flag> enable a flag\n"
|
||||
" -fno-<flag> disable a flag\n"
|
||||
" -fhelp list possible flags\n");
|
||||
con_out(" -W<warning> enable a warning\n"
|
||||
" -Wno-<warning> disable a warning\n"
|
||||
" -Wall enable all warnings\n");
|
||||
con_out(" -Werror treat warnings as errors\n"
|
||||
" -Werror-<warning> treat a warning as error\n"
|
||||
" -Wno-error-<warning> opposite of the above\n");
|
||||
con_out(" -Whelp list possible warnings\n");
|
||||
con_out(" -O<number> optimization level\n"
|
||||
" -O<name> enable specific optimization\n"
|
||||
" -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;
|
||||
}
|
||||
|
||||
/* command line parsing */
|
||||
static bool options_witharg(int *argc_, char ***argv_, char **out) {
|
||||
int argc = *argc_;
|
||||
char **argv = *argv_;
|
||||
|
||||
if (argv[0][2]) {
|
||||
*out = argv[0]+2;
|
||||
return true;
|
||||
}
|
||||
/* eat up the next */
|
||||
if (argc < 2) /* no parameter was provided */
|
||||
return false;
|
||||
|
||||
*out = argv[1];
|
||||
--*argc_;
|
||||
++*argv_;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool options_long_witharg_all(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] == '=') {
|
||||
/* using --opt=param */
|
||||
*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;
|
||||
}
|
||||
static bool options_long_witharg(const char *optname, int *argc_, char ***argv_, char **out) {
|
||||
return options_long_witharg_all(optname, argc_, argv_, out, 2, true);
|
||||
}
|
||||
static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, char **out) {
|
||||
return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
|
||||
}
|
||||
|
||||
static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
||||
bool argend = false;
|
||||
size_t itr;
|
||||
char buffer[1024];
|
||||
char *config = nullptr;
|
||||
|
||||
while (!argend && argc > 1) {
|
||||
char *argarg;
|
||||
argitem item;
|
||||
ppitem macro;
|
||||
|
||||
++argv;
|
||||
--argc;
|
||||
|
||||
if (argv[0][0] == '-') {
|
||||
/* All gcc-type long options */
|
||||
if (options_long_gcc("std", &argc, &argv, &argarg)) {
|
||||
if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
|
||||
|
||||
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);
|
||||
opts_set(opts.flags, TRANSLATABLE_STRINGS, true);
|
||||
opts_set(opts.flags, INITIALIZED_NONCONSTANTS, true);
|
||||
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;
|
||||
|
||||
} else if (!strcmp(argarg, "qcc")) {
|
||||
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
|
||||
opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
|
||||
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCC;
|
||||
|
||||
} else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
|
||||
|
||||
opts_set(opts.flags, FTEPP, true);
|
||||
opts_set(opts.flags, TRANSLATABLE_STRINGS, true);
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
|
||||
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;
|
||||
|
||||
} else if (!strcmp(argarg, "qccx")) {
|
||||
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCCX;
|
||||
|
||||
} else {
|
||||
con_out("Unknown standard: %s\n", argarg);
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
|
||||
|
||||
OPTS_OPTION_BOOL(OPTION_FORCECRC) = true;
|
||||
OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, nullptr, 0);
|
||||
continue;
|
||||
}
|
||||
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("progsrc", &argc, &argv, &argarg)) {
|
||||
OPTS_OPTION_STR(OPTION_PROGSRC) = argarg;
|
||||
*has_progs_src = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* show defaults (like pathscale) */
|
||||
if (!strcmp(argv[0]+1, "show-defaults")) {
|
||||
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
|
||||
if (!OPTS_FLAG(itr))
|
||||
continue;
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
util_strtononcmd(opts_flag_list[itr].name, buffer, strlen(opts_flag_list[itr].name) + 1);
|
||||
|
||||
con_out("-f%s ", buffer);
|
||||
}
|
||||
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
|
||||
if (!OPTS_WARN(itr))
|
||||
continue;
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
util_strtononcmd(opts_warn_list[itr].name, buffer, strlen(opts_warn_list[itr].name) + 1);
|
||||
con_out("-W%s ", buffer);
|
||||
}
|
||||
con_out("\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[0]+1, "debug")) {
|
||||
OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "dump")) {
|
||||
OPTS_OPTION_BOOL(OPTION_DUMP) = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "dumpfin")) {
|
||||
OPTS_OPTION_BOOL(OPTION_DUMPFIN) = 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 */
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
/* break; never reached because of exit(0) */
|
||||
|
||||
case 'v':
|
||||
version();
|
||||
exit(0);
|
||||
|
||||
case 'E':
|
||||
OPTS_OPTION_BOOL(OPTION_PP_ONLY) = true;
|
||||
opts_set(opts.flags, FTEPP_PREDEFS, true); /* predefs on for -E */
|
||||
break;
|
||||
|
||||
/* debug turns on -flno */
|
||||
case 'g':
|
||||
opts_setflag("LNO", true);
|
||||
OPTS_OPTION_BOOL(OPTION_G) = true;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
OPTS_OPTION_BOOL(OPTION_QUIET) = true;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
if (!strlen(argv[0]+2)) {
|
||||
con_err("expected name after -D\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!(argarg = strchr(argv[0] + 2, '='))) {
|
||||
macro.name = util_strdup(argv[0]+2);
|
||||
macro.value = nullptr;
|
||||
} else {
|
||||
*argarg='\0'; /* terminate for name */
|
||||
macro.name = util_strdup(argv[0]+2);
|
||||
macro.value = util_strdup(argarg+1);
|
||||
}
|
||||
vec_push(ppems, macro);
|
||||
break;
|
||||
|
||||
/* handle all -fflags */
|
||||
case 'f':
|
||||
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
|
||||
if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
|
||||
con_out("Possible flags:\n\n");
|
||||
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
|
||||
util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
|
||||
con_out(" -f%s\n", buffer);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
else if (!strncmp(argv[0]+2, "NO_", 3)) {
|
||||
if (!opts_setflag(argv[0]+5, false)) {
|
||||
con_out("unknown flag: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!opts_setflag(argv[0]+2, true)) {
|
||||
con_out("unknown flag: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
|
||||
if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
|
||||
con_out("Possible warnings:\n");
|
||||
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
|
||||
util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
|
||||
con_out(" -W%s\n", buffer);
|
||||
if (itr == WARN_DEBUG)
|
||||
con_out(" Warnings included by -Wall:\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "NO_ERROR") ||
|
||||
!strcmp(argv[0]+2, "NO_ERROR_ALL"))
|
||||
{
|
||||
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
|
||||
opts.werror[itr] = 0;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "ERROR") ||
|
||||
!strcmp(argv[0]+2, "ERROR_ALL"))
|
||||
{
|
||||
opts_backup_non_Werror_all();
|
||||
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 < 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 < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
|
||||
opts.warn[itr] = 0xFFFFFFFFL;
|
||||
opts_restore_non_Wall();
|
||||
break;
|
||||
}
|
||||
else if (!strncmp(argv[0]+2, "ERROR_", 6)) {
|
||||
if (!opts_setwerror(argv[0]+8, true)) {
|
||||
con_out("unknown warning: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp(argv[0]+2, "NO_ERROR_", 9)) {
|
||||
if (!opts_setwerror(argv[0]+11, false)) {
|
||||
con_out("unknown warning: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!strncmp(argv[0]+2, "NO_", 3)) {
|
||||
if (!opts_setwarn(argv[0]+5, false)) {
|
||||
con_out("unknown warning: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!opts_setwarn(argv[0]+2, true)) {
|
||||
con_out("unknown warning: %s\n", argv[0]+2);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if (!options_witharg(&argc, &argv, &argarg)) {
|
||||
con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
|
||||
return false;
|
||||
}
|
||||
if (util_isdigit(argarg[0])) {
|
||||
uint32_t val = (uint32_t)strtol(argarg, nullptr, 10);
|
||||
OPTS_OPTION_U32(OPTION_O) = val;
|
||||
opts_setoptimlevel(val);
|
||||
} else {
|
||||
util_strtocmd(argarg, argarg, strlen(argarg)+1);
|
||||
if (!strcmp(argarg, "HELP")) {
|
||||
con_out("Possible optimizations:\n");
|
||||
for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
|
||||
util_strtononcmd(opts_opt_list[itr].name, buffer, sizeof(buffer));
|
||||
con_out(" -O%-20s (-O%u)\n", buffer, opts_opt_oflag[itr]);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(argarg, "ALL"))
|
||||
opts_setoptimlevel(OPTS_OPTION_U32(OPTION_O) = 9999);
|
||||
else if (!strncmp(argarg, "NO_", 3)) {
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!opts_setoptim(argarg, true)) {
|
||||
con_out("unknown optimization: %s\n", argarg);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
if (!options_witharg(&argc, &argv, &argarg)) {
|
||||
con_out("option -o requires an argument: the output file name\n");
|
||||
return false;
|
||||
}
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
|
||||
opts_output_wasset = true;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
case 's':
|
||||
item.type = argv[0][1] == 'a' ? TYPE_ASM : TYPE_SRC;
|
||||
if (!options_witharg(&argc, &argv, &argarg)) {
|
||||
con_out("option -a requires a filename %s\n",
|
||||
(argv[0][1] == 'a' ? "containing QC-asm" : "containing a progs.src formatted list"));
|
||||
return false;
|
||||
}
|
||||
item.filename = argarg;
|
||||
vec_push(items, item);
|
||||
if (item.type == TYPE_SRC) {
|
||||
*has_progs_src = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case '-':
|
||||
if (!argv[0][2]) {
|
||||
/* anything following -- is considered a non-option argument */
|
||||
argend = true;
|
||||
break;
|
||||
}
|
||||
/* All long options without arguments */
|
||||
else if (!strcmp(argv[0]+2, "help")) {
|
||||
usage();
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "version")) {
|
||||
version();
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "quiet")) {
|
||||
OPTS_OPTION_BOOL(OPTION_QUIET) = true;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "add-info")) {
|
||||
OPTS_OPTION_BOOL(OPTION_ADD_INFO) = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* All long options with arguments */
|
||||
if (options_long_witharg("output", &argc, &argv, &argarg)) {
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
|
||||
opts_output_wasset = true;
|
||||
} else {
|
||||
con_out("Unknown parameter: %s\n", argv[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
con_out("Unknown parameter: %s\n", argv[0]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* it's a QC filename */
|
||||
item.filename = argv[0];
|
||||
item.type = TYPE_QC;
|
||||
vec_push(items, item);
|
||||
}
|
||||
}
|
||||
opts_ini_init(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* returns the line number, or -1 on error */
|
||||
static bool progs_nextline(char **out, size_t *alen, FILE *src) {
|
||||
int len;
|
||||
char *line;
|
||||
char *start;
|
||||
char *end;
|
||||
|
||||
line = *out;
|
||||
len = util_getline(&line, alen, src);
|
||||
if (len == -1)
|
||||
return false;
|
||||
|
||||
/* start at first non-blank */
|
||||
for (start = line; util_isspace(*start); ++start) {}
|
||||
/* end at the first non-blank */
|
||||
for (end = start; *end && !util_isspace(*end); ++end) {}
|
||||
|
||||
*out = line;
|
||||
/* move the actual filename to the beginning */
|
||||
while (start != end) {
|
||||
*line++ = *start++;
|
||||
}
|
||||
*line = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
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_QCC, (1024 << 3));
|
||||
|
||||
util_seed(time(0));
|
||||
|
||||
if (!options_parse(argc, argv, &has_progs_src)) {
|
||||
return usage();
|
||||
}
|
||||
|
||||
if (OPTS_FLAG(TRUE_EMPTY_STRINGS) && OPTS_FLAG(FALSE_EMPTY_STRINGS)) {
|
||||
con_err("-ftrue-empty-strings and -ffalse-empty-strings are mutually exclusive");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* the standard decides which set of operators to use */
|
||||
if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
|
||||
operators = c_operators;
|
||||
operator_count = GMQCC_ARRAY_COUNT(c_operators);
|
||||
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
|
||||
operators = fte_operators;
|
||||
operator_count = GMQCC_ARRAY_COUNT(fte_operators);
|
||||
} else {
|
||||
operators = qcc_operators;
|
||||
operator_count = GMQCC_ARRAY_COUNT(qcc_operators);
|
||||
}
|
||||
|
||||
if (operators == fte_operators) {
|
||||
/* fix ternary? */
|
||||
if (OPTS_FLAG(CORRECT_TERNARY)) {
|
||||
oper_info *newops;
|
||||
if (operators[operator_count-2].id != opid1(',') ||
|
||||
operators[operator_count-1].id != opid2(':','?'))
|
||||
{
|
||||
con_err("internal error: operator precedence table wasn't updated correctly!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
operators_free = true;
|
||||
newops = (oper_info*)mem_a(sizeof(operators[0]) * operator_count);
|
||||
memcpy(newops, operators, sizeof(operators[0]) * operator_count);
|
||||
memcpy(&newops[operator_count-2], &operators[operator_count-1], sizeof(newops[0]));
|
||||
memcpy(&newops[operator_count-1], &operators[operator_count-2], sizeof(newops[0]));
|
||||
newops[operator_count-2].prec = newops[operator_count-1].prec+1;
|
||||
operators = newops;
|
||||
}
|
||||
}
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_DUMP)) {
|
||||
for (itr = 0; itr < COUNT_FLAGS; ++itr)
|
||||
con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
|
||||
for (itr = 0; itr < COUNT_WARNINGS; ++itr)
|
||||
con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
|
||||
|
||||
con_out("output = %s\n", OPTS_OPTION_STR(OPTION_OUTPUT));
|
||||
con_out("optimization level = %u\n", OPTS_OPTION_U32(OPTION_O));
|
||||
con_out("standard = %u\n", OPTS_OPTION_U32(OPTION_STANDARD));
|
||||
}
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (opts_output_wasset) {
|
||||
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;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
else {
|
||||
outfile = con_default_out();
|
||||
}
|
||||
}
|
||||
|
||||
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (!(parser = parser_create())) {
|
||||
con_err("failed to initialize parser\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
|
||||
if (!(ftepp = ftepp_create())) {
|
||||
con_err("failed to initialize parser\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* add macros */
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
|
||||
for (itr = 0; itr < vec_size(ppems); itr++) {
|
||||
ftepp_add_macro(ftepp, ppems[itr].name, ppems[itr].value);
|
||||
mem_d(ppems[itr].name);
|
||||
|
||||
/* can be null */
|
||||
if (ppems[itr].value)
|
||||
mem_d(ppems[itr].value);
|
||||
}
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
size_t linelen = 0;
|
||||
bool has_first_line = false;
|
||||
|
||||
src = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
|
||||
if (!src) {
|
||||
con_err("failed to open `%s` for reading\n", OPTS_OPTION_STR(OPTION_PROGSRC));
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
while (progs_nextline(&line, &linelen, src)) {
|
||||
argitem item;
|
||||
|
||||
if (!line[0] || (line[0] == '/' && line[1] == '/')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(src);
|
||||
mem_d(line);
|
||||
}
|
||||
|
||||
if (vec_size(items)) {
|
||||
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
|
||||
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
for (itr = 0; itr < vec_size(items); ++itr) {
|
||||
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
|
||||
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
{
|
||||
con_out(" item: %s (%s)\n",
|
||||
items[itr].filename,
|
||||
( (items[itr].type == TYPE_QC ? "qc" :
|
||||
(items[itr].type == TYPE_ASM ? "asm" :
|
||||
(items[itr].type == TYPE_SRC ? "progs.src" :
|
||||
("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)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
out = ftepp_get(ftepp);
|
||||
if (out)
|
||||
fprintf(outfile, "%s", out);
|
||||
ftepp_flush(ftepp);
|
||||
}
|
||||
else {
|
||||
if (OPTS_FLAG(FTEPP)) {
|
||||
const char *data;
|
||||
if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
data = ftepp_get(ftepp);
|
||||
if (vec_size(data)) {
|
||||
if (!parser_compile_string(parser, items[itr].filename, data, vec_size(data))) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
ftepp_flush(ftepp);
|
||||
}
|
||||
else {
|
||||
if (!parser_compile_file(parser, items[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_progs_src) {
|
||||
mem_d(items[itr].filename);
|
||||
items[itr].filename = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ftepp_finish(ftepp);
|
||||
ftepp = nullptr;
|
||||
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (ftepp)
|
||||
ftepp_finish(ftepp);
|
||||
con_close();
|
||||
vec_free(items);
|
||||
vec_free(ppems);
|
||||
|
||||
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
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();
|
||||
|
||||
if (!retval && compile_errors)
|
||||
retval = 1;
|
||||
return retval;
|
||||
}
|
51
misc/check-doc.sh
Executable file
51
misc/check-doc.sh
Executable file
|
@ -0,0 +1,51 @@
|
|||
#!/bin/sh
|
||||
prog=$0
|
||||
|
||||
die() {
|
||||
echo "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
want() {
|
||||
test -e "$1" && return
|
||||
echo "$prog: missing $1"
|
||||
echo "$prog: run this script from the top of a gmqcc source tree"
|
||||
exit 1
|
||||
}
|
||||
|
||||
for i in opts.def \
|
||||
doc/gmqcc.1 \
|
||||
gmqcc.ini.example
|
||||
do want "$i"; done
|
||||
|
||||
# y/_ABCDEFGHIJKLMNOPQRSTUVWXYZ/-abcdefghijklmnopqrstuvwxyz/;
|
||||
check_opt() {
|
||||
opt_def_name=$1
|
||||
arg_char=$2
|
||||
|
||||
for i in $(sed -ne \
|
||||
'/^#ifdef GMQCC_TYPE_'${opt_def_name}'$/,/^#endif/{
|
||||
/GMQCC_DEFINE_FLAG/{
|
||||
s/^.*GMQCC_DEFINE_FLAG(\([^,)]*\)[),].*$/\1/;p;
|
||||
}
|
||||
}' opts.def)
|
||||
do
|
||||
opt=$(echo "$i" | tr -- '_A-Z' '-a-z')
|
||||
grep -qF -- ".It Fl "${arg_char}" Ns Cm $opt" \
|
||||
doc/gmqcc.1 || echo "doc/gmqcc.1: missing: -${arg_char}$opt"
|
||||
grep -q -- "[^a-zA-Z_]$i[^a-zA-Z_]" \
|
||||
gmqcc.ini.example || echo "gmqcc.ini.example: missing: $i"
|
||||
done
|
||||
}
|
||||
|
||||
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
|
424
opts.cpp
Normal file
424
opts.cpp
Normal file
|
@ -0,0 +1,424 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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_STR(OPTION_PROGSRC) = "progs.src";
|
||||
|
||||
/* warnings */
|
||||
opts_set(opts.warn, WARN_UNUSED_VARIABLE, true);
|
||||
opts_set(opts.warn, WARN_USED_UNINITIALIZED, true);
|
||||
opts_set(opts.warn, WARN_UNKNOWN_CONTROL_SEQUENCE, true);
|
||||
opts_set(opts.warn, WARN_EXTENSIONS, true);
|
||||
opts_set(opts.warn, WARN_FIELD_REDECLARED, true);
|
||||
opts_set(opts.warn, WARN_MISSING_RETURN_VALUES, true);
|
||||
opts_set(opts.warn, WARN_INVALID_PARAMETER_COUNT, true);
|
||||
opts_set(opts.warn, WARN_LOCAL_CONSTANTS, true);
|
||||
opts_set(opts.warn, WARN_VOID_VARIABLES, true);
|
||||
opts_set(opts.warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
|
||||
opts_set(opts.warn, WARN_VARIADIC_FUNCTION, true);
|
||||
opts_set(opts.warn, WARN_FRAME_MACROS, true);
|
||||
opts_set(opts.warn, WARN_EFFECTLESS_STATEMENT, true);
|
||||
opts_set(opts.warn, WARN_END_SYS_FIELDS, true);
|
||||
opts_set(opts.warn, WARN_ASSIGN_FUNCTION_TYPES, true);
|
||||
opts_set(opts.warn, WARN_CPP, true);
|
||||
opts_set(opts.warn, WARN_MULTIFILE_IF, true);
|
||||
opts_set(opts.warn, WARN_DOUBLE_DECLARATION, true);
|
||||
opts_set(opts.warn, WARN_CONST_VAR, true);
|
||||
opts_set(opts.warn, WARN_MULTIBYTE_CHARACTER, true);
|
||||
opts_set(opts.warn, WARN_UNKNOWN_PRAGMAS, true);
|
||||
opts_set(opts.warn, WARN_UNREACHABLE_CODE, true);
|
||||
opts_set(opts.warn, WARN_UNKNOWN_ATTRIBUTE, true);
|
||||
opts_set(opts.warn, WARN_RESERVED_NAMES, true);
|
||||
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);
|
||||
opts_set(opts.flags, CORRECT_TERNARY, true);
|
||||
opts_set(opts.flags, BAIL_ON_WERROR, true);
|
||||
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() {
|
||||
size_t i;
|
||||
for (i = 0; i <= WARN_DEBUG; ++i)
|
||||
opts_set(opts.warn_backup, i, OPTS_WARN(i));
|
||||
}
|
||||
|
||||
void opts_restore_non_Wall() {
|
||||
size_t i;
|
||||
for (i = 0; i <= WARN_DEBUG; ++i)
|
||||
opts_set(opts.warn, i, OPTS_GENERIC(opts.warn_backup, i));
|
||||
}
|
||||
|
||||
void opts_backup_non_Werror_all() {
|
||||
size_t i;
|
||||
for (i = 0; i <= WARN_DEBUG; ++i)
|
||||
opts_set(opts.werror_backup, i, OPTS_WERROR(i));
|
||||
}
|
||||
|
||||
void opts_restore_non_Werror_all() {
|
||||
size_t i;
|
||||
for (i = 0; i <= WARN_DEBUG; ++i)
|
||||
opts_set(opts.werror, i, OPTS_GENERIC(opts.werror_backup, i));
|
||||
}
|
||||
|
||||
void opts_init(const char *output, int standard, size_t arraysize) {
|
||||
opts_setdefault();
|
||||
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = output;
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = standard;
|
||||
OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!strcmp(name, list[i].name)) {
|
||||
longbit lb = list[i].bit;
|
||||
|
||||
if (on)
|
||||
flags[lb.idx] |= (1<<(lb.bit));
|
||||
else
|
||||
flags[lb.idx] &= ~(1<<(lb.bit));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool opts_setflag (const char *name, bool on) {
|
||||
return opts_setflag_all(name, on, opts.flags, opts_flag_list, COUNT_FLAGS);
|
||||
}
|
||||
bool opts_setwarn (const char *name, bool on) {
|
||||
return opts_setflag_all(name, on, opts.warn, opts_warn_list, COUNT_WARNINGS);
|
||||
}
|
||||
bool opts_setwerror(const char *name, bool on) {
|
||||
return opts_setflag_all(name, on, opts.werror, opts_warn_list, COUNT_WARNINGS);
|
||||
}
|
||||
bool opts_setoptim (const char *name, bool on) {
|
||||
return opts_setflag_all(name, on, opts.optimization, opts_opt_list, COUNT_OPTIMIZATIONS);
|
||||
}
|
||||
|
||||
void opts_set(uint32_t *flags, size_t idx, bool on) {
|
||||
longbit lb;
|
||||
LONGBIT_SET(lb, idx);
|
||||
|
||||
if (on)
|
||||
flags[lb.idx] |= (1u<<(lb.bit));
|
||||
else
|
||||
flags[lb.idx] &= ~(1u<<(lb.bit));
|
||||
}
|
||||
|
||||
void opts_setoptimlevel(unsigned int level) {
|
||||
size_t i;
|
||||
for (i = 0; i < COUNT_OPTIMIZATIONS; ++i)
|
||||
opts_set(opts.optimization, i, level >= opts_opt_oflag[i]);
|
||||
|
||||
if (!level)
|
||||
opts.optimizeoff = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Standard configuration parser and subsystem. Yes, optionally you may
|
||||
* create ini files or cfg (the driver accepts both) for a project opposed
|
||||
* to supplying just a progs.src (since you also may need to supply command
|
||||
* line arguments or set the options of the compiler) [which cannot be done
|
||||
* from a progs.src.
|
||||
*/
|
||||
static char *opts_ini_rstrip(char *s) {
|
||||
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 && util_isspace(*s))
|
||||
s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
static char *opts_ini_next(const char *s, char c) {
|
||||
bool last = false;
|
||||
while (*s && *s != c && !(last && *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 **),
|
||||
char **errorhandle,
|
||||
char **parse_file
|
||||
) {
|
||||
size_t linesize;
|
||||
size_t lineno = 1;
|
||||
size_t error = 0;
|
||||
char *line = nullptr;
|
||||
char section_data[2048] = "";
|
||||
char oldname_data[2048] = "";
|
||||
|
||||
/* parsing and reading variables */
|
||||
char *parse_beg;
|
||||
char *parse_end;
|
||||
char *read_name;
|
||||
char *read_value;
|
||||
|
||||
while (util_getline(&line, &linesize, filehandle) != EOF) {
|
||||
parse_beg = line;
|
||||
|
||||
/* handle BOM */
|
||||
if (lineno == 1 && (
|
||||
(unsigned char)parse_beg[0] == 0xEF &&
|
||||
(unsigned char)parse_beg[1] == 0xBB &&
|
||||
(unsigned char)parse_beg[2] == 0xBF
|
||||
)
|
||||
) {
|
||||
parse_beg ++; /* 0xEF */
|
||||
parse_beg ++; /* 0xBB */
|
||||
parse_beg ++; /* 0xBF */
|
||||
}
|
||||
|
||||
if (*(parse_beg = opts_ini_lskip(opts_ini_rstrip(parse_beg))) == ';' || *parse_beg == '#') {
|
||||
/* ignore '#' is a perl extension */
|
||||
} else if (*parse_beg == '[') {
|
||||
/* section found */
|
||||
if (*(parse_end = opts_ini_next(parse_beg + 1, ']')) == ']') {
|
||||
* parse_end = '\0'; /* terminate bro */
|
||||
util_strncpy(section_data, parse_beg + 1, sizeof(section_data));
|
||||
section_data[sizeof(section_data) - 1] = '\0';
|
||||
*oldname_data = '\0';
|
||||
} else if (!error) {
|
||||
/* otherwise set error to the current line number */
|
||||
error = lineno;
|
||||
}
|
||||
} 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, ':');
|
||||
|
||||
if (*parse_end == '=' || *parse_end == ':') {
|
||||
*parse_end = '\0'; /* terminate bro */
|
||||
read_name = opts_ini_rstrip(parse_beg);
|
||||
read_value = opts_ini_lskip(parse_end + 1);
|
||||
if (*(parse_end = opts_ini_next(read_value, '\0')) == ';')
|
||||
* parse_end = '\0';
|
||||
opts_ini_rstrip(read_value);
|
||||
|
||||
/* valid name value pair, lets call down to handler */
|
||||
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, 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;
|
||||
}
|
||||
}
|
||||
lineno++;
|
||||
}
|
||||
mem_d(line);
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* returns true/false for a char that contains ("true" or "false" or numeric 0/1)
|
||||
*/
|
||||
static bool opts_ini_bool(const char *value) {
|
||||
if (!strcmp(value, "true")) return true;
|
||||
if (!strcmp(value, "false")) return false;
|
||||
return !!strtol(value, nullptr, 10);
|
||||
}
|
||||
|
||||
static char *opts_ini_load(const char *section, const char *name, const char *value, char **parse_file) {
|
||||
char *error = nullptr;
|
||||
bool found = false;
|
||||
|
||||
/*
|
||||
* undef all of these because they may still be defined like in my
|
||||
* case they where.
|
||||
*/
|
||||
#undef GMQCC_TYPE_FLAGS
|
||||
#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) \
|
||||
if (!strcmp(section, "flags") && !strcmp(name, #X)) { \
|
||||
opts_set(opts.flags, X, opts_ini_bool(value)); \
|
||||
found = true; \
|
||||
}
|
||||
#include "opts.def"
|
||||
|
||||
/* warnings */
|
||||
#define GMQCC_TYPE_WARNS
|
||||
#define GMQCC_DEFINE_FLAG(X) \
|
||||
if (!strcmp(section, "warnings") && !strcmp(name, #X)) { \
|
||||
opts_set(opts.warn, WARN_##X, opts_ini_bool(value)); \
|
||||
found = true; \
|
||||
}
|
||||
#include "opts.def"
|
||||
|
||||
/* Werror-individuals */
|
||||
#define GMQCC_TYPE_WARNS
|
||||
#define GMQCC_DEFINE_FLAG(X) \
|
||||
if (!strcmp(section, "errors") && !strcmp(name, #X)) { \
|
||||
opts_set(opts.werror, WARN_##X, opts_ini_bool(value)); \
|
||||
found = true; \
|
||||
}
|
||||
#include "opts.def"
|
||||
|
||||
/* optimizations */
|
||||
#define GMQCC_TYPE_OPTIMIZATIONS
|
||||
#define GMQCC_DEFINE_FLAG(X,Y) \
|
||||
if (!strcmp(section, "optimizations") && !strcmp(name, #X)) { \
|
||||
opts_set(opts.optimization, OPTIM_##X, opts_ini_bool(value)); \
|
||||
found = true; \
|
||||
}
|
||||
#include "opts.def"
|
||||
|
||||
/* nothing was found ever! */
|
||||
if (!found) {
|
||||
if (strcmp(section, "includes") &&
|
||||
strcmp(section, "flags") &&
|
||||
strcmp(section, "warnings") &&
|
||||
strcmp(section, "optimizations"))
|
||||
{
|
||||
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 {
|
||||
static const char *expected_something = "expected something";
|
||||
vec_append(error, strlen(expected_something), expected_something);
|
||||
}
|
||||
}
|
||||
vec_push(error, '\0');
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Actual loading subsystem, this finds the ini or cfg file, and properly
|
||||
* loads it and executes it to set compiler options.
|
||||
*/
|
||||
void opts_ini_init(const char *file) {
|
||||
/*
|
||||
* Possible matches are:
|
||||
* gmqcc.ini
|
||||
* gmqcc.cfg
|
||||
*/
|
||||
char *error = nullptr;
|
||||
char *parse_file = nullptr;
|
||||
size_t line;
|
||||
FILE *ini;
|
||||
|
||||
if (!file) {
|
||||
/* try ini */
|
||||
if (!(ini = fopen((file = "gmqcc.ini"), "r")))
|
||||
/* try cfg */
|
||||
if (!(ini = fopen((file = "gmqcc.cfg"), "r")))
|
||||
return;
|
||||
} else if (!(ini = fopen(file, "r")))
|
||||
return;
|
||||
|
||||
con_out("found ini file `%s`\n", file);
|
||||
|
||||
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, parse_file, line, 0 /*TODO: column for ini error*/, "error", error);
|
||||
vec_free(error);
|
||||
}
|
||||
mem_d(parse_file);
|
||||
|
||||
fclose(ini);
|
||||
}
|
126
opts.def
Normal file
126
opts.def
Normal file
|
@ -0,0 +1,126 @@
|
|||
#ifndef GMQCC_DEFINE_FLAG
|
||||
# error "bad opts.def usage"
|
||||
#endif
|
||||
|
||||
/* codegen flags */
|
||||
#ifdef GMQCC_TYPE_FLAGS
|
||||
GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
|
||||
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)
|
||||
GMQCC_DEFINE_FLAG(TRANSLATABLE_STRINGS)
|
||||
GMQCC_DEFINE_FLAG(INITIALIZED_NONCONSTANTS)
|
||||
GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES)
|
||||
GMQCC_DEFINE_FLAG(LNO)
|
||||
GMQCC_DEFINE_FLAG(CORRECT_TERNARY)
|
||||
GMQCC_DEFINE_FLAG(SINGLE_VECTOR_DEFS)
|
||||
GMQCC_DEFINE_FLAG(CORRECT_LOGIC)
|
||||
GMQCC_DEFINE_FLAG(TRUE_EMPTY_STRINGS)
|
||||
GMQCC_DEFINE_FLAG(FALSE_EMPTY_STRINGS)
|
||||
GMQCC_DEFINE_FLAG(UTF8)
|
||||
GMQCC_DEFINE_FLAG(BAIL_ON_WERROR)
|
||||
GMQCC_DEFINE_FLAG(LOOP_LABELS)
|
||||
GMQCC_DEFINE_FLAG(UNTYPED_NIL)
|
||||
GMQCC_DEFINE_FLAG(PERMISSIVE)
|
||||
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 */
|
||||
#ifdef GMQCC_TYPE_WARNS
|
||||
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)
|
||||
GMQCC_DEFINE_FLAG(FIELD_REDECLARED)
|
||||
GMQCC_DEFINE_FLAG(MISSING_RETURN_VALUES)
|
||||
GMQCC_DEFINE_FLAG(INVALID_PARAMETER_COUNT)
|
||||
GMQCC_DEFINE_FLAG(LOCAL_SHADOWS)
|
||||
GMQCC_DEFINE_FLAG(LOCAL_CONSTANTS)
|
||||
GMQCC_DEFINE_FLAG(VOID_VARIABLES)
|
||||
GMQCC_DEFINE_FLAG(IMPLICIT_FUNCTION_POINTER)
|
||||
GMQCC_DEFINE_FLAG(VARIADIC_FUNCTION)
|
||||
GMQCC_DEFINE_FLAG(FRAME_MACROS)
|
||||
GMQCC_DEFINE_FLAG(EFFECTLESS_STATEMENT)
|
||||
GMQCC_DEFINE_FLAG(END_SYS_FIELDS)
|
||||
GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES)
|
||||
GMQCC_DEFINE_FLAG(CPP)
|
||||
GMQCC_DEFINE_FLAG(MULTIFILE_IF)
|
||||
GMQCC_DEFINE_FLAG(DOUBLE_DECLARATION)
|
||||
GMQCC_DEFINE_FLAG(CONST_VAR)
|
||||
GMQCC_DEFINE_FLAG(MULTIBYTE_CHARACTER)
|
||||
GMQCC_DEFINE_FLAG(TERNARY_PRECEDENCE)
|
||||
GMQCC_DEFINE_FLAG(UNKNOWN_PRAGMAS)
|
||||
GMQCC_DEFINE_FLAG(UNREACHABLE_CODE)
|
||||
GMQCC_DEFINE_FLAG(UNKNOWN_ATTRIBUTE)
|
||||
GMQCC_DEFINE_FLAG(RESERVED_NAMES)
|
||||
GMQCC_DEFINE_FLAG(UNINITIALIZED_CONSTANT)
|
||||
GMQCC_DEFINE_FLAG(DIFFERENT_QUALIFIERS)
|
||||
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
|
||||
GMQCC_DEFINE_FLAG(PEEPHOLE, 1)
|
||||
GMQCC_DEFINE_FLAG(TAIL_RECURSION, 1)
|
||||
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS, 3)
|
||||
GMQCC_DEFINE_FLAG(LOCAL_TEMPS, 3)
|
||||
GMQCC_DEFINE_FLAG(GLOBAL_TEMPS, 3)
|
||||
GMQCC_DEFINE_FLAG(STRIP_CONSTANT_NAMES, 1)
|
||||
GMQCC_DEFINE_FLAG(OVERLAP_STRINGS, 2)
|
||||
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
|
||||
GMQCC_DEFINE_FLAG(O)
|
||||
GMQCC_DEFINE_FLAG(OUTPUT)
|
||||
GMQCC_DEFINE_FLAG(QUIET)
|
||||
GMQCC_DEFINE_FLAG(G)
|
||||
GMQCC_DEFINE_FLAG(STANDARD)
|
||||
GMQCC_DEFINE_FLAG(DEBUG)
|
||||
GMQCC_DEFINE_FLAG(DUMPFIN)
|
||||
GMQCC_DEFINE_FLAG(DUMP)
|
||||
GMQCC_DEFINE_FLAG(FORCECRC)
|
||||
GMQCC_DEFINE_FLAG(FORCED_CRC)
|
||||
GMQCC_DEFINE_FLAG(PP_ONLY)
|
||||
GMQCC_DEFINE_FLAG(MAX_ARRAY_SIZE)
|
||||
GMQCC_DEFINE_FLAG(ADD_INFO)
|
||||
GMQCC_DEFINE_FLAG(PROGSRC)
|
||||
GMQCC_DEFINE_FLAG(COVERAGE)
|
||||
GMQCC_DEFINE_FLAG(STATE_FPS)
|
||||
#endif
|
||||
|
||||
/* some cleanup so we don't have to */
|
||||
#undef GMQCC_TYPE_FLAGS
|
||||
#undef GMQCC_TYPE_WARNS
|
||||
#undef GMQCC_TYPE_OPTIONS
|
||||
#undef GMQCC_TYPE_OPTIMIZATIONS
|
||||
#undef GMQCC_DEFINE_FLAG
|
6416
parser.cpp
Normal file
6416
parser.cpp
Normal file
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
|
35
propsal.txt
35
propsal.txt
|
@ -1,35 +0,0 @@
|
|||
This is a propsal to extend the progs.dat file format without breaking
|
||||
backwards compatability. Currently the progs file format header has a
|
||||
description simaler to this:
|
||||
|
||||
struct {
|
||||
uint32_t version;
|
||||
uint32_t crc16;
|
||||
....
|
||||
uint32_t entfield;
|
||||
}
|
||||
|
||||
The obvious notable issue here is version and crc16 are larger than they
|
||||
essentially need to be, if we made version and crc16 both uint16_t we can
|
||||
give ourselfs 32 bytes (2x16) to store additional data that can be used
|
||||
to make smaller progs.dat files.
|
||||
|
||||
I propose a new structual layout like this:
|
||||
struct {
|
||||
uint16_t version;
|
||||
uint16_t flags; /* contains a skip field */
|
||||
uint16_t crc16;
|
||||
uint16_t skip; /* skiped globals */
|
||||
....
|
||||
uint32_t entfield;
|
||||
}
|
||||
|
||||
about 45% of globals are zero, if we could order them at the top of the
|
||||
globals array we can essentially use the skip field to specify how much
|
||||
zero globals the engine would have to populate (instead of being stored
|
||||
in the actual file itself) flags can specify if the progs.dat file skiped
|
||||
globals on the write of the progs.dat file.
|
||||
|
||||
Of course only one bit in the flags would have to be set to specify if the
|
||||
file contains a skip field. Which lends itself to the fact that flags could
|
||||
later be extended for other things.
|
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,163 +0,0 @@
|
|||
#ifndef TEST_AST_MACROS_HDR
|
||||
#define TEST_AST_MACROS_HDR
|
||||
|
||||
#define TESTVARS() \
|
||||
ast_block *curblock; \
|
||||
lex_ctx ctx
|
||||
|
||||
#define TESTINIT() \
|
||||
ctx.file = NULL; \
|
||||
ctx.line = 1;
|
||||
|
||||
#define DEFVAR(name) \
|
||||
ast_value *name
|
||||
|
||||
#define VAR(type, name) \
|
||||
name = ast_value_new(ctx, #name, type)
|
||||
|
||||
#define VARnamed(type, name, varname) \
|
||||
name = ast_value_new(ctx, #varname, type)
|
||||
|
||||
#define MKGLOBAL(name) \
|
||||
assert(globals_add(name) >= 0)
|
||||
|
||||
#define FIELD(type, name) \
|
||||
name = ast_value_new(ctx, #name, TYPE_FIELD); \
|
||||
do { \
|
||||
ast_value *field_##name = ast_value_new(ctx, #name, type); \
|
||||
name->expression.next = (ast_expression*)field_##name; \
|
||||
MKFIELD(name); \
|
||||
} while (0)
|
||||
|
||||
#define MKFIELD(name) \
|
||||
assert(fields_add(name) >= 0)
|
||||
|
||||
#define MKCONSTFLOAT(name, value) \
|
||||
do { \
|
||||
name->isconst = true; \
|
||||
name->constval.vfloat = value; \
|
||||
MKGLOBAL(name); \
|
||||
} while(0)
|
||||
|
||||
#define MKCONSTSTRING(name, value) \
|
||||
do { \
|
||||
name->isconst = true; \
|
||||
name->constval.vstring = util_strdup(value); \
|
||||
MKGLOBAL(name); \
|
||||
} while(0)
|
||||
|
||||
#define MKCONSTVECTOR(name, valx, valy, valz) \
|
||||
do { \
|
||||
name->isconst = true; \
|
||||
name->constval.vvec.x = (valx); \
|
||||
name->constval.vvec.y = (valy); \
|
||||
name->constval.vvec.z = (valz); \
|
||||
MKGLOBAL(name); \
|
||||
} while(0)
|
||||
|
||||
#define STATE(a) \
|
||||
do { \
|
||||
ast_expression *exp = (ast_expression*)(a); \
|
||||
assert(ast_block_exprs_add(curblock, exp)); \
|
||||
} while(0)
|
||||
|
||||
#define ASSIGN(op, a, b) \
|
||||
(ast_expression*)ast_store_new(ctx, INSTR_##op, (ast_expression*)(a), (ast_expression*)(b))
|
||||
|
||||
#define BIN(op, a, b) \
|
||||
(ast_expression*)ast_binary_new(ctx, INSTR_##op, (ast_expression*)(a), (ast_expression*)(b))
|
||||
|
||||
#define ENTFIELD(a, b) \
|
||||
(ast_expression*)ast_entfield_new(ctx, (ast_expression*)(a), (ast_expression*)(b))
|
||||
|
||||
#define VECMEM(vec, mem) \
|
||||
(ast_expression*)ast_member_new(ctx, (ast_expression*)(vec), (mem))
|
||||
|
||||
#define CALL(what) \
|
||||
do { \
|
||||
ast_call *call = ast_call_new(ctx, (ast_expression*)what); \
|
||||
|
||||
#define CALLPARAM(x) \
|
||||
assert(ast_call_params_add(call, (ast_expression*)x));
|
||||
|
||||
#define ENDCALL() \
|
||||
STATE(call); \
|
||||
} while(0)
|
||||
|
||||
#define ENDCALLWITH(as, where) \
|
||||
{ \
|
||||
ast_expression *as = (ast_expression*)call; \
|
||||
where; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define WHILE(cond) \
|
||||
do { \
|
||||
ast_expression *wh_cond = (ast_expression*)(cond); \
|
||||
ast_block *wh_body = ast_block_new(ctx); \
|
||||
ast_block *oldcur = curblock; \
|
||||
ast_loop *loop; \
|
||||
curblock = wh_body;
|
||||
|
||||
#define ENDWHILE() \
|
||||
curblock = oldcur; \
|
||||
loop = ast_loop_new(ctx, NULL, (ast_expression*)wh_cond, \
|
||||
NULL, NULL, (ast_expression*)wh_body); \
|
||||
assert(loop); \
|
||||
STATE(loop); \
|
||||
} while(0)
|
||||
|
||||
#define BUILTIN(name, outtype, number) \
|
||||
do { \
|
||||
ast_function *func_##name; \
|
||||
ast_value *thisfuncval; \
|
||||
ast_function *thisfunc; \
|
||||
DEFVAR(return_##name); \
|
||||
VARnamed(TYPE_FUNCTION, name, name); \
|
||||
VARnamed(outtype, return_##name, "#returntype"); \
|
||||
name->expression.next = (ast_expression*)return_##name; \
|
||||
MKGLOBAL(name); \
|
||||
func_##name = ast_function_new(ctx, #name, name); \
|
||||
thisfunc = func_##name; \
|
||||
(void)thisfunc; \
|
||||
thisfuncval = name; \
|
||||
(void)thisfuncval; \
|
||||
assert(functions_add(func_##name) >= 0); \
|
||||
func_##name->builtin = number;
|
||||
|
||||
#define ENDBUILTIN() } while(0)
|
||||
|
||||
#define PARAM(ptype, name) \
|
||||
do { \
|
||||
DEFVAR(parm); \
|
||||
VARnamed(ptype, parm, name); \
|
||||
assert(ast_value_params_add(thisfuncval, parm)); \
|
||||
} while(0)
|
||||
|
||||
#define FUNCTION(name, outtype) \
|
||||
do { \
|
||||
ast_function *thisfunc; \
|
||||
ast_function *func_##name; \
|
||||
ast_block *my_funcblock; \
|
||||
DEFVAR(var_##name); \
|
||||
DEFVAR(return_##name); \
|
||||
VARnamed(TYPE_FUNCTION, var_##name, name); \
|
||||
VARnamed(outtype, return_##name, "#returntype"); \
|
||||
var_##name->expression.next = (ast_expression*)return_##name; \
|
||||
MKGLOBAL(var_##name); \
|
||||
func_##name = ast_function_new(ctx, #name, var_##name); \
|
||||
thisfunc = func_##name; \
|
||||
(void)thisfunc; \
|
||||
assert(functions_add(func_##name) >= 0); \
|
||||
my_funcblock = ast_block_new(ctx); \
|
||||
assert(my_funcblock); \
|
||||
assert(ast_function_blocks_add(func_##name, my_funcblock)); \
|
||||
curblock = my_funcblock;
|
||||
|
||||
#define MKLOCAL(var) \
|
||||
assert(ast_block_locals_add(curblock, var))
|
||||
|
||||
#define ENDFUNCTION(name) \
|
||||
} while(0)
|
||||
|
||||
#endif
|
201
test/ast-test.c
201
test/ast-test.c
|
@ -1,201 +0,0 @@
|
|||
#include "gmqcc.h"
|
||||
#include "ast.h"
|
||||
|
||||
/* NOTE: it's a test - I'll abort() on epic-failure */
|
||||
|
||||
#ifdef assert
|
||||
# undef assert
|
||||
#endif
|
||||
/* (note: 'do {} while(0)' forces the need for a semicolon after assert() */
|
||||
#define assert(x) do { if ( !(x) ) { printf("Assertion failed: %s\n", #x); abort(); } } while(0)
|
||||
|
||||
VECTOR_MAKE(ast_value*, globals);
|
||||
VECTOR_MAKE(ast_value*, fields);
|
||||
VECTOR_MAKE(ast_function*, functions);
|
||||
|
||||
uint32_t opts_flags[1 + (COUNT_FLAGS / 32)];
|
||||
uint32_t opts_warn [1 + (COUNT_WARNINGS / 32)];
|
||||
|
||||
uint32_t opts_O = 1;
|
||||
const char *opts_output = "progs.dat";
|
||||
int opts_standard = COMPILER_GMQCC;
|
||||
bool opts_debug = false;
|
||||
bool opts_memchk = false;
|
||||
bool opts_werror = false;
|
||||
|
||||
#include "ast-macros.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t i;
|
||||
|
||||
ir_builder *ir;
|
||||
|
||||
TESTVARS();
|
||||
|
||||
DEFVAR(vi);
|
||||
DEFVAR(vx);
|
||||
DEFVAR(f0);
|
||||
DEFVAR(f1);
|
||||
DEFVAR(f5);
|
||||
DEFVAR(cv3x4x5);
|
||||
DEFVAR(cv1x1x1);
|
||||
DEFVAR(sHello);
|
||||
DEFVAR(sNL);
|
||||
DEFVAR(print);
|
||||
DEFVAR(ftos);
|
||||
DEFVAR(spawn);
|
||||
|
||||
DEFVAR(mema);
|
||||
DEFVAR(memb);
|
||||
DEFVAR(memv);
|
||||
DEFVAR(pawn);
|
||||
|
||||
/* opts_debug = true; */
|
||||
|
||||
BUILTIN(print, TYPE_VOID, -1);
|
||||
PARAM(TYPE_STRING, text);
|
||||
ENDBUILTIN();
|
||||
|
||||
BUILTIN(ftos, TYPE_STRING, -2);
|
||||
PARAM(TYPE_FLOAT, value);
|
||||
ENDBUILTIN();
|
||||
|
||||
BUILTIN(spawn, TYPE_ENTITY, -3);
|
||||
ENDBUILTIN();
|
||||
|
||||
TESTINIT();
|
||||
VAR(TYPE_FLOAT, f0);
|
||||
VAR(TYPE_FLOAT, f1);
|
||||
VAR(TYPE_FLOAT, f5);
|
||||
VAR(TYPE_STRING, sHello);
|
||||
VAR(TYPE_STRING, sNL);
|
||||
VAR(TYPE_VECTOR, cv3x4x5);
|
||||
VAR(TYPE_VECTOR, cv1x1x1);
|
||||
|
||||
FIELD(TYPE_FLOAT, mema);
|
||||
FIELD(TYPE_FLOAT, memb);
|
||||
FIELD(TYPE_VECTOR, memv);
|
||||
|
||||
MKCONSTFLOAT(f0, 0.0);
|
||||
MKCONSTFLOAT(f1, 1.0);
|
||||
MKCONSTFLOAT(f5, 5.0);
|
||||
MKCONSTSTRING(sHello, "Hello, World\n");
|
||||
MKCONSTSTRING(sNL, "\n");
|
||||
MKCONSTVECTOR(cv3x4x5, 3, 4, 5);
|
||||
MKCONSTVECTOR(cv1x1x1, 1, 1, 1);
|
||||
|
||||
FUNCTION(foo, TYPE_VOID);
|
||||
ENDFUNCTION(foo);
|
||||
|
||||
#define PRINTNL() do { CALL(print) CALLPARAM(sNL) ENDCALL(); } while(0)
|
||||
|
||||
FUNCTION(main, TYPE_VOID);
|
||||
|
||||
VAR(TYPE_FLOAT, vi);
|
||||
VAR(TYPE_FLOAT, vx);
|
||||
VAR(TYPE_ENTITY, pawn);
|
||||
|
||||
MKLOCAL(vi);
|
||||
MKLOCAL(vx);
|
||||
MKLOCAL(pawn);
|
||||
|
||||
STATE(ASSIGN(STORE_F, vi, f0));
|
||||
WHILE(BIN(LT, vi, f5));
|
||||
STATE(ASSIGN(STORE_F, vx, BIN(MUL_F, vi, f5)));
|
||||
STATE(ASSIGN(STORE_F, vi, BIN(ADD_F, vi, f1)));
|
||||
ENDWHILE();
|
||||
|
||||
CALL(print)
|
||||
CALLPARAM(sHello)
|
||||
ENDCALL();
|
||||
|
||||
CALL(spawn)
|
||||
ENDCALLWITH(newent, STATE(ASSIGN(STORE_ENT, pawn, newent)));
|
||||
|
||||
STATE(ASSIGN(STOREP_F, ENTFIELD(pawn, mema), f5));
|
||||
STATE(ASSIGN(STOREP_F, ENTFIELD(pawn, memb), f1));
|
||||
STATE(ASSIGN(STOREP_V, ENTFIELD(pawn, memv), cv3x4x5));
|
||||
CALL(ftos)
|
||||
CALLPARAM(ENTFIELD(pawn, mema))
|
||||
ENDCALLWITH(output,
|
||||
CALL(print)
|
||||
CALLPARAM(output)
|
||||
CALLPARAM(sNL)
|
||||
ENDCALL();
|
||||
);
|
||||
CALL(ftos)
|
||||
CALLPARAM(ENTFIELD(pawn, memb))
|
||||
ENDCALLWITH(output,
|
||||
CALL(print)
|
||||
CALLPARAM(output)
|
||||
CALLPARAM(sNL)
|
||||
ENDCALL();
|
||||
);
|
||||
CALL(ftos)
|
||||
CALLPARAM(ENTFIELD(pawn, VECMEM(memv, 2)))
|
||||
ENDCALLWITH(output,
|
||||
CALL(print)
|
||||
CALLPARAM(output)
|
||||
CALLPARAM(sNL)
|
||||
ENDCALL();
|
||||
);
|
||||
|
||||
ENDFUNCTION(main);
|
||||
|
||||
ir = ir_builder_new("ast_test");
|
||||
assert(ir);
|
||||
|
||||
/* gen fields */
|
||||
for (i = 0; i < fields_elements; ++i) {
|
||||
if (!ast_global_codegen(fields_data[i], ir)) {
|
||||
assert(!"failed to generate field");
|
||||
}
|
||||
}
|
||||
/* gen globals */
|
||||
for (i = 0; i < globals_elements; ++i) {
|
||||
if (!ast_global_codegen(globals_data[i], ir)) {
|
||||
assert(!"failed to generate global");
|
||||
}
|
||||
}
|
||||
|
||||
/* gen functions */
|
||||
for (i = 0; i < functions_elements; ++i) {
|
||||
if (!ast_function_codegen(functions_data[i], ir)) {
|
||||
assert(!"failed to generate function");
|
||||
}
|
||||
if (!ir_function_finalize(functions_data[i]->ir_func))
|
||||
assert(!"finalize on function failed...");
|
||||
}
|
||||
|
||||
|
||||
/* dump */
|
||||
ir_builder_dump(ir, printf);
|
||||
|
||||
/* Now create a file */
|
||||
if (!ir_builder_generate(ir, "test_ast.dat"))
|
||||
printf("*** failed to generate code\n");
|
||||
|
||||
/* ir cleanup */
|
||||
ir_builder_delete(ir);
|
||||
|
||||
/* cleanup */
|
||||
/* Functions must be deleted FIRST since their expressions
|
||||
* reference global variables.
|
||||
*/
|
||||
for (i = 0; i < functions_elements; ++i) {
|
||||
ast_function_delete(functions_data[i]);
|
||||
}
|
||||
if (functions_data)
|
||||
mem_d(functions_data);
|
||||
|
||||
/* We must delete not only globals, but also the functions'
|
||||
* ast_values (their type and name), that's why we added them to the globals vector.
|
||||
*/
|
||||
for (i = 0; i < globals_elements; ++i) {
|
||||
ast_value_delete(globals_data[i]);
|
||||
}
|
||||
if (globals_data)
|
||||
mem_d(globals_data);
|
||||
return 0;
|
||||
}
|
150
test/ir-test.c
150
test/ir-test.c
|
@ -1,150 +0,0 @@
|
|||
#include "gmqcc.h"
|
||||
#include "ir.h"
|
||||
|
||||
#ifdef assert
|
||||
# undef assert
|
||||
#endif
|
||||
/* (note: 'do {} while(0)' forces the need for a semicolon after assert() */
|
||||
#define assert(x) do { if ( !(x) ) { printf("Assertion failed: %s\n", #x); abort(); } } while(0)
|
||||
|
||||
int main()
|
||||
{
|
||||
int lf;
|
||||
|
||||
ir_builder *b = ir_builder_new("test");
|
||||
ir_value *va = ir_builder_create_global(b, "a", TYPE_FLOAT);
|
||||
ir_value *v3 = ir_builder_create_global(b, "const_f_3", TYPE_FLOAT);
|
||||
ir_value *vb = ir_builder_create_global(b, "b", TYPE_FLOAT);
|
||||
ir_value *vc = ir_builder_create_global(b, "c", TYPE_FLOAT);
|
||||
ir_value *vd = ir_builder_create_global(b, "d", TYPE_FLOAT);
|
||||
|
||||
ir_value *life1 = ir_builder_create_global(b, "life1", TYPE_FLOAT);
|
||||
ir_value *life2 = ir_builder_create_global(b, "life2", TYPE_FLOAT);
|
||||
ir_value *life3 = ir_builder_create_global(b, "life3", TYPE_FLOAT);
|
||||
|
||||
ir_function *fmain = NULL;
|
||||
ir_value *la = NULL;
|
||||
ir_block *bmain = NULL;
|
||||
ir_block *blt = NULL;
|
||||
ir_block *bge = NULL;
|
||||
ir_block *bend = NULL;
|
||||
ir_value *sum = NULL;
|
||||
ir_value *prd = NULL;
|
||||
ir_value *less = NULL;
|
||||
ir_value *x1 = NULL;
|
||||
ir_value *vig = NULL;
|
||||
ir_value *x2 = NULL;
|
||||
ir_instr *retphi = NULL;
|
||||
ir_value *retval = NULL;
|
||||
|
||||
assert(ir_value_set_float(v3, 3.0f) );
|
||||
assert(ir_value_set_float(vb, 4.0f) );
|
||||
assert(ir_value_set_float(vc, 10.0f) );
|
||||
assert(ir_value_set_float(vd, 20.0f) );
|
||||
|
||||
fmain = ir_builder_create_function(b, "main");
|
||||
assert(fmain);
|
||||
|
||||
la = ir_function_create_local(fmain, "loc1", TYPE_FLOAT);
|
||||
assert(la);
|
||||
|
||||
assert( bmain = ir_function_create_block(fmain, "top") );
|
||||
assert( blt = ir_function_create_block(fmain, "less") );
|
||||
assert( bge = ir_function_create_block(fmain, "greaterequal") );
|
||||
assert( bend = ir_function_create_block(fmain, "end") );
|
||||
|
||||
assert(ir_block_create_store_op(bmain, INSTR_STORE_F, va, v3));
|
||||
assert( sum = ir_block_create_add(bmain, "%sum", va, vb) );
|
||||
assert( prd = ir_block_create_mul(bmain, "%mul", sum, vc) );
|
||||
assert( less = ir_block_create_binop(bmain, "%less", INSTR_LT, prd, vd) );
|
||||
|
||||
assert(ir_block_create_if(bmain, less, blt, bge));
|
||||
|
||||
x1 = ir_block_create_binop(blt, "%x1", INSTR_ADD_F, sum, v3);
|
||||
assert(x1);
|
||||
assert(ir_block_create_goto(blt, bend));
|
||||
|
||||
vig = ir_block_create_binop(bge, "%ignore", INSTR_ADD_F, va, vb);
|
||||
assert(vig);
|
||||
assert(ir_block_create_store_op(bge, INSTR_STORE_F, la, vig));
|
||||
x2 = ir_block_create_binop(bge, "%x2", INSTR_ADD_F, sum, v3);
|
||||
assert(x2);
|
||||
assert(ir_block_create_goto(bge, bend));
|
||||
|
||||
retphi = ir_block_create_phi(bend, "%retval", TYPE_FLOAT);
|
||||
assert(retphi);
|
||||
assert(ir_phi_add(retphi, blt, x1));
|
||||
assert(ir_phi_add(retphi, bge, x2));
|
||||
retval = ir_phi_value(retphi);
|
||||
assert(retval);
|
||||
assert(ir_block_create_return(bend, retval));
|
||||
|
||||
/*
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 31));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 33));
|
||||
printf("%i should be 0\n", ir_value_life_merge(va, 33));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 1));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 2));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 20));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 21));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 8));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 9));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 3));
|
||||
printf("%i should be 0\n", ir_value_life_merge(va, 9));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 17));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 18));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 19));
|
||||
printf("%i should be 0\n", ir_value_life_merge(va, 19));
|
||||
ir_value_dump_life(va, printf);
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 10));
|
||||
printf("%i should be 1\n", ir_value_life_merge(va, 9));
|
||||
printf("%i should be 0\n", ir_value_life_merge(va, 10));
|
||||
printf("%i should be 0\n", ir_value_life_merge(va, 10));
|
||||
ir_value_dump_life(va, printf);
|
||||
*/
|
||||
|
||||
ir_builder_dump(b, printf);
|
||||
assert(ir_function_finalize(fmain));
|
||||
ir_builder_dump(b, printf);
|
||||
|
||||
ir_value_dump_life(sum, printf);
|
||||
ir_value_dump_life(prd, printf);
|
||||
ir_value_dump_life(less, printf);
|
||||
ir_value_dump_life(x1, printf);
|
||||
ir_value_dump_life(x2, printf);
|
||||
ir_value_dump_life(retval, printf);
|
||||
ir_value_dump_life(vig, printf);
|
||||
ir_value_dump_life(la, printf);
|
||||
|
||||
ir_value_life_merge_into(retval, vig);
|
||||
ir_value_dump_life(retval, printf);
|
||||
ir_value_life_merge(x1, 12);
|
||||
ir_value_life_merge(x1, 13);
|
||||
ir_value_life_merge(x1, 14);
|
||||
ir_value_life_merge_into(retval, x1);
|
||||
ir_value_dump_life(retval, printf);
|
||||
ir_value_life_merge(x1, 20);
|
||||
ir_value_life_merge(x1, 21);
|
||||
ir_value_life_merge(x1, 22);
|
||||
ir_value_life_merge_into(retval, x1);
|
||||
ir_value_dump_life(retval, printf);
|
||||
ir_value_life_merge(x2, 1);
|
||||
ir_value_life_merge(x2, 2);
|
||||
ir_value_life_merge_into(retval, x2);
|
||||
ir_value_dump_life(retval, printf);
|
||||
for (lf = 4; lf <= 15; ++lf)
|
||||
ir_value_life_merge(life1, lf);
|
||||
ir_value_life_merge_into(retval, life1);
|
||||
ir_value_dump_life(retval, printf);
|
||||
for (lf = 17; lf <= 18; ++lf)
|
||||
ir_value_life_merge(life2, lf);
|
||||
ir_value_life_merge_into(retval, life2);
|
||||
ir_value_dump_life(retval, printf);
|
||||
for (lf = 2; lf <= 29; ++lf)
|
||||
ir_value_life_merge(life3, lf);
|
||||
ir_value_life_merge_into(retval, life3);
|
||||
ir_value_dump_life(retval, printf);
|
||||
|
||||
ir_builder_delete(b);
|
||||
return 0;
|
||||
}
|
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
|
34
tests/aliases.qc
Normal file
34
tests/aliases.qc
Normal file
|
@ -0,0 +1,34 @@
|
|||
float alias_1 = 3.14;
|
||||
void alias_2() {
|
||||
print("alias_2\n");
|
||||
}
|
||||
|
||||
[[alias("alias_2")]] void alias_2_aliased();
|
||||
[[alias("alias_1")]] float alias_1_aliased;
|
||||
|
||||
|
||||
// alias to an alias?
|
||||
vector alias_3;
|
||||
[[alias("alias_3")]] vector alias_3_aliased;
|
||||
|
||||
// expected output
|
||||
// alias_2
|
||||
// 3.14
|
||||
void main() {
|
||||
alias_2_aliased();
|
||||
|
||||
alias_3_aliased= '1 2 3';
|
||||
|
||||
print(
|
||||
ftos(
|
||||
alias_1_aliased
|
||||
),
|
||||
"\n"
|
||||
);
|
||||
|
||||
print(
|
||||
"x ", ftos(alias_3_aliased_x), "\n",
|
||||
"y ", ftos(alias_3_aliased_y), "\n",
|
||||
"z ", ftos(alias_3_aliased_z), "\n"
|
||||
);
|
||||
}
|
9
tests/aliases.tmpl
Normal file
9
tests/aliases.tmpl
Normal file
|
@ -0,0 +1,9 @@
|
|||
I: aliases.qc
|
||||
D: test aliases
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
M: alias_2
|
||||
M: 3.14
|
||||
M: x 1
|
||||
M: y 2
|
||||
M: z 3
|
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
|
52
tests/arrays.qc
Normal file
52
tests/arrays.qc
Normal file
|
@ -0,0 +1,52 @@
|
|||
float glob[7];
|
||||
|
||||
.float above;
|
||||
.float flds[6];
|
||||
.float below;
|
||||
|
||||
void main() {
|
||||
float loc[6];
|
||||
|
||||
loc[0] = 1000;
|
||||
loc[1] = 1100;
|
||||
loc[2] = 1200;
|
||||
loc[3] = 1300;
|
||||
loc[4] = 1400;
|
||||
loc[5] = 1500;
|
||||
|
||||
float i;
|
||||
|
||||
for (i = 0; i < 6; i += 1)
|
||||
loc[i] += 1;
|
||||
for (i = 0; i < 5; i += 1)
|
||||
print(ftos(loc[i]), " ");
|
||||
print(ftos(loc[i]), "\n");
|
||||
|
||||
glob[0] = 1000;
|
||||
glob[1] = 1100;
|
||||
glob[2] = 1200;
|
||||
glob[3] = 1300;
|
||||
glob[4] = 1400;
|
||||
glob[5] = 1500;
|
||||
glob[6] = 1600;
|
||||
for (i = 0; i < 7; i += 1)
|
||||
glob[i] += 1;
|
||||
for (i = 0; i < 6; i += 1)
|
||||
print(ftos(glob[i]), " ");
|
||||
print(ftos(glob[i]), "\n");
|
||||
|
||||
entity e = spawn();
|
||||
e.above = 7777;
|
||||
e.below = 9999;
|
||||
e.flds[0] = 1000;
|
||||
e.flds[1] = 1100;
|
||||
e.flds[2] = 1200;
|
||||
e.flds[3] = 1300;
|
||||
e.flds[4] = 1400;
|
||||
e.flds[5] = 1500;
|
||||
for (i = 0; i < 6; i += 1)
|
||||
e.flds[i] += 1;
|
||||
for (i = 0; i < 5; i += 1)
|
||||
print(ftos(e.flds[i]), " ");
|
||||
print(ftos(e.flds[i]), "\n");
|
||||
}
|
7
tests/arrays.tmpl
Normal file
7
tests/arrays.tmpl
Normal file
|
@ -0,0 +1,7 @@
|
|||
I: arrays2.qc
|
||||
D: initialized arrays
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
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
|
37
tests/bitnot.qc
Normal file
37
tests/bitnot.qc
Normal file
|
@ -0,0 +1,37 @@
|
|||
void main() {
|
||||
float a; a = 1;
|
||||
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");
|
||||
}
|
11
tests/bitnot.tmpl
Normal file
11
tests/bitnot.tmpl
Normal file
|
@ -0,0 +1,11 @@
|
|||
# used to test the builtins
|
||||
I: bitnot.qc
|
||||
D: test bitwise not operators (fteqcc operators)
|
||||
T: -execute
|
||||
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'
|
23
tests/break.qc
Normal file
23
tests/break.qc
Normal file
|
@ -0,0 +1,23 @@
|
|||
void test(float brkat, float contat) {
|
||||
float i;
|
||||
|
||||
for (i = 0; i < 10; i += 1) {
|
||||
if (i == contat) {
|
||||
print("ct ");
|
||||
continue;
|
||||
}
|
||||
print(ftos(i), " ");
|
||||
if (i == brkat) {
|
||||
print("brk ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
print("end\n");
|
||||
}
|
||||
|
||||
void main() {
|
||||
test(-1, -1);
|
||||
test( 3, -1);
|
||||
test(-1, 3);
|
||||
test( 5, 2);
|
||||
}
|
8
tests/break.tmpl
Normal file
8
tests/break.tmpl
Normal file
|
@ -0,0 +1,8 @@
|
|||
I: break.qc
|
||||
D: test break and continue
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
M: 0 1 2 3 4 5 6 7 8 9 end
|
||||
M: 0 1 2 3 brk end
|
||||
M: 0 1 2 ct 4 5 6 7 8 9 end
|
||||
M: 0 1 ct 3 4 5 brk end
|
3
tests/builtin.qc
Normal file
3
tests/builtin.qc
Normal file
|
@ -0,0 +1,3 @@
|
|||
void() main = {
|
||||
print("hello world");
|
||||
}
|
7
tests/builtin.tmpl
Normal file
7
tests/builtin.tmpl
Normal file
|
@ -0,0 +1,7 @@
|
|||
# used to test the builtins
|
||||
I: builtin.qc
|
||||
D: test builtin functions
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
E: $null
|
||||
M: hello world
|
|
@ -1,6 +1,3 @@
|
|||
void(string, ...) print = #1;
|
||||
string(float) ftos = #2;
|
||||
|
||||
float(float x, float y, float z) sum = {
|
||||
return x + y + z;
|
||||
};
|
||||
|
@ -11,4 +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");
|
||||
|
||||
};
|
6
tests/calls.tmpl
Normal file
6
tests/calls.tmpl
Normal file
|
@ -0,0 +1,6 @@
|
|||
I: calls.qc
|
||||
D: test calls
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
E: -float 100 -float 200 -float 300
|
||||
M: 4600
|
12
tests/correct-logic-1-s.tmpl
Normal file
12
tests/correct-logic-1-s.tmpl
Normal file
|
@ -0,0 +1,12 @@
|
|||
I: correct-logic.qc
|
||||
D: vector logic flags
|
||||
T: -execute
|
||||
C: -std=fteqcc -fshort-logic
|
||||
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
|
||||
M: x, x -> 0 1 1 1 0
|
||||
M: 0, y -> 1 0 0 0 1
|
||||
M: y, 0 -> 0 0 0 0 1
|
||||
M: y, y -> 0 0 0 0 1
|
12
tests/correct-logic-1.tmpl
Normal file
12
tests/correct-logic-1.tmpl
Normal file
|
@ -0,0 +1,12 @@
|
|||
I: correct-logic.qc
|
||||
D: vector logic flags
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
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
|
||||
M: x, x -> 0 1 1 1 0
|
||||
M: 0, y -> 1 0 0 0 1
|
||||
M: y, 0 -> 0 0 0 0 1
|
||||
M: y, y -> 0 0 0 0 1
|
12
tests/correct-logic-2-s.tmpl
Normal file
12
tests/correct-logic-2-s.tmpl
Normal file
|
@ -0,0 +1,12 @@
|
|||
I: correct-logic.qc
|
||||
D: vector logic flags
|
||||
T: -execute
|
||||
C: -std=fteqcc -fcorrect-logic -fshort-logic
|
||||
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
|
||||
M: x, x -> 0 1 1 1 0
|
||||
M: 0, y -> 1 0 1 0 1
|
||||
M: y, 0 -> 0 0 1 1 0
|
||||
M: y, y -> 0 1 1 1 0
|
12
tests/correct-logic-2.tmpl
Normal file
12
tests/correct-logic-2.tmpl
Normal file
|
@ -0,0 +1,12 @@
|
|||
I: correct-logic.qc
|
||||
D: vector logic flags
|
||||
T: -execute
|
||||
C: -std=fteqcc -fcorrect-logic
|
||||
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
|
||||
M: x, x -> 0 1 1 1 0
|
||||
M: 0, y -> 1 0 1 0 1
|
||||
M: y, 0 -> 0 0 1 1 0
|
||||
M: y, y -> 0 1 1 1 0
|
24
tests/correct-logic.qc
Normal file
24
tests/correct-logic.qc
Normal file
|
@ -0,0 +1,24 @@
|
|||
float test_s_not (vector s) { return !s; }
|
||||
float test_s_and (vector s, vector t) { return s && t; }
|
||||
float test_s_or (vector s, vector t) { return s || t; }
|
||||
float test_s_if (vector s) { if (s) return 1; return 0; }
|
||||
float test_s_ifnot(vector s) { if not (s) return 1; return 0; }
|
||||
|
||||
void test(vector s, vector t) {
|
||||
print(ftos(!!test_s_not (s)), " ");
|
||||
print(ftos(!!test_s_and (s, t)), " ");
|
||||
print(ftos(!!test_s_or (s, t)), " ");
|
||||
print(ftos(!!test_s_if (s)), " ");
|
||||
print(ftos(!!test_s_ifnot(s)), "\n");
|
||||
}
|
||||
|
||||
void main() {
|
||||
print(" ! & | i N\n");
|
||||
print("0, 0 -> "); test('0 0 0', '0 0 0');
|
||||
print("0, x -> "); test('0 0 0', '1 0 0');
|
||||
print("x, 0 -> "); test('1 0 0', '0 0 0');
|
||||
print("x, x -> "); test('1 0 0', '1 0 0');
|
||||
print("0, y -> "); test('0 0 0', '0 1 0');
|
||||
print("y, 0 -> "); test('0 1 0', '0 0 0');
|
||||
print("y, y -> "); test('0 1 0', '0 1 0');
|
||||
}
|
14
tests/correct-vs-short-1.tmpl
Normal file
14
tests/correct-vs-short-1.tmpl
Normal file
|
@ -0,0 +1,14 @@
|
|||
I: correct-vs-short.qc
|
||||
D: correct-logic vs short-logic without perl-logic
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
M: X & | B
|
||||
M: 0 0 0, 0 0 0 :: 0 0 0
|
||||
M: 0 0 0, 5 0 0 :: 0 2 1
|
||||
M: 5 0 0, 0 0 0 :: 0 2 1
|
||||
M: 5 0 0, 5 0 0 :: 2 2 2
|
||||
M: Y & | B
|
||||
M: 0 0 0, 0 0 0 :: 0 0 0
|
||||
M: 0 0 0, 0 5 0 :: 0 0 0
|
||||
M: 0 5 0, 0 0 0 :: 0 0 0
|
||||
M: 0 5 0, 0 5 0 :: 0 0 0
|
14
tests/correct-vs-short-2.tmpl
Normal file
14
tests/correct-vs-short-2.tmpl
Normal file
|
@ -0,0 +1,14 @@
|
|||
I: correct-vs-short.qc
|
||||
D: correct-logic vs short-logic without perl-logic
|
||||
T: -execute
|
||||
C: -std=fteqcc -fcorrect-logic
|
||||
M: X & | B
|
||||
M: 0 0 0, 0 0 0 :: 0 0 0
|
||||
M: 0 0 0, 5 0 0 :: 0 2 1
|
||||
M: 5 0 0, 0 0 0 :: 0 2 1
|
||||
M: 5 0 0, 5 0 0 :: 2 2 2
|
||||
M: Y & | B
|
||||
M: 0 0 0, 0 0 0 :: 0 0 0
|
||||
M: 0 0 0, 0 5 0 :: 0 2 1
|
||||
M: 0 5 0, 0 0 0 :: 0 2 1
|
||||
M: 0 5 0, 0 5 0 :: 2 2 2
|
14
tests/correct-vs-short-3.tmpl
Normal file
14
tests/correct-vs-short-3.tmpl
Normal file
|
@ -0,0 +1,14 @@
|
|||
I: correct-vs-short.qc
|
||||
D: correct-logic vs short-logic without perl-logic
|
||||
T: -execute
|
||||
C: -std=fteqcc -fcorrect-logic -fshort-logic
|
||||
M: X & | B
|
||||
M: 0 0 0, 0 0 0 :: 0 0 0
|
||||
M: 0 0 0, 5 0 0 :: 0 2 1
|
||||
M: 5 0 0, 0 0 0 :: 0 2 1
|
||||
M: 5 0 0, 5 0 0 :: 2 2 2
|
||||
M: Y & | B
|
||||
M: 0 0 0, 0 0 0 :: 0 0 0
|
||||
M: 0 0 0, 0 5 0 :: 0 2 1
|
||||
M: 0 5 0, 0 0 0 :: 0 2 1
|
||||
M: 0 5 0, 0 5 0 :: 2 2 2
|
18
tests/correct-vs-short.qc
Normal file
18
tests/correct-vs-short.qc
Normal file
|
@ -0,0 +1,18 @@
|
|||
void test(vector a, vector b) {
|
||||
print(ftos((a && b) + (a && b)), " ");
|
||||
print(ftos((a || b) + (a || b)), " ");
|
||||
print(ftos((a && b) + (a || b)), "\n");
|
||||
}
|
||||
|
||||
void main() {
|
||||
print("X & | B\n");
|
||||
print("0 0 0, 0 0 0 :: "); test('0 0 0', '0 0 0');
|
||||
print("0 0 0, 5 0 0 :: "); test('0 0 0', '5 0 0');
|
||||
print("5 0 0, 0 0 0 :: "); test('5 0 0', '0 0 0');
|
||||
print("5 0 0, 5 0 0 :: "); test('5 0 0', '5 0 0');
|
||||
print("Y & | B\n");
|
||||
print("0 0 0, 0 0 0 :: "); test('0 0 0', '0 0 0');
|
||||
print("0 0 0, 0 5 0 :: "); test('0 0 0', '0 5 0');
|
||||
print("0 5 0, 0 0 0 :: "); test('0 5 0', '0 0 0');
|
||||
print("0 5 0, 0 5 0 :: "); test('0 5 0', '0 5 0');
|
||||
}
|
21
tests/defs.qh
Normal file
21
tests/defs.qh
Normal file
|
@ -0,0 +1,21 @@
|
|||
// builtins for the standalone qcvm included with gmqcc
|
||||
// in exec.c These should be updated to reflect the new
|
||||
// builtins. I no event shall you even consider adding
|
||||
// these individually per test.
|
||||
|
||||
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
|
68
tests/enum.qc
Normal file
68
tests/enum.qc
Normal file
|
@ -0,0 +1,68 @@
|
|||
enum {
|
||||
// this behaviour is confusing, but I like that
|
||||
// we support it.
|
||||
__ = (__ - 1),
|
||||
A = (__ + 1),
|
||||
|
||||
B,
|
||||
C
|
||||
};
|
||||
|
||||
enum {
|
||||
D = C + B,
|
||||
E = C + C,
|
||||
F = C + D,
|
||||
};
|
||||
|
||||
enum {
|
||||
G = (B + F), H = (C + F),
|
||||
I = (D + F), J = (B + I)
|
||||
};
|
||||
enum {
|
||||
K = A + B - C + D - E + F *
|
||||
G - H + I - J + A - B -
|
||||
J + A,
|
||||
L,
|
||||
M,
|
||||
N
|
||||
};
|
||||
|
||||
enum : flag {
|
||||
F1, /* = 1 << 1 */
|
||||
F2, /* = 1 << 2 */
|
||||
F3 /* = 1 << 3 */
|
||||
};
|
||||
|
||||
/* reversed enumeration */
|
||||
enum : reverse {
|
||||
R1, // 3
|
||||
R2, // 2
|
||||
R3, // 1
|
||||
R4 // 0
|
||||
};
|
||||
|
||||
void main() {
|
||||
print(ftos(A), "\n");
|
||||
print(ftos(B), "\n");
|
||||
print(ftos(C), "\n");
|
||||
print(ftos(D), "\n");
|
||||
print(ftos(E), "\n");
|
||||
print(ftos(F), "\n");
|
||||
print(ftos(G), "\n");
|
||||
print(ftos(H), "\n");
|
||||
print(ftos(I), "\n");
|
||||
print(ftos(J), "\n");
|
||||
print(ftos(K), "\n");
|
||||
print(ftos(L), "\n");
|
||||
print(ftos(M), "\n");
|
||||
print(ftos(N), "\n");
|
||||
|
||||
print(ftos(F1), "\n");
|
||||
print(ftos(F2), "\n");
|
||||
print(ftos(F3), "\n");
|
||||
|
||||
print(ftos(R1), "\n");
|
||||
print(ftos(R2), "\n");
|
||||
print(ftos(R3), "\n");
|
||||
print(ftos(R4), "\n");
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue