dparse.parser 0/3648(0%) line coverage

      
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270
280
290
300
310
320
330
340
350
360
370
380
390
400
410
420
430
440
450
460
470
480
490
500
510
520
530
540
550
560
570
580
590
600
610
620
630
640
650
660
670
680
690
700
710
720
730
740
750
760
770
780
790
800
810
820
830
840
850
860
870
880
890
900
910
920
930
940
950
960
970
980
990
1000
1010
1020
1030
1040
1050
1060
1070
1080
1090
1100
1110
1120
1130
1140
1150
1160
1170
1180
1190
1200
1210
1220
1230
1240
1250
1260
1270
1280
1290
1300
1310
1320
1330
1340
1350
1360
1370
1380
1390
1400
1410
1420
1430
1440
1450
1460
1470
1480
1490
1500
1510
1520
1530
1540
1550
1560
1570
1580
1590
1600
1610
1620
1630
1640
1650
1660
1670
1680
1690
1700
1710
1720
1730
1740
1750
1760
1770
1780
1790
1800
1810
1820
1830
1840
1850
1860
1870
1880
1890
1900
1910
1920
1930
1940
1950
1960
1970
1980
1990
2000
2010
2020
2030
2040
2050
2060
2070
2080
2090
2100
2110
2120
2130
2140
2150
2160
2170
2180
2190
2200
2210
2220
2230
2240
2250
2260
2270
2280
2290
2300
2310
2320
2330
2340
2350
2360
2370
2380
2390
2400
2410
2420
2430
2440
2450
2460
2470
2480
2490
2500
2510
2520
2530
2540
2550
2560
2570
2580
2590
2600
2610
2620
2630
2640
2650
2660
2670
2680
2690
2700
2710
2720
2730
2740
2750
2760
2770
2780
2790
2800
2810
2820
2830
2840
2850
2860
2870
2880
2890
2900
2910
2920
2930
2940
2950
2960
2970
2980
2990
3000
3010
3020
3030
3040
3050
3060
3070
3080
3090
3100
3110
3120
3130
3140
3150
3160
3170
3180
3190
3200
3210
3220
3230
3240
3250
3260
3270
3280
3290
3300
3310
3320
3330
3340
3350
3360
3370
3380
3390
3400
3410
3420
3430
3440
3450
3460
3470
3480
3490
3500
3510
3520
3530
3540
3550
3560
3570
3580
3590
3600
3610
3620
3630
3640
3650
3660
3670
3680
3690
3700
3710
3720
3730
3740
3750
3760
3770
3780
3790
3800
3810
3820
3830
3840
3850
3860
3870
3880
3890
3900
3910
3920
3930
3940
3950
3960
3970
3980
3990
4000
4010
4020
4030
4040
4050
4060
4070
4080
4090
4100
4110
4120
4130
4140
4150
4160
4170
4180
4190
4200
4210
4220
4230
4240
4250
4260
4270
4280
4290
4300
4310
4320
4330
4340
4350
4360
4370
4380
4390
4400
4410
4420
4430
4440
4450
4460
4470
4480
4490
4500
4510
4520
4530
4540
4550
4560
4570
4580
4590
4600
4610
4620
4630
4640
4650
4660
4670
4680
4690
4700
4710
4720
4730
4740
4750
4760
4770
4780
4790
4800
4810
4820
4830
4840
4850
4860
4870
4880
4890
4900
4910
4920
4930
4940
4950
4960
4970
4980
4990
5000
5010
5020
5030
5040
5050
5060
5070
5080
5090
5100
5110
5120
5130
5140
5150
5160
5170
5180
5190
5200
5210
5220
5230
5240
5250
5260
5270
5280
5290
5300
5310
5320
5330
5340
5350
5360
5370
5380
5390
5400
5410
5420
5430
5440
5450
5460
5470
5480
5490
5500
5510
5520
5530
5540
5550
5560
5570
5580
5590
5600
5610
5620
5630
5640
5650
5660
5670
5680
5690
5700
5710
5720
5730
5740
5750
5760
5770
5780
5790
5800
5810
5820
5830
5840
5850
5860
5870
5880
5890
5900
5910
5920
5930
5940
5950
5960
5970
5980
5990
6000
6010
6020
6030
6040
6050
6060
6070
6080
6090
6100
6110
6120
6130
6140
6150
6160
6170
6180
6190
6200
6210
6220
6230
6240
6250
6260
6270
6280
6290
6300
6310
6320
6330
6340
6350
6360
6370
6380
6390
6400
6410
6420
6430
6440
6450
6460
6470
6480
6490
6500
6510
6520
6530
6540
6550
6560
6570
6580
6590
6600
6610
6620
6630
6640
6650
6660
6670
6680
6690
6700
6710
6720
6730
6740
6750
6760
6770
6780
6790
6800
6810
6820
6830
6840
6850
6860
6870
6880
6890
6900
6910
6920
6930
6940
6950
6960
6970
6980
6990
7000
7010
7020
7030
7040
7050
7060
7070
7080
7090
7100
7110
7120
7130
7140
7150
7160
7170
7180
7190
7200
7210
7220
7230
7240
7250
7260
7270
7280
7290
7300
7310
7320
7330
7340
7350
7360
7370
7380
7390
7400
7410
7420
7430
7440
7450
7460
7470
7480
7490
7500
7510
7520
7530
7540
7550
7560
7570
7580
7590
7600
7610
7620
7630
7640
7650
7660
7670
7680
7690
7700
7710
7720
7730
7740
7750
7760
7770
7780
7790
7800
7810
7820
7830
7840
7850
7860
7870
7880
7890
7900
7910
7920
7930
7940
7950
7960
7970
7980
7990
8000
8010
8020
8030
8040
8050
8060
8070
8080
8090
8100
8110
8120
8130
8140
8150
8160
8170
8180
8190
8200
8210
8220
8230
8240
8250
8260
8270
8280
8290
8300
8310
8320
8330
8340
8350
8360
8370
8380
8390
8400
8410
8420
8430
8440
8450
8460
8470
8480
8490
8500
8510
8520
8530
8540
8550
8560
8570
8580
8590
8600
8610
8620
8630
8640
8650
8660
8670
8680
8690
8700
8710
8720
8730
8740
8750
8760
8770
8780
8790
8800
8810
8820
8830
8840
8850
8860
8870
8880
8890
8900
8910
8920
8930
8940
8950
8960
8970
8980
8990
9000
9010
9020
9030
9040
9050
9060
9070
9080
9090
9100
9110
9120
9130
9140
9150
9160
9170
9180
9190
9200
9210
9220
9230
9240
9250
9260
9270
9280
9290
9300
9310
9320
9330
9340
9350
9360
9370
9380
9390
9400
9410
9420
9430
9440
9450
9460
9470
9480
9490
9500
9510
9520
9530
9540
9550
9560
9570
9580
9590
9600
9610
9620
9630
9640
9650
9660
9670
9680
9690
9700
9710
9720
9730
9740
9750
9760
9770
9780
9790
9800
9810
9820
9830
9840
9850
9860
9870
9880
9890
9900
9910
9920
9930
9940
9950
9960
9970
9980
9990
10000
10010
10020
10030
10040
10050
10060
10070
10080
10090
10100
10110
10120
10130
10140
10150
10160
10170
10180
10190
10200
10210
10220
10230
10240
10250
10260
10270
10280
10290
10300
10310
10320
10330
10340
10350
10360
10370
10380
10390
10400
10410
10420
10430
10440
10450
10460
10470
10480
10490
10500
10510
10520
10530
10540
10550
10560
10570
10580
10590
10600
10610
10620
10630
10640
10650
10660
10670
10680
10690
10700
10710
10720
10730
10740
10750
10760
10770
10780
10790
10800
10810
10820
10830
10840
10850
10860
10870
10880
10890
10900
10910
10920
10930
10940
10950
10960
10970
10980
10990
11000
11010
11020
11030
11040
11050
11060
11070
11080
11090
11100
11110
11120
11130
11140
11150
11160
11170
11180
11190
11200
11210
11220
11230
11240
11250
11260
11270
11280
11290
11300
11310
11320
11330
11340
11350
11360
11370
11380
11390
11400
11410
11420
11430
11440
11450
11460
11470
11480
11490
11500
11510
11520
11530
11540
11550
11560
11570
11580
11590
11600
11610
11620
11630
11640
11650
11660
11670
11680
11690
11700
11710
11720
11730
11740
11750
11760
11770
11780
11790
11800
11810
11820
11830
11840
11850
11860
11870
11880
11890
11900
11910
11920
11930
11940
11950
11960
11970
11980
11990
12000
12010
12020
12030
12040
12050
12060
12070
12080
12090
12100
12110
12120
12130
12140
12150
12160
12170
12180
12190
12200
12210
12220
12230
12240
12250
12260
12270
12280
12290
12300
12310
12320
12330
12340
12350
12360
12370
12380
12390
12400
12410
12420
12430
12440
12450
12460
12470
12480
12490
12500
12510
12520
12530
12540
12550
12560
12570
12580
12590
12600
12610
12620
12630
12640
12650
12660
12670
12680
12690
12700
12710
12720
12730
12740
12750
12760
12770
12780
12790
12800
12810
12820
12830
12840
12850
12860
12870
12880
12890
12900
12910
12920
12930
12940
12950
12960
12970
12980
12990
13000
13010
13020
13030
13040
13050
13060
13070
13080
13090
13100
13110
13120
13130
13140
13150
13160
13170
13180
13190
13200
13210
13220
13230
13240
13250
13260
13270
13280
13290
13300
13310
13320
13330
13340
13350
13360
13370
13380
13390
13400
13410
13420
13430
13440
13450
13460
13470
13480
13490
13500
13510
13520
13530
13540
13550
13560
13570
13580
13590
13600
13610
13620
13630
13640
13650
13660
13670
13680
13690
13700
13710
13720
13730
13740
13750
13760
13770
13780
13790
13800
13810
13820
13830
13840
13850
13860
13870
13880
13890
13900
13910
13920
13930
13940
13950
13960
13970
13980
13990
14000
14010
14020
14030
14040
14050
14060
14070
14080
14090
14100
14110
14120
14130
14140
14150
14160
14170
14180
14190
14200
14210
14220
14230
14240
14250
14260
14270
14280
14290
14300
14310
14320
14330
14340
14350
14360
14370
14380
14390
14400
14410
14420
14430
14440
14450
14460
14470
14480
14490
14500
14510
14520
14530
14540
14550
14560
14570
14580
14590
14600
14610
14620
14630
14640
14650
14660
14670
14680
14690
14700
14710
14720
14730
14740
14750
14760
14770
14780
14790
14800
14810
14820
14830
14840
14850
14860
14870
14880
14890
14900
14910
14920
14930
14940
14950
14960
14970
14980
14990
15000
15010
15020
15030
15040
15050
15060
15070
15080
15090
15100
15110
15120
15130
15140
15150
15160
15170
15180
15190
15200
15210
15220
15230
15240
15250
15260
15270
15280
15290
15300
15310
15320
15330
15340
15350
15360
15370
15380
15390
15400
15410
15420
15430
15440
15450
15460
15470
15480
15490
15500
15510
15520
15530
15540
15550
15560
15570
15580
15590
15600
15610
15620
15630
15640
15650
15660
15670
15680
15690
15700
15710
15720
15730
15740
15750
15760
15770
15780
15790
15800
15810
15820
15830
15840
15850
15860
15870
15880
15890
15900
15910
15920
15930
15940
15950
15960
15970
15980
15990
16000
16010
16020
16030
16040
16050
16060
16070
16080
16090
16100
16110
16120
16130
16140
16150
16160
16170
16180
16190
16200
16210
16220
16230
16240
16250
16260
16270
16280
16290
16300
16310
16320
16330
16340
16350
16360
16370
16380
16390
16400
16410
16420
16430
16440
16450
16460
16470
16480
16490
16500
16510
16520
16530
16540
16550
16560
16570
16580
16590
16600
16610
16620
16630
16640
16650
16660
16670
16680
16690
16700
16710
16720
16730
16740
16750
16760
16770
16780
16790
16800
16810
16820
16830
16840
16850
16860
16870
16880
16890
16900
16910
16920
16930
16940
16950
16960
16970
16980
16990
17000
17010
17020
17030
17040
17050
17060
17070
17080
17090
17100
17110
17120
17130
17140
17150
17160
17170
17180
17190
17200
17210
17220
17230
17240
17250
17260
17270
17280
17290
17300
17310
17320
17330
17340
17350
17360
17370
17380
17390
17400
17410
17420
17430
17440
17450
17460
17470
17480
17490
17500
17510
17520
17530
17540
17550
17560
17570
17580
17590
17600
17610
17620
17630
17640
17650
17660
17670
17680
17690
17700
17710
17720
17730
17740
17750
17760
17770
17780
17790
17800
17810
17820
17830
17840
17850
17860
17870
17880
17890
17900
17910
17920
17930
17940
17950
17960
17970
17980
17990
18000
18010
18020
18030
18040
18050
18060
18070
18080
18090
18100
18110
18120
18130
18140
18150
18160
18170
18180
18190
18200
18210
18220
18230
18240
18250
18260
18270
18280
18290
18300
18310
18320
18330
18340
18350
18360
18370
18380
18390
18400
18410
18420
18430
18440
18450
18460
18470
18480
18490
18500
18510
18520
18530
18540
18550
18560
18570
18580
18590
18600
18610
18620
18630
18640
18650
18660
18670
18680
18690
18700
18710
18720
18730
18740
18750
18760
18770
18780
18790
18800
18810
18820
18830
18840
18850
18860
18870
18880
18890
18900
18910
18920
18930
18940
18950
18960
18970
18980
18990
19000
19010
19020
19030
19040
19050
19060
19070
19080
19090
19100
19110
19120
19130
19140
19150
19160
19170
19180
19190
19200
19210
19220
19230
19240
19250
19260
19270
19280
19290
19300
19310
19320
19330
19340
19350
19360
19370
19380
19390
19400
19410
19420
19430
19440
19450
19460
19470
19480
19490
19500
19510
19520
19530
19540
19550
19560
19570
19580
19590
19600
19610
19620
19630
19640
19650
19660
19670
19680
19690
19700
19710
19720
19730
19740
19750
19760
19770
19780
19790
19800
19810
19820
19830
19840
19850
19860
19870
19880
19890
19900
19910
19920
19930
19940
19950
19960
19970
19980
19990
20000
20010
20020
20030
20040
20050
20060
20070
20080
20090
20100
20110
20120
20130
20140
20150
20160
20170
20180
20190
20200
20210
20220
20230
20240
20250
20260
20270
20280
20290
20300
20310
20320
20330
20340
20350
20360
20370
20380
20390
20400
20410
20420
20430
20440
20450
20460
20470
20480
20490
20500
20510
20520
20530
20540
20550
20560
20570
20580
20590
20600
20610
20620
20630
20640
20650
20660
20670
20680
20690
20700
20710
20720
20730
20740
20750
20760
20770
20780
20790
20800
20810
20820
20830
20840
20850
20860
20870
20880
20890
20900
20910
20920
20930
20940
20950
20960
20970
20980
20990
21000
21010
21020
21030
21040
21050
21060
21070
21080
21090
21100
21110
21120
21130
21140
21150
21160
21170
21180
21190
21200
21210
21220
21230
21240
21250
21260
21270
21280
21290
21300
21310
21320
21330
21340
21350
21360
21370
21380
21390
21400
21410
21420
21430
21440
21450
21460
21470
21480
21490
21500
21510
21520
21530
21540
21550
21560
21570
21580
21590
21600
21610
21620
21630
21640
21650
21660
21670
21680
21690
21700
21710
21720
21730
21740
21750
21760
21770
21780
21790
21800
21810
21820
21830
21840
21850
21860
21870
21880
21890
21900
21910
21920
21930
21940
21950
21960
21970
21980
21990
22000
22010
22020
22030
22040
22050
22060
22070
22080
22090
22100
22110
22120
22130
22140
22150
22160
22170
22180
22190
22200
22210
22220
22230
22240
22250
22260
22270
22280
22290
22300
22310
22320
22330
22340
22350
22360
22370
22380
22390
22400
22410
22420
22430
22440
22450
22460
22470
22480
22490
22500
22510
22520
22530
22540
22550
22560
22570
22580
22590
22600
22610
22620
22630
22640
22650
22660
22670
22680
22690
22700
22710
22720
22730
22740
22750
22760
22770
22780
22790
22800
22810
22820
22830
22840
22850
22860
22870
22880
22890
22900
22910
22920
22930
22940
22950
22960
22970
22980
22990
23000
23010
23020
23030
23040
23050
23060
23070
23080
23090
23100
23110
23120
23130
23140
23150
23160
23170
23180
23190
23200
23210
23220
23230
23240
23250
23260
23270
23280
23290
23300
23310
23320
23330
23340
23350
23360
23370
23380
23390
23400
23410
23420
23430
23440
23450
23460
23470
23480
23490
23500
23510
23520
23530
23540
23550
23560
23570
23580
23590
23600
23610
23620
23630
23640
23650
23660
23670
23680
23690
23700
23710
23720
23730
23740
23750
23760
23770
23780
23790
23800
23810
23820
23830
23840
23850
23860
23870
23880
23890
23900
23910
23920
23930
23940
23950
23960
23970
23980
23990
24000
24010
24020
24030
24040
24050
24060
24070
24080
24090
24100
24110
24120
24130
24140
24150
24160
24170
24180
24190
24200
24210
24220
24230
24240
24250
24260
24270
24280
24290
24300
24310
24320
24330
24340
24350
24360
24370
24380
24390
24400
24410
24420
24430
24440
24450
24460
24470
24480
24490
24500
24510
24520
24530
24540
24550
24560
24570
24580
24590
24600
24610
24620
24630
24640
24650
24660
24670
24680
24690
24700
24710
24720
24730
24740
24750
24760
24770
24780
24790
24800
24810
24820
24830
24840
24850
24860
24870
24880
24890
24900
24910
24920
24930
24940
24950
24960
24970
24980
24990
25000
25010
25020
25030
25040
25050
25060
25070
25080
25090
25100
25110
25120
25130
25140
25150
25160
25170
25180
25190
25200
25210
25220
25230
25240
25250
25260
25270
25280
25290
25300
25310
25320
25330
25340
25350
25360
25370
25380
25390
25400
25410
25420
25430
25440
25450
25460
25470
25480
25490
25500
25510
25520
25530
25540
25550
25560
25570
25580
25590
25600
25610
25620
25630
25640
25650
25660
25670
25680
25690
25700
25710
25720
25730
25740
25750
25760
25770
25780
25790
25800
25810
25820
25830
25840
25850
25860
25870
25880
25890
25900
25910
25920
25930
25940
25950
25960
25970
25980
25990
26000
26010
26020
26030
26040
26050
26060
26070
26080
26090
26100
26110
26120
26130
26140
26150
26160
26170
26180
26190
26200
26210
26220
26230
26240
26250
26260
26270
26280
26290
26300
26310
26320
26330
26340
26350
26360
26370
26380
26390
26400
26410
26420
26430
26440
26450
26460
26470
26480
26490
26500
26510
26520
26530
26540
26550
26560
26570
26580
26590
26600
26610
26620
26630
26640
26650
26660
26670
26680
26690
26700
26710
26720
26730
26740
26750
26760
26770
26780
26790
26800
26810
26820
26830
26840
26850
26860
26870
26880
26890
26900
26910
26920
26930
26940
26950
26960
26970
26980
26990
27000
27010
27020
27030
27040
27050
27060
27070
27080
27090
27100
27110
27120
27130
27140
27150
27160
27170
27180
27190
27200
27210
27220
27230
27240
27250
27260
27270
27280
27290
27300
27310
27320
27330
27340
27350
27360
27370
27380
27390
27400
27410
27420
27430
27440
27450
27460
27470
27480
27490
27500
27510
27520
27530
27540
27550
27560
27570
27580
27590
27600
27610
27620
27630
27640
27650
27660
27670
27680
27690
27700
27710
27720
27730
27740
27750
27760
27770
27780
27790
27800
27810
27820
27830
27840
27850
27860
27870
27880
27890
27900
27910
27920
27930
27940
27950
27960
27970
27980
27990
28000
28010
28020
28030
28040
28050
28060
28070
28080
28090
28100
28110
28120
28130
28140
28150
28160
28170
28180
28190
28200
28210
28220
28230
28240
28250
28260
28270
28280
28290
28300
28310
28320
28330
28340
28350
28360
28370
28380
28390
28400
28410
28420
28430
28440
28450
28460
28470
28480
28490
28500
28510
28520
28530
28540
28550
28560
28570
28580
28590
28600
28610
28620
28630
28640
28650
28660
28670
28680
28690
28700
28710
28720
28730
28740
28750
28760
28770
28780
28790
28800
28810
28820
28830
28840
28850
28860
28870
28880
28890
28900
28910
28920
28930
28940
28950
28960
28970
28980
28990
29000
29010
29020
29030
29040
29050
29060
29070
29080
29090
29100
29110
29120
29130
29140
29150
29160
29170
29180
29190
29200
29210
29220
29230
29240
29250
29260
29270
29280
29290
29300
29310
29320
29330
29340
29350
29360
29370
29380
29390
29400
29410
29420
29430
29440
29450
29460
29470
29480
29490
29500
29510
29520
29530
29540
29550
29560
29570
29580
29590
29600
29610
29620
29630
29640
29650
29660
29670
29680
29690
29700
29710
29720
29730
29740
29750
29760
29770
29780
29790
29800
29810
29820
29830
29840
29850
29860
29870
29880
29890
29900
29910
29920
29930
29940
29950
29960
29970
29980
29990
30000
30010
30020
30030
30040
30050
30060
30070
30080
30090
30100
30110
30120
30130
30140
30150
30160
30170
30180
30190
30200
30210
30220
30230
30240
30250
30260
30270
30280
30290
30300
30310
30320
30330
30340
30350
30360
30370
30380
30390
30400
30410
30420
30430
30440
30450
30460
30470
30480
30490
30500
30510
30520
30530
30540
30550
30560
30570
30580
30590
30600
30610
30620
30630
30640
30650
30660
30670
30680
30690
30700
30710
30720
30730
30740
30750
30760
30770
30780
30790
30800
30810
30820
30830
30840
30850
30860
30870
30880
30890
30900
30910
30920
30930
30940
30950
30960
30970
30980
30990
31000
31010
31020
31030
31040
31050
31060
31070
31080
31090
31100
31110
31120
31130
31140
31150
31160
31170
31180
31190
31200
31210
31220
31230
31240
31250
31260
31270
31280
31290
31300
31310
31320
31330
31340
31350
31360
31370
31380
31390
31400
31410
31420
31430
31440
31450
31460
31470
31480
31490
31500
31510
31520
31530
31540
31550
31560
31570
31580
31590
31600
31610
31620
31630
31640
31650
31660
31670
31680
31690
31700
31710
31720
31730
31740
31750
31760
31770
31780
31790
31800
31810
31820
31830
31840
31850
31860
31870
31880
31890
31900
31910
31920
31930
31940
31950
31960
31970
31980
31990
32000
32010
32020
32030
32040
32050
32060
32070
32080
32090
32100
32110
32120
32130
32140
32150
32160
32170
32180
32190
32200
32210
32220
32230
32240
32250
32260
32270
32280
32290
32300
32310
32320
32330
32340
32350
32360
32370
32380
32390
32400
32410
32420
32430
32440
32450
32460
32470
32480
32490
32500
32510
32520
32530
32540
32550
32560
32570
32580
32590
32600
32610
32620
32630
32640
32650
32660
32670
32680
32690
32700
32710
32720
32730
32740
32750
32760
32770
32780
32790
32800
32810
32820
32830
32840
32850
32860
32870
32880
32890
32900
32910
32920
32930
32940
32950
32960
32970
32980
32990
33000
33010
33020
33030
33040
33050
33060
33070
33080
33090
33100
33110
33120
33130
33140
33150
33160
33170
33180
33190
33200
33210
33220
33230
33240
33250
33260
33270
33280
33290
33300
33310
33320
33330
33340
33350
33360
33370
33380
33390
33400
33410
33420
33430
33440
33450
33460
33470
33480
33490
33500
33510
33520
33530
33540
33550
33560
33570
33580
33590
33600
33610
33620
33630
33640
33650
33660
33670
33680
33690
33700
33710
33720
33730
33740
33750
33760
33770
33780
33790
33800
33810
33820
33830
33840
33850
33860
33870
33880
33890
33900
33910
33920
33930
33940
33950
33960
33970
33980
33990
34000
34010
34020
34030
34040
34050
34060
34070
34080
34090
34100
34110
34120
34130
34140
34150
34160
34170
34180
34190
34200
34210
34220
34230
34240
34250
34260
34270
34280
34290
34300
34310
34320
34330
34340
34350
34360
34370
34380
34390
34400
34410
34420
34430
34440
34450
34460
34470
34480
34490
34500
34510
34520
34530
34540
34550
34560
34570
34580
34590
34600
34610
34620
34630
34640
34650
34660
34670
34680
34690
34700
34710
34720
34730
34740
34750
34760
34770
34780
34790
34800
34810
34820
34830
34840
34850
34860
34870
34880
34890
34900
34910
34920
34930
34940
34950
34960
34970
34980
34990
35000
35010
35020
35030
35040
35050
35060
35070
35080
35090
35100
35110
35120
35130
35140
35150
35160
35170
35180
35190
35200
35210
35220
35230
35240
35250
35260
35270
35280
35290
35300
35310
35320
35330
35340
35350
35360
35370
35380
35390
35400
35410
35420
35430
35440
35450
35460
35470
35480
35490
35500
35510
35520
35530
35540
35550
35560
35570
35580
35590
35600
35610
35620
35630
35640
35650
35660
35670
35680
35690
35700
35710
35720
35730
35740
35750
35760
35770
35780
35790
35800
35810
35820
35830
35840
35850
35860
35870
35880
35890
35900
35910
35920
35930
35940
35950
35960
35970
35980
35990
36000
36010
36020
36030
36040
36050
36060
36070
36080
36090
36100
36110
36120
36130
36140
36150
36160
36170
36180
36190
36200
36210
36220
36230
36240
36250
36260
36270
36280
36290
36300
36310
36320
36330
36340
36350
36360
36370
36380
36390
36400
36410
36420
36430
36440
36450
36460
36470
36480
36490
36500
36510
36520
36530
36540
36550
36560
36570
36580
36590
36600
36610
36620
36630
36640
36650
36660
36670
36680
36690
36700
36710
36720
36730
36740
36750
36760
36770
36780
36790
36800
36810
36820
36830
36840
36850
36860
36870
36880
36890
36900
36910
36920
36930
36940
36950
36960
36970
36980
36990
37000
37010
37020
37030
37040
37050
37060
37070
37080
37090
37100
37110
37120
37130
37140
37150
37160
37170
37180
37190
37200
37210
37220
37230
37240
37250
37260
37270
37280
37290
37300
37310
37320
37330
37340
37350
37360
37370
37380
37390
37400
37410
37420
37430
37440
37450
37460
37470
37480
37490
37500
37510
37520
37530
37540
37550
37560
37570
37580
37590
37600
37610
37620
37630
37640
37650
37660
37670
37680
37690
37700
37710
37720
37730
37740
37750
37760
37770
37780
37790
37800
37810
37820
37830
37840
37850
37860
37870
37880
37890
37900
37910
37920
37930
37940
37950
37960
37970
37980
37990
38000
38010
38020
38030
38040
38050
38060
38070
38080
38090
38100
38110
38120
38130
38140
38150
38160
38170
38180
38190
38200
38210
38220
38230
38240
38250
38260
38270
38280
38290
38300
38310
38320
38330
38340
38350
38360
38370
38380
38390
38400
38410
38420
38430
38440
38450
38460
38470
38480
38490
38500
38510
38520
38530
38540
38550
38560
38570
38580
38590
38600
38610
38620
38630
38640
38650
38660
38670
38680
38690
38700
38710
38720
38730
38740
38750
38760
38770
38780
38790
38800
38810
38820
38830
38840
38850
38860
38870
38880
38890
38900
38910
38920
38930
38940
38950
38960
38970
38980
38990
39000
39010
39020
39030
39040
39050
39060
39070
39080
39090
39100
39110
39120
39130
39140
39150
39160
39170
39180
39190
39200
39210
39220
39230
39240
39250
39260
39270
39280
39290
39300
39310
39320
39330
39340
39350
39360
39370
39380
39390
39400
39410
39420
39430
39440
39450
39460
39470
39480
39490
39500
39510
39520
39530
39540
39550
39560
39570
39580
39590
39600
39610
39620
39630
39640
39650
39660
39670
39680
39690
39700
39710
39720
39730
39740
39750
39760
39770
39780
39790
39800
39810
39820
39830
39840
39850
39860
39870
39880
39890
39900
39910
39920
39930
39940
39950
39960
39970
39980
39990
40000
40010
40020
40030
40040
40050
40060
40070
40080
40090
40100
40110
40120
40130
40140
40150
40160
40170
40180
40190
40200
40210
40220
40230
40240
40250
40260
40270
40280
40290
40300
40310
40320
40330
40340
40350
40360
40370
40380
40390
40400
40410
40420
40430
40440
40450
40460
40470
40480
40490
40500
40510
40520
40530
40540
40550
40560
40570
40580
40590
40600
40610
40620
40630
40640
40650
40660
40670
40680
40690
40700
40710
40720
40730
40740
40750
40760
40770
40780
40790
40800
40810
40820
40830
40840
40850
40860
40870
40880
40890
40900
40910
40920
40930
40940
40950
40960
40970
40980
40990
41000
41010
41020
41030
41040
41050
41060
41070
41080
41090
41100
41110
41120
41130
41140
41150
41160
41170
41180
41190
41200
41210
41220
41230
41240
41250
41260
41270
41280
41290
41300
41310
41320
41330
41340
41350
41360
41370
41380
41390
41400
41410
41420
41430
41440
41450
41460
41470
41480
41490
41500
41510
41520
41530
41540
41550
41560
41570
41580
41590
41600
41610
41620
41630
41640
41650
41660
41670
41680
41690
41700
41710
41720
41730
41740
41750
41760
41770
41780
41790
41800
41810
41820
41830
41840
41850
41860
41870
41880
41890
41900
41910
41920
41930
41940
41950
41960
41970
41980
41990
42000
42010
42020
42030
42040
42050
42060
42070
42080
42090
42100
42110
42120
42130
42140
42150
42160
42170
42180
42190
42200
42210
42220
42230
42240
42250
42260
42270
42280
42290
42300
42310
42320
42330
42340
42350
42360
42370
42380
42390
42400
42410
42420
42430
42440
42450
42460
42470
42480
42490
42500
42510
42520
42530
42540
42550
42560
42570
42580
42590
42600
42610
42620
42630
42640
42650
42660
42670
42680
42690
42700
42710
42720
42730
42740
42750
42760
42770
42780
42790
42800
42810
42820
42830
42840
42850
42860
42870
42880
42890
42900
42910
42920
42930
42940
42950
42960
42970
42980
42990
43000
43010
43020
43030
43040
43050
43060
43070
43080
43090
43100
43110
43120
43130
43140
43150
43160
43170
43180
43190
43200
43210
43220
43230
43240
43250
43260
43270
43280
43290
43300
43310
43320
43330
43340
43350
43360
43370
43380
43390
43400
43410
43420
43430
43440
43450
43460
43470
43480
43490
43500
43510
43520
43530
43540
43550
43560
43570
43580
43590
43600
43610
43620
43630
43640
43650
43660
43670
43680
43690
43700
43710
43720
43730
43740
43750
43760
43770
43780
43790
43800
43810
43820
43830
43840
43850
43860
43870
43880
43890
43900
43910
43920
43930
43940
43950
43960
43970
43980
43990
44000
44010
44020
44030
44040
44050
44060
44070
44080
44090
44100
44110
44120
44130
44140
44150
44160
44170
44180
44190
44200
44210
44220
44230
44240
44250
44260
44270
44280
44290
44300
44310
44320
44330
44340
44350
44360
44370
44380
44390
44400
44410
44420
44430
44440
44450
44460
44470
44480
44490
44500
44510
44520
44530
44540
44550
44560
44570
44580
44590
44600
44610
44620
44630
44640
44650
44660
44670
44680
44690
44700
44710
44720
44730
44740
44750
44760
44770
44780
44790
44800
44810
44820
44830
44840
44850
44860
44870
44880
44890
44900
44910
44920
44930
44940
44950
44960
44970
44980
44990
45000
45010
45020
45030
45040
45050
45060
45070
45080
45090
45100
45110
45120
45130
45140
45150
45160
45170
45180
45190
45200
45210
45220
45230
45240
45250
45260
45270
45280
45290
45300
45310
45320
45330
45340
45350
45360
45370
45380
45390
45400
45410
45420
45430
45440
45450
45460
45470
45480
45490
45500
45510
45520
45530
45540
45550
45560
45570
45580
45590
45600
45610
45620
45630
45640
45650
45660
45670
45680
45690
45700
45710
45720
45730
45740
45750
45760
45770
45780
45790
45800
45810
45820
45830
45840
45850
45860
45870
45880
45890
45900
45910
45920
45930
45940
45950
45960
45970
45980
45990
46000
46010
46020
46030
46040
46050
46060
46070
46080
46090
46100
46110
46120
46130
46140
46150
46160
46170
46180
46190
46200
46210
46220
46230
46240
46250
46260
46270
46280
46290
46300
46310
46320
46330
46340
46350
46360
46370
46380
46390
46400
46410
46420
46430
46440
46450
46460
46470
46480
46490
46500
46510
46520
46530
46540
46550
46560
46570
46580
46590
46600
46610
46620
46630
46640
46650
46660
46670
46680
46690
46700
46710
46720
46730
46740
46750
46760
46770
46780
46790
46800
46810
46820
46830
46840
46850
46860
46870
46880
46890
46900
46910
46920
46930
46940
46950
46960
46970
46980
46990
47000
47010
47020
47030
47040
47050
47060
47070
47080
47090
47100
47110
47120
47130
47140
47150
47160
47170
47180
47190
47200
47210
47220
47230
47240
47250
47260
47270
47280
47290
47300
47310
47320
47330
47340
47350
47360
47370
47380
47390
47400
47410
47420
47430
47440
47450
47460
47470
47480
47490
47500
47510
47520
47530
47540
47550
47560
47570
47580
47590
47600
47610
47620
47630
47640
47650
47660
47670
47680
47690
47700
47710
47720
47730
47740
47750
47760
47770
47780
47790
47800
47810
47820
47830
47840
47850
47860
47870
47880
47890
47900
47910
47920
47930
47940
47950
47960
47970
47980
47990
48000
48010
48020
48030
48040
48050
48060
48070
48080
48090
48100
48110
48120
48130
48140
48150
48160
48170
48180
48190
48200
48210
48220
48230
48240
48250
48260
48270
48280
48290
48300
48310
48320
48330
48340
48350
48360
48370
48380
48390
48400
48410
48420
48430
48440
48450
48460
48470
48480
48490
48500
48510
48520
48530
48540
48550
48560
48570
48580
48590
48600
48610
48620
48630
48640
48650
48660
48670
48680
48690
48700
48710
48720
48730
48740
48750
48760
48770
48780
48790
48800
48810
48820
48830
48840
48850
48860
48870
48880
48890
48900
48910
48920
48930
48940
48950
48960
48970
48980
48990
49000
49010
49020
49030
49040
49050
49060
49070
49080
49090
49100
49110
49120
49130
49140
49150
49160
49170
49180
49190
49200
49210
49220
49230
49240
49250
49260
49270
49280
49290
49300
49310
49320
49330
49340
49350
49360
49370
49380
49390
49400
49410
49420
49430
49440
49450
49460
49470
49480
49490
49500
49510
49520
49530
49540
49550
49560
49570
49580
49590
49600
49610
49620
49630
49640
49650
49660
49670
49680
49690
49700
49710
49720
49730
49740
49750
49760
49770
49780
49790
49800
49810
49820
49830
49840
49850
49860
49870
49880
49890
49900
49910
49920
49930
49940
49950
49960
49970
49980
49990
50000
50010
50020
50030
50040
50050
50060
50070
50080
50090
50100
50110
50120
50130
50140
50150
50160
50170
50180
50190
50200
50210
50220
50230
50240
50250
50260
50270
50280
50290
50300
50310
50320
50330
50340
50350
50360
50370
50380
50390
50400
50410
50420
50430
50440
50450
50460
50470
50480
50490
50500
50510
50520
50530
50540
50550
50560
50570
50580
50590
50600
50610
50620
50630
50640
50650
50660
50670
50680
50690
50700
50710
50720
50730
50740
50750
50760
50770
50780
50790
50800
50810
50820
50830
50840
50850
50860
50870
50880
50890
50900
50910
50920
50930
50940
50950
50960
50970
50980
50990
51000
51010
51020
51030
51040
51050
51060
51070
51080
51090
51100
51110
51120
51130
51140
51150
51160
51170
51180
51190
51200
51210
51220
51230
51240
51250
51260
51270
51280
51290
51300
51310
51320
51330
51340
51350
51360
51370
51380
51390
51400
51410
51420
51430
51440
51450
51460
51470
51480
51490
51500
51510
51520
51530
51540
51550
51560
51570
51580
51590
51600
51610
51620
51630
51640
51650
51660
51670
51680
51690
51700
51710
51720
51730
51740
51750
51760
51770
51780
51790
51800
51810
51820
51830
51840
51850
51860
51870
51880
51890
51900
51910
51920
51930
51940
51950
51960
51970
51980
51990
52000
52010
52020
52030
52040
52050
52060
52070
52080
52090
52100
52110
52120
52130
52140
52150
52160
52170
52180
52190
52200
52210
52220
52230
52240
52250
52260
52270
52280
52290
52300
52310
52320
52330
52340
52350
52360
52370
52380
52390
52400
52410
52420
52430
52440
52450
52460
52470
52480
52490
52500
52510
52520
52530
52540
52550
52560
52570
52580
52590
52600
52610
52620
52630
52640
52650
52660
52670
52680
52690
52700
52710
52720
52730
52740
52750
52760
52770
52780
52790
52800
52810
52820
52830
52840
52850
52860
52870
52880
52890
52900
52910
52920
52930
52940
52950
52960
52970
52980
52990
53000
53010
53020
53030
53040
53050
53060
53070
53080
53090
53100
53110
53120
53130
53140
53150
53160
53170
53180
53190
53200
53210
53220
53230
53240
53250
53260
53270
53280
53290
53300
53310
53320
53330
53340
53350
53360
53370
53380
53390
53400
53410
53420
53430
53440
53450
53460
53470
53480
53490
53500
53510
53520
53530
53540
53550
53560
53570
53580
53590
53600
53610
53620
53630
53640
53650
53660
53670
53680
53690
53700
53710
53720
53730
53740
53750
53760
53770
53780
53790
53800
53810
53820
53830
53840
53850
53860
53870
53880
53890
53900
53910
53920
53930
53940
53950
53960
53970
53980
53990
54000
54010
54020
54030
54040
54050
54060
54070
54080
54090
54100
54110
54120
54130
54140
54150
54160
54170
54180
54190
54200
54210
54220
54230
54240
54250
54260
54270
54280
54290
54300
54310
54320
54330
54340
54350
54360
54370
54380
54390
54400
54410
54420
54430
54440
54450
54460
54470
54480
54490
54500
54510
54520
54530
54540
54550
54560
54570
54580
54590
54600
54610
54620
54630
54640
54650
54660
54670
54680
54690
54700
54710
54720
54730
54740
54750
54760
54770
54780
54790
54800
54810
54820
54830
54840
54850
54860
54870
54880
54890
54900
54910
54920
54930
54940
54950
54960
54970
54980
54990
55000
55010
55020
55030
55040
55050
55060
55070
55080
55090
55100
55110
55120
55130
55140
55150
55160
55170
55180
55190
55200
55210
55220
55230
55240
55250
55260
55270
55280
55290
55300
55310
55320
55330
55340
55350
55360
55370
55380
55390
55400
55410
55420
55430
55440
55450
55460
55470
55480
55490
55500
55510
55520
55530
55540
55550
55560
55570
55580
55590
55600
55610
55620
55630
55640
55650
55660
55670
55680
55690
55700
55710
55720
55730
55740
55750
55760
55770
55780
55790
55800
55810
55820
55830
55840
55850
55860
55870
55880
55890
55900
55910
55920
55930
55940
55950
55960
55970
55980
55990
56000
56010
56020
56030
56040
56050
56060
56070
56080
56090
56100
56110
56120
56130
56140
56150
56160
56170
56180
56190
56200
56210
56220
56230
56240
56250
56260
56270
56280
56290
56300
56310
56320
56330
56340
56350
56360
56370
56380
56390
56400
56410
56420
56430
56440
56450
56460
56470
56480
56490
56500
56510
56520
56530
56540
56550
56560
56570
56580
56590
56600
56610
56620
56630
56640
56650
56660
56670
56680
56690
56700
56710
56720
56730
56740
56750
56760
56770
56780
56790
56800
56810
56820
56830
56840
56850
56860
56870
56880
56890
56900
56910
56920
56930
56940
56950
56960
56970
56980
56990
57000
57010
57020
57030
57040
57050
57060
57070
57080
57090
57100
57110
57120
57130
57140
57150
57160
57170
57180
57190
57200
57210
57220
57230
57240
57250
57260
57270
57280
57290
57300
57310
57320
57330
57340
57350
57360
57370
57380
57390
57400
57410
57420
57430
57440
57450
57460
57470
57480
57490
57500
57510
57520
57530
57540
57550
57560
57570
57580
57590
57600
57610
57620
57630
57640
57650
57660
57670
57680
57690
57700
57710
57720
57730
57740
57750
57760
57770
57780
57790
57800
57810
57820
57830
57840
57850
57860
57870
57880
57890
57900
57910
57920
57930
57940
57950
57960
57970
57980
57990
58000
58010
58020
58030
58040
58050
58060
58070
58080
58090
58100
58110
58120
58130
58140
58150
58160
58170
58180
58190
58200
58210
58220
58230
58240
58250
58260
58270
58280
58290
58300
58310
58320
58330
58340
58350
58360
58370
58380
58390
58400
58410
58420
58430
58440
58450
58460
58470
58480
58490
58500
58510
58520
58530
58540
58550
58560
58570
58580
58590
58600
58610
58620
58630
58640
58650
58660
58670
58680
58690
58700
58710
58720
58730
58740
58750
58760
58770
58780
58790
58800
58810
58820
58830
58840
58850
58860
58870
58880
58890
58900
58910
58920
58930
58940
58950
58960
58970
58980
58990
59000
59010
59020
59030
59040
59050
59060
59070
59080
59090
59100
59110
59120
59130
59140
59150
59160
59170
59180
59190
59200
59210
59220
59230
59240
59250
59260
59270
59280
59290
59300
59310
59320
59330
59340
59350
59360
59370
59380
59390
59400
59410
59420
59430
59440
59450
59460
59470
59480
59490
59500
59510
59520
59530
59540
59550
59560
59570
59580
59590
59600
59610
59620
59630
59640
59650
59660
59670
59680
59690
59700
59710
59720
59730
59740
59750
59760
59770
59780
59790
59800
59810
59820
59830
59840
59850
59860
59870
59880
59890
59900
59910
59920
59930
59940
59950
59960
59970
59980
59990
60000
60010
60020
60030
60040
60050
60060
60070
60080
60090
60100
60110
60120
60130
60140
60150
60160
60170
60180
60190
60200
60210
60220
60230
60240
60250
60260
60270
60280
60290
60300
60310
60320
60330
60340
60350
60360
60370
60380
60390
60400
60410
60420
60430
60440
60450
60460
60470
60480
60490
60500
60510
60520
60530
60540
60550
60560
60570
60580
60590
60600
60610
60620
60630
60640
60650
60660
60670
60680
60690
60700
60710
60720
60730
60740
60750
60760
60770
60780
60790
60800
60810
60820
60830
60840
60850
60860
60870
60880
60890
60900
60910
60920
60930
60940
60950
60960
60970
60980
60990
61000
61010
61020
61030
61040
61050
61060
61070
61080
61090
61100
61110
61120
61130
61140
61150
61160
61170
61180
61190
61200
61210
61220
61230
61240
61250
61260
61270
61280
61290
61300
61310
61320
61330
61340
61350
61360
61370
61380
61390
61400
61410
61420
61430
61440
61450
61460
61470
61480
61490
61500
61510
61520
61530
61540
61550
61560
61570
61580
61590
61600
61610
61620
61630
61640
61650
61660
61670
61680
61690
61700
61710
61720
61730
61740
61750
61760
61770
61780
61790
61800
61810
61820
61830
61840
61850
61860
61870
61880
61890
61900
61910
61920
61930
61940
61950
61960
61970
61980
61990
62000
62010
62020
62030
62040
62050
62060
62070
62080
62090
62100
62110
62120
62130
62140
62150
62160
62170
62180
62190
62200
62210
62220
62230
62240
62250
62260
62270
62280
62290
62300
62310
62320
62330
62340
62350
62360
62370
62380
62390
62400
62410
62420
62430
62440
62450
62460
62470
62480
62490
62500
62510
62520
62530
62540
62550
62560
62570
62580
62590
62600
62610
62620
62630
62640
62650
62660
62670
62680
62690
62700
62710
62720
62730
62740
62750
62760
62770
62780
62790
62800
62810
62820
62830
62840
62850
62860
62870
62880
62890
62900
62910
62920
62930
62940
62950
62960
62970
62980
62990
63000
63010
63020
63030
63040
63050
63060
63070
63080
63090
63100
63110
63120
63130
63140
63150
63160
63170
63180
63190
63200
63210
63220
63230
63240
63250
63260
63270
63280
63290
63300
63310
63320
63330
63340
63350
63360
63370
63380
63390
63400
63410
63420
63430
63440
63450
63460
63470
63480
63490
63500
63510
63520
63530
63540
63550
63560
63570
63580
63590
63600
63610
63620
63630
63640
63650
63660
63670
63680
63690
63700
63710
63720
63730
63740
63750
63760
63770
63780
63790
63800
63810
63820
63830
63840
63850
63860
63870
63880
63890
63900
63910
63920
63930
63940
63950
63960
63970
63980
63990
64000
64010
64020
64030
64040
64050
64060
64070
64080
64090
64100
64110
64120
64130
64140
64150
64160
64170
64180
64190
64200
64210
64220
64230
64240
64250
64260
64270
64280
64290
64300
64310
64320
64330
64340
64350
64360
64370
64380
64390
64400
64410
64420
64430
64440
64450
64460
64470
64480
64490
64500
64510
64520
64530
64540
64550
64560
64570
64580
64590
64600
64610
64620
64630
64640
64650
64660
64670
64680
64690
64700
64710
64720
64730
64740
64750
64760
64770
64780
64790
64800
64810
64820
64830
64840
64850
64860
64870
64880
64890
64900
64910
64920
64930
64940
64950
64960
64970
64980
64990
65000
65010
65020
65030
65040
65050
65060
65070
65080
65090
65100
65110
65120
65130
65140
65150
65160
65170
65180
65190
65200
65210
65220
65230
65240
65250
65260
65270
65280
65290
65300
65310
65320
65330
65340
65350
65360
65370
65380
65390
65400
65410
65420
65430
65440
65450
65460
65470
65480
65490
65500
65510
65520
65530
65540
65550
65560
65570
65580
65590
65600
65610
65620
65630
65640
65650
65660
65670
65680
65690
65700
65710
65720
65730
65740
65750
65760
65770
65780
65790
65800
65810
65820
65830
65840
65850
65860
65870
65880
65890
65900
65910
65920
65930
65940
65950
65960
65970
65980
65990
66000
66010
66020
66030
66040
66050
66060
66070
66080
66090
66100
66110
66120
66130
66140
66150
66160
66170
66180
66190
66200
66210
66220
66230
66240
66250
66260
66270
66280
66290
66300
66310
66320
66330
66340
66350
66360
66370
66380
66390
66400
66410
66420
66430
66440
66450
66460
66470
66480
66490
66500
66510
66520
66530
66540
66550
66560
66570
66580
66590
66600
66610
66620
66630
66640
66650
66660
66670
66680
66690
66700
66710
66720
66730
66740
66750
66760
66770
66780
66790
66800
66810
66820
66830
66840
66850
66860
66870
66880
66890
66900
66910
66920
66930
66940
66950
66960
66970
66980
66990
67000
67010
67020
67030
67040
67050
67060
67070
67080
67090
67100
67110
67120
67130
67140
67150
67160
67170
67180
67190
67200
67210
67220
67230
67240
67250
67260
67270
67280
67290
67300
67310
67320
67330
67340
67350
67360
67370
67380
67390
67400
67410
67420
67430
67440
67450
67460
67470
67480
67490
67500
67510
67520
67530
67540
67550
67560
67570
67580
67590
67600
67610
67620
67630
67640
67650
67660
67670
67680
67690
67700
67710
67720
67730
67740
67750
67760
67770
67780
67790
67800
67810
67820
67830
67840
67850
67860
67870
67880
67890
67900
67910
67920
67930
67940
67950
67960
67970
67980
67990
68000
68010
68020
68030
68040
68050
68060
68070
68080
68090
68100
68110
68120
68130
68140
68150
68160
68170
68180
68190
68200
68210
68220
68230
68240
68250
68260
68270
68280
68290
68300
68310
68320
68330
68340
68350
68360
68370
68380
68390
68400
68410
68420
68430
68440
68450
68460
68470
68480
68490
68500
68510
68520
68530
68540
68550
68560
68570
68580
68590
68600
68610
68620
68630
68640
68650
68660
68670
68680
68690
68700
68710
68720
68730
68740
68750
68760
68770
68780
68790
68800
68810
68820
68830
68840
68850
68860
68870
68880
68890
68900
68910
68920
68930
68940
68950
68960
68970
68980
68990
69000
69010
69020
69030
69040
69050
69060
69070
69080
69090
69100
69110
69120
69130
69140
69150
69160
69170
69180
69190
69200
69210
69220
69230
69240
69250
69260
69270
69280
69290
69300
69310
69320
69330
69340
69350
69360
69370
69380
69390
69400
69410
69420
69430
69440
69450
69460
69470
69480
69490
69500
69510
69520
69530
69540
69550
69560
69570
69580
69590
69600
69610
69620
69630
69640
69650
69660
69670
69680
69690
69700
69710
69720
69730
69740
69750
69760
69770
69780
69790
69800
69810
69820
69830
69840
69850
69860
69870
69880
69890
69900
69910
69920
69930
69940
69950
69960
69970
69980
69990
70000
70010
70020
70030
70040
70050
70060
70070
70080
70090
70100
70110
70120
70130
70140
70150
70160
70170
70180
70190
70200
70210
70220
70230
70240
70250
70260
70270
70280
70290
70300
70310
70320
70330
70340
70350
70360
70370
70380
70390
70400
70410
70420
70430
70440
70450
70460
70470
70480
70490
70500
70510
70520
70530
70540
70550
70560
70570
70580
70590
70600
70610
70620
70630
70640
70650
70660
70670
70680
70690
70700
70710
70720
70730
70740
70750
70760
70770
70780
70790
70800
70810
70820
70830
70840
70850
70860
70870
70880
70890
70900
70910
70920
70930
70940
70950
70960
70970
70980
70990
71000
71010
71020
71030
71040
71050
71060
71070
71080
71090
71100
71110
71120
71130
71140
71150
71160
71170
71180
71190
71200
71210
71220
71230
71240
71250
71260
71270
71280
71290
71300
71310
71320
71330
71340
71350
71360
71370
71380
71390
71400
71410
71420
71430
71440
71450
71460
71470
71480
71490
71500
71510
71520
71530
71540
71550
71560
71570
71580
71590
71600
71610
71620
71630
71640
71650
71660
71670
71680
71690
71700
71710
71720
71730
71740
71750
71760
71770
71780
71790
71800
71810
71820
71830
71840
71850
71860
71870
71880
71890
71900
71910
71920
71930
71940
71950
71960
71970
71980
71990
72000
72010
72020
72030
72040
72050
72060
72070
72080
72090
72100
72110
72120
72130
72140
72150
72160
72170
72180
72190
72200
72210
72220
72230
72240
72250
72260
72270
72280
72290
72300
72310
72320
72330
72340
72350
72360
72370
72380
72390
72400
72410
72420
72430
72440
72450
72460
72470
72480
72490
72500
72510
72520
72530
72540
72550
72560
72570
72580
72590
72600
72610
72620
72630
72640
72650
72660
72670
72680
72690
72700
72710
72720
72730
72740
72750
72760
72770
72780
72790
72800
72810
72820
72830
72840
72850
72860
72870
72880
72890
72900
72910
72920
72930
72940
72950
72960
72970
72980
72990
73000
73010
73020
73030
73040
73050
73060
73070
73080
73090
73100
73110
73120
73130
73140
73150
73160
73170
73180
73190
73200
73210
73220
73230
73240
73250
73260
73270
73280
73290
73300
73310
73320
73330
73340
73350
73360
73370
73380
73390
73400
73410
73420
73430
73440
73450
73460
73470
73480
73490
73500
73510
73520
73530
73540
73550
73560
73570
73580
73590
73600
73610
73620
73630
73640
73650
73660
73670
73680
73690
73700
73710
73720
73730
73740
73750
73760
73770
73780
73790
73800
73810
73820
73830
73840
73850
73860
73870
73880
73890
73900
73910
73920
73930
73940
73950
73960
73970
73980
73990
74000
74010
74020
74030
74040
74050
74060
74070
74080
74090
74100
74110
74120
74130
74140
74150
74160
74170
74180
74190
74200
74210
74220
74230
74240
74250
74260
74270
74280
74290
74300
74310
74320
74330
74340
74350
74360
74370
74380
74390
74400
74410
74420
74430
74440
74450
74460
74470
74480
74490
74500
74510
74520
74530
74540
74550
74560
74570
74580
74590
74600
74610
74620
74630
74640
74650
74660
74670
74680
74690
74700
74710
74720
74730
74740
74750
74760
74770
74780
74790
74800
74810
74820
74830
74840
74850
74860
74870
74880
74890
74900
74910
74920
74930
74940
74950
74960
74970
74980
74990
75000
75010
75020
75030
75040
75050
75060
75070
75080
75090
75100
75110
75120
75130
75140
75150
75160
75170
75180
75190
75200
75210
75220
75230
75240
75250
75260
75270
75280
75290
75300
75310
75320
75330
75340
75350
75360
75370
75380
75390
75400
75410
75420
75430
75440
75450
75460
75470
75480
75490
75500
75510
75520
75530
75540
75550
75560
75570
75580
75590
75600
75610
75620
75630
75640
75650
75660
75670
75680
75690
75700
75710
75720
75730
75740
75750
75760
75770
75780
75790
75800
75810
75820
75830
75840
75850
75860
75870
75880
75890
75900
75910
75920
75930
75940
75950
75960
75970
75980
75990
76000
76010
76020
76030
76040
76050
76060
76070
76080
76090
76100
76110
76120
76130
76140
76150
76160
76170
76180
76190
76200
76210
76220
76230
76240
76250
76260
76270
76280
76290
76300
76310
76320
76330
76340
76350
76360
76370
76380
76390
76400
76410
76420
76430
76440
76450
76460
76470
76480
76490
76500
76510
76520
76530
76540
76550
76560
76570
76580
76590
76600
76610
76620
76630
76640
76650
76660
76670
76680
76690
76700
76710
76720
76730
76740
76750
76760
76770
76780
76790
76800
76810
76820
76830
76840
76850
76860
76870
76880
76890
76900
76910
76920
76930
76940
76950
76960
76970
76980
76990
77000
77010
77020
77030
77040
77050
77060
77070
77080
77090
77100
77110
77120
77130
77140
77150
77160
77170
77180
77190
77200
77210
77220
77230
77240
77250
77260
77270
77280
77290
77300
77310
77320
77330
77340
77350
77360
77370
77380
77390
77400
77410
77420
77430
77440
77450
77460
77470
77480
77490
77500
77510
77520
77530
77540
77550
77560
77570
77580
77590
77600
77610
77620
77630
77640
77650
77660
77670
77680
77690
77700
77710
77720
77730
77740
77750
77760
77770
77780
77790
77800
77810
77820
77830
77840
77850
77860
77870
77880
77890
77900
77910
77920
77930
77940
77950
77960
77970
77980
77990
78000
78010
78020
78030
78040
78050
78060
78070
78080
78090
78100
78110
78120
78130
78140
78150
78160
78170
78180
78190
78200
78210
78220
78230
78240
78250
78260
78270
78280
78290
78300
78310
78320
78330
78340
78350
78360
78370
78380
78390
78400
78410
78420
78430
78440
78450
78460
78470
78480
78490
78500
78510
78520
78530
78540
78550
78560
78570
78580
78590
78600
78610
78620
78630
78640
78650
78660
78670
78680
78690
78700
78710
78720
78730
78740
78750
78760
78770
78780
78790
78800
78810
78820
78830
78840
78850
78860
78870
78880
78890
78900
78910
78920
78930
78940
78950
78960
78970
78980
78990
79000
79010
79020
79030
79040
79050
79060
79070
79080
79090
79100
79110
79120
79130
79140
79150
79160
79170
79180
79190
79200
79210
79220
79230
79240
79250
79260
79270
79280
79290
79300
79310
79320
79330
79340
79350
79360
79370
79380
79390
79400
79410
79420
79430
79440
79450
79460
79470
79480
79490
79500
79510
79520
79530
79540
79550
79560
79570
79580
79590
79600
79610
79620
79630
79640
79650
79660
79670
79680
79690
79700
79710
79720
79730
79740
79750
79760
79770
79780
79790
79800
79810
79820
79830
79840
79850
79860
79870
79880
79890
79900
79910
79920
79930
79940
79950
79960
79970
79980
79990
80000
80010
80020
80030
80040
80050
80060
80070
80080
80090
80100
80110
80120
80130
80140
80150
80160
80170
80180
80190
80200
80210
80220
80230
80240
80250
80260
80270
80280
80290
80300
80310
80320
80330
80340
80350
80360
80370
80380
80390
80400
80410
80420
80430
80440
80450
80460
80470
80480
80490
80500
80510
80520
80530
80540
80550
80560
80570
80580
80590
80600
80610
80620
80630
80640
80650
80660
80670
80680
80690
80700
80710
80720
80730
80740
80750
80760
80770
80780
80790
80800
80810
80820
80830
80840
80850
80860
80870
80880
80890
80900
80910
80920
80930
80940
80950
80960
80970
80980
80990
81000
81010
81020
81030
81040
81050
81060
81070
81080
81090
81100
81110
81120
81130
81140
81150
81160
81170
81180
81190
81200
81210
81220
81230
81240
81250
81260
81270
81280
81290
81300
81310
81320
81330
81340
81350
81360
81370
81380
81390
81400
81410
81420
81430
81440
81450
81460
81470
81480
81490
81500
81510
81520
81530
81540
81550
81560
81570
81580
81590
81600
81610
81620
81630
81640
81650
81660
81670
81680
81690
81700
81710
81720
81730
81740
81750
81760
81770
81780
81790
81800
81810
81820
81830
81840
81850
81860
81870
81880
81890
81900
81910
81920
81930
81940
81950
81960
81970
81980
81990
82000
82010
82020
82030
82040
82050
82060
82070
82080
82090
82100
82110
82120
82130
82140
82150
82160
82170
82180
82190
82200
82210
82220
82230
82240
82250
82260
82270
82280
82290
82300
82310
82320
82330
82340
82350
82360
82370
82380
82390
82400
82410
82420
82430
82440
82450
82460
82470
82480
82490
82500
82510
82520
82530
82540
82550
82560
82570
82580
82590
82600
82610
82620
82630
82640
82650
82660
82670
82680
82690
82700
82710
82720
82730
82740
82750
82760
82770
82780
82790
82800
82810
82820
82830
82840
82850
82860
82870
82880
82890
82900
82910
82920
82930
82940
82950
82960
82970
82980
82990
83000
83010
83020
83030
83040
83050
83060
83070
83080
83090
83100
83110
83120
83130
83140
83150
83160
83170
83180
83190
83200
83210
83220
83230
83240
83250
83260
83270
83280
83290
83300
83310
83320
83330
83340
83350
83360
83370
83380
83390
83400
83410
83420
83430
83440
83450
83460
83470
83480
83490
83500
83510
83520
83530
83540
83550
83560
83570
83580
83590
83600
83610
83620
83630
83640
83650
83660
83670
83680
83690
83700
83710
83720
83730
83740
83750
83760
83770
83780
83790
83800
83810
83820
83830
83840
83850
83860
83870
83880
83890
83900
83910
83920
83930
83940
83950
83960
83970
83980
83990
84000
84010
84020
84030
84040
84050
84060
84070
84080
84090
84100
84110
84120
84130
84140
84150
84160
84170
84180
84190
84200
84210
84220
84230
84240
84250
84260
84270
84280
84290
84300
84310
84320
84330
84340
84350
84360
84370
84380
84390
84400
84410
84420
84430
84440
84450
84460
84470
84480
84490
84500
84510
84520
84530
84540
84550
84560
84570
84580
84590
84600
84610
84620
84630
84640
84650
84660
84670
84680
84690
84700
84710
84720
84730
84740
84750
84760
84770
84780
84790
84800
84810
84820
84830
84840
84850
84860
84870
84880
84890
84900
84910
84920
84930
84940
84950
84960
84970
84980
84990
85000
85010
85020
85030
85040
85050
85060
85070
85080
85090
85100
85110
85120
85130
85140
85150
85160
85170
85180
85190
85200
85210
85220
85230
85240
85250
85260
85270
85280
85290
85300
85310
85320
85330
85340
85350
85360
85370
85380
85390
85400
85410
85420
85430
85440
85450
85460
85470
85480
85490
85500
85510
85520
85530
85540
85550
85560
85570
85580
85590
85600
85610
85620
85630
85640
85650
85660
85670
85680
85690
85700
85710
85720
85730
85740
85750
85760
85770
85780
85790
85800
85810
85820
85830
85840
85850
85860
85870
85880
85890
85900
85910
85920
85930
85940
85950
85960
85970
85980
85990
86000
86010
86020
86030
86040
86050
86060
86070
86080
86090
86100
86110
86120
86130
86140
86150
86160
86170
86180
86190
86200
86210
86220
86230
86240
86250
86260
86270
86280
86290
86300
86310
86320
86330
86340
86350
86360
86370
86380
86390
86400
86410
86420
86430
86440
86450
86460
86470
86480
86490
86500
86510
86520
86530
86540
86550
86560
86570
86580
86590
86600
86610
86620
86630
86640
86650
86660
86670
86680
86690
86700
86710
86720
86730
86740
86750
86760
86770
86780
86790
86800
86810
86820
86830
86840
86850
86860
86870
86880
86890
86900
86910
86920
86930
86940
86950
86960
86970
86980
86990
87000
87010
87020
87030
87040
87050
87060
87070
87080
87090
87100
87110
87120
87130
87140
87150
87160
87170
87180
87190
87200
87210
87220
87230
87240
87250
87260
87270
87280
87290
87300
87310
87320
87330
87340
87350
87360
87370
87380
87390
87400
87410
87420
87430
87440
87450
87460
87470
87480
87490
87500
87510
87520
87530
87540
87550
87560
87570
87580
87590
87600
87610
87620
87630
87640
87650
87660
87670
87680
87690
87700
87710
87720
87730
87740
87750
87760
87770
87780
87790
87800
87810
87820
87830
87840
87850
87860
87870
87880
87890
87900
87910
87920
87930
87940
87950
87960
87970
87980
87990
88000
88010
88020
88030
88040
88050
88060
88070
88080
88090
88100
88110
88120
88130
88140
88150
88160
88170
88180
88190
88200
88210
88220
88230
88240
88250
88260
88270
88280
88290
88300
88310
88320
88330
88340
88350
88360
88370
88380
88390
88400
88410
88420
88430
88440
88450
88460
88470
88480
88490
88500
88510
88520
88530
88540
88550
88560
88570
88580
88590
88600
88610
88620
88630
88640
88650
88660
88670
88680
88690
88700
88710
88720
88730
88740
88750
88760
88770
88780
88790
88800
88810
88820
88830
88840
88850
88860
88870
88880
88890
88900
88910
88920
88930
88940
88950
88960
88970
88980
88990
89000
89010
89020
89030
89040
89050
89060
89070
89080
89090
89100
89110
89120
89130
89140
89150
89160
89170
89180
89190
89200
89210
89220
89230
89240
89250
89260
89270
89280
89290
89300
89310
89320
89330
89340
89350
89360
89370
89380
89390
89400
89410
89420
89430
89440
89450
89460
89470
89480
89490
89500
89510
89520
89530
89540
89550
89560
89570
89580
89590
89600
89610
89620
89630
89640
89650
89660
89670
89680
89690
89700
89710
89720
89730
89740
89750
89760
89770
89780
89790
89800
89810
89820
89830
89840
89850
89860
89870
89880
89890
89900
89910
89920
89930
89940
89950
89960
89970
89980
89990
90000
90010
90020
90030
90040
90050
90060
90070
90080
90090
90100
90110
90120
90130
90140
90150
90160
90170
90180
90190
90200
90210
90220
90230
90240
90250
90260
90270
90280
90290
90300
90310
90320
90330
90340
90350
90360
90370
90380
90390
90400
90410
90420
90430
90440
90450
90460
90470
90480
90490
90500
90510
90520
90530
90540
90550
90560
90570
90580
90590
90600
90610
90620
90630
90640
90650
90660
90670
90680
90690
90700
90710
90720
90730
90740
90750
90760
90770
90780
90790
90800
90810
90820
90830
90840
// Written in the D programming language module dparse.parser; import dparse.lexer; import dparse.ast; import dparse.rollback_allocator; import dparse.stack_buffer; import std.experimental.allocator.mallocator; import std.experimental.allocator; import std.conv; import std.algorithm; import std.array; import std.string : format; // Uncomment this if you want ALL THE OUTPUT // Caution: generates 180 megabytes of logging for std.datetime //version = dparse_verbose; /** * Prototype for a custom parser message function or delegate. * Parameters passed are a file name, a line, a column, a message and a `bool` * that indicates if the message is a warning (`false`) or a if it's an error (`true`). */ alias MessageFunction = void function(string fileName , size_t line, size_t column, string message, bool isError); /// ditto alias MessageDelegate = void delegate(string, size_t, size_t, string, bool); /** * Parser configuration struct */ struct ParserConfig { /// The tokens parsed by dparse.lexer. const(Token)[] tokens; /// The name of the file being parsed string fileName; /// A pointer to a rollback allocator. RollbackAllocator* allocator; /// An optional function used to handle warnings and errors. MessageFunction messageFunction; /// An optional delegate used to handle warnings and errors. /// Set either this one or messageFunction, not both. MessageDelegate messageDelegate; /// An optional pointer to a variable receiving the error count. uint* errorCount; /// An optional pointer to a variable receiving the warning count. uint* warningCount; } /** * Params: * parserConfig = a parser configuration. * Returns: * The parsed module. */ Module parseModule()(auto ref ParserConfig parserConfig) { auto parser = new Parser(); with (parserConfig) { parser.fileName = fileName; parser.tokens = tokens; parser.messageFunction = messageFunction; parser.messageDelegate = messageDelegate; parser.allocator = allocator; } Module mod = parser.parseModule(); with (parserConfig) { if (warningCount !is null) *warningCount = parser.warningCount; if (errorCount !is null) *errorCount = parser.errorCount; } return mod; } /** * Params: * tokens = The tokens parsed by dparse.lexer. * fileName = The name of the file being parsed. * allocator = A pointer to a rollback allocator. * messageFuncOrDg = Either a function or a delegate that receives the parser messages. * errorCount = An optional pointer to a variable receiving the error count. * warningCount = An optional pointer to a variable receiving the warning count. * Returns: * The parsed module. */ Module parseModule(F)(const(Token)[] tokens, string fileName, RollbackAllocator* allocator, F messageFuncOrDg = null, uint* errorCount = null, uint* warningCount = null) { static if (is(F)) { static if (is(F : MessageFunction)) return ParserConfig(tokens, fileName, allocator, messageFuncOrDg, null, errorCount, warningCount).parseModule(); else static if (is(F : MessageDelegate)) return ParserConfig(tokens, fileName, allocator, null, messageFuncOrDg, errorCount, warningCount).parseModule(); else static assert(0, "F must be a MessageFunction or a MessageDelegate"); } else { return ParserConfig(tokens, fileName, allocator, null, null, null, null).parseModule(); } } /** * D Parser. * * It is sometimes useful to sub-class Parser to skip over things that are not * interesting. For example, DCD skips over function bodies when caching symbols * from imported files. */ class Parser { /** * Parses an AddExpression. * * $(GRAMMAR $(RULEDEF addExpression): * $(RULE mulExpression) * | $(RULE addExpression) $(LPAREN)$(LITERAL '+') | $(LITERAL'-') | $(LITERAL'~')$(RPAREN) $(RULE mulExpression) * ;) */ ExpressionNode parseAddExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AddExpression, MulExpression, tok!"+", tok!"-", tok!"~")(); } /** * Parses an AliasDeclaration. * * $(GRAMMAR $(RULEDEF aliasDeclaration): * $(LITERAL 'alias') $(RULE aliasInitializer) $(LPAREN)$(LITERAL ',') $(RULE aliasInitializer)$(RPAREN)* $(LITERAL ';') * | $(LITERAL 'alias') $(RULE storageClass)* $(RULE type) $(RULE declaratorIdentifierList) $(LITERAL ';') * | $(LITERAL 'alias') $(RULE storageClass)* $(RULE type) $(RULE identifier) $(LITERAL '(') $(RULE parameters) $(LITERAL ')') $(memberFunctionAttribute)* $(LITERAL ';') * ;) */ AliasDeclaration parseAliasDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasDeclaration; mixin(tokenCheck!"alias"); node.comment = comment; comment = null; if (startsWith(tok!"identifier", tok!"=") || startsWith(tok!"identifier", tok!"(")) { StackBuffer initializers; do { if (!initializers.put(parseAliasInitializer())) return null; if (currentIs(tok!",")) advance(); else break; } while (moreTokens()); ownArray(node.initializers, initializers); } else { StackBuffer storageClasses; while (moreTokens() && isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); mixin (parseNodeQ!(`node.type`, `Type`)); mixin (parseNodeQ!(`node.declaratorIdentifierList`, `DeclaratorIdentifierList`)); if (currentIs(tok!"(")) { mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } } return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AliasAssign. * * $(GRAMMAR $(RULEDEF aliasAssign): * $(LITERAL Identifier) $(LITERAL '=') $(RULE type) */ AliasAssign parseAliasAssign() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasAssign; node.comment = comment; comment = null; mixin(tokenCheck!(`node.identifier`, "identifier")); mixin(tokenCheck!"="); mixin(parseNodeQ!(`node.type`, `Type`)); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AliasInitializer. * * $(GRAMMAR $(RULEDEF aliasInitializer): * $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE storageClass)* $(RULE type) * | $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE storageClass)* $(RULE type) $(RULE parameters) $(RULE memberFunctionAttribute)* * | $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE functionLiteralExpression) * ;) */ AliasInitializer parseAliasInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasInitializer; mixin (tokenCheck!(`node.name`, "identifier")); if (currentIs(tok!"(")) mixin (parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); mixin(tokenCheck!"="); bool isFunction() { if (currentIsOneOf(tok!"function", tok!"delegate", tok!"{")) return true; if (startsWith(tok!"identifier", tok!"=>")) return true; const b = setBookmark(); scope(exit) goToBookmark(b); if (currentIs(tok!"(") || currentIs(tok!"ref") && peekIs(tok!"(")) { if (currentIs(tok!"ref")) advance(); const t = peekPastParens(); if (t !is null) { if (t.type == tok!"=>" || t.type == tok!"{" || isMemberFunctionAttribute(t.type)) return true; } } return false; } if (isFunction) mixin (parseNodeQ!(`node.functionLiteralExpression`, `FunctionLiteralExpression`)); else { StackBuffer storageClasses; while (moreTokens() && isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); mixin (parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"(")) { mixin (parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AliasThisDeclaration. * * $(GRAMMAR $(RULEDEF aliasThisDeclaration): * $(LITERAL 'alias') $(LITERAL Identifier) $(LITERAL 'this') $(LITERAL ';') * ;) */ AliasThisDeclaration parseAliasThisDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasThisDeclaration; mixin(tokenCheck!"alias"); mixin(tokenCheck!(`node.identifier`, "identifier")); mixin(tokenCheck!"this"); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AlignAttribute. * * $(GRAMMAR $(RULEDEF alignAttribute): * $(LITERAL 'align') ($(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)'))? * ;) */ AlignAttribute parseAlignAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AlignAttribute; expect(tok!"align"); if (currentIs(tok!"(")) { mixin(tokenCheck!"("); mixin(parseNodeQ!("node.assignExpression", "AssignExpression")); mixin(tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AndAndExpression. * * $(GRAMMAR $(RULEDEF andAndExpression): * $(RULE orExpression) * | $(RULE andAndExpression) $(LITERAL '&&') $(RULE orExpression) * ;) */ ExpressionNode parseAndAndExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AndAndExpression, OrExpression, tok!"&&")(); } /** * Parses an AndExpression. * * $(GRAMMAR $(RULEDEF andExpression): * $(RULE cmpExpression) * | $(RULE andExpression) $(LITERAL '&') $(RULE cmpExpression) * ;) */ ExpressionNode parseAndExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AndExpression, CmpExpression, tok!"&")(); } /** * Parses an ArgumentList. * * $(GRAMMAR $(RULEDEF argumentList): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression)?)* * ;) */ ArgumentList parseArgumentList() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("argument list expected instead of EOF"); return null; } size_t startLocation = current().index; auto node = parseCommaSeparatedRule!(ArgumentList, AssignExpression)(true); mixin (nullCheck!`node`); node.startLocation = startLocation; if (moreTokens) node.endLocation = current().index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses Arguments. * * $(GRAMMAR $(RULEDEF arguments): * $(LITERAL '$(LPAREN)') $(RULE argumentList)? $(LITERAL '$(RPAREN)') * ;) */ Arguments parseArguments() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Arguments; mixin(tokenCheck!"("); if (!currentIs(tok!")")) mixin (parseNodeQ!(`node.argumentList`, `ArgumentList`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayInitializer. * * $(GRAMMAR $(RULEDEF arrayInitializer): * $(LITERAL '[') $(LITERAL ']') * | $(LITERAL '[') $(RULE arrayMemberInitialization) ($(LITERAL ',') $(RULE arrayMemberInitialization)?)* $(LITERAL ']') * ;) */ ArrayInitializer parseArrayInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayInitializer; const open = expect(tok!"["); mixin (nullCheck!`open`); node.startLocation = open.index; StackBuffer arrayMemberInitializations; while (moreTokens()) { if (currentIs(tok!"]")) break; if (!arrayMemberInitializations.put(parseArrayMemberInitialization())) return null; if (currentIs(tok!",")) advance(); else break; } ownArray(node.arrayMemberInitializations, arrayMemberInitializations); const close = expect(tok!"]"); mixin (nullCheck!`close`); node.endLocation = close.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayLiteral. * * $(GRAMMAR $(RULEDEF arrayLiteral): * $(LITERAL '[') $(RULE argumentList)? $(LITERAL ']') * ;) */ ArrayLiteral parseArrayLiteral() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayLiteral; mixin(tokenCheck!"["); if (!currentIs(tok!"]")) mixin (parseNodeQ!(`node.argumentList`, `ArgumentList`)); mixin(tokenCheck!"]"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayMemberInitialization. * * $(GRAMMAR $(RULEDEF arrayMemberInitialization): * ($(RULE assignExpression) $(LITERAL ':'))? $(RULE nonVoidInitializer) * ;) */ ArrayMemberInitialization parseArrayMemberInitialization() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayMemberInitialization; switch (current.type) { case tok!"[": immutable b = setBookmark(); skipBrackets(); if (currentIs(tok!":")) { goToBookmark(b); mixin (parseNodeQ!(`node.assignExpression`, `AssignExpression`)); advance(); // : mixin (parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); break; } else { goToBookmark(b); goto case; } case tok!"{": mixin (parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); break; default: auto assignExpression = parseAssignExpression(); mixin (nullCheck!`assignExpression`); if (currentIs(tok!":")) { node.assignExpression = assignExpression; advance(); mixin(parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); } else { node.nonVoidInitializer = allocator.make!NonVoidInitializer; node.nonVoidInitializer.assignExpression = assignExpression; } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmAddExp * * $(GRAMMAR $(RULEDEF asmAddExp): * $(RULE asmMulExp) * | $(RULE asmAddExp) ($(LITERAL '+') | $(LITERAL '-')) $(RULE asmMulExp) * ;) */ ExpressionNode parseAsmAddExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmAddExp, AsmMulExp, tok!"+", tok!"-")(); } /** * Parses an AsmAndExp * * $(GRAMMAR $(RULEDEF asmAndExp): * $(RULE asmEqualExp) * | $(RULE asmAndExp) $(LITERAL '&') $(RULE asmEqualExp) * ;) */ ExpressionNode parseAsmAndExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmAndExp, AsmEqualExp, tok!"&"); } /** * Parses an AsmBrExp * * $(GRAMMAR $(RULEDEF asmBrExp): * $(RULE asmUnaExp) * | $(RULE asmBrExp)? $(LITERAL '[') $(RULE asmExp) $(LITERAL ']') * ;) */ AsmBrExp parseAsmBrExp() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("Found end-of-file when expecting an AsmBrExp", false); return null; } AsmBrExp node = allocator.make!AsmBrExp(); size_t line = current.line; size_t column = current.column; if (currentIs(tok!"[")) { advance(); // [ mixin (parseNodeQ!(`node.asmExp`, `AsmExp`)); mixin(tokenCheck!"]"); if (currentIs(tok!"[")) goto brLoop; } else { mixin(parseNodeQ!(`node.asmUnaExp`, `AsmUnaExp`)); brLoop: while (currentIs(tok!"[")) { AsmBrExp br = allocator.make!AsmBrExp(); // huehuehuehue br.asmBrExp = node; br.line = current().line; br.column = current().column; node = br; node.line = line; node.column = column; advance(); // [ mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); mixin(tokenCheck!"]"); } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmEqualExp * * $(GRAMMAR $(RULEDEF asmEqualExp): * $(RULE asmRelExp) * | $(RULE asmEqualExp) ('==' | '!=') $(RULE asmRelExp) * ;) */ ExpressionNode parseAsmEqualExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmEqualExp, AsmRelExp, tok!"==", tok!"!=")(); } /** * Parses an AsmExp * * $(GRAMMAR $(RULEDEF asmExp): * $(RULE asmLogOrExp) ($(LITERAL '?') $(RULE asmExp) $(LITERAL ':') $(RULE asmExp))? * ;) */ ExpressionNode parseAsmExp() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmExp node = allocator.make!AsmExp; mixin(parseNodeQ!(`node.left`, `AsmLogOrExp`)); if (currentIs(tok!"?")) { advance(); mixin(parseNodeQ!(`node.middle`, `AsmExp`)); mixin(tokenCheck!":"); mixin(parseNodeQ!(`node.right`, `AsmExp`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmInstruction * * $(GRAMMAR $(RULEDEF asmInstruction): * $(LITERAL Identifier) * | $(LITERAL 'align') $(LITERAL IntegerLiteral) * | $(LITERAL 'align') $(LITERAL Identifier) * | $(LITERAL Identifier) $(LITERAL ':') $(RULE asmInstruction) * | $(LITERAL Identifier) $(RULE operands) * | $(LITERAL 'in') $(RULE operands) * | $(LITERAL 'out') $(RULE operands) * | $(LITERAL 'int') $(RULE operands) * | $(LITERAL ';') * ;) */ AsmInstruction parseAsmInstruction(ref bool maybeGccASm) { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmInstruction node = allocator.make!AsmInstruction; if (currentIs(tok!";")) { warn("Empty asm instruction"); node.tokens = tokens[startIndex .. index]; return node; } if (currentIs(tok!"align")) { advance(); // align node.hasAlign = true; if (currentIsOneOf(tok!"intLiteral", tok!"identifier")) { node.identifierOrIntegerOrOpcode = advance(); if (!currentIs(tok!";")) { error("`;` expected."); if (moreTokens()) advance(); return null; } } else { error("Identifier or integer literal expected."); return null; } } else if (currentIsOneOf(tok!"identifier", tok!"in", tok!"out", tok!"int")) { node.identifierOrIntegerOrOpcode = advance(); if (node.identifierOrIntegerOrOpcode == tok!"identifier" && currentIs(tok!":")) { advance(); // : node.isLabel = true; if (currentIs(tok!";")) { node.tokens = tokens[startIndex .. index]; return node; } node.asmInstruction = parseAsmInstruction(maybeGccASm); if (node.asmInstruction is null) return null; } else if (!currentIs(tok!";")) mixin(parseNodeQ!(`node.operands`, `Operands`)); } else { maybeGccASm = true; return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmLogAndExp * * $(GRAMMAR $(RULEDEF asmLogAndExp): * $(RULE asmOrExp) * $(RULE asmLogAndExp) $(LITERAL '&&') $(RULE asmOrExp) * ;) */ ExpressionNode parseAsmLogAndExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmLogAndExp, AsmOrExp, tok!"&&"); } /** * Parses an AsmLogOrExp * * $(GRAMMAR $(RULEDEF asmLogOrExp): * $(RULE asmLogAndExp) * | $(RULE asmLogOrExp) '||' $(RULE asmLogAndExp) * ;) */ ExpressionNode parseAsmLogOrExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmLogOrExp, AsmLogAndExp, tok!"||")(); } /** * Parses an AsmMulExp * * $(GRAMMAR $(RULEDEF asmMulExp): * $(RULE asmBrExp) * | $(RULE asmMulExp) ($(LITERAL '*') | $(LITERAL '/') | $(LITERAL '%')) $(RULE asmBrExp) * ;) */ ExpressionNode parseAsmMulExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmMulExp, AsmBrExp, tok!"*", tok!"/", tok!"%")(); } /** * Parses an AsmOrExp * * $(GRAMMAR $(RULEDEF asmOrExp): * $(RULE asmXorExp) * | $(RULE asmOrExp) $(LITERAL '|') $(RULE asmXorExp) * ;) */ ExpressionNode parseAsmOrExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmOrExp, AsmXorExp, tok!"|")(); } /** * Parses an AsmPrimaryExp * * $(GRAMMAR $(RULEDEF asmPrimaryExp): * $(LITERAL IntegerLiteral) * | $(LITERAL FloatLiteral) * | $(LITERAL StringLiteral) * | $(RULE register) * | $(RULE register : AsmExp) * | $(RULE identifierChain) * | $(LITERAL '$') * | $(LITERAL 'this') * | $(LITERAL '__LOCAL_SIZE') * ;) */ AsmPrimaryExp parseAsmPrimaryExp() { import std.range : assumeSorted; mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmPrimaryExp node = allocator.make!AsmPrimaryExp(); switch (current().type) { foreach (NL; NumberLiterals) {case NL:} case tok!"stringLiteral": case tok!"$": case tok!"this": node.token = advance(); break; case tok!"identifier": if (assumeSorted(REGISTER_NAMES).equalRange(current().text).length > 0) { trace("Found register"); mixin (nullCheck!`(node.register = parseRegister())`); if (currentIs(tok!":")) { advance(); mixin(parseNodeQ!(`node.segmentOverrideSuffix`, `AsmExp`)); } } else mixin(parseNodeQ!(`node.identifierChain`, `IdentifierChain`)); break; default: error("Float literal, integer literal, `$`, `this` or identifier expected."); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmRelExp * * $(GRAMMAR $(RULEDEF asmRelExp): * $(RULE asmShiftExp) * | $(RULE asmRelExp) (($(LITERAL '<') | $(LITERAL '<=') | $(LITERAL '>') | $(LITERAL '>=')) $(RULE asmShiftExp))? * ;) */ ExpressionNode parseAsmRelExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmRelExp, AsmShiftExp, tok!"<", tok!"<=", tok!">", tok!">=")(); } /** * Parses an AsmShiftExp * * $(GRAMMAR $(RULEDEF asmShiftExp): * $(RULE asmAddExp) * $(RULE asmShiftExp) ($(LITERAL '<<') | $(LITERAL '>>') | $(LITERAL '>>>')) $(RULE asmAddExp) * ;) */ ExpressionNode parseAsmShiftExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmShiftExp, AsmAddExp, tok!"<<", tok!">>", tok!">>>"); } /** * Parses an AsmStatement * * $(GRAMMAR $(RULEDEF asmStatement): * $(LITERAL 'asm') $(RULE functionAttributes)? $(LITERAL '{') ( $(RULE asmInstruction)+ | $(RULE gccAsmInstruction)+ ) $(LITERAL '}') * ;) */ AsmStatement parseAsmStatement() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmStatement node = allocator.make!AsmStatement; advance(); // asm StackBuffer functionAttributes; while (isAttribute()) { if (!functionAttributes.put(parseFunctionAttribute())) { error("Function attribute or `{` expected"); return null; } } ownArray(node.functionAttributes, functionAttributes); expect(tok!"{"); // DMD-style and GCC-style assembly might look identical in the beginning. // Try DMD style first and restart with GCC if it fails because of GCC elements bool maybeGccStyle; const instrStart = allocator.setCheckpoint(); const instrStartIdx = index; StackBuffer instructions; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!instructions.put(parseAsmInstruction(maybeGccStyle))) { if (maybeGccStyle) break; allocator.rollback(c); } else expect(tok!";"); } if (!maybeGccStyle) { ownArray(node.asmInstructions, instructions); } else { // Revert to the beginning of the first instruction destroy(instructions); allocator.rollback(instrStart); index = instrStartIdx; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!instructions.put(parseGccAsmInstruction())) allocator.rollback(c); else expect(tok!";"); } ownArray(node.gccAsmInstructions, instructions); } expect(tok!"}"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmTypePrefix * * Note that in the following grammar definition the first identifier must * be "near", "far", "word", "dword", or "qword". The second identifier must * be "ptr". * * $(GRAMMAR $(RULEDEF asmTypePrefix): * $(LITERAL Identifier) $(LITERAL Identifier)? * | $(LITERAL 'byte') $(LITERAL Identifier)? * | $(LITERAL 'short') $(LITERAL Identifier)? * | $(LITERAL 'int') $(LITERAL Identifier)? * | $(LITERAL 'float') $(LITERAL Identifier)? * | $(LITERAL 'double') $(LITERAL Identifier)? * | $(LITERAL 'real') $(LITERAL Identifier)? * ;) */ AsmTypePrefix parseAsmTypePrefix() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; switch (current().type) { case tok!"identifier": case tok!"byte": case tok!"short": case tok!"int": case tok!"float": case tok!"double": case tok!"real": AsmTypePrefix node = allocator.make!AsmTypePrefix(); node.left = advance(); if (node.left.type == tok!"identifier") switch (node.left.text) { case "near": case "far": case "word": case "dword": case "qword": break; default: error("ASM type node expected"); return null; } if (currentIs(tok!"identifier") && current().text == "ptr") node.right = advance(); node.tokens = tokens[startIndex .. index]; return node; default: error("Expected an identifier, `byte`, `short`, `int`, `float`, `double`, or `real`"); return null; } } /** * Parses an AsmUnaExp * * $(GRAMMAR $(RULEDEF asmUnaExp): * $(RULE asmTypePrefix) $(RULE asmExp) * | $(LITERAL Identifier) $(RULE asmExp) * | $(LITERAL '+') $(RULE asmUnaExp) * | $(LITERAL '-') $(RULE asmUnaExp) * | $(LITERAL '!') $(RULE asmUnaExp) * | $(LITERAL '~') $(RULE asmUnaExp) * | $(RULE asmPrimaryExp) * ;) */ AsmUnaExp parseAsmUnaExp() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmUnaExp node = allocator.make!AsmUnaExp(); switch (current().type) { case tok!"+": case tok!"-": case tok!"!": case tok!"~": node.prefix = advance(); mixin(parseNodeQ!(`node.asmUnaExp`, `AsmUnaExp`)); break; case tok!"byte": case tok!"short": case tok!"int": case tok!"float": case tok!"double": case tok!"real": typePrefix: mixin(parseNodeQ!(`node.asmTypePrefix`, `AsmTypePrefix`)); mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); break; case tok!"identifier": switch (current().text) { case "offsetof": case "seg": node.prefix = advance(); mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); break; case "near": case "far": case "word": case "dword": case "qword": goto typePrefix; default: goto outerDefault; } break; outerDefault: default: mixin(parseNodeQ!(`node.asmPrimaryExp`, `AsmPrimaryExp`)); break; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmXorExp * * $(GRAMMAR $(RULEDEF asmXorExp): * $(RULE asmAndExp) * | $(RULE asmXorExp) $(LITERAL '^') $(RULE asmAndExp) * ;) */ ExpressionNode parseAsmXorExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmXorExp, AsmAndExp, tok!"^")(); } /** * Parses an AssertArguments * * $(GRAMMAR $(RULEDEF assertArguments): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression))? $(LITERAL ',')? * ;) */ AssertArguments parseAssertArguments() { auto startIndex = index; auto node = allocator.make!AssertArguments; mixin(parseNodeQ!(`node.assertion`, `AssignExpression`)); if (currentIs(tok!",")) advance(); if (currentIs(tok!")")) return node; mixin(parseNodeQ!(`node.message`, `AssignExpression`)); if (currentIs(tok!",")) advance(); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AssertExpression * * $(GRAMMAR $(RULEDEF assertExpression): * $(LITERAL 'assert') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') * ;) */ AssertExpression parseAssertExpression() { auto startIndex = index; mixin(traceEnterAndExit!(__FUNCTION__)); auto node = allocator.make!AssertExpression; node.line = current.line; node.column = current.column; advance(); // "assert" mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AssignExpression * * $(GRAMMAR $(RULEDEF assignExpression): * $(RULE ternaryExpression) ($(RULE assignOperator) $(RULE assignExpression))? * ; *$(RULEDEF assignOperator): * $(LITERAL '=') * | $(LITERAL '>>>=') * | $(LITERAL '>>=') * | $(LITERAL '<<=') * | $(LITERAL '+=') * | $(LITERAL '-=') * | $(LITERAL '*=') * | $(LITERAL '%=') * | $(LITERAL '&=') * | $(LITERAL '/=') * | $(LITERAL '|=') * | $(LITERAL '^^=') * | $(LITERAL '^=') * | $(LITERAL '~=') * ;) */ ExpressionNode parseAssignExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("Assign expression expected instead of EOF"); return null; } auto ternary = parseTernaryExpression(); if (ternary is null) return null; if (currentIsOneOf(tok!"=", tok!">>>=", tok!">>=", tok!"<<=", tok!"+=", tok!"-=", tok!"*=", tok!"%=", tok!"&=", tok!"/=", tok!"|=", tok!"^^=", tok!"^=", tok!"~=")) { auto node = allocator.make!AssignExpression; node.line = current().line; node.column = current().column; node.ternaryExpression = ternary; node.operator = advance().type; mixin(parseNodeQ!(`node.expression`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } return ternary; } /** * Parses an AssocArrayLiteral * * $(GRAMMAR $(RULEDEF assocArrayLiteral): * $(LITERAL '[') $(RULE keyValuePairs) $(LITERAL ']') * ;) */ AssocArrayLiteral parseAssocArrayLiteral() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin (simpleParse!(AssocArrayLiteral, tok!"[", "keyValuePairs|parseKeyValuePairs", tok!"]")); } /** * Parses an AtAttribute * * $(GRAMMAR $(RULEDEF atAttribute): * $(LITERAL '@') $(LITERAL Identifier) * | $(LITERAL '@') $(LITERAL Identifier) $(LITERAL '$(LPAREN)') $(RULE argumentList)? $(LITERAL '$(RPAREN)') * | $(LITERAL '@') $(LITERAL '$(LPAREN)') $(RULE argumentList) $(LITERAL '$(RPAREN)') * | $(LITERAL '@') $(RULE templateInstance) * ;) */ AtAttribute parseAtAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AtAttribute; const start = expect(tok!"@"); mixin (nullCheck!`start`); if (!moreTokens) { error("`(`, or identifier expected"); return null; } node.startLocation = start.index; switch (current.type) { case tok!"identifier": if (peekIs(tok!"!")) mixin(parseNodeQ!(`node.templateInstance`, `TemplateInstance`)); else node.identifier = advance(); if (currentIs(tok!"(")) { advance(); // ( node.useParen = true; if (!currentIs(tok!")")) mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); expect(tok!")"); } break; case tok!"(": advance(); node.useParen = true; mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); expect(tok!")"); break; default: error("`(`, or identifier expected"); return null; } if (moreTokens) node.endLocation = current().index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Attribute * * $(GRAMMAR $(RULEDEF attribute): * $(RULE pragmaExpression) * | $(RULE alignAttribute) * | $(RULE deprecated) * | $(RULE atAttribute) * | $(RULE linkageAttribute) * | $(LITERAL 'export') * | $(LITERAL 'package') ($(LITERAL "(") $(RULE identifierChain) $(LITERAL ")"))? * | $(LITERAL 'private') * | $(LITERAL 'protected') * | $(LITERAL 'public') * | $(LITERAL 'static') * | $(LITERAL 'extern') * | $(LITERAL 'abstract') * | $(LITERAL 'final') * | $(LITERAL 'override') * | $(LITERAL 'synchronized') * | $(LITERAL 'auto') * | $(LITERAL 'scope') * | $(LITERAL 'const') * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'shared') * | $(LITERAL '__gshared') * | $(LITERAL 'nothrow') * | $(LITERAL 'pure') * | $(LITERAL 'ref') * | $(LITERAL 'throw') * ;) */ Attribute parseAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Attribute; switch (current.type) { case tok!"pragma": mixin(parseNodeQ!(`node.pragmaExpression`, `PragmaExpression`)); break; case tok!"deprecated": mixin(parseNodeQ!(`node.deprecated_`, `Deprecated`)); break; case tok!"align": mixin(parseNodeQ!(`node.alignAttribute`, `AlignAttribute`)); break; case tok!"@": mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); break; case tok!"package": node.attribute = advance(); if (currentIs(tok!"(")) { expect(tok!"("); mixin(parseNodeQ!(`node.identifierChain`, `IdentifierChain`)); expect(tok!")"); } break; case tok!"extern": if (peekIs(tok!"(")) { mixin(parseNodeQ!(`node.linkageAttribute`, `LinkageAttribute`)); break; } else goto case; case tok!"private": case tok!"protected": case tok!"public": case tok!"export": case tok!"static": case tok!"abstract": case tok!"final": case tok!"override": case tok!"synchronized": case tok!"auto": case tok!"scope": case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"__gshared": case tok!"nothrow": case tok!"pure": case tok!"ref": case tok!"throw": node.attribute = advance(); break; default: return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AttributeDeclaration * * $(GRAMMAR $(RULEDEF attributeDeclaration): * $(RULE _attribute) $(LITERAL ':') * ;) */ AttributeDeclaration parseAttributeDeclaration(Attribute attribute = null) { auto startIndex = index; auto node = allocator.make!AttributeDeclaration; node.line = current.line; node.attribute = attribute is null ? parseAttribute() : attribute; expect(tok!":"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AutoDeclaration * * $(GRAMMAR $(RULEDEF autoDeclaration): * $(RULE storageClass)+ $(RULE autoDeclarationPart) ($(LITERAL ',') $(RULE autoDeclarationPart))* $(LITERAL ';') * ;) */ AutoDeclaration parseAutoDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AutoDeclaration; node.comment = comment; comment = null; StackBuffer storageClasses; while (isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); StackBuffer parts; do { if (!parts.put(parseAutoDeclarationPart())) return null; if (currentIs(tok!",")) advance(); else break; } while (moreTokens()); ownArray(node.parts, parts); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AutoDeclarationPart * * $(GRAMMAR $(RULEDEF autoDeclarationPart): * $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE initializer) * ;) */ AutoDeclarationPart parseAutoDeclarationPart() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto part = allocator.make!AutoDeclarationPart; auto i = expect(tok!"identifier"); if (i is null) return null; part.identifier = *i; if (currentIs(tok!"(")) mixin(parseNodeQ!("part.templateParameters", "TemplateParameters")); mixin(tokenCheck!"="); mixin(parseNodeQ!("part.initializer", "Initializer")); part.tokens = tokens[startIndex .. index]; return part; } /** * Parses a BlockStatement * * $(GRAMMAR $(RULEDEF blockStatement): * $(LITERAL '{') $(RULE declarationsAndStatements)? $(LITERAL '}') * ;) */ BlockStatement parseBlockStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!BlockStatement; const openBrace = expect(tok!"{"); mixin (nullCheck!`openBrace`); node.startLocation = openBrace.index; if (!currentIs(tok!"}")) { mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); } const closeBrace = expect(tok!"}"); if (closeBrace !is null) node.endLocation = closeBrace.index; else { trace("Could not find end of block statement."); node.endLocation = size_t.max; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BreakStatement * * $(GRAMMAR $(RULEDEF breakStatement): * $(LITERAL 'break') $(LITERAL Identifier)? $(LITERAL ';') * ;) */ BreakStatement parseBreakStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; expect(tok!"break"); if (!moreTokens) return null; auto node = allocator.make!BreakStatement; switch (current.type) { case tok!"identifier": node.label = advance(); mixin(tokenCheck!";"); break; case tok!";": advance(); break; default: error("Identifier or semicolon expected following `break`"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BaseClass * * $(GRAMMAR $(RULEDEF baseClass): * $(RULE type2) * ;) */ BaseClass parseBaseClass() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!BaseClass; if (!moreTokens) return null; if (current.type.isProtection()) { warn("Use of base class protection is deprecated."); advance(); } if ((node.type2 = parseType2()) is null) { return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BaseClassList * * $(GRAMMAR $(RULEDEF baseClassList): * $(RULE baseClass) ($(LITERAL ',') $(RULE baseClass))* * ;) */ BaseClassList parseBaseClassList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(BaseClassList, BaseClass)(); } /** * Parses an BuiltinType * * $(GRAMMAR $(RULEDEF builtinType): * $(LITERAL 'bool') * | $(LITERAL 'byte') * | $(LITERAL 'ubyte') * | $(LITERAL 'short') * | $(LITERAL 'ushort') * | $(LITERAL 'int') * | $(LITERAL 'uint') * | $(LITERAL 'long') * | $(LITERAL 'ulong') * | $(LITERAL 'char') * | $(LITERAL 'wchar') * | $(LITERAL 'dchar') * | $(LITERAL 'float') * | $(LITERAL 'double') * | $(LITERAL 'real') * | $(LITERAL 'ifloat') * | $(LITERAL 'idouble') * | $(LITERAL 'ireal') * | $(LITERAL 'cfloat') * | $(LITERAL 'cdouble') * | $(LITERAL 'creal') * | $(LITERAL 'void') * ;) */ IdType parseBuiltinType() { mixin(traceEnterAndExit!(__FUNCTION__)); return advance().type; } /** * Parses a CaseRangeStatement * * $(GRAMMAR $(RULEDEF caseRangeStatement): * $(LITERAL 'case') $(RULE assignExpression) $(LITERAL ':') $(LITERAL '...') $(LITERAL 'case') $(RULE assignExpression) $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ CaseRangeStatement parseCaseRangeStatement(ExpressionNode low) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CaseRangeStatement; assert (low !is null); node.low = low; mixin(tokenCheck!":"); mixin(tokenCheck!".."); expect(tok!"case"); mixin(parseNodeQ!(`node.high`, `AssignExpression`)); const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an CaseStatement * * $(GRAMMAR $(RULEDEF caseStatement): * $(LITERAL 'case') $(RULE _argumentList) $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ CaseStatement parseCaseStatement(ArgumentList argumentList = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CaseStatement; node.argumentList = argumentList; const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin (nullCheck!`node.declarationsAndStatements = parseDeclarationsAndStatements(false)`); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a CastExpression * * $(GRAMMAR $(RULEDEF castExpression): * $(LITERAL 'cast') $(LITERAL '$(LPAREN)') ($(RULE type) | $(RULE castQualifier))? $(LITERAL '$(RPAREN)') $(RULE unaryExpression) * ;) */ CastExpression parseCastExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CastExpression; expect(tok!"cast"); mixin(tokenCheck!"("); if (!currentIs(tok!")")) { if (isCastQualifier()) mixin(parseNodeQ!(`node.castQualifier`, `CastQualifier`)); else mixin(parseNodeQ!(`node.type`, `Type`)); } mixin(tokenCheck!")"); mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a CastQualifier * * $(GRAMMAR $(RULEDEF castQualifier): * $(LITERAL 'const') * | $(LITERAL 'const') $(LITERAL 'shared') * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'inout') $(LITERAL 'shared') * | $(LITERAL 'shared') * | $(LITERAL 'shared') $(LITERAL 'const') * | $(LITERAL 'shared') $(LITERAL 'inout') * ;) */ CastQualifier parseCastQualifier() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CastQualifier; if (!moreTokens) return null; switch (current.type) { case tok!"inout": case tok!"const": node.first = advance(); if (currentIs(tok!"shared")) node.second = advance(); break; case tok!"shared": node.first = advance(); if (currentIsOneOf(tok!"const", tok!"inout")) node.second = advance(); break; case tok!"immutable": node.first = advance(); break; default: error("`const`, `immutable`, `inout`, or `shared` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Catch * * $(GRAMMAR $(RULEDEF catch): * $(LITERAL 'catch') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL Identifier)? $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ Catch parseCatch() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Catch; expect(tok!"catch"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"identifier")) node.identifier = advance(); mixin(tokenCheck!")"); mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Catches * * $(GRAMMAR $(RULEDEF catches): * $(RULE catch)+ * | $(RULE catch)* $(RULE lastCatch) * ;) */ Catches parseCatches() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Catches; StackBuffer catches; while (moreTokens()) { if (!currentIs(tok!"catch")) break; if (peekIs(tok!"(")) { if (!catches.put(parseCatch())) return null; } else { mixin(parseNodeQ!(`node.lastCatch`, `LastCatch`)); break; } } ownArray(node.catches, catches); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ClassDeclaration * * $(GRAMMAR $(RULEDEF classDeclaration): * $(LITERAL 'class') $(LITERAL Identifier) $(LITERAL ';') * | $(LITERAL 'class') $(LITERAL Identifier) ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(RULE structBody) | $(LITERAL ';')) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) ($(LITERAL ':') $(RULE baseClassList))? $(RULE constraint)? $(RULE structBody) * ;) */ ClassDeclaration parseClassDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ClassDeclaration; expect(tok!"class"); return parseInterfaceOrClass(node, startIndex); } /** * Parses a CmpExpression * * $(GRAMMAR $(RULEDEF cmpExpression): * $(RULE shiftExpression) * | $(RULE equalExpression) * | $(RULE identityExpression) * | $(RULE relExpression) * | $(RULE inExpression) * ;) */ ExpressionNode parseCmpExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto shift = parseShiftExpression(); if (shift is null) return null; if (!moreTokens()) return shift; switch (current.type) { case tok!"is": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.identityExpression = parseIdentityExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"in": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.inExpression = parseInExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"!": auto node = allocator.make!CmpExpression; if (peekIs(tok!"is")) mixin (nullCheck!`node.identityExpression = parseIdentityExpression(shift)`); else if (peekIs(tok!"in")) mixin (nullCheck!`node.inExpression = parseInExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"<": case tok!"<=": case tok!">": case tok!">=": case tok!"!<>=": case tok!"!<>": case tok!"<>": case tok!"<>=": case tok!"!>": case tok!"!>=": case tok!"!<": case tok!"!<=": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.relExpression = parseRelExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"==": case tok!"!=": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.equalExpression = parseEqualExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; default: return shift; } } /** * Parses a CompileCondition * * $(GRAMMAR $(RULEDEF compileCondition): * $(RULE versionCondition) * | $(RULE debugCondition) * | $(RULE staticIfCondition) * ;) */ CompileCondition parseCompileCondition() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CompileCondition; if (!moreTokens) return null; switch (current.type) { case tok!"version": mixin(parseNodeQ!(`node.versionCondition`, `VersionCondition`)); break; case tok!"debug": mixin(parseNodeQ!(`node.debugCondition`, `DebugCondition`)); break; case tok!"static": mixin(parseNodeQ!(`node.staticIfCondition`, `StaticIfCondition`)); break; default: error("`version`, `debug`, or `static` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ConditionalDeclaration * * $(GRAMMAR $(RULEDEF conditionalDeclaration): * $(RULE compileCondition) $(RULE declaration) * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * ;) */ ConditionalDeclaration parseConditionalDeclaration(bool strict, bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ConditionalDeclaration; mixin(parseNodeQ!(`node.compileCondition`, `CompileCondition`)); StackBuffer trueDeclarations; if (currentIs(tok!":") || currentIs(tok!"{")) { immutable bool brace = currentIs(tok!"{"); node.trueStyle = brace ? DeclarationListStyle.block : DeclarationListStyle.colon; advance(); while (moreTokens() && !currentIs(tok!"}") && !currentIs(tok!"else")) { immutable c = allocator.setCheckpoint(); if (!trueDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) { allocator.rollback(c); return null; } } if (brace) mixin(tokenCheck!"}"); } else { if (!trueDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; node.trueStyle = DeclarationListStyle.single; } ownArray(node.trueDeclarations, trueDeclarations); if (currentIs(tok!"else")) { node.hasElse = true; advance(); } else { node.tokens = tokens[startIndex .. index]; return node; } StackBuffer falseDeclarations; if (currentIs(tok!":") || currentIs(tok!"{")) { immutable bool brace = currentIs(tok!"{"); node.falseStyle = brace ? DeclarationListStyle.block : DeclarationListStyle.colon; advance(); while (moreTokens() && !currentIs(tok!"}")) if (!falseDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; if (brace) mixin(tokenCheck!"}"); } else { if (!falseDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; node.falseStyle = DeclarationListStyle.single; } ownArray(node.falseDeclarations, falseDeclarations); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ConditionalStatement * * $(GRAMMAR $(RULEDEF conditionalStatement): * $(RULE compileCondition) $(RULE declarationOrStatement) ($(LITERAL 'else') $(RULE declarationOrStatement))? * ;) */ ConditionalStatement parseConditionalStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ConditionalStatement; mixin(parseNodeQ!(`node.compileCondition`, `CompileCondition`)); mixin(parseNodeQ!(`node.trueStatement`, `DeclarationOrStatement`)); if (currentIs(tok!"else")) { advance(); mixin(parseNodeQ!(`node.falseStatement`, `DeclarationOrStatement`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Constraint * * $(GRAMMAR $(RULEDEF constraint): * $(LITERAL 'if') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') * ;) */ Constraint parseConstraint() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Constraint; auto ifToken = expect(tok!"if"); mixin (nullCheck!`ifToken`); node.location = ifToken.index; mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Constructor * * $(GRAMMAR $(RULEDEF constructor): * $(LITERAL 'this') $(RULE templateParameters)? $(RULE parameters) $(RULE memberFunctionAttribute)* $(RULE constraint)? ($(RULE functionBody) | $(LITERAL ';')) * ;) */ Constructor parseConstructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Constructor node = allocator.make!Constructor; node.comment = comment; comment = null; const t = expect(tok!"this"); mixin (nullCheck!`t`); node.location = t.index; node.line = t.line; node.column = t.column; const p = peekPastParens(); bool isTemplate = false; if (p !is null && p.type == tok!"(") { isTemplate = true; mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); } mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); if (isTemplate && currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); if (currentIs(tok!";")) advance(); else mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ContinueStatement * * $(GRAMMAR $(RULEDEF continueStatement): * $(LITERAL 'continue') $(LITERAL Identifier)? $(LITERAL ';') * ;) */ ContinueStatement parseContinueStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"continue"); if (!moreTokens) return null; auto node = allocator.make!ContinueStatement; switch (current.type) { case tok!"identifier": node.label = advance(); mixin(tokenCheck!";"); break; case tok!";": advance(); break; default: error("Identifier or semicolon expected following `continue`"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DebugCondition * * $(GRAMMAR $(RULEDEF debugCondition): * $(LITERAL 'debug') ($(LITERAL '$(LPAREN)') ($(LITERAL IntegerLiteral) | $(LITERAL Identifier)) $(LITERAL '$(RPAREN)'))? * ;) */ DebugCondition parseDebugCondition() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DebugCondition; const d = expect(tok!"debug"); mixin (nullCheck!`d`); node.debugIndex = d.index; if (currentIs(tok!"(")) { advance(); if (currentIsOneOf(tok!"intLiteral", tok!"identifier")) node.identifierOrInteger = advance(); else { error(`Integer literal or identifier expected`); return null; } mixin(tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DebugSpecification * * $(GRAMMAR $(RULEDEF debugSpecification): * $(LITERAL 'debug') $(LITERAL '=') ($(LITERAL Identifier) | $(LITERAL IntegerLiteral)) $(LITERAL ';') * ;) */ DebugSpecification parseDebugSpecification() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DebugSpecification; mixin(tokenCheck!"debug"); mixin(tokenCheck!"="); if (currentIsOneOf(tok!"identifier", tok!"intLiteral")) node.identifierOrInteger = advance(); else { error("Integer literal or identifier expected"); return null; } mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Declaration * * Params: * strict = if true, do not return partial AST nodes on errors. * mustBeDeclaration = do not parse as a declaration if it could be parsed as a function call * inTemplateDeclaration = if this function is called from a templated context * * $(GRAMMAR $(RULEDEF declaration): * $(RULE attribute)* $(RULE declaration2) * | $(RULE attribute)+ $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * ; * $(RULEDEF declaration2): * $(RULE aliasDeclaration) * | $(RULR aliasAssign) * | $(RULE aliasThisDeclaration) * | $(RULE anonymousEnumDeclaration) * | $(RULE attributeDeclaration) * | $(RULE classDeclaration) * | $(RULE conditionalDeclaration) * | $(RULE constructor) * | $(RULE debugSpecification) * | $(RULE destructor) * | $(RULE enumDeclaration) * | $(RULE eponymousTemplateDeclaration) * | $(RULE functionDeclaration) * | $(RULE importDeclaration) * | $(RULE interfaceDeclaration) * | $(RULE invariant) * | $(RULE mixinDeclaration) * | $(RULE mixinTemplateDeclaration) * | $(RULE pragmaDeclaration) * | $(RULE sharedStaticConstructor) * | $(RULE sharedStaticDestructor) * | $(RULE staticAssertDeclaration) * | $(RULE staticConstructor) * | $(RULE staticDestructor) * | $(RULE structDeclaration) * | $(RULE templateDeclaration) * | $(RULE unionDeclaration) * | $(RULE unittest) * | $(RULE variableDeclaration) * | $(RULE versionSpecification) * ;) */ Declaration parseDeclaration(bool strict = false, bool mustBeDeclaration = false, bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Declaration; if (!moreTokens) { error("declaration expected instead of EOF"); return null; } if (current.comment !is null) comment = current.comment; size_t autoStorageClassStart = size_t.max; DecType isAuto; StackBuffer attributes; do { isAuto = isAutoDeclaration(autoStorageClassStart); if (isAuto != DecType.other && index == autoStorageClassStart) break; if (!isAttribute()) break; immutable c = allocator.setCheckpoint(); auto attr = parseAttribute(); if (attr is null) { allocator.rollback(c); break; } if (currentIs(tok!":")) { node.attributeDeclaration = parseAttributeDeclaration(attr); mixin(nullCheck!`node.attributeDeclaration`); ownArray(node.attributes, attributes); node.tokens = tokens[startIndex .. index]; return node; } else attributes.put(attr); } while (moreTokens()); ownArray(node.attributes, attributes); if (!moreTokens) { error("declaration expected instead of EOF"); return null; } if (!currentIs(tok!"enum")) // #165: handle enums separatly b/c of EponymousTemplateDeclaration { if (isAuto == DecType.autoVar) { mixin(nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, true)`); node.tokens = tokens[startIndex .. index]; return node; } else if (isAuto == DecType.autoFun) { mixin(nullCheck!`node.functionDeclaration = parseFunctionDeclaration(null, true)`); node.tokens = tokens[startIndex .. index]; return node; } } switch (current.type) { case tok!"asm": case tok!"break": case tok!"case": case tok!"continue": case tok!"default": case tok!"do": case tok!"for": case tok!"foreach": case tok!"foreach_reverse": case tok!"goto": case tok!"if": case tok!"return": case tok!"switch": case tok!"throw": case tok!"try": case tok!"while": case tok!"assert": goto default; case tok!";": // http://d.puremagic.com/issues/show_bug.cgi?id=4559 warn("Empty declaration"); advance(); break; case tok!"{": if (node.attributes.empty) { error("declaration expected instead of `{`"); return null; } advance(); StackBuffer declarations; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!declarations.put(parseDeclaration(strict, false, inTemplateDeclaration))) { allocator.rollback(c); return null; } } ownArray(node.declarations, declarations); mixin(tokenCheck!"}"); break; case tok!"alias": if (startsWith(tok!"alias", tok!"identifier", tok!"this")) mixin(parseNodeQ!(`node.aliasThisDeclaration`, `AliasThisDeclaration`)); else mixin(parseNodeQ!(`node.aliasDeclaration`, `AliasDeclaration`)); break; case tok!"class": mixin(parseNodeQ!(`node.classDeclaration`, `ClassDeclaration`)); break; case tok!"this": if (!mustBeDeclaration && peekIs(tok!"(")) { // Do not parse as a declaration if we could parse as a function call. ++index; const past = peekPastParens(); --index; if (past !is null && past.type == tok!";") return null; } if (startsWith(tok!"this", tok!"(", tok!"this", tok!")")) mixin(parseNodeQ!(`node.postblit`, `Postblit`)); else mixin(parseNodeQ!(`node.constructor`, `Constructor`)); break; case tok!"~": mixin(parseNodeQ!(`node.destructor`, `Destructor`)); break; case tok!"enum": immutable b = setBookmark(); advance(); // enum if (currentIsOneOf(tok!":", tok!"{")) { goToBookmark(b); mixin(parseNodeQ!(`node.anonymousEnumDeclaration`, `AnonymousEnumDeclaration`)); } else if (currentIs(tok!"identifier")) { advance(); if (currentIs(tok!"(")) { skipParens(); // () if (currentIs(tok!"(")) skipParens(); if (!currentIs(tok!"=")) { goToBookmark(b); node.functionDeclaration = parseFunctionDeclaration(null, true, node.attributes); mixin (nullCheck!`node.functionDeclaration`); } else { goToBookmark(b); mixin(parseNodeQ!(`node.eponymousTemplateDeclaration`, `EponymousTemplateDeclaration`)); } } else if (currentIsOneOf(tok!":", tok!"{", tok!";")) { goToBookmark(b); mixin(parseNodeQ!(`node.enumDeclaration`, `EnumDeclaration`)); } else { immutable bool eq = currentIs(tok!"="); goToBookmark(b); mixin (nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, eq)`); } } else { immutable bool s = isStorageClass(); goToBookmark(b); mixin (nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, s)`); } break; case tok!"import": mixin(parseNodeQ!(`node.importDeclaration`, `ImportDeclaration`)); break; case tok!"interface": mixin(parseNodeQ!(`node.interfaceDeclaration`, `InterfaceDeclaration`)); break; case tok!"mixin": if (peekIs(tok!"template")) mixin(parseNodeQ!(`node.mixinTemplateDeclaration`, `MixinTemplateDeclaration`)); else { immutable b = setBookmark(); advance(); if (currentIs(tok!"(")) { const t = peekPastParens(); if (t !is null && t.type == tok!";") { goToBookmark(b); mixin(parseNodeQ!(`node.mixinDeclaration`, `MixinDeclaration`)); } else { goToBookmark(b); error("Declaration expected"); return null; } } else { goToBookmark(b); mixin(parseNodeQ!(`node.mixinDeclaration`, `MixinDeclaration`)); } } break; case tok!"pragma": mixin(parseNodeQ!(`node.pragmaDeclaration`, `PragmaDeclaration`)); break; case tok!"shared": if (startsWith(tok!"shared", tok!"static", tok!"this")) mixin(parseNodeQ!(`node.sharedStaticConstructor`, `SharedStaticConstructor`)); else if (startsWith(tok!"shared", tok!"static", tok!"~")) mixin(parseNodeQ!(`node.sharedStaticDestructor`, `SharedStaticDestructor`)); else goto type; break; case tok!"static": if (peekIs(tok!"this")) mixin(parseNodeQ!(`node.staticConstructor`, `StaticConstructor`)); else if (peekIs(tok!"~")) mixin(parseNodeQ!(`node.staticDestructor`, `StaticDestructor`)); else if (peekIs(tok!"if")) mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); else if (peekIs(tok!"assert")) mixin(parseNodeQ!(`node.staticAssertDeclaration`, `StaticAssertDeclaration`)); else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse")) mixin(nullCheck!(`node.staticForeachDeclaration = parseStaticForeachDeclaration(inTemplateDeclaration)`)); else goto type; break; case tok!"struct": mixin(parseNodeQ!(`node.structDeclaration`, `StructDeclaration`)); break; case tok!"template": mixin(parseNodeQ!(`node.templateDeclaration`, `TemplateDeclaration`)); break; case tok!"union": mixin(parseNodeQ!(`node.unionDeclaration`, `UnionDeclaration`)); break; case tok!"invariant": mixin(parseNodeQ!(`node.invariant_`, `Invariant`)); break; case tok!"unittest": mixin(parseNodeQ!(`node.unittest_`, `Unittest`)); break; case tok!"identifier": if (inTemplateDeclaration && peekIs(tok!"=")) { mixin(parseNodeQ!(`node.aliasAssign`, `AliasAssign`)); break; } else goto type; case tok!".": case tok!"const": case tok!"immutable": case tok!"inout": case tok!"scope": case tok!"typeof": case tok!"__vector": case tok!"__traits": foreach (B; BasicTypes) { case B: } type: Type t = parseType(); if (t is null || !currentIs(tok!"identifier")) { if (t) error("no identifier for declarator"); return null; } const b2 = setBookmark(); auto savedComment = comment; node.variableDeclaration = parseVariableDeclaration(t, false); if (node.variableDeclaration is null) { goToBookmark(b2); if (savedComment && comment is null) comment = savedComment; node.functionDeclaration = parseFunctionDeclaration(t, false); } else abandonBookmark(b2); if (!node.variableDeclaration && !node.functionDeclaration) { error("invalid variable declaration or function declaration", false); return null; } break; case tok!"version": if (peekIs(tok!"(")) mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); else if (peekIs(tok!"=")) mixin(parseNodeQ!(`node.versionSpecification`, `VersionSpecification`)); else { error("`=` or `(` expected following `version`"); return null; } break; case tok!"debug": if (peekIs(tok!"=")) mixin(parseNodeQ!(`node.debugSpecification`, `DebugSpecification`)); else mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); break; default: error("Declaration expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses DeclarationsAndStatements * * $(GRAMMAR $(RULEDEF declarationsAndStatements): * $(RULE declarationOrStatement)+ * ;) */ DeclarationsAndStatements parseDeclarationsAndStatements(bool includeCases = true) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeclarationsAndStatements; StackBuffer declarationsAndStatements; while (!currentIsOneOf(tok!"}", tok!"else") && moreTokens() && suppressedErrorCount <= MAX_ERRORS) { if (currentIs(tok!"case") && !includeCases) break; if (currentIs(tok!"while")) { immutable b = setBookmark(); scope (exit) goToBookmark(b); advance(); if (currentIs(tok!"(")) { const p = peekPastParens(); if (p !is null && *p == tok!";") break; } } immutable c = allocator.setCheckpoint(); if (!declarationsAndStatements.put(parseDeclarationOrStatement())) { allocator.rollback(c); // detect the pattern ".}" for DCD. This is what happens when // located at the end of a well delimited body/scope and requesting // completion. This is also a case where it's sure sure that // there's no ambiguity, even if it happens during a lookup: // it's not a decl, it's not a statement, it's an error. if (currentIs(tok!"}") && index > 0 && previous == tok!".") break; if (!suppressMessages.empty) return null; // better for DCD, if the end of the block is reached then // go back, allowing the following declarations to be in // the right scope, instead of the block we were in. if (index > 0 && previous == tok!"}") { index -= 1; break; } } } ownArray(node.declarationsAndStatements, declarationsAndStatements); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeclarationOrStatement * * $(GRAMMAR $(RULEDEF declarationOrStatement): * $(RULE declaration) * | $(RULE statement) * ;) */ DeclarationOrStatement parseDeclarationOrStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeclarationOrStatement; if (moreTokens) node.startLocation = current.index; // "Any ambiguities in the grammar between Statements and // Declarations are resolved by the declarations taking precedence." immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); auto d = parseDeclaration(true, false); if (d is null) { allocator.rollback(c); goToBookmark(b); mixin(parseNodeQ!(`node.statement`, `Statement`)); } else { // TODO: Make this more efficient. Right now we parse the declaration // twice, once with errors and warnings ignored, and once with them // printed. Maybe store messages to then be abandoned or written later? allocator.rollback(c); goToBookmark(b); node.declaration = parseDeclaration(true, true); } if (moreTokens) node.endLocation = current.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Declarator * * $(GRAMMAR $(RULEDEF declarator): * $(LITERAL Identifier) * | $(LITERAL Identifier) $(LITERAL '=') $(RULE initializer) * | $(LITERAL Identifier) $(RULE templateParameters) $(LITERAL '=') $(RULE initializer) * ;) */ Declarator parseDeclarator() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Declarator node = allocator.make!Declarator; const id = expect(tok!"identifier"); mixin (nullCheck!`id`); node.name = *id; if (currentIs(tok!"[")) // dmd doesn't accept pointer after identifier { warn("C-style array declaration."); StackBuffer typeSuffixes; while (moreTokens() && currentIs(tok!"[")) if (!typeSuffixes.put(parseTypeSuffix())) return null; ownArray(node.cstyle, typeSuffixes); } if (currentIs(tok!"(")) { mixin (nullCheck!`(node.templateParameters = parseTemplateParameters())`); mixin(tokenCheck!"="); mixin (nullCheck!`(node.initializer = parseInitializer())`); } else if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.initializer`, `Initializer`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeclaratorIdentifierList * * $(GRAMMAR $(RULEDEF declaratorIdentifierList): * $(LITERAL Identifier) ($(LITERAL ',') $(LITERAL Identifier))* * ;) */ DeclaratorIdentifierList parseDeclaratorIdentifierList() { auto node = allocator.make!DeclaratorIdentifierList; auto startIndex = index; StackBuffer identifiers; while (moreTokens()) { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); identifiers.put(*ident); if (currentIs(tok!",")) { advance(); continue; } else break; } ownArray(node.identifiers, identifiers); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DefaultStatement * * $(GRAMMAR $(RULEDEF defaultStatement): * $(LITERAL 'default') $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ DefaultStatement parseDefaultStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DefaultStatement; mixin(tokenCheck!"default"); const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeleteExpression * * $(GRAMMAR $(RULEDEF deleteExpression): * $(LITERAL 'delete') $(RULE unaryExpression) * ;) */ DeleteExpression parseDeleteExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeleteExpression; node.line = current.line; node.column = current.column; mixin(tokenCheck!"delete"); mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Deprecated attribute * * $(GRAMMAR $(RULEDEF deprecated): * $(LITERAL 'deprecated') ($(LITERAL '$(LPAREN)') $(LITERAL StringLiteral)+ $(LITERAL '$(RPAREN)'))? * ;) */ Deprecated parseDeprecated() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Deprecated; mixin(tokenCheck!"deprecated"); if (currentIs(tok!"(")) { advance(); mixin (parseNodeQ!(`node.assignExpression`, `AssignExpression`)); mixin (tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Destructor * * $(GRAMMAR $(RULEDEF destructor): * $(LITERAL '~') $(LITERAL 'this') $(LITERAL '$(LPAREN)') $(LITERAL '$(RPAREN)') $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ';')) * ;) */ Destructor parseDestructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Destructor; node.comment = comment; comment = null; mixin(tokenCheck!"~"); if (!moreTokens) { error("`this` expected"); return null; } node.index = current.index; node.line = current.line; node.column = current.column; mixin(tokenCheck!"this"); mixin(tokenCheck!"("); mixin(tokenCheck!")"); if (currentIs(tok!";")) advance(); else { StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DoStatement * * $(GRAMMAR $(RULEDEF doStatement): * $(LITERAL 'do') $(RULE statementNoCaseNoDefault) $(LITERAL 'while') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') $(LITERAL ';') * ;) */ DoStatement parseDoStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"do"); if (!moreTokens) return null; auto node = allocator.make!DoStatement; mixin(parseNodeQ!(`node.statementNoCaseNoDefault`, `StatementNoCaseNoDefault`)); mixin(tokenCheck!"while"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumBody * * $(GRAMMAR $(RULEDEF enumBody): * $(LITERAL '{') $(RULE enumMember) ($(LITERAL ',') $(RULE enumMember)?)* $(LITERAL '}') * ;) */ EnumBody parseEnumBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumBody node = allocator.make!EnumBody; const open = expect(tok!"{"); mixin (nullCheck!`open`); node.startLocation = open.index; StackBuffer enumMembers; EnumMember last; while (moreTokens()) { if (currentIsOneOf(tok!"identifier", tok!"@", tok!"deprecated")) { auto c = allocator.setCheckpoint(); auto e = parseEnumMember(); if (!enumMembers.put(e)) allocator.rollback(c); else last = e; if (currentIs(tok!",")) { if (last !is null && last.comment is null) last.comment = current.trailingComment; advance(); if (!currentIs(tok!"}")) continue; } if (currentIs(tok!"}")) { if (last !is null && last.comment is null) last.comment = tokens[index - 1].trailingComment; break; } else { error("`,` or `}` expected"); if (currentIs(tok!"}")) break; } } else error("Enum member expected"); } ownArray(node.enumMembers, enumMembers); const close = expect (tok!"}"); if (close !is null) node.endLocation = close.index; node.tokens = tokens[startIndex .. index]; return node; } /** * $(GRAMMAR $(RULEDEF anonymousEnumMember): * $(RULE type) $(LITERAL identifier) $(LITERAL '=') $(RULE assignExpression) * | $(LITERAL identifier) $(LITERAL '=') $(RULE assignExpression) * | $(LITERAL identifier) * ;) */ AnonymousEnumMember parseAnonymousEnumMember(bool typeAllowed) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AnonymousEnumMember; if (currentIs(tok!"identifier") && peekIsOneOf(tok!",", tok!"=", tok!"}")) { node.comment = current.comment; mixin(tokenCheck!(`node.name`, `identifier`)); if (currentIs(tok!"=")) { advance(); // = goto assign; } } else if (typeAllowed) { node.comment = current.comment; mixin(parseNodeQ!(`node.type`, `Type`)); mixin(tokenCheck!(`node.name`, `identifier`)); mixin(tokenCheck!"="); assign: mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } else { error("Cannot specify anonymous enum member type if anonymous enum has a base type."); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * $(GRAMMAR $(RULEDEF anonymousEnumDeclaration): * $(LITERAL 'enum') ($(LITERAL ':') $(RULE type))? $(LITERAL '{') $(RULE anonymousEnumMember)+ $(LITERAL '}') * ;) */ AnonymousEnumDeclaration parseAnonymousEnumDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AnonymousEnumDeclaration; mixin(tokenCheck!"enum"); immutable bool hasBaseType = currentIs(tok!":"); if (hasBaseType) { advance(); mixin(parseNodeQ!(`node.baseType`, `Type`)); } mixin(tokenCheck!"{"); StackBuffer members; AnonymousEnumMember last; while (moreTokens()) { if (currentIs(tok!",")) { if (last !is null && last.comment is null) last.comment = current.trailingComment; advance(); continue; } else if (currentIs(tok!"}")) { if (last !is null && last.comment is null) last.comment = tokens[index - 1].trailingComment; break; } else { immutable c = allocator.setCheckpoint(); auto e = parseAnonymousEnumMember(!hasBaseType); if (!members.put(e)) allocator.rollback(c); else last = e; } } ownArray(node.members, members); mixin(tokenCheck!"}"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumDeclaration * * $(GRAMMAR $(RULEDEF enumDeclaration): * $(LITERAL 'enum') $(LITERAL Identifier) ($(LITERAL ':') $(RULE type))? $(LITERAL ';') * | $(LITERAL 'enum') $(LITERAL Identifier) ($(LITERAL ':') $(RULE type))? $(RULE enumBody) * ;) */ EnumDeclaration parseEnumDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EnumDeclaration; mixin(tokenCheck!"enum"); mixin (tokenCheck!(`node.name`, `identifier`)); node.comment = comment; comment = null; if (currentIs(tok!":")) { advance(); // skip ':' mixin(parseNodeQ!(`node.type`, `Type`)); } if (currentIs(tok!";")) { advance(); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.enumBody`, `EnumBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumMemberAttribute * * $(GRAMMAR $(RULEDEF enumMemberAttribute): * $(RULE atAttribute) * | $(RULE deprecated) * ;) */ EnumMemberAttribute parseEnumMemberAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumMemberAttribute node; if (currentIs(tok!"@")) { node = allocator.make!EnumMemberAttribute; mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); } else if (currentIs(tok!"deprecated")) { node = allocator.make!EnumMemberAttribute; mixin(parseNodeQ!(`node.deprecated_`, `Deprecated`)); } if (node) node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumMember * * $(GRAMMAR $(RULEDEF enumMember): * ($(RULE enumMemberAttribute))* $(LITERAL Identifier) ($(LITERAL '=') $(RULE assignExpression))? * ;) */ EnumMember parseEnumMember() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumMember node = allocator.make!EnumMember; node.comment = current.comment; StackBuffer emas; while (moreTokens()) { if (!emas.put(parseEnumMemberAttribute())) break; } ownArray(node.enumMemberAttributes, emas); mixin (tokenCheck!(`node.name`, `identifier`)); if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EponymousTemplateDeclaration * * $(GRAMMAR $(RULEDEF eponymousTemplateDeclaration): * $(LITERAL 'enum') $(LITERAL Identifier) $(RULE templateParameters) $(LITERAL '=') $(RULE assignExpression) $(LITERAL ';') * ;) */ EponymousTemplateDeclaration parseEponymousTemplateDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EponymousTemplateDeclaration; node.comment = current.comment; advance(); // enum const ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.name = *ident; mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); expect(tok!"="); node.assignExpression = parseAssignExpression(); if (node.assignExpression is null) mixin(parseNodeQ!(`node.type`, `Type`)); expect(tok!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EqualExpression * * $(GRAMMAR $(RULEDEF equalExpression): * $(RULE shiftExpression) ($(LITERAL '==') | $(LITERAL '!=')) $(RULE shiftExpression) * ;) */ EqualExpression parseEqualExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EqualExpression; node.left = shift is null ? parseShiftExpression() : shift; mixin (nullCheck!`node.left`); if (currentIsOneOf(tok!"==", tok!"!=")) node.operator = advance().type; mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Expression * * $(GRAMMAR $(RULEDEF expression): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression))* * ;) */ Expression parseExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); if (suppressedErrorCount > MAX_ERRORS) return null; if (!moreTokens()) { error("Expected expression instead of EOF"); return null; } return parseCommaSeparatedRule!(Expression, AssignExpression, true)(); } /** * Parses an ExpressionStatement * * $(GRAMMAR $(RULEDEF expressionStatement): * $(RULE _expression) $(LITERAL ';') * ;) */ ExpressionStatement parseExpressionStatement(Expression expression = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ExpressionStatement; node.expression = expression is null ? parseExpression() : expression; if (node.expression is null || expect(tok!";") is null) return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FinalSwitchStatement * * $(GRAMMAR $(RULEDEF finalSwitchStatement): * $(LITERAL 'final') $(RULE switchStatement) * ;) */ FinalSwitchStatement parseFinalSwitchStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin (simpleParse!(FinalSwitchStatement, tok!"final", "switchStatement|parseSwitchStatement")); } /** * Parses a Finally * * $(GRAMMAR $(RULEDEF finally): * $(LITERAL 'finally') $(RULE declarationOrStatement) * ;) */ Finally parseFinally() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Finally; mixin(tokenCheck!"finally"); mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForStatement * * $(GRAMMAR $(RULEDEF forStatement): * $(LITERAL 'for') $(LITERAL '$(LPAREN)') ($(RULE declaration) | $(RULE statementNoCaseNoDefault)) $(RULE expression)? $(LITERAL ';') $(RULE expression)? $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ ForStatement parseForStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ForStatement; mixin(tokenCheck!"for"); if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); if (currentIs(tok!";")) advance(); else mixin(parseNodeQ!(`node.initialization`, `DeclarationOrStatement`)); if (currentIs(tok!";")) advance(); else { mixin(parseNodeQ!(`node.test`, `Expression`)); expect(tok!";"); } if (!currentIs(tok!")")) mixin(parseNodeQ!(`node.increment`, `Expression`)); mixin(tokenCheck!")"); // Intentionally return an incomplete parse tree so that DCD will work // more correctly. if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a StaticForeachDeclaration * * $(GRAMMAR $(RULEDEF staticForeachDeclaration): * $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachTypeList) $(LITERAL ';') $(RULE expression) $(LITERAL '$(RPAREN)') ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')) * | $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachType) $(LITERAL ';') $(RULE expression) $(LITERAL '..') $(RULE expression) $(LITERAL '$(RPAREN)') ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')) * ;) */ StaticForeachDeclaration parseStaticForeachDeclaration(bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"static"); auto decl = parseForeach!true(inTemplateDeclaration); if (decl) decl.tokens = tokens[startIndex .. index]; return decl; } /** * Parses a StaticForeachStatement * * $(GRAMMAR $(RULEDEF staticForeachStatement): * $(LITERAL 'static') $(RULE foreachStatement) * ;) */ StaticForeachStatement parseStaticForeachStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(StaticForeachStatement, tok!"static", "foreachStatement|parseForeachStatement")); } /** * Parses a ForeachStatement * * $(GRAMMAR $(RULEDEF foreachStatement): * ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachTypeList) $(LITERAL ';') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * | ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachType) $(LITERAL ';') $(RULE expression) $(LITERAL '..') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ ForeachStatement parseForeachStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseForeach!false(); } Foreach!declOnly parseForeach(bool declOnly = false)(bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Foreach!declOnly node = allocator.make!(Foreach!declOnly); if (currentIsOneOf(tok!"foreach", tok!"foreach_reverse")) node.type = advance().type; else { error("`foreach` or `foreach_reverse` expected"); return null; } if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); ForeachTypeList feType = parseForeachTypeList(); mixin (nullCheck!`feType`); immutable bool canBeRange = feType.items.length == 1; mixin(tokenCheck!";"); mixin(parseNodeQ!(`node.low`, `Expression`)); mixin (nullCheck!`node.low`); if (currentIs(tok!"..")) { if (!canBeRange) { error(`Cannot have more than one foreach variable for a foreach range statement`); return null; } advance(); mixin(parseNodeQ!(`node.high`, `Expression`)); node.foreachType = feType.items[0]; mixin (nullCheck!`node.high`); } else { node.foreachTypeList = feType; } mixin(tokenCheck!")"); if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; // this line makes DCD better } static if (declOnly) { node.style = currentIs(tok!"{") ? DeclarationListStyle.block : DeclarationListStyle.single; StackBuffer declarations; if (currentIs(tok!"{")) { advance(); while (moreTokens() && !currentIs(tok!"}")) { immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); if (declarations.put(parseDeclaration(true, true, inTemplateDeclaration))) abandonBookmark(b); else { goToBookmark(b); allocator.rollback(c); return null; } } mixin(tokenCheck!"}"); } else if (!declarations.put(parseDeclaration(true, true, inTemplateDeclaration))) return null; ownArray(node.declarations, declarations); } else mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForeachType * * $(GRAMMAR $(RULEDEF foreachType): * ($(LITERAL 'ref') | $(LITERAL 'alias') | $(LITERAL 'enum') | $(RULE typeConstructor))* $(RULE type)? $(LITERAL Identifier) * ;) */ ForeachType parseForeachType() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ForeachType; while (moreTokens()) { IdType typeConstructor; if (currentIs(tok!"ref")) { node.isRef = true; advance(); } else if (currentIs(tok!"alias")) { node.isAlias = true; advance(); } else if (currentIs(tok!"enum")) { node.isEnum = true; advance(); } else if (tok!"" != (typeConstructor = parseTypeConstructor(false))) { trace("\033[01;36mType constructor"); node.typeConstructors ~= typeConstructor; } else break; } if (currentIs(tok!"identifier") && peekIsOneOf(tok!",", tok!";")) { node.identifier = advance(); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.type`, `Type`)); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForeachTypeList * * $(GRAMMAR $(RULEDEF foreachTypeList): * $(RULE foreachType) ($(LITERAL ',') $(RULE foreachType))* * ;) */ ForeachTypeList parseForeachTypeList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(ForeachTypeList, ForeachType)(); } /** * Parses a FunctionAttribute * * $(GRAMMAR $(RULEDEF functionAttribute): * $(RULE atAttribute) * | $(LITERAL 'pure') * | $(LITERAL 'nothrow') * ;) */ FunctionAttribute parseFunctionAttribute(bool validate = true) { auto startIndex = index; auto node = allocator.make!FunctionAttribute; switch (current.type) { case tok!"@": mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); break; case tok!"pure": case tok!"nothrow": node.token = advance(); break; default: if (validate) error("`@`attribute, `pure`, or `nothrow` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionBody. * Note that any change of this function must also be applied in dsymbol SimpleParser, which can be found * $(LINK2 https://github.com/dlang-community/dsymbol/blob/master/src/dsymbol/conversion/package.d, here). * * $(GRAMMAR $(RULEDEF functionBody): * $(RULE specifiedFunctionBody) * | $(RULE missingFunctionBody) * ;) */ FunctionBody parseFunctionBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionBody; immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); auto missingFunctionBody = parseMissingFunctionBody(); if (missingFunctionBody !is null) { abandonBookmark(b); node.missingFunctionBody = missingFunctionBody; } else { allocator.rollback(c); goToBookmark(b, false); auto shortenedFunctionBody = parseShortenedFunctionBody(); if (shortenedFunctionBody !is null) { abandonBookmark(b); node.shortenedFunctionBody = shortenedFunctionBody; } else { allocator.rollback(c); goToBookmark(b); mixin(parseNodeQ!(`node.specifiedFunctionBody`, `SpecifiedFunctionBody`)); } } node.endLocation = previous.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionCallExpression * * $(GRAMMAR $(RULEDEF functionCallExpression): * $(RULE symbol) $(RULE arguments) * | $(RULE unaryExpression) $(RULE arguments) * | $(RULE type) $(RULE arguments) * ;) */ FunctionCallExpression parseFunctionCallExpression(UnaryExpression unary = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionCallExpression; switch (current.type) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"scope": case tok!"pure": case tok!"nothrow": mixin(parseNodeQ!(`node.type`, `Type`)); mixin(parseNodeQ!(`node.arguments`, `Arguments`)); break; default: if (unary !is null) node.unaryExpression = unary; else mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); if (currentIs(tok!"!")) mixin(parseNodeQ!(`node.templateArguments`, `TemplateArguments`)); if (unary !is null) mixin(parseNodeQ!(`node.arguments`, `Arguments`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionContract * * $(GRAMMAR $(RULEDEF functionContract): * $(RULE inOutContractExpression) * | $(RULE inOutStatement) * ;) */ FunctionContract parseFunctionContract(bool allowStatement = true) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionContract; if (allowStatement && (peekIs(tok!"{") || (currentIs(tok!"out") && peekAre(tok!"(", tok!"identifier", tok!")")))) mixin(parseNodeQ!(`node.inOutStatement`, `InOutStatement`)); else if (peekIs(tok!"(")) mixin(parseNodeQ!(`node.inOutContractExpression`, `InOutContractExpression`)); else { error(allowStatement ? "`{` or `(` expected" : "`(` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionDeclaration * * $(GRAMMAR $(RULEDEF functionDeclaration): * ($(RULE storageClass)+ | $(RULE _type)) $(LITERAL Identifier) $(RULE parameters) $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ';')) * | ($(RULE storageClass)+ | $(RULE _type)) $(LITERAL Identifier) $(RULE templateParameters) $(RULE parameters) $(RULE memberFunctionAttribute)* $(RULE constraint)? ($(RULE functionBody) | $(LITERAL ';')) * ;) */ FunctionDeclaration parseFunctionDeclaration(Type type = null, bool isAuto = false, Attribute[] attributes = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionDeclaration; node.comment = comment; comment = null; StackBuffer memberFunctionAttributes; node.attributes = attributes; if (isAuto) { StackBuffer storageClasses; while (isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); foreach (a; node.attributes) { if (a.attribute == tok!"auto") node.hasAuto = true; else if (a.attribute == tok!"ref") node.hasRef = true; else continue; } } else { while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; if (type is null) mixin(parseNodeQ!(`node.returnType`, `Type`)); else node.returnType = type; } mixin(tokenCheck!(`node.name`, "identifier")); if (!currentIs(tok!"(")) { error("`(` expected"); return null; } const p = peekPastParens(); immutable bool isTemplate = p !is null && p.type == tok!"("; if (isTemplate) mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); mixin(parseNodeQ!(`node.parameters`, `Parameters`)); while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; if (isTemplate && currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); if (node.functionBody && node.functionBody.specifiedFunctionBody && node.functionBody.specifiedFunctionBody.blockStatement && node.functionBody.specifiedFunctionBody.blockStatement.tokens.length) attachComment(node, node.functionBody.specifiedFunctionBody.blockStatement.tokens[$ - 1].trailingComment); ownArray(node.memberFunctionAttributes, memberFunctionAttributes); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionLiteralExpression * * $(GRAMMAR $(RULEDEF functionLiteralExpression): * | $(LITERAL 'delegate') $(LITERAL 'ref')? $(RULE type)? ($(RULE parameters) $(RULE functionAttribute)*)? $(RULE specifiedFunctionBody) * | $(LITERAL 'function') $(LITERAL 'ref')? $(RULE type)? ($(RULE parameters) $(RULE functionAttribute)*)? $(RULE specifiedFunctionBody) * | $(LITERAL 'ref')? $(RULE parameters) $(RULE functionAttribute)* $(RULE specifiedFunctionBody) * | $(RULE specifiedFunctionBody) * | $(LITERAL Identifier) $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'function') $(LITERAL 'ref')? $(RULE type)? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'delegate') $(LITERAL 'ref')? $(RULE type)? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'ref')? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * ;) */ FunctionLiteralExpression parseFunctionLiteralExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionLiteralExpression; node.line = current.line; node.column = current.column; if (currentIsOneOf(tok!"function", tok!"delegate")) { node.functionOrDelegate = advance().type; if (currentIs(tok!"ref")) { advance(); node.isReturnRef = true; } if (!currentIsOneOf(tok!"(", tok!"in", tok!"do", tok!"out", tok!"{", tok!"=>") && current.text != "body") mixin(parseNodeQ!(`node.returnType`, `Type`)); } if (startsWith(tok!"identifier", tok!"=>")) { node.identifier = advance(); advance(); // => mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } else if (currentIs(tok!"(") || currentIs(tok!"ref") && peekIs(tok!"(")) { if (currentIs(tok!"ref")) { advance(); node.isReturnRef = true; } mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (currentIsMemberFunctionAttribute()) { auto c = allocator.setCheckpoint(); if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) { allocator.rollback(c); break; } } ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } if (currentIs(tok!"=>")) { advance(); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } else mixin(parseNodeQ!(`node.specifiedFunctionBody`, `SpecifiedFunctionBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmInstruction using GCC Assembler * * $(GRAMMAR $(RULEDEF gccAsmInstruction): * | $(RULE expression) $(LITERAL ':') $(RULE gccAsmOperandList)? ($(LITERAL ':') $(RULE gccAsmOperandList)? ($(LITERAL ':') $(RULE stringLiteralList))? )? $(LITERAL ';') * | $(RULE expression) $(LITERAL ':') $(LITERAL ':') $(RULE gccAsmOperandList)? $(LITERAL ':') $(RULE stringLiteralList) $(LITERAL ';') $(LITERAL ':') $(RULE declaratorIdentifierList) $(LITERAL ';') * ;) */ /* * References: * - [1] https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html * - [2] https://wiki.dlang.org/Using_GDC * - [3] https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d * * Separated into a different method because one cannot interleave DMD & GCC asm * <asm-qualifiers> (volatile, inline, goto) not supperted (yet?) */ GccAsmInstruction parseGccAsmInstruction() { mixin(traceEnterAndExit!(__FUNCTION__)); const startIndex = index; auto node = allocator.make!GccAsmInstruction(); // Allow empty asm instructions if (currentIs(tok!";")) { warn("Empty asm instruction"); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!("node.assemblerTemplate", "Expression")); // GDC allows e.g. asm { mixin(<some asm instruction>); } if (!currentIs(tok!";")) { mixin(tokenCheck!":"); if (!currentIsOneOf(tok!":", tok!";")) mixin(parseNodeQ!(`node.outputOperands`, `GccAsmOperandList`)); if (skip(tok!":")) { if (!currentIsOneOf(tok!":", tok!";")) mixin(parseNodeQ!(`node.inputOperands`, `GccAsmOperandList`)); if (skip(tok!":")) { if (!currentIs(tok!":")) mixin(parseNodeQ!("node.registers", "StringLiteralList")); if (skip(tok!":")) { size_t cp; if (node.outputOperands) { error("goto-labels only allowed without output operands!", false); cp = allocator.setCheckpoint(); } // Parse even with the error above for better error reporting mixin(parseNodeQ!("node.gotos", "DeclaratorIdentifierList")); if (cp) { allocator.rollback(cp); return null; } } } } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a GccAsmOperandList * * $(GRAMMAR $(RULEDEF gccAsmOperandList): * $(RULE gccAsmOperand) ($(LITERAL ',') $(RULE gccAsmOperand))* * ;) */ GccAsmOperandList parseGccAsmOperandList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(GccAsmOperandList, GccAsmOperand)(); } /** * Parses a GccAsmOperand * * $(GRAMMAR $(RULEDEF gccAsmOperand): * ($(LITERAL '[') $(RULE identifier) $(LITERAL ']'))? $(RULE stringLiteral) $(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)') * ;) */ GccAsmOperand parseGccAsmOperand() { mixin(traceEnterAndExit!(__FUNCTION__)); const startIndex = index; auto node = allocator.make!GccAsmOperand(); if (currentIs(tok!"[")) { advance(); if (auto t = expect(tok!"identifier")) node.symbolicName = *t; mixin(tokenCheck!"]"); } mixin(tokenCheck!("node.constraint", "stringLiteral")); // GCC actually requires braces but GDC didn't for quite some time, // see https://github.com/dlang/dmd/pull/10820 const hasParens = skip(tok!"("); if (!hasParens) warn("Omitting parenthesis around operands is deprecated!"); mixin(parseNodeQ!("node.expression", "AssignExpression")); if (hasParens) expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a GotoStatement * * $(GRAMMAR $(RULEDEF gotoStatement): * $(LITERAL 'goto') ($(LITERAL Identifier) | $(LITERAL 'default') | $(LITERAL 'case') $(RULE expression)?) $(LITERAL ';') * ;) */ GotoStatement parseGotoStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!GotoStatement; mixin(tokenCheck!"goto"); if (!moreTokens) return null; switch (current.type) { case tok!"identifier": case tok!"default": node.label = advance(); break; case tok!"case": node.label = advance(); if (!currentIs(tok!";")) mixin(parseNodeQ!(`node.expression`, `Expression`)); break; default: error("Identifier, `default`, or `case` expected"); return null; } mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierChain * * $(GRAMMAR $(RULEDEF identifierChain): * $(LITERAL Identifier) ($(LITERAL '.') $(LITERAL Identifier))* * ;) */ IdentifierChain parseIdentifierChain() { auto startIndex = index; auto node = allocator.make!IdentifierChain; StackBuffer identifiers; while (moreTokens()) { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); identifiers.put(*ident); if (currentIs(tok!".")) { advance(); continue; } else break; } ownArray(node.identifiers, identifiers); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TypeIdentifierPart. * * $(GRAMMAR $(RULEDEF typeIdentifierPart): * $(RULE identifierOrTemplateInstance) * | $(RULE identifierOrTemplateInstance) $(LITERAL '.') $(RULE typeIdentifierPart) * | $(RULE identifierOrTemplateInstance) $(LITERAL '[') $(RULE assignExpression) $(LITERAL ']') * | $(RULE identifierOrTemplateInstance) $(LITERAL '[') $(RULE assignExpression) $(LITERAL ']') $(LITERAL '.') $(RULE typeIdentifierPart) * ;) */ TypeIdentifierPart parseTypeIdentifierPart() { auto startIndex = index; TypeIdentifierPart node = allocator.make!TypeIdentifierPart; if (currentIs(tok!".")) { node.dot = true; advance(); } mixin(parseNodeQ!(`node.identifierOrTemplateInstance`, `IdentifierOrTemplateInstance`)); if (currentIs(tok!"[")) { // dyn arrays -> type suffixes if (peekIs(tok!"]")) { node.tokens = tokens[startIndex .. index - 1]; return node; } const b = setBookmark(); advance(); const cp = allocator.setCheckpoint; node.indexer = parseAssignExpression(); // here we can have a type (AA key) if (node.indexer is null) { goToBookmark(b); return node; } // indexer followed by ".." -> sliceExp -> type suffix else if (currentIs(tok!"..")) { allocator.rollback(cp); node.indexer = null; goToBookmark(b); return node; } // otherwise either the index of a type list or a dim abandonBookmark(b); expect(tok!"]"); if (!currentIs(tok!".")) { node.tokens = tokens[startIndex .. index]; return node; } } if (currentIs(tok!".")) { advance(); mixin(parseNodeQ!(`node.typeIdentifierPart`, `TypeIdentifierPart`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierOrTemplateChain * * $(GRAMMAR $(RULEDEF identifierOrTemplateChain): * $(RULE identifierOrTemplateInstance) ($(LITERAL '.') $(RULE identifierOrTemplateInstance))* * ;) */ IdentifierOrTemplateChain parseIdentifierOrTemplateChain() { auto startIndex = index; auto node = allocator.make!IdentifierOrTemplateChain; StackBuffer identifiersOrTemplateInstances; while (moreTokens()) { auto c = allocator.setCheckpoint(); if (!identifiersOrTemplateInstances.put(parseIdentifierOrTemplateInstance())) { allocator.rollback(c); if (identifiersOrTemplateInstances.length == 0) return null; else break; } if (!currentIs(tok!".")) break; else advance(); } ownArray(node.identifiersOrTemplateInstances, identifiersOrTemplateInstances); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierOrTemplateInstance * * $(GRAMMAR $(RULEDEF identifierOrTemplateInstance): * $(LITERAL Identifier) * | $(RULE templateInstance) * ;) */ IdentifierOrTemplateInstance parseIdentifierOrTemplateInstance() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IdentifierOrTemplateInstance; if (peekIs(tok!"!") && !startsWith(tok!"identifier", tok!"!", tok!"is") && !startsWith(tok!"identifier", tok!"!", tok!"in")) { mixin(parseNodeQ!(`node.templateInstance`, `TemplateInstance`)); } else { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentityExpression * * $(GRAMMAR $(RULEDEF identityExpression): * $(RULE shiftExpression) ($(LITERAL 'is') | ($(LITERAL '!') $(LITERAL 'is'))) $(RULE shiftExpression) * ;) */ ExpressionNode parseIdentityExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IdentityExpression; mixin(nullCheck!`node.left = shift is null ? parseShiftExpression() : shift`); if (currentIs(tok!"!")) { advance(); node.negated = true; } mixin(tokenCheck!"is"); mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IfStatement * * $(GRAMMAR $(RULEDEF ifStatement): * $(LITERAL 'if') $(LITERAL '$(LPAREN)') $(RULE ifCondition) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) ($(LITERAL 'else') $(RULE declarationOrStatement))? *$(RULEDEF ifCondition): * $(LITERAL 'auto') $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE typeConstructors) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE typeConstructors)? $(RULE type) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE expression) * ;) */ IfStatement parseIfStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; IfStatement node = allocator.make!IfStatement; node.line = current().line; node.column = current().column; mixin(tokenCheck!"if"); if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); const b = setBookmark(); // ex. case: // `if (auto identifier = exp)` if (currentIs(tok!"auto") && peekIs(tok!"identifier")) { abandonBookmark(b); advance(); node.identifier = advance(); mixin(tokenCheck!"="); mixin(parseNodeQ!(`node.expression`, `Expression`)); } // `if (const shared ...` if (!node.expression && moreTokens && isTypeCtor(current.type)) { while (moreTokens) { // type ctor followed by open param is part of the type if (!isTypeCtor(current.type) || peekIs(tok!"(")) break; node.typeCtors ~= advance().type; } } // ex. case: // if (const shared stuff = exp) if (!node.expression && node.typeCtors.length && currentIs(tok!"identifier") && peekIs(tok!"=")) { abandonBookmark(b); node.identifier = advance(); advance(); mixin(parseNodeQ!(`node.expression`, `Expression`)); } if (!node.expression) { const c = allocator.setCheckpoint(); // ex. cases: // if (Type stuff = exp) // if (const shared Type stuff = exp) // if (const shared const(Type) stuff = exp) if (Type tp = parseType()) { if (currentIs(tok!"identifier") && peekIs(tok!"=")) { abandonBookmark(b); node.type = tp; node.identifier = advance(); advance(); mixin(parseNodeQ!(`node.expression`, `Expression`)); } // will try an expr since Type and Expression are ambiguous else allocator.rollback(c); } } // Relational expressions, unaries and such as condition if (!node.expression) { goToBookmark(b); mixin(parseNodeQ!(`node.expression`, `Expression`)); } if (!node.expression) { error("expression or declaration expected"); } mixin(tokenCheck!")"); if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; // this line makes DCD better } mixin(parseNodeQ!(`node.thenStatement`, `DeclarationOrStatement`)); if (currentIs(tok!"else")) { advance(); mixin(parseNodeQ!(`node.elseStatement`, `DeclarationOrStatement`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportBind * * $(GRAMMAR $(RULEDEF importBind): * $(LITERAL Identifier) ($(LITERAL '=') $(LITERAL Identifier))? * ;) */ ImportBind parseImportBind() { auto startIndex = index; auto node = allocator.make!ImportBind; const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.left = *ident; if (currentIs(tok!"=")) { advance(); const id = expect(tok!"identifier"); mixin(nullCheck!`id`); node.right = *id; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses ImportBindings * * $(GRAMMAR $(RULEDEF importBindings): * $(RULE _singleImport) $(LITERAL ':') $(RULE importBind) ($(LITERAL ',') $(RULE importBind))* * ;) */ ImportBindings parseImportBindings(SingleImport singleImport) { auto startIndex = index; auto node = allocator.make!ImportBindings; mixin(nullCheck!`node.singleImport = singleImport is null ? parseSingleImport() : singleImport`); mixin(tokenCheck!":"); StackBuffer importBinds; while (moreTokens()) { immutable c = allocator.setCheckpoint(); if (importBinds.put(parseImportBind())) { if (currentIs(tok!",")) advance(); else break; } else { allocator.rollback(c); break; } } ownArray(node.importBinds, importBinds); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportDeclaration * * $(GRAMMAR $(RULEDEF importDeclaration): * $(LITERAL 'import') $(RULE singleImport) ($(LITERAL ',') $(RULE singleImport))* ($(LITERAL ',') $(RULE importBindings))? $(LITERAL ';') * | $(LITERAL 'import') $(RULE importBindings) $(LITERAL ';') * ;) */ ImportDeclaration parseImportDeclaration() { auto startIndex = index; auto node = allocator.make!ImportDeclaration; node.startIndex = current().index; mixin(tokenCheck!"import"); SingleImport si = parseSingleImport(); if (si is null) return null; if (currentIs(tok!":")) node.importBindings = parseImportBindings(si); else { StackBuffer singleImports; singleImports.put(si); if (currentIs(tok!",")) { advance(); while (moreTokens()) { auto single = parseSingleImport(); mixin(nullCheck!`single`); if (currentIs(tok!":")) { node.importBindings = parseImportBindings(single); break; } else { singleImports.put(single); if (currentIs(tok!",")) advance(); else break; } } } ownArray(node.singleImports, singleImports); } node.endIndex = (moreTokens() ? current() : previous()).index + 1; mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportExpression * * $(GRAMMAR $(RULEDEF importExpression): * $(LITERAL 'import') $(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)') * ;) */ ImportExpression parseImportExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(ImportExpression, tok!"import", tok!"(", "assignExpression|parseAssignExpression", tok!")")); } /** * Parses an Index * * $(GRAMMAR $(RULEDEF index): * $(RULE assignExpression) ($(LITERAL '..') $(RULE assignExpression))? * ; * ) */ Index parseIndex() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Index(); mixin(parseNodeQ!(`node.low`, `AssignExpression`)); if (currentIs(tok!"..")) { advance(); mixin(parseNodeQ!(`node.high`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IndexExpression * * $(GRAMMAR $(RULEDEF indexExpression): * $(RULE _unaryExpression) $(LITERAL '[') $(LITERAL ']') * | $(RULE _unaryExpression) $(LITERAL '[') $(RULE index) ($(LITERAL ',') $(RULE index))* $(LITERAL ']') * ; * ) */ IndexExpression parseIndexExpression(UnaryExpression unaryExpression = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IndexExpression; mixin(nullCheck!`node.unaryExpression = unaryExpression is null ? parseUnaryExpression() : unaryExpression`); mixin(tokenCheck!"["); StackBuffer indexes; while (true) { if (currentIs(tok!"]")) break; if (!(indexes.put(parseIndex()))) return null; if (!moreTokens()) { error("Expected ',' or ']' instead of EOF"); return null; } if (currentIs(tok!",")) advance(); else break; } ownArray(node.indexes, indexes); mixin(tokenCheck!"]"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InContractExpression * * $(GRAMMAR $(RULEDEF inContractExpression): * $(LITERAL 'in') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') * ;) */ InContractExpression parseInContractExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InContractExpression; const i = expect(tok!"in"); mixin(nullCheck!`i`); node.inTokenLocation = i.index; mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InExpression * * $(GRAMMAR $(RULEDEF inExpression): * $(RULE shiftExpression) ($(LITERAL 'in') | ($(LITERAL '!') $(LITERAL 'in'))) $(RULE shiftExpression) * ;) */ ExpressionNode parseInExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InExpression; mixin(nullCheck!`node.left = shift is null ? parseShiftExpression() : shift`); if (currentIs(tok!"!")) { node.negated = true; advance(); } mixin(tokenCheck!"in"); mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InOutContractExpression * * $(GRAMMAR $(RULEDEF inOutContractExpression): * $(RULE inContractExpression) * | $(RULE outContractExpression) * ;) */ InOutContractExpression parseInOutContractExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InOutContractExpression; if (currentIs(tok!"in")) mixin(parseNodeQ!(`node.inContractExpression`, `InContractExpression`)); else if (currentIs(tok!"out")) mixin(parseNodeQ!(`node.outContractExpression`, `OutContractExpression`)); else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InOutStatement * * $(GRAMMAR $(RULEDEF inOutStatement): * $(RULE inStatement) * | $(RULE outStatement) * ;) */ InOutStatement parseInOutStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InOutStatement; if (currentIs(tok!"in")) mixin(parseNodeQ!(`node.inStatement`, `InStatement`)); else if (currentIs(tok!"out")) mixin(parseNodeQ!(`node.outStatement`, `OutStatement`)); else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InStatement * * $(GRAMMAR $(RULEDEF inStatement): * $(LITERAL 'in') $(RULE blockStatement) * ;) */ InStatement parseInStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InStatement; const i = expect(tok!"in"); mixin(nullCheck!`i`); node.inTokenLocation = i.index; mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Initializer * * $(GRAMMAR $(RULEDEF initializer): * $(LITERAL 'void') * | $(RULE nonVoidInitializer) * ;) */ Initializer parseInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Initializer; if (currentIs(tok!"void") && peekIsOneOf(tok!",", tok!";")) advance(); else mixin(parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InterfaceDeclaration * * $(GRAMMAR $(RULEDEF interfaceDeclaration): * $(LITERAL 'interface') $(LITERAL Identifier) $(LITERAL ';') * | $(LITERAL 'interface') $(LITERAL Identifier) ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'interface') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'interface') $(LITERAL Identifier) $(RULE templateParameters) ($(LITERAL ':') $(RULE baseClassList))? $(RULE constraint)? $(RULE structBody) * ;) */ InterfaceDeclaration parseInterfaceDeclaration() { auto startIndex = index; auto node = allocator.make!InterfaceDeclaration; expect(tok!"interface"); return parseInterfaceOrClass(node, startIndex); } /** * Parses an Invariant * * $(GRAMMAR $(RULEDEF invariant): * $(LITERAL 'invariant') ($(LITERAL '$(LPAREN)') $(LITERAL '$(LPAREN)'))? $(RULE blockStatement) * | $(LITERAL 'invariant') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') $(LITERAL ';') * ;) */ Invariant parseInvariant() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Invariant; node.index = current.index; node.line = current.line; mixin(tokenCheck!"invariant"); bool mustHaveBlock; if (currentIs(tok!"(") && peekIs(tok!")")) { mustHaveBlock = true; node.useParen = true; advance(); advance(); } if (currentIs(tok!"{")) { if (currentIs(tok!"(")) { advance(); mixin(tokenCheck!")"); } mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); } else if (!mustHaveBlock && currentIs(tok!"(")) { advance(); node.useParen = true; mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); mixin(tokenCheck!";"); } else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IsExpression * * $(GRAMMAR $(RULEDEF isExpression): * $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL ':') $(RULE typeSpecialization) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '=') $(RULE typeSpecialization) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL ':') $(RULE typeSpecialization) $(LITERAL ',') $(RULE templateParameterList) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '=') $(RULE typeSpecialization) $(LITERAL ',') $(RULE templateParameterList) $(LITERAL '$(RPAREN)') * ;) */ IsExpression parseIsExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IsExpression; mixin(tokenCheck!"is"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"identifier")) node.identifier = advance(); if (currentIsOneOf(tok!"==", tok!":")) { node.equalsOrColon = advance().type; mixin(parseNodeQ!(`node.typeSpecialization`, `TypeSpecialization`)); if (currentIs(tok!",")) { advance(); mixin(parseNodeQ!(`node.templateParameterList`, `TemplateParameterList`)); } } mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a KeyValuePair * * $(GRAMMAR $(RULEDEF keyValuePair): * $(RULE assignExpression) $(LITERAL ':') $(RULE assignExpression) * ;) */ KeyValuePair parseKeyValuePair() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!KeyValuePair; mixin(parseNodeQ!(`node.key`, `AssignExpression`)); mixin(tokenCheck!":"); mixin(parseNodeQ!(`node.value`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses KeyValuePairs * * $(GRAMMAR $(RULEDEF keyValuePairs): * $(RULE keyValuePair) ($(LITERAL ',') $(RULE keyValuePair))* $(LITERAL ',')? * ;) */ KeyValuePairs parseKeyValuePairs() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!KeyValuePairs; StackBuffer keyValuePairs; while (moreTokens()) { if (!keyValuePairs.put(parseKeyValuePair())) return null; if (currentIs(tok!",")) { advance(); if (currentIs(tok!"]")) break; } else break; } ownArray(node.keyValuePairs, keyValuePairs); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a LabeledStatement * * $(GRAMMAR $(RULEDEF labeledStatement): * $(LITERAL Identifier) $(LITERAL ':') $(RULE declarationOrStatement)? * ;) */ LabeledStatement parseLabeledStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!LabeledStatement; const ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.identifier = *ident; expect(tok!":"); if (!currentIs(tok!"}")) mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a LastCatch * * $(GRAMMAR $(RULEDEF lastCatch): * $(LITERAL 'catch') $(RULE statementNoCaseNoDefault) * ;) */ LastCatch parseLastCatch() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!LastCatch; const t = expect(tok!"catch"); mixin (nullCheck!`t`); node.line = t.line; node.column = t.column; mixin(parseNodeQ!(`node.statementNoCaseNoDefault`, `StatementNoCaseNoDefault`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a LinkageAttribute * * $(GRAMMAR $(RULEDEF linkageAttribute): * $(LITERAL 'extern') $(LITERAL '$(LPAREN)') $(LITERAL Identifier) $(LITERAL '$(RPAREN)') * | $(LITERAL 'extern') $(LITERAL '$(LPAREN)') $(LITERAL Identifier) $(LITERAL '-') $(LITERAL Identifier) $(LITERAL '$(RPAREN)') * | $(LITERAL 'extern') $(LITERAL '$(LPAREN)') $(LITERAL Identifier) $(LITERAL '++') ($(LITERAL ',') $(RULE typeIdentifierPart) | $(RULE namespaceList) | $(LITERAL 'struct') | $(LITERAL 'class'))? $(LITERAL '$(RPAREN)') * ;) */ LinkageAttribute parseLinkageAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!LinkageAttribute; mixin (tokenCheck!"extern"); mixin (tokenCheck!"("); const ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.identifier = *ident; if (currentIs(tok!"++")) { advance(); node.hasPlusPlus = true; if (currentIs(tok!",")) { advance(); if (currentIsOneOf(tok!"struct", tok!"class")) node.classOrStruct = advance().type; else if (currentIs(tok!"identifier")) mixin(parseNodeQ!(`node.typeIdentifierPart`, `TypeIdentifierPart`)); else mixin(parseNodeQ!(`node.cppNamespaces`, `NamespaceList`)); } } else if (currentIs(tok!"-")) { advance(); mixin(tokenCheck!"identifier"); } expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a MemberFunctionAttribute * * $(GRAMMAR $(RULEDEF memberFunctionAttribute): * $(RULE functionAttribute) * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'shared') * | $(LITERAL 'const') * | $(LITERAL 'return') * | $(LITERAL 'scope') * ;) */ MemberFunctionAttribute parseMemberFunctionAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) return null; auto node = allocator.make!MemberFunctionAttribute; switch (current.type) { case tok!"@": mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); break; case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"const": case tok!"pure": case tok!"nothrow": case tok!"return": case tok!"scope": case tok!"throw": node.tokenType = advance().type; break; default: error(`Member function attribute expected`); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a MissingFunctionBody * * $(GRAMMAR $(RULEDEF missingFunctionBody): * $(LITERAL ';') * | $(RULE functionContract)* $(LITERAL ';') * ;) */ MissingFunctionBody parseMissingFunctionBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!MissingFunctionBody; StackBuffer contracts; while (currentIsOneOf(tok!"in", tok!"out")) { if (auto c = parseFunctionContract()) contracts.put(c); } ownArray(node.functionContracts, contracts); if (node.functionContracts.length == 0 || node.functionContracts[$ - 1].inOutContractExpression !is null) { if (expect(tok!";") is null) return null; } else if (moreTokens() && (currentIs(tok!"do") || current.text == "body")) return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a MixinDeclaration * * $(GRAMMAR $(RULEDEF mixinDeclaration): * $(RULE mixinExpression) $(LITERAL ';') * | $(RULE templateMixinExpression) $(LITERAL ';') * ;) */ MixinDeclaration parseMixinDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!MixinDeclaration; if (peekIsOneOf(tok!"identifier", tok!"typeof", tok!".")) mixin(parseNodeQ!(`node.templateMixinExpression`, `TemplateMixinExpression`)); else if (peekIs(tok!"(")) mixin(parseNodeQ!(`node.mixinExpression`, `MixinExpression`)); else { error("`(` or identifier expected"); return null; } expect(tok!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a MixinExpression * * $(GRAMMAR $(RULEDEF mixinExpression): * $(LITERAL 'mixin') $(LITERAL '$(LPAREN)') $(RULE argumentList) $(LITERAL '$(RPAREN)') * ;) */ MixinExpression parseMixinExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!MixinExpression; expect(tok!"mixin"); expect(tok!"("); mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a MixinTemplateDeclaration * * $(GRAMMAR $(RULEDEF mixinTemplateDeclaration): * $(LITERAL 'mixin') $(RULE templateDeclaration) * ;) */ MixinTemplateDeclaration parseMixinTemplateDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!MixinTemplateDeclaration; mixin(tokenCheck!"mixin"); mixin(parseNodeQ!(`node.templateDeclaration`, `TemplateDeclaration`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a MixinTemplateName * * $(GRAMMAR $(RULEDEF mixinTemplateName): * $(RULE symbol) * | $(RULE typeofExpression) $(LITERAL '.') $(RULE identifierOrTemplateChain) * ;) */ MixinTemplateName parseMixinTemplateName() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!MixinTemplateName; if (currentIs(tok!"typeof")) { mixin(parseNodeQ!(`node.typeofExpression`, `TypeofExpression`)); expect(tok!"."); mixin(parseNodeQ!(`node.identifierOrTemplateChain`, `IdentifierOrTemplateChain`)); } else mixin(parseNodeQ!(`node.symbol`, `Symbol`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Module * * $(GRAMMAR $(RULEDEF module): * $(RULE moduleDeclaration)? $(RULE declaration)* * ;) */ Module parseModule() out (retVal) { assert(retVal !is null); } do { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Module m = allocator.make!Module; if (currentIs(tok!"scriptLine")) m.scriptLine = advance(); bool isModule; { immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); while (currentIs(tok!"@") || currentIs(tok!"deprecated")) { parseAttribute(); } isModule = currentIs(tok!"module"); goToBookmark(b); allocator.rollback(c); } if (isModule) { immutable c = allocator.setCheckpoint(); m.moduleDeclaration = parseModuleDeclaration(); if (m.moduleDeclaration is null) allocator.rollback(c); } StackBuffer declarations; while (moreTokens()) { immutable c = allocator.setCheckpoint(); if (!declarations.put(parseDeclaration(true, true))) allocator.rollback(c); } ownArray(m.declarations, declarations); m.tokens = tokens[startIndex .. index]; return m; } /** * Parses a ModuleDeclaration * * $(GRAMMAR $(RULEDEF moduleDeclaration): * $(RULE atAttribute)* $(RULE deprecated)? $(RULE atAttribute)* $(LITERAL 'module') $(RULE identifierChain) $(LITERAL ';') * ;) */ ModuleDeclaration parseModuleDeclaration() { auto startIndex = index; ModuleDeclaration node = allocator.make!ModuleDeclaration; StackBuffer attributeBuffer; while (currentIs(tok!"@")) attributeBuffer.put(parseAtAttribute()); if (currentIs(tok!"deprecated")) mixin(parseNodeQ!(`node.deprecated_`, `Deprecated`)); while (currentIs(tok!"@")) attributeBuffer.put(parseAtAttribute()); ownArray(node.atAttributes, attributeBuffer); const start = expect(tok!"module"); mixin(nullCheck!`start`); mixin(parseNodeQ!(`node.moduleName`, `IdentifierChain`)); node.comment = start.comment; if (node.comment is null) node.comment = start.trailingComment; comment = null; const end = expect(tok!";"); node.startLocation = start.index; if (end !is null) node.endLocation = end.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a MulExpression. * * $(GRAMMAR $(RULEDEF mulExpression): * $(RULE powExpression) * | $(RULE mulExpression) ($(LITERAL '*') | $(LITERAL '/') | $(LITERAL '%')) $(RULE powExpression) * ;) */ ExpressionNode parseMulExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(MulExpression, PowExpression, tok!"*", tok!"/", tok!"%")(); } /** * Parses a NamespaceList. * * $(GRAMMAR $(RULEDEF namespaceList): * $(RULE ternaryExpression) ($(LITERAL ',') $(RULE ternaryExpression)?)* $(LITERAL ',')? * ;) */ NamespaceList parseNamespaceList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(NamespaceList, TernaryExpression)(true); } /** * Parses a NewAnonClassExpression * * $(GRAMMAR $(RULEDEF newAnonClassExpression): * $(LITERAL 'new') $(RULE arguments)? $(LITERAL 'class') $(RULE arguments)? $(RULE baseClassList)? $(RULE structBody) * ;) */ NewAnonClassExpression parseNewAnonClassExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!NewAnonClassExpression; expect(tok!"new"); if (currentIs(tok!"(")) mixin(parseNodeQ!(`node.allocatorArguments`, `Arguments`)); expect(tok!"class"); if (currentIs(tok!"(")) mixin(parseNodeQ!(`node.constructorArguments`, `Arguments`)); if (!currentIs(tok!"{")) mixin(parseNodeQ!(`node.baseClassList`, `BaseClassList`)); mixin(parseNodeQ!(`node.structBody`, `StructBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a NewExpression * * $(GRAMMAR $(RULEDEF newExpression): * $(LITERAL 'new') $(RULE type) (($(LITERAL '[') $(RULE assignExpression) $(LITERAL ']')) | $(RULE arguments))? * | $(RULE newAnonClassExpression) * ;) */ NewExpression parseNewExpression() { // Parse ambiguity. // auto a = new int[10]; // ^^^^^^^ // auto a = new int[10]; // ^^^**** mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!NewExpression; if (peekIsOneOf(tok!"class", tok!"(")) mixin(parseNodeQ!(`node.newAnonClassExpression`, `NewAnonClassExpression`)); else { expect(tok!"new"); mixin(parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"[")) { advance(); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); expect(tok!"]"); } else if (currentIs(tok!"(")) mixin(parseNodeQ!(`node.arguments`, `Arguments`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a NonVoidInitializer * * $(GRAMMAR $(RULEDEF nonVoidInitializer): * $(RULE assignExpression) * | $(RULE arrayInitializer) * | $(RULE structInitializer) * ;) */ NonVoidInitializer parseNonVoidInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; NonVoidInitializer node = allocator.make!NonVoidInitializer; if (!currentIs(tok!"[")) { const b = setBookmark(); if (ExpressionNode ae = parseAssignExpression()) { abandonBookmark(b); node.assignExpression = ae; node.tokens = tokens[startIndex .. index]; return node; } goToBookmark(b); if (!currentIs(tok!"{")) { return null; } else if (StructInitializer si = parseStructInitializer) { node.structInitializer = si; node.tokens = tokens[startIndex .. index]; return node; } else return null; } else { // issue 156: // the expression that gives an array element is usually a primary // so look if there are two open brackets + colon when they are closed bool isAA; const bk = setBookmark(); advance(); if (currentIs(tok!"[")) { advance(); const c = peekPastBrackets(); isAA = c !is null && c.type == tok!":"; } goToBookmark(bk); const b = peekPastBrackets(); if (!isAA && b !is null && (b.type == tok!"," || b.type == tok!")" || b.type == tok!"]" || b.type == tok!"}" || b.type == tok!";")) { mixin(parseNodeQ!(`node.arrayInitializer`, `ArrayInitializer`)); } else mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses Operands * * $(GRAMMAR $(RULEDEF operands): * $(RULE asmExp) * | $(RULE asmExp) $(LITERAL ',') $(RULE operands) * ;) */ Operands parseOperands() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Operands node = allocator.make!Operands; StackBuffer expressions; while (true) { if (!expressions.put(parseAsmExp())) return null; if (currentIs(tok!",")) advance(); else break; } ownArray(node.operands, expressions); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an OrExpression * * $(GRAMMAR $(RULEDEF orExpression): * $(RULE xorExpression) * | $(RULE orExpression) $(LITERAL '|') $(RULE xorExpression) * ;) */ ExpressionNode parseOrExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(OrExpression, XorExpression, tok!"|")(); } /** * Parses an OrOrExpression * * $(GRAMMAR $(RULEDEF orOrExpression): * $(RULE andAndExpression) * | $(RULE orOrExpression) $(LITERAL '||') $(RULE andAndExpression) * ;) */ ExpressionNode parseOrOrExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(OrOrExpression, AndAndExpression, tok!"||")(); } /** * Parses an OutContractExpression * * $(GRAMMAR $(RULEDEF outContractExpression): * $(LITERAL 'out') $(LITERAL '$(LPAREN)') $(LITERAL Identifier)? $(LITERAL ';') $(RULE assertArguments) $(LITERAL '$(RPAREN)') * ;) */ OutContractExpression parseOutContractExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!OutContractExpression; const o = expect(tok!"out"); mixin(nullCheck!`o`); node.outTokenLocation = o.index; mixin(tokenCheck!"("); if (currentIs(tok!"identifier")) node.parameter = advance(); mixin(tokenCheck!";"); mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an OutStatement * * $(GRAMMAR $(RULEDEF outStatement): * $(LITERAL 'out') ($(LITERAL '$(LPAREN)') $(LITERAL Identifier) $(LITERAL '$(RPAREN)'))? $(RULE blockStatement) * ;) */ OutStatement parseOutStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!OutStatement; const o = expect(tok!"out"); mixin(nullCheck!`o`); node.outTokenLocation = o.index; if (currentIs(tok!"(")) { advance(); const ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.parameter = *ident; expect(tok!")"); } mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Parameter * * $(GRAMMAR $(RULEDEF parameter): * $(RULE parameterAttribute)* $(RULE type) * | $(RULE parameterAttribute)* $(RULE type) $(LITERAL Identifier)? $(LITERAL '...') * | $(RULE parameterAttribute)* $(RULE type) $(LITERAL Identifier)? ($(LITERAL '=') $(RULE assignExpression))? * ;) */ Parameter parseParameter() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Parameter; StackBuffer parameterAttributes; while (moreTokens()) { if (ParameterAttribute pa = parseParameterAttribute(false)) parameterAttributes.put(pa); else break; } // Parsed the attributes of the variadic attributes. // Abort and defer to parseVariadicArgumentsAttributes if (currentIs(tok!"...")) return null; ownArray(node.parameterAttributes, parameterAttributes); mixin(parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"identifier")) { node.name = advance(); if (currentIs(tok!"...")) { advance(); node.vararg = true; } else if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.default_`, `AssignExpression`)); if (currentIs(tok!"...")) { advance(); node.vararg = true; } } else if (currentIs(tok!"[")) { StackBuffer typeSuffixes; while(moreTokens() && currentIs(tok!"[")) if (!typeSuffixes.put(parseTypeSuffix())) return null; ownArray(node.cstyle, typeSuffixes); } } else if (currentIs(tok!"...")) { node.vararg = true; advance(); } else if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.default_`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ParameterAttribute * * $(GRAMMAR $(RULEDEF parameterAttribute): * $(RULE atAttribute) * | $(RULE typeConstructor) * | $(LITERAL 'final') * | $(LITERAL 'in') * | $(LITERAL 'lazy') * | $(LITERAL 'out') * | $(LITERAL 'ref') * | $(LITERAL 'scope') * | $(LITERAL 'auto') * | $(LITERAL 'return') * ;) */ ParameterAttribute parseParameterAttribute(bool validate = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) return null; auto node = allocator.make!ParameterAttribute; switch (current.type) { case tok!"@": if (AtAttribute aa = parseAtAttribute()) { node.atAttribute = aa; node.tokens = tokens[startIndex .. index]; return node; } else { validate = true; goto default; } case tok!"immutable": case tok!"shared": case tok!"const": case tok!"inout": if (peekIs(tok!"(")) return null; else goto case; case tok!"final": case tok!"in": case tok!"lazy": case tok!"out": case tok!"ref": case tok!"scope": case tok!"auto": case tok!"return": node.idType = advance().type; node.tokens = tokens[startIndex .. index]; return node; default: if (validate) error("Parameter attribute expected"); return null; } } /** * Parses Parameters * * $(GRAMMAR $(RULEDEF parameters): * $(LITERAL '$(LPAREN)') $(RULE parameter) ($(LITERAL ',') $(RULE parameter))* ($(LITERAL ',') $(RULE variadicArgumentsAttributes)? $(LITERAL '...'))? $(LITERAL '$(RPAREN)') * | $(LITERAL '$(LPAREN)') $(RULE variadicArgumentsAttributes)? $(LITERAL '...') $(LITERAL '$(RPAREN)') * | $(LITERAL '$(LPAREN)') $(LITERAL '$(RPAREN)') * ;) */ Parameters parseParameters() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Parameters; mixin(tokenCheck!"("); if (currentIs(tok!")")) { advance(); // ) node.tokens = tokens[startIndex .. index]; return node; } if (currentIs(tok!"...")) { advance(); node.hasVarargs = true; mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } StackBuffer parameters; while (moreTokens()) { if (currentIs(tok!"...")) { advance(); node.hasVarargs = true; break; } if (currentIs(tok!")")) break; // Save starting point to deal with attributed variadics, e.g. // int printf(in char* format, scope const ...); const startIdx = index; auto cp = allocator.setCheckpoint(); if (!parameters.put(parseParameter())) { // parseParameter fails for C-style variadics, they are parsed below if (!currentIs(tok!"...")) return null; // Reset to the beginning of the current parameters index = startIdx; allocator.rollback(cp); node.hasVarargs = true; mixin(parseNodeQ!(`node.varargsAttributes`, `VariadicArgumentsAttributes`)); mixin(tokenCheck!"..."); break; } if (currentIs(tok!",")) advance(); else break; } ownArray(node.parameters, parameters); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses attributes of C-style variadic parameters. * * $(GRAMMAR $(RULEDEF variadicArgumentsAttributes): * $(RULE variadicArgumentsAttribute)+ * ;) */ ParameterAttribute[] parseVariadicArgumentsAttributes() { mixin(traceEnterAndExit!(__FUNCTION__)); StackBuffer attributes; while (moreTokens() && !currentIs(tok!"...")) { if (!attributes.put(parseVariadicArgumentsAttribute())) return null; } ParameterAttribute[] buffer; ownArray(buffer, attributes); return buffer; } /** * Parses an attribute of C-style variadic parameters. * * $(GRAMMAR $(RULEDEF variadicArgumentsAttribute): * $(LITERAL 'const') * | $(LITERAL 'immutable') * | $(LITERAL 'scope') * | $(LITERAL 'shared') * | $(LITERAL 'return') * ;) */ ParameterAttribute parseVariadicArgumentsAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto node = allocator.make!ParameterAttribute(); auto startIndex = index; if (!currentIsOneOf(tok!"const", tok!"immutable", tok!"shared", tok!"scope", tok!"return")) { error("`const`, `immutable`, `shared`, `scope` or `return` expected"); return null; } node.idType = current.type; advance(); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Postblit * * $(GRAMMAR $(RULEDEF postblit): * $(LITERAL 'this') $(LITERAL '$(LPAREN)') $(LITERAL 'this') $(LITERAL '$(RPAREN)') $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ';')) * ;) */ Postblit parsePostblit() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Postblit; node.line = current.line; node.column = current.column; node.location = current.index; index += 4; StackBuffer memberFunctionAttributes; while (currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); if (currentIs(tok!";")) advance(); else mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a PowExpression * * $(GRAMMAR $(RULEDEF powExpression): * $(RULE unaryExpression) * | $(RULE powExpression) $(LITERAL '^^') $(RULE unaryExpression) * ;) */ ExpressionNode parsePowExpression() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(PowExpression, UnaryExpression, tok!"^^")(); } /** * Parses a PragmaDeclaration * * $(GRAMMAR $(RULEDEF pragmaDeclaration): * $(RULE pragmaExpression) $(LITERAL ';') * ;) */ PragmaDeclaration parsePragmaDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(PragmaDeclaration, "pragmaExpression|parsePragmaExpression", tok!";")); } /** * Parses a PragmaExpression * * $(GRAMMAR $(RULEDEF pragmaExpression): * $(RULE 'pragma') $(LITERAL '$(LPAREN)') $(LITERAL Identifier) ($(LITERAL ',') $(RULE argumentList))? $(LITERAL '$(RPAREN)') * ;) */ PragmaExpression parsePragmaExpression() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!PragmaExpression; expect(tok!"pragma"); expect(tok!"("); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; if (currentIs(tok!",")) { advance(); mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); } expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a PragmaStatement * * $(GRAMMAR $(RULEDEF pragmaStatement): * $(RULE pragmaExpression) $(RULE statement) * | $(RULE pragmaExpression) $(RULE blockStatement) * | $(RULE pragmaExpression) $(LITERAL ';') * ;) */ PragmaStatement parsePragmaStatement() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!PragmaStatement; mixin(parseNodeQ!(`node.pragmaExpression`, `PragmaExpression`)); if (current == tok!"{") { mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); } else if (current == tok!";") { advance(); } else { mixin(parseNodeQ!(`node.statement`, `Statement`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a PrimaryExpression * * $(GRAMMAR $(RULEDEF primaryExpression): * $(RULE identifierOrTemplateInstance) * | $(LITERAL '.') $(RULE identifierOrTemplateInstance) * | $(RULE typeConstructor) $(LITERAL '$(LPAREN)') $(RULE basicType) $(LITERAL '$(RPAREN)') $(LITERAL '.') $(LITERAL Identifier) * | $(RULE basicType) $(LITERAL '.') $(LITERAL Identifier) * | $(RULE basicType) $(RULE arguments) * | $(RULE typeofExpression) * | $(RULE typeidExpression) * | $(RULE vector) * | $(RULE arrayLiteral) * | $(RULE assocArrayLiteral) * | $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') * | $(RULE isExpression) * | $(RULE functionLiteralExpression) * | $(RULE traitsExpression) * | $(RULE mixinExpression) * | $(RULE importExpression) * | $(LITERAL '$') * | $(LITERAL 'this') * | $(LITERAL 'super') * | $(LITERAL '_null') * | $(LITERAL '_true') * | $(LITERAL '_false') * | $(LITERAL '___DATE__') * | $(LITERAL '___FILE__') * | $(LITERAL '___FILE_FULL_PATH__') * | $(LITERAL '___FUNCTION__') * | $(LITERAL '___LINE__') * | $(LITERAL '___MODULE__') * | $(LITERAL '___PRETTY_FUNCTION__') * | $(LITERAL '___TIME__') * | $(LITERAL '___TIMESTAMP__') * | $(LITERAL '___VENDOR__') * | $(LITERAL '___VERSION__') * | $(LITERAL IntegerLiteral) * | $(LITERAL FloatLiteral) * | $(LITERAL StringLiteral)+ * | $(LITERAL CharacterLiteral) * ;) */ PrimaryExpression parsePrimaryExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!PrimaryExpression; if (!moreTokens()) { error("Expected primary statement instead of EOF"); return null; } switch (current.type) { case tok!".": node.dot = advance(); goto case; case tok!"identifier": if (peekIs(tok!"=>")) mixin(parseNodeQ!(`node.functionLiteralExpression`, `FunctionLiteralExpression`)); else mixin(parseNodeQ!(`node.identifierOrTemplateInstance`, `IdentifierOrTemplateInstance`)); break; case tok!"immutable": case tok!"const": case tok!"inout": case tok!"shared": { node.typeConstructor = advance(); expect(tok!"("); mixin(parseNodeQ!(`node.type`, `Type`)); expect(tok!")"); expect(tok!"."); const ident = expect(tok!"identifier"); if (ident !is null) node.primary = *ident; break; } foreach (B; BasicTypes) { case B: } node.basicType = advance(); if (currentIs(tok!".")) { advance(); const t = expect(tok!"identifier"); if (t !is null) node.primary = *t; } else if (currentIs(tok!"(")) mixin(parseNodeQ!(`node.arguments`, `Arguments`)); else goto default; break; case tok!"function": case tok!"delegate": case tok!"{": case tok!"in": case tok!"out": case tok!"do": mixin(parseNodeQ!(`node.functionLiteralExpression`, `FunctionLiteralExpression`)); break; case tok!"typeof": mixin(parseNodeQ!(`node.typeofExpression`, `TypeofExpression`)); break; case tok!"typeid": mixin(parseNodeQ!(`node.typeidExpression`, `TypeidExpression`)); break; case tok!"__vector": mixin(parseNodeQ!(`node.vector`, `Vector`)); break; case tok!"[": if (isAssociativeArrayLiteral()) mixin(parseNodeQ!(`node.assocArrayLiteral`, `AssocArrayLiteral`)); else mixin(parseNodeQ!(`node.arrayLiteral`, `ArrayLiteral`)); break; case tok!"ref": if (peekIs(tok!"(")) { mixin(parseNodeQ!(`node.functionLiteralExpression`, `FunctionLiteralExpression`)); break; } else goto default; case tok!"(": immutable b = setBookmark(); skipParens(); while (isAttribute()) parseAttribute(); if (currentIsOneOf(tok!"=>", tok!"{")) { goToBookmark(b); mixin(parseNodeQ!(`node.functionLiteralExpression`, `FunctionLiteralExpression`)); } else { goToBookmark(b); advance(); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); } break; case tok!"is": mixin(parseNodeQ!(`node.isExpression`, `IsExpression`)); break; case tok!"__traits": mixin(parseNodeQ!(`node.traitsExpression`, `TraitsExpression`)); break; case tok!"mixin": mixin(parseNodeQ!(`node.mixinExpression`, `MixinExpression`)); break; case tok!"import": mixin(parseNodeQ!(`node.importExpression`, `ImportExpression`)); break; case tok!"this": case tok!"super": foreach (L; Literals) { case L: } if (currentIsOneOf(tok!"stringLiteral", tok!"wstringLiteral", tok!"dstringLiteral")) { node.primary = advance(); bool alreadyWarned = false; while (currentIsOneOf(tok!"stringLiteral", tok!"wstringLiteral", tok!"dstringLiteral")) { if (!alreadyWarned) { warn("Implicit concatenation of string literals"); alreadyWarned = true; } node.primary.text ~= advance().text; } } else node.primary = advance(); break; default: error("Primary expression expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Register * * $(GRAMMAR $(RULEDEF register): * $(LITERAL Identifier) * | $(LITERAL Identifier) $(LITERAL '$(LPAREN)') $(LITERAL IntegerLiteral) $(LITERAL '$(RPAREN)') * ;) */ Register parseRegister() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Register; const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; if (currentIs(tok!"(")) { advance(); const intLit = expect(tok!"intLiteral"); mixin(nullCheck!`intLit`); node.intLiteral = *intLit; expect(tok!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a RelExpression * * $(GRAMMAR $(RULEDEF relExpression): * $(RULE shiftExpression) * | $(RULE relExpression) $(RULE relOperator) $(RULE shiftExpression) * ; *$(RULEDEF relOperator): * $(LITERAL '<') * | $(LITERAL '<=') * | $(LITERAL '>') * | $(LITERAL '>=') * | $(LITERAL '!<>=') * | $(LITERAL '!<>') * | $(LITERAL '<>') * | $(LITERAL '<>=') * | $(LITERAL '!>') * | $(LITERAL '!>=') * | $(LITERAL '!<') * | $(LITERAL '!<=') * ;) */ ExpressionNode parseRelExpression(ExpressionNode shift) { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(RelExpression, ShiftExpression, tok!"<", tok!"<=", tok!">", tok!">=", tok!"!<>=", tok!"!<>", tok!"<>", tok!"<>=", tok!"!>", tok!"!>=", tok!"!>=", tok!"!<", tok!"!<=")(shift); } /** * Parses a ReturnStatement * * $(GRAMMAR $(RULEDEF returnStatement): * $(LITERAL 'return') $(RULE expression)? $(LITERAL ';') * ;) */ ReturnStatement parseReturnStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ReturnStatement; const start = expect(tok!"return"); mixin(nullCheck!`start`); node.startLocation = start.index; if (!currentIs(tok!";")) mixin(parseNodeQ!(`node.expression`, `Expression`)); const semicolon = expect(tok!";"); mixin(nullCheck!`semicolon`); node.endLocation = semicolon.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ScopeGuardStatement * * $(GRAMMAR $(RULEDEF scopeGuardStatement): * $(LITERAL 'scope') $(LITERAL '$(LPAREN)') $(LITERAL Identifier) $(LITERAL '$(RPAREN)') $(RULE statementNoCaseNoDefault) * ;) */ ScopeGuardStatement parseScopeGuardStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ScopeGuardStatement; expect(tok!"scope"); expect(tok!"("); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; expect(tok!")"); mixin(parseNodeQ!(`node.statementNoCaseNoDefault`, `StatementNoCaseNoDefault`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a SharedStaticConstructor * * $(GRAMMAR $(RULEDEF sharedStaticConstructor): * $(LITERAL 'shared') $(LITERAL 'static') $(LITERAL 'this') $(LITERAL '$(LPAREN)') $(LITERAL '$(RPAREN)') $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ";")) * ;) */ SharedStaticConstructor parseSharedStaticConstructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!SharedStaticConstructor; node.location = current().index; mixin(tokenCheck!"shared"); mixin(tokenCheck!"static"); return parseStaticCtorDtorCommon(node, startIndex); } /** * Parses a SharedStaticDestructor * * $(GRAMMAR $(RULEDEF sharedStaticDestructor): * $(LITERAL 'shared') $(LITERAL 'static') $(LITERAL '~') $(LITERAL 'this') $(LITERAL '$(LPAREN)') $(LITERAL '$(RPAREN)') $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ";")) * ;) */ SharedStaticDestructor parseSharedStaticDestructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!SharedStaticDestructor; node.location = current().index; mixin(tokenCheck!"shared"); mixin(tokenCheck!"static"); mixin(tokenCheck!"~"); return parseStaticCtorDtorCommon(node, startIndex); } /** * Parses a ShiftExpression * * $(GRAMMAR $(RULEDEF shiftExpression): * $(RULE addExpression) * | $(RULE shiftExpression) ($(LITERAL '<<') | $(LITERAL '>>') | $(LITERAL '>>>')) $(RULE addExpression) * ;) */ ExpressionNode parseShiftExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(ShiftExpression, AddExpression, tok!"<<", tok!">>", tok!">>>")(); } /** * Parses a SingleImport * * $(GRAMMAR $(RULEDEF singleImport): * ($(LITERAL Identifier) $(LITERAL '='))? $(RULE identifierChain) * ;) */ SingleImport parseSingleImport() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!SingleImport; if (startsWith(tok!"identifier", tok!"=")) { node.rename = advance(); // identifier advance(); // = } mixin(parseNodeQ!(`node.identifierChain`, `IdentifierChain`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ShortenedFunctionBody * * $(GRAMMAR $(RULEDEF shortenedFunctionBody): * $(RULE inOutContractExpression)* $(LITERAL '=>') $(RULE expression) $(LITERAL ';') * ;) */ ShortenedFunctionBody parseShortenedFunctionBody() { mixin(traceEnterAndExit!(__FUNCTION__)); immutable startIndex = index; auto node = allocator.make!ShortenedFunctionBody; StackBuffer contracts; while (currentIsOneOf(tok!"in", tok!"out")) { if (auto c = parseFunctionContract(false)) contracts.put(c); } ownArray(node.functionContracts, contracts); mixin(tokenCheck!"=>"); mixin(parseNodeQ!("node.expression", "Expression")); mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a SpecifiedFunctionBody * * $(GRAMMAR $(RULEDEF specifiedFunctionBody): * $(LITERAL 'do')? $(RULE blockStatement) * | $(RULE functionContract)* $(RULE inOutContractExpression) $(LITERAL 'do')? $(RULE blockStatement) * | $(RULE functionContract)* $(RULE inOutStatement) $(LITERAL 'do') $(RULE blockStatement) * ;) */ SpecifiedFunctionBody parseSpecifiedFunctionBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!SpecifiedFunctionBody; StackBuffer contracts; bool requireDo; while (currentIsOneOf(tok!"in", tok!"out")) { if (auto c = parseFunctionContract()) { requireDo = c.inOutStatement !is null; contracts.put(c); } } ownArray(node.functionContracts, contracts); node.hasDo = currentIs(tok!"do"); if (currentIs(tok!"do") || (currentIs(tok!"identifier") && current.text == "body")) { requireDo = false; advance(); } if (requireDo) { error("`do` expected after InStatement or OutStatement"); return null; } mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Statement * * $(GRAMMAR $(RULEDEF statement): * $(RULE statementNoCaseNoDefault) * | $(RULE caseStatement) * | $(RULE caseRangeStatement) * | $(RULE defaultStatement) * ;) */ Statement parseStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Statement; if (!moreTokens()) { error("Expected statement instead of EOF"); return null; } switch (current.type) { case tok!"case": advance(); auto argumentList = parseArgumentList(); if (argumentList is null) return null; if (argumentList.items.length == 1 && startsWith(tok!":", tok!"..")) node.caseRangeStatement = parseCaseRangeStatement(argumentList.items[0]); else node.caseStatement = parseCaseStatement(argumentList); break; case tok!"default": mixin(parseNodeQ!(`node.defaultStatement`, `DefaultStatement`)); break; default: mixin(parseNodeQ!(`node.statementNoCaseNoDefault`, `StatementNoCaseNoDefault`)); break; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a StatementNoCaseNoDefault * * $(GRAMMAR $(RULEDEF statementNoCaseNoDefault): * $(RULE labeledStatement) * | $(RULE blockStatement) * | $(RULE ifStatement) * | $(RULE whileStatement) * | $(RULE doStatement) * | $(RULE forStatement) * | $(RULE foreachStatement) * | $(RULE switchStatement) * | $(RULE finalSwitchStatement) * | $(RULE continueStatement) * | $(RULE breakStatement) * | $(RULE returnStatement) * | $(RULE gotoStatement) * | $(RULE withStatement) * | $(RULE synchronizedStatement) * | $(RULE tryStatement) * | $(RULE scopeGuardStatement) * | $(RULE pragmaStatement) * | $(RULE asmStatement) * | $(RULE conditionalStatement) * | $(RULE staticAssertStatement) * | $(RULE versionSpecification) * | $(RULE debugSpecification) * | $(RULE expressionStatement) * ;) */ StatementNoCaseNoDefault parseStatementNoCaseNoDefault() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens()) return null; auto node = allocator.make!StatementNoCaseNoDefault; node.startLocation = current().index; switch (current.type) { case tok!"{": mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); break; case tok!"if": mixin(parseNodeQ!(`node.ifStatement`, `IfStatement`)); break; case tok!"while": mixin(parseNodeQ!(`node.whileStatement`, `WhileStatement`)); break; case tok!"do": mixin(parseNodeQ!(`node.doStatement`, `DoStatement`)); break; case tok!"for": mixin(parseNodeQ!(`node.forStatement`, `ForStatement`)); break; case tok!"foreach": case tok!"foreach_reverse": mixin(parseNodeQ!(`node.foreachStatement`, `ForeachStatement`)); break; case tok!"switch": mixin(parseNodeQ!(`node.switchStatement`, `SwitchStatement`)); break; case tok!"continue": mixin(parseNodeQ!(`node.continueStatement`, `ContinueStatement`)); break; case tok!"break": mixin(parseNodeQ!(`node.breakStatement`, `BreakStatement`)); break; case tok!"return": mixin(parseNodeQ!(`node.returnStatement`, `ReturnStatement`)); break; case tok!"goto": mixin(parseNodeQ!(`node.gotoStatement`, `GotoStatement`)); break; case tok!"with": mixin(parseNodeQ!(`node.withStatement`, `WithStatement`)); break; case tok!"synchronized": mixin(parseNodeQ!(`node.synchronizedStatement`, `SynchronizedStatement`)); break; case tok!"try": mixin(parseNodeQ!(`node.tryStatement`, `TryStatement`)); break; case tok!"scope": mixin(parseNodeQ!(`node.scopeGuardStatement`, `ScopeGuardStatement`)); break; case tok!"asm": mixin(parseNodeQ!(`node.asmStatement`, `AsmStatement`)); break; case tok!"pragma": mixin(parseNodeQ!(`node.pragmaStatement`, `PragmaStatement`)); break; case tok!"final": if (peekIs(tok!"switch")) { mixin(parseNodeQ!(`node.finalSwitchStatement`, `FinalSwitchStatement`)); break; } else { error("`switch` expected"); return null; } case tok!"debug": if (peekIs(tok!"=")) mixin(parseNodeQ!(`node.debugSpecification`, `DebugSpecification`)); else mixin(parseNodeQ!(`node.conditionalStatement`, `ConditionalStatement`)); break; case tok!"version": if (peekIs(tok!"=")) mixin(parseNodeQ!(`node.versionSpecification`, `VersionSpecification`)); else mixin(parseNodeQ!(`node.conditionalStatement`, `ConditionalStatement`)); break; case tok!"static": if (peekIs(tok!"if")) mixin(parseNodeQ!(`node.conditionalStatement`, `ConditionalStatement`)); else if (peekIs(tok!"assert")) mixin(parseNodeQ!(`node.staticAssertStatement`, `StaticAssertStatement`)); else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse")) mixin(parseNodeQ!(`node.staticForeachStatement`, `StaticForeachStatement`)); else { error("`if`, `assert`, `foreach` or `foreach_reverse` expected."); return null; } break; case tok!"identifier": if (peekIs(tok!":")) { mixin(parseNodeQ!(`node.labeledStatement`, `LabeledStatement`)); break; } goto default; case tok!"delete": case tok!"assert": default: mixin(parseNodeQ!(`node.expressionStatement`, `ExpressionStatement`)); break; } node.endLocation = tokens[index - 1].index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a StaticAssertDeclaration * * $(GRAMMAR $(RULEDEF staticAssertDeclaration): * $(RULE staticAssertStatement) * ;) */ StaticAssertDeclaration parseStaticAssertDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(StaticAssertDeclaration, "staticAssertStatement|parseStaticAssertStatement")); } /** * Parses a StaticAssertStatement * * $(GRAMMAR $(RULEDEF staticAssertStatement): * $(LITERAL 'static') $(RULE assertExpression) $(LITERAL ';') * ;) */ StaticAssertStatement parseStaticAssertStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(StaticAssertStatement, tok!"static", "assertExpression|parseAssertExpression", tok!";")); } /** * Parses a StaticConstructor * * $(GRAMMAR $(RULEDEF staticConstructor): * $(LITERAL 'static') $(LITERAL 'this') $(LITERAL '$(LPAREN)') $(LITERAL '$(RPAREN)') $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ";")) * ;) */ StaticConstructor parseStaticConstructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!StaticConstructor; node.location = current().index; mixin(tokenCheck!"static"); return parseStaticCtorDtorCommon(node, startIndex); } /** * Parses a StaticDestructor * * $(GRAMMAR $(RULEDEF staticDestructor): * $(LITERAL 'static') $(LITERAL '~') $(LITERAL 'this') $(LITERAL '$(LPAREN)') $(LITERAL '$(RPAREN)') $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ";")) * ;) */ StaticDestructor parseStaticDestructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!StaticDestructor; node.location = current().index; mixin(tokenCheck!"static"); mixin(tokenCheck!"~"); return parseStaticCtorDtorCommon(node, startIndex); } /** * Parses an StaticIfCondition * * $(GRAMMAR $(RULEDEF staticIfCondition): * $(LITERAL 'static') $(LITERAL 'if') $(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)') * ;) */ StaticIfCondition parseStaticIfCondition() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(StaticIfCondition, tok!"static", tok!"if", tok!"(", "assignExpression|parseAssignExpression", tok!")")); } /** * Parses a StorageClass * * $(GRAMMAR $(RULEDEF storageClass): * $(RULE alignAttribute) * | $(RULE linkageAttribute) * | $(RULE atAttribute) * | $(RULE typeConstructor) * | $(RULE deprecated) * | $(LITERAL 'abstract') * | $(LITERAL 'auto') * | $(LITERAL 'enum') * | $(LITERAL 'extern') * | $(LITERAL 'final') * | $(LITERAL 'nothrow') * | $(LITERAL 'override') * | $(LITERAL 'pure') * | $(LITERAL 'ref') * | $(LITERAL '___gshared') * | $(LITERAL 'scope') * | $(LITERAL 'static') * | $(LITERAL 'synchronized') * | $(LITERAL 'throw') * ;) */ StorageClass parseStorageClass() { if (!moreTokens) return null; auto startIndex = index; auto node = allocator.make!StorageClass; switch (current.type) { case tok!"@": mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); break; case tok!"deprecated": mixin(parseNodeQ!(`node.deprecated_`, `Deprecated`)); break; case tok!"align": mixin(parseNodeQ!(`node.alignAttribute`, `AlignAttribute`)); break; case tok!"extern": if (peekIs(tok!"(")) { mixin(parseNodeQ!(`node.linkageAttribute`, `LinkageAttribute`)); break; } else goto case; case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"abstract": case tok!"auto": case tok!"enum": case tok!"final": case tok!"nothrow": case tok!"override": case tok!"pure": case tok!"ref": case tok!"__gshared": case tok!"scope": case tok!"static": case tok!"synchronized": case tok!"throw": node.token = advance(); break; default: error(`Storage class expected`); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a StringLiteralList * * $(GRAMMAR $(RULEDEF stringLiteralList): * $(RULE stringLiteral) ($(LITERAL ',') $(RULE stringLiteral))* * ;) */ private StringLiteralList parseStringLiteralList() { mixin(traceEnterAndExit!(__FUNCTION__)); const startIndex = index; auto node = allocator.make!(StringLiteralList)(); StackBuffer sb; while (true) { if (!currentIs(tok!"stringLiteral")) { error("Expected `stringLiteral` instead of `" ~ current.text ~ '`'); return null; } sb.put(advance()); if (currentIsOneOf(tok!":", tok!";")) break; mixin(tokenCheck!","); } node.tokens = tokens[startIndex .. index]; ownArray(node.items, sb); return node; } /** * Parses a StructBody * * $(GRAMMAR $(RULEDEF structBody): * $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * ;) */ StructBody parseStructBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!StructBody; const start = expect(tok!"{"); if (start !is null) node.startLocation = start.index; StackBuffer declarations; while (!currentIs(tok!"}") && moreTokens()) { immutable c = allocator.setCheckpoint(); if (!declarations.put(parseDeclaration(true, true))) allocator.rollback(c); } ownArray(node.declarations, declarations); const end = expect(tok!"}"); if (end !is null) node.endLocation = end.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a StructDeclaration * * $(GRAMMAR $(RULEDEF structDeclaration): * $(LITERAL 'struct') $(LITERAL Identifier) ($(RULE templateParameters) $(RULE constraint)?)? ($(RULE structBody) | $(LITERAL ';')) * | $(LITERAL 'struct') $(RULE structBody) * ;) */ StructDeclaration parseStructDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!StructDeclaration; const t = expect(tok!"struct"); if (currentIs(tok!"identifier")) node.name = advance(); else { node.name.line = t.line; node.name.column = t.column; } node.comment = comment; comment = null; if (currentIs(tok!"(")) { mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); if (currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); } if (currentIs(tok!"{")) { mixin(parseNodeQ!(`node.structBody`, `StructBody`)); if (node.structBody.tokens.length) attachComment(node, node.structBody.tokens[$ - 1].trailingComment); } else if (currentIs(tok!";")) advance(); else { error("Template Parameters, Struct Body, or Semicolon expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an StructInitializer * * $(GRAMMAR $(RULEDEF structInitializer): * $(LITERAL '{') $(RULE structMemberInitializers)? $(LITERAL '}') * ;) */ StructInitializer parseStructInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!StructInitializer; const a = expect(tok!"{"); mixin (nullCheck!`a`); node.startLocation = a.index; if (currentIs(tok!"}")) { node.endLocation = current.index; advance(); } else { mixin(parseNodeQ!(`node.structMemberInitializers`, `StructMemberInitializers`)); const e = expect(tok!"}"); mixin (nullCheck!`e`); node.endLocation = e.index; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a StructMemberInitializer * * $(GRAMMAR $(RULEDEF structMemberInitializer): * ($(LITERAL Identifier) $(LITERAL ':'))? $(RULE nonVoidInitializer) * ;) */ StructMemberInitializer parseStructMemberInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!StructMemberInitializer; if (startsWith(tok!"identifier", tok!":")) { node.identifier = advance(); index++; } mixin(parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses StructMemberInitializers * * $(GRAMMAR $(RULEDEF structMemberInitializers): * $(RULE structMemberInitializer) ($(LITERAL ',') $(RULE structMemberInitializer)?)* * ;) */ StructMemberInitializers parseStructMemberInitializers() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!StructMemberInitializers; StackBuffer structMemberInitializers; do { auto c = allocator.setCheckpoint(); if (!structMemberInitializers.put(parseStructMemberInitializer())) allocator.rollback(c); if (currentIs(tok!",")) advance(); else break; } while (moreTokens() && !currentIs(tok!"}")); ownArray(node.structMemberInitializers, structMemberInitializers); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a SwitchStatement * * $(GRAMMAR $(RULEDEF switchStatement): * $(LITERAL 'switch') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE statement) * ;) */ SwitchStatement parseSwitchStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!SwitchStatement; expect(tok!"switch"); expect(tok!"("); // DCD #453 // with just `switch(stuff` returns a non null node, // which allows DCD to gives completion on `stuff`. if (auto e = parseExpression()) { node.expression = e; if (currentIs(tok!")")) { advance(); // returns null only from here. mixin(parseNodeQ!(`node.statement`, `Statement`)); } } else error("Error, expression expected after `switch(`", false); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Symbol * * $(GRAMMAR $(RULEDEF symbol): * $(LITERAL '.')? $(RULE identifierOrTemplateChain) * ;) */ Symbol parseSymbol() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Symbol; if (currentIs(tok!".")) { node.dot = true; advance(); } mixin(parseNodeQ!(`node.identifierOrTemplateChain`, `IdentifierOrTemplateChain`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a SynchronizedStatement * * $(GRAMMAR $(RULEDEF synchronizedStatement): * $(LITERAL 'synchronized') ($(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)'))? $(RULE statementNoCaseNoDefault) * ;) */ SynchronizedStatement parseSynchronizedStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; expect(tok!"synchronized"); if (!moreTokens) return null; auto node = allocator.make!SynchronizedStatement; if (currentIs(tok!"(")) { expect(tok!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); expect(tok!")"); } mixin(parseNodeQ!(`node.statementNoCaseNoDefault`, `StatementNoCaseNoDefault`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateAliasParameter * * $(GRAMMAR $(RULEDEF templateAliasParameter): * $(LITERAL 'alias') $(RULE type)? $(LITERAL Identifier) ($(LITERAL ':') ($(RULE type) | $(RULE assignExpression)))? ($(LITERAL '=') ($(RULE type) | $(RULE assignExpression)))? * ;) */ TemplateAliasParameter parseTemplateAliasParameter() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateAliasParameter; expect(tok!"alias"); if (currentIs(tok!"identifier") && !peekIs(tok!".")) { if (peekIsOneOf(tok!",", tok!")", tok!"=", tok!":")) node.identifier = advance(); else goto type; } else { type: mixin(parseNodeQ!(`node.type`, `Type`)); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; } if (currentIs(tok!":")) { advance(); if (isType()) mixin(parseNodeQ!(`node.colonType`, `Type`)); else mixin(parseNodeQ!(`node.colonExpression`, `AssignExpression`)); } if (currentIs(tok!"=")) { advance(); if (isType()) mixin(parseNodeQ!(`node.assignType`, `Type`)); else mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateArgument * * $(GRAMMAR $(RULEDEF templateArgument): * $(RULE type) * | $(RULE assignExpression) * ;) */ TemplateArgument parseTemplateArgument() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto p = index in cachedTypeChecks; auto node = allocator.make!TemplateArgument; if (p !is null) { if (*p) node.type = parseType(); else mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } else { immutable b = setBookmark(); auto t = parseType(); if (t !is null && currentIsOneOf(tok!",", tok!")")) { cachedTypeChecks[startIndex] = true; abandonBookmark(b); node.type = t; } else { cachedTypeChecks[startIndex] = false; goToBookmark(b); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateArgumentList * * $(GRAMMAR $(RULEDEF templateArgumentList): * $(RULE templateArgument) ($(LITERAL ',') $(RULE templateArgument)?)* * ;) */ TemplateArgumentList parseTemplateArgumentList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(TemplateArgumentList, TemplateArgument)(true); } /** * Parses TemplateArguments * * $(GRAMMAR $(RULEDEF templateArguments): * $(LITERAL '!') ($(LITERAL '$(LPAREN)') $(RULE templateArgumentList)? $(LITERAL '$(RPAREN)')) | $(RULE templateSingleArgument) * ;) */ TemplateArguments parseTemplateArguments() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateArguments; expect(tok!"!"); if (currentIs(tok!"(")) { advance(); if (!currentIs(tok!")")) mixin(parseNodeQ!(`node.templateArgumentList`, `TemplateArgumentList`)); mixin(tokenCheck!")"); } else mixin(parseNodeQ!(`node.templateSingleArgument`, `TemplateSingleArgument`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateDeclaration * * $(GRAMMAR $(RULEDEF templateDeclaration): * $(LITERAL 'template') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * ;) */ TemplateDeclaration parseTemplateDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateDeclaration; node.comment = comment; comment = null; expect(tok!"template"); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.name = *ident; mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); if (currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); const start = expect(tok!"{"); mixin(nullCheck!`start`); node.startLocation = start.index; StackBuffer declarations; while (moreTokens() && !currentIs(tok!"}")) { immutable c = allocator.setCheckpoint(); if (!declarations.put(parseDeclaration(true, true, true))) allocator.rollback(c); } ownArray(node.declarations, declarations); const end = expect(tok!"}"); if (end !is null) { node.endLocation = end.index; attachComment(node, end.trailingComment); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateInstance * * $(GRAMMAR $(RULEDEF templateInstance): * $(LITERAL Identifier) $(RULE templateArguments) * ;) */ TemplateInstance parseTemplateInstance() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateInstance; const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; mixin(parseNodeQ!(`node.templateArguments`, `TemplateArguments`)); if (node.templateArguments is null) return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateMixinExpression * * $(GRAMMAR $(RULEDEF templateMixinExpression): * $(LITERAL 'mixin') $(RULE mixinTemplateName) $(RULE templateArguments)? $(LITERAL Identifier)? * ;) */ TemplateMixinExpression parseTemplateMixinExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateMixinExpression; mixin(tokenCheck!"mixin"); mixin(parseNodeQ!(`node.mixinTemplateName`, `MixinTemplateName`)); if (currentIs(tok!"!")) mixin(parseNodeQ!(`node.templateArguments`, `TemplateArguments`)); if (currentIs(tok!"identifier")) node.identifier = advance(); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateParameter * * $(GRAMMAR $(RULEDEF templateParameter): * $(RULE templateTypeParameter) * | $(RULE templateValueParameter) * | $(RULE templateAliasParameter) * | $(RULE templateTupleParameter) * | $(RULE templateThisParameter) * ;) */ TemplateParameter parseTemplateParameter() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) return null; auto node = allocator.make!TemplateParameter; switch (current.type) { case tok!"alias": mixin(parseNodeQ!(`node.templateAliasParameter`, `TemplateAliasParameter`)); break; case tok!"identifier": if (peekIs(tok!"...")) mixin(parseNodeQ!(`node.templateTupleParameter`, `TemplateTupleParameter`)); else if (peekIsOneOf(tok!":", tok!"=", tok!",", tok!")")) mixin(parseNodeQ!(`node.templateTypeParameter`, `TemplateTypeParameter`)); else mixin(parseNodeQ!(`node.templateValueParameter`, `TemplateValueParameter`)); break; case tok!"this": mixin(parseNodeQ!(`node.templateThisParameter`, `TemplateThisParameter`)); break; default: mixin(parseNodeQ!(`node.templateValueParameter`, `TemplateValueParameter`)); break; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateParameterList * * $(GRAMMAR $(RULEDEF templateParameterList): * $(RULE templateParameter) ($(LITERAL ',') $(RULE templateParameter)?)* $(LITERAL ',')? * ;) */ TemplateParameterList parseTemplateParameterList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(TemplateParameterList, TemplateParameter)(true); } /** * Parses TemplateParameters * * $(GRAMMAR $(RULEDEF templateParameters): * $(LITERAL '$(LPAREN)') $(RULE templateParameterList)? $(LITERAL '$(RPAREN)') * ;) */ TemplateParameters parseTemplateParameters() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateParameters; mixin(tokenCheck!"("); if (!currentIs(tok!")")) mixin(parseNodeQ!(`node.templateParameterList`, `TemplateParameterList`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateSingleArgument * * $(GRAMMAR $(RULEDEF templateSingleArgument): * $(RULE builtinType) * | $(LITERAL Identifier) * | $(LITERAL CharacterLiteral) * | $(LITERAL StringLiteral) * | $(LITERAL IntegerLiteral) * | $(LITERAL FloatLiteral) * | $(LITERAL '_true') * | $(LITERAL '_false') * | $(LITERAL '_null') * | $(LITERAL 'this') * | $(LITERAL '___DATE__') * | $(LITERAL '___FILE__') * | $(LITERAL '___FILE_FULL_PATH__') * | $(LITERAL '___FUNCTION__') * | $(LITERAL '___LINE__') * | $(LITERAL '___MODULE__') * | $(LITERAL '___PRETTY_FUNCTION__') * | $(LITERAL '___TIME__') * | $(LITERAL '___TIMESTAMP__') * | $(LITERAL '___VENDOR__') * | $(LITERAL '___VERSION__') * ;) */ TemplateSingleArgument parseTemplateSingleArgument() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateSingleArgument; if (!moreTokens) { error("template argument expected instead of EOF"); return null; } switch (current.type) { case tok!"this": case tok!"identifier": foreach (B; Literals) { case B: } foreach (C; BasicTypes) { case C: } node.token = advance(); break; default: error(`Invalid template argument. (Try enclosing in parenthesis?)`); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateThisParameter * * $(GRAMMAR $(RULEDEF templateThisParameter): * $(LITERAL 'this') $(RULE templateTypeParameter) * ;) */ TemplateThisParameter parseTemplateThisParameter() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateThisParameter; expect(tok!"this"); mixin(parseNodeQ!(`node.templateTypeParameter`, `TemplateTypeParameter`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an TemplateTupleParameter * * $(GRAMMAR $(RULEDEF templateTupleParameter): * $(LITERAL Identifier) $(LITERAL '...') * ;) */ TemplateTupleParameter parseTemplateTupleParameter() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateTupleParameter; const i = expect(tok!"identifier"); if (i is null) return null; node.identifier = *i; mixin(tokenCheck!"..."); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateTypeParameter * * $(GRAMMAR $(RULEDEF templateTypeParameter): * $(LITERAL Identifier) ($(LITERAL ':') $(RULE type))? ($(LITERAL '=') $(RULE type))? * ;) */ TemplateTypeParameter parseTemplateTypeParameter() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateTypeParameter; const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; if (currentIs(tok!":")) { advance(); mixin(parseNodeQ!(`node.colonType`, `Type`)); } if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.assignType`, `Type`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateValueParameter * * $(GRAMMAR $(RULEDEF templateValueParameter): * $(RULE type) $(LITERAL Identifier) ($(LITERAL ':') $(RULE assignExpression))? $(RULE templateValueParameterDefault)? * ;) */ TemplateValueParameter parseTemplateValueParameter() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateValueParameter; mixin(parseNodeQ!(`node.type`, `Type`)); mixin(tokenCheck!(`node.identifier`, "identifier")); if (currentIs(tok!":")) { advance(); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } if (currentIs(tok!"=")) mixin(parseNodeQ!(`node.templateValueParameterDefault`, `TemplateValueParameterDefault`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TemplateValueParameterDefault * * $(GRAMMAR $(RULEDEF templateValueParameterDefault): * $(LITERAL '=') $(LITERAL '___DATE__') * | $(LITERAL '=') $(LITERAL '___FILE__') * | $(LITERAL '=') $(LITERAL '___FILE_FULL_PATH__') * | $(LITERAL '=') $(LITERAL '___FUNCTION__') * | $(LITERAL '=') $(LITERAL '___LINE__') * | $(LITERAL '=') $(LITERAL '___MODULE__') * | $(LITERAL '=') $(LITERAL '___PRETTY_FUNCTION__') * | $(LITERAL '=') $(LITERAL '___TIME__') * | $(LITERAL '=') $(LITERAL '___TIMESTAMP__') * | $(LITERAL '=') $(LITERAL '___VENDOR__') * | $(LITERAL '=') $(LITERAL '___VERSION__') * | $(LITERAL '=') $(RULE assignExpression) * ;) */ TemplateValueParameterDefault parseTemplateValueParameterDefault() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TemplateValueParameterDefault; expect(tok!"="); switch (current.type) { case tok!"__FILE__": case tok!"__FILE_FULL_PATH__": case tok!"__MODULE__": case tok!"__LINE__": case tok!"__FUNCTION__": case tok!"__PRETTY_FUNCTION__": node.token = advance(); break; default: mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); break; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TernaryExpression * * $(GRAMMAR $(RULEDEF ternaryExpression): * $(RULE orOrExpression) ($(LITERAL '?') $(RULE expression) $(LITERAL ':') $(RULE ternaryExpression))? * ;) */ ExpressionNode parseTernaryExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto orOrExpression = parseOrOrExpression(); if (orOrExpression is null) return null; if (currentIs(tok!"?")) { TernaryExpression node = allocator.make!TernaryExpression; node.orOrExpression = orOrExpression; advance(); mixin(parseNodeQ!(`node.expression`, `Expression`)); auto colon = expect(tok!":"); mixin(nullCheck!`colon`); node.colon = *colon; mixin(parseNodeQ!(`node.ternaryExpression`, `TernaryExpression`)); node.tokens = tokens[startIndex .. index]; return node; } return orOrExpression; } /** * Parses a ThrowExpression * * $(GRAMMAR $(RULEDEF throwExpression): * $(LITERAL 'throw') $(RULE assignExpression) * ;) */ ThrowExpression parseThrowExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ThrowExpression; expect(tok!"throw"); mixin(parseNodeQ!(`node.expression`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an TraitsExpression * * $(GRAMMAR $(RULEDEF traitsExpression): * $(LITERAL '___traits') $(LITERAL '$(LPAREN)') $(LITERAL Identifier) $(LITERAL ',') $(RULE templateArgumentList) $(LITERAL '$(RPAREN)') * ;) */ TraitsExpression parseTraitsExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TraitsExpression; mixin(tokenCheck!"__traits"); mixin(tokenCheck!"("); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; if (currentIs(tok!",")) { advance(); mixin (nullCheck!`(node.templateArgumentList = parseTemplateArgumentList())`); } mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TryStatement * * $(GRAMMAR $(RULEDEF tryStatement): * $(LITERAL 'try') $(RULE declarationOrStatement) ($(RULE catches) | $(RULE catches) $(RULE finally) | $(RULE finally)) * ;) */ TryStatement parseTryStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TryStatement; expect(tok!"try"); mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); if (currentIs(tok!"catch")) mixin(parseNodeQ!(`node.catches`, `Catches`)); if (currentIs(tok!"finally")) mixin(parseNodeQ!(`node.finally_`, `Finally`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Type * * $(GRAMMAR $(RULEDEF type): * $(RULE typeConstructors)? $(RULE type2) $(RULE typeSuffix)* * ;) */ Type parseType() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Type; if (!moreTokens) { error("type expected"); return null; } switch (current.type) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": if (!peekIs(tok!"(")) mixin(parseNodeQ!(`node.typeConstructors`, `TypeConstructors`)); break; default: break; } mixin(parseNodeQ!(`node.type2`, `Type2`)); StackBuffer typeSuffixes; loop: while (moreTokens()) switch (current.type) { case tok!"[": // Allow this to fail because of the madness that is the // newExpression rule. Something starting with '[' may be arguments // to the newExpression instead of part of the type auto newBookmark = setBookmark(); auto c = allocator.setCheckpoint(); if (typeSuffixes.put(parseTypeSuffix())) abandonBookmark(newBookmark); else { allocator.rollback(c); goToBookmark(newBookmark); break loop; } break; case tok!"*": case tok!"delegate": case tok!"function": if (!typeSuffixes.put(parseTypeSuffix())) return null; break; default: break loop; } ownArray(node.typeSuffixes, typeSuffixes); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Type2 * * $(GRAMMAR $(RULEDEF type2): * $(RULE builtinType) * | $(RULE typeIdentifierPart) * | $(LITERAL 'super') $(LITERAL '.') $(RULE typeIdentifierPart) * | $(LITERAL 'this') $(LITERAL '.') $(RULE typeIdentifierPart) * | $(RULE typeofExpression) ($(LITERAL '.') $(RULE typeIdentifierPart))? * | $(RULE typeConstructor) $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL '$(RPAREN)') * | $(RULE traitsExpression) * | $(RULE vector) * | $(RULE mixinExpression) * ;) */ Type2 parseType2() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Type2; if (!moreTokens) { error("type2 expected instead of EOF"); return null; } switch (current.type) { case tok!"identifier": case tok!".": mixin(parseNodeQ!(`node.typeIdentifierPart`, `TypeIdentifierPart`)); break; foreach (B; BasicTypes) { case B: } node.builtinType = parseBuiltinType(); break; case tok!"super": case tok!"this": // note: super can be removed but `this` can be an alias to an instance. node.superOrThis = advance().type; if (currentIs(tok!".")) { advance(); mixin(parseNodeQ!(`node.typeIdentifierPart`, `TypeIdentifierPart`)); } break; case tok!"__traits": mixin(parseNodeQ!(`node.traitsExpression`, `TraitsExpression`)); break; case tok!"typeof": if ((node.typeofExpression = parseTypeofExpression()) is null) return null; if (currentIs(tok!".")) { advance(); mixin(parseNodeQ!(`node.typeIdentifierPart`, `TypeIdentifierPart`)); } break; case tok!"mixin": mixin(parseNodeQ!(`node.mixinExpression`, `MixinExpression`)); break; case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": node.typeConstructor = advance().type; mixin(tokenCheck!"("); mixin (nullCheck!`(node.type = parseType())`); mixin(tokenCheck!")"); break; case tok!"__vector": if ((node.vector = parseVector()) is null) return null; break; default: error("Basic type, type constructor, symbol, `typeof`, `__traits`, " ~ "`__vector` or `mixin` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TypeConstructor * * $(GRAMMAR $(RULEDEF typeConstructor): * $(LITERAL 'const') * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'shared') * ;) */ IdType parseTypeConstructor(bool validate = true) { mixin(traceEnterAndExit!(__FUNCTION__)); switch (current.type) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": if (!peekIs(tok!"(")) return advance().type; goto default; default: if (validate) error("`const`, `immutable`, `inout`, or `shared` expected"); return tok!""; } } /** * Parses TypeConstructors * * $(GRAMMAR $(RULEDEF typeConstructors): * $(RULE typeConstructor)+ * ;) */ IdType[] parseTypeConstructors() { mixin(traceEnterAndExit!(__FUNCTION__)); IdType[] r; while (moreTokens()) { IdType type = parseTypeConstructor(false); if (type == tok!"") break; else r ~= type; } return r; } /** * Parses a TypeSpecialization * * $(GRAMMAR $(RULEDEF typeSpecialization): * $(RULE type) * | $(LITERAL 'struct') * | $(LITERAL 'union') * | $(LITERAL 'class') * | $(LITERAL 'interface') * | $(LITERAL 'enum') * | $(LITERAL '__vector') * | $(LITERAL 'function') * | $(LITERAL 'delegate') * | $(LITERAL 'super') * | $(LITERAL 'const') * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'shared') * | $(LITERAL 'return') * | $(LITERAL '__parameters') * | $(LITERAL 'module') * | $(LITERAL 'package') * ;) */ TypeSpecialization parseTypeSpecialization() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TypeSpecialization; switch (current.type) { case tok!"struct": case tok!"union": case tok!"class": case tok!"interface": case tok!"enum": case tok!"__vector": case tok!"function": case tok!"delegate": case tok!"super": case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"return": case tok!"__parameters": case tok!"module": case tok!"package": if (peekIsOneOf(tok!")", tok!",")) { node.token = advance(); break; } goto default; default: mixin(parseNodeQ!(`node.type`, `Type`)); break; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TypeSuffix * * $(GRAMMAR $(RULEDEF typeSuffix): * $(LITERAL '*') * | $(LITERAL '[') $(RULE type)? $(LITERAL ']') * | $(LITERAL '[') $(RULE assignExpression) $(LITERAL ']') * | $(LITERAL '[') $(RULE assignExpression) $(LITERAL '..') $(RULE assignExpression) $(LITERAL ']') * | ($(LITERAL 'delegate') | $(LITERAL 'function')) $(RULE parameters) $(RULE memberFunctionAttribute)* * ;) */ TypeSuffix parseTypeSuffix() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TypeSuffix; switch (current.type) { case tok!"*": node.star = advance(); node.tokens = tokens[startIndex .. index]; return node; case tok!"[": node.array = true; advance(); if (currentIs(tok!"]")) { advance(); node.tokens = tokens[startIndex .. index]; return node; } auto bookmark = setBookmark(); auto type = parseType(); if (type !is null && currentIs(tok!"]")) { abandonBookmark(bookmark); node.type = type; } else { goToBookmark(bookmark); mixin(parseNodeQ!(`node.low`, `AssignExpression`)); mixin (nullCheck!`node.low`); if (currentIs(tok!"..")) { advance(); mixin(parseNodeQ!(`node.high`, `AssignExpression`)); mixin (nullCheck!`node.high`); } } mixin(tokenCheck!"]"); node.tokens = tokens[startIndex .. index]; return node; case tok!"delegate": case tok!"function": node.delegateOrFunction = advance(); mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); node.tokens = tokens[startIndex .. index]; return node; default: error("`*`, `[`, `delegate`, or `function` expected."); return null; } } /** * Parses a TypeidExpression * * $(GRAMMAR $(RULEDEF typeidExpression): * $(LITERAL 'typeid') $(LITERAL '$(LPAREN)') ($(RULE type) | $(RULE expression)) $(LITERAL '$(RPAREN)') * ;) */ TypeidExpression parseTypeidExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TypeidExpression; expect(tok!"typeid"); expect(tok!"("); immutable b = setBookmark(); auto t = parseType(); if (t is null || !currentIs(tok!")")) { goToBookmark(b); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin (nullCheck!`node.expression`); } else { abandonBookmark(b); node.type = t; } expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TypeofExpression * * $(GRAMMAR $(RULEDEF typeofExpression): * $(LITERAL 'typeof') $(LITERAL '$(LPAREN)') ($(RULE expression) | $(LITERAL 'return')) $(LITERAL '$(RPAREN)') * ;) */ TypeofExpression parseTypeofExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!TypeofExpression; expect(tok!"typeof"); expect(tok!"("); if (currentIs(tok!"return")) node.return_ = advance(); else mixin(parseNodeQ!(`node.expression`, `Expression`)); expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a UnaryExpression * * $(GRAMMAR $(RULEDEF unaryExpression): * $(RULE primaryExpression) * | $(LITERAL '&') $(RULE unaryExpression) * | $(LITERAL '!') $(RULE unaryExpression) * | $(LITERAL '*') $(RULE unaryExpression) * | $(LITERAL '+') $(RULE unaryExpression) * | $(LITERAL '-') $(RULE unaryExpression) * | $(LITERAL '~') $(RULE unaryExpression) * | $(LITERAL '++') $(RULE unaryExpression) * | $(LITERAL '--') $(RULE unaryExpression) * | $(RULE newExpression) * | $(RULE deleteExpression) * | $(RULE castExpression) * | $(RULE assertExpression) * | $(RULE throwExpression) * | $(RULE functionCallExpression) * | $(RULE indexExpression) * | $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL '$(RPAREN)') $(LITERAL '.') $(RULE identifierOrTemplateInstance) * | $(RULE unaryExpression) $(LITERAL '.') $(RULE newExpression) * | $(RULE unaryExpression) $(LITERAL '.') $(RULE identifierOrTemplateInstance) * | $(RULE unaryExpression) $(LITERAL '--') * | $(RULE unaryExpression) $(LITERAL '++') * ;) */ UnaryExpression parseUnaryExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens()) return null; auto node = allocator.make!UnaryExpression; switch (current.type) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": immutable b = setBookmark(); if (peekIs(tok!"(")) { advance(); const past = peekPastParens(); if (past !is null && past.type == tok!".") { goToBookmark(b); goto default; } } goToBookmark(b); goto case; case tok!"scope": case tok!"pure": case tok!"nothrow": mixin(parseNodeQ!(`node.functionCallExpression`, `FunctionCallExpression`)); break; case tok!"&": case tok!"!": case tok!"*": case tok!"+": case tok!"-": case tok!"~": case tok!"++": case tok!"--": node.prefix = advance(); mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); break; case tok!"new": mixin(parseNodeQ!(`node.newExpression`, `NewExpression`)); break; case tok!"delete": mixin(parseNodeQ!(`node.deleteExpression`, `DeleteExpression`)); break; case tok!"cast": mixin(parseNodeQ!(`node.castExpression`, `CastExpression`)); break; case tok!"assert": mixin(parseNodeQ!(`node.assertExpression`, `AssertExpression`)); break; case tok!"throw": mixin(parseNodeQ!(`node.throwExpression`, `ThrowExpression`)); break; case tok!"(": // handle (type).identifier immutable b = setBookmark(); skipParens(); if (startsWith(tok!".", tok!"identifier")) { // go back to the ( goToBookmark(b); immutable b2 = setBookmark(); advance(); auto t = parseType(); if (t is null || !currentIs(tok!")")) { goToBookmark(b); goto default; } abandonBookmark(b2); node.type = t; advance(); // ) advance(); // . mixin(parseNodeQ!(`node.identifierOrTemplateInstance`, `IdentifierOrTemplateInstance`)); break; } else { // not (type).identifier, so treat as primary expression goToBookmark(b); goto default; } default: mixin(parseNodeQ!(`node.primaryExpression`, `PrimaryExpression`)); break; } loop: while (moreTokens()) switch (current.type) { case tok!"!": if (peekIs(tok!"(")) { index++; const p = peekPastParens(); immutable bool jump = (currentIs(tok!"(") && p !is null && p.type == tok!"(") || peekIs(tok!"("); index--; if (jump) goto case tok!"("; else break loop; } else break loop; case tok!"(": auto newUnary = allocator.make!UnaryExpression(); // Allows DCD to get the call tips // see https://github.com/dlang-community/DCD/issues/405 if (peekIs(tok!"}")) { error("Error, expected parameters or `)`", false); advance(); if (newUnary) newUnary.tokens = tokens[startIndex .. index]; return newUnary; } mixin (nullCheck!`newUnary.functionCallExpression = parseFunctionCallExpression(node)`); node = newUnary; break; case tok!"++": case tok!"--": auto n = allocator.make!UnaryExpression(); n.unaryExpression = node; n.suffix = advance(); node = n; break; case tok!"[": auto n = allocator.make!UnaryExpression; n.indexExpression = parseIndexExpression(node); node = n; break; case tok!".": node.dotLocation = current.index; advance(); auto n = allocator.make!UnaryExpression(); n.unaryExpression = node; if (currentIs(tok!"new")) mixin(parseNodeQ!(`node.newExpression`, `NewExpression`)); else n.identifierOrTemplateInstance = parseIdentifierOrTemplateInstance(); node = n; break; default: break loop; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an UnionDeclaration * * $(GRAMMAR $(RULEDEF unionDeclaration): * $(LITERAL 'union') $(LITERAL Identifier) ($(RULE templateParameters) $(RULE constraint)?)? ($(RULE structBody) | $(LITERAL ';')) * | $(LITERAL 'union') $(RULE structBody) * ;) */ UnionDeclaration parseUnionDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!UnionDeclaration; node.comment = comment; comment = null; // grab line number even if it's anonymous const t = expect(tok!"union"); if (currentIs(tok!"identifier")) { node.name = advance(); if (currentIs(tok!"(")) { mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); if (currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); } goto semiOrStructBody; } else { node.name.line = t.line; node.name.column = t.column; semiOrStructBody: if (currentIs(tok!";")) { advance(); } else { mixin(parseNodeQ!(`node.structBody`, `StructBody`)); if (node.structBody.tokens.length) attachComment(node, node.structBody.tokens[$ - 1].trailingComment); } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Unittest * * $(GRAMMAR $(RULEDEF unittest): * $(LITERAL 'unittest') $(RULE blockStatement) * ;) */ Unittest parseUnittest() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin (simpleParse!(Unittest, tok!"unittest", "blockStatement|parseBlockStatement")); } /** * Parses a VariableDeclaration * * $(GRAMMAR $(RULEDEF variableDeclaration): * $(RULE storageClass)* $(RULE _type) $(RULE declarator) ($(LITERAL ',') $(RULE declarator))* $(LITERAL ';') * | $(RULE autoDeclaration) * ;) */ VariableDeclaration parseVariableDeclaration(Type type = null, bool isAuto = false) { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!VariableDeclaration; if (isAuto) { mixin(parseNodeQ!(`node.autoDeclaration`, `AutoDeclaration`)); node.comment = node.autoDeclaration.comment; node.tokens = tokens[startIndex .. index]; return node; } StackBuffer storageClasses; while (isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); node.type = type is null ? parseType() : type; node.comment = comment; comment = null; StackBuffer declarators; Declarator last; while (true) { auto declarator = parseDeclarator(); if (!declarators.put(declarator)) return null; else last = declarator; if (moreTokens() && currentIs(tok!",")) { if (node.comment !is null) declarator.comment = node.comment ~ "\n" ~ current.trailingComment; else declarator.comment = current.trailingComment; advance(); } else break; } ownArray(node.declarators, declarators); const semicolon = expect(tok!";"); mixin (nullCheck!`semicolon`); if (node.comment !is null) { if (semicolon.trailingComment is null) last.comment = node.comment; else last.comment = node.comment ~ "\n" ~ semicolon.trailingComment; } else last.comment = semicolon.trailingComment; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Vector * * $(GRAMMAR $(RULEDEF vector): * $(LITERAL '___vector') ($(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL '$(RPAREN)'))? * ;) */ Vector parseVector() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!currentIs(tok!"__vector")) return null; Vector node = allocator.make!Vector; advance(); if (currentIs(tok!"(")) { advance(); mixin(parseNodeQ!("node.type", "Type")); if (!currentIs(tok!")")) return null; advance(); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a VersionCondition * * $(GRAMMAR $(RULEDEF versionCondition): * $(LITERAL 'version') $(LITERAL '$(LPAREN)') ($(LITERAL IntegerLiteral) | $(LITERAL Identifier) | $(LITERAL 'unittest') | $(LITERAL 'assert')) $(LITERAL '$(RPAREN)') * ;) */ VersionCondition parseVersionCondition() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!VersionCondition; const v = expect(tok!"version"); mixin(nullCheck!`v`); node.versionIndex = v.index; mixin(tokenCheck!"("); if (currentIsOneOf(tok!"intLiteral", tok!"identifier", tok!"unittest", tok!"assert")) node.token = advance(); else { error("Expected an integer literal, an identifier, `assert`, or `unittest`"); return null; } expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a VersionSpecification * * $(GRAMMAR $(RULEDEF versionSpecification): * $(LITERAL 'version') $(LITERAL '=') ($(LITERAL Identifier) | $(LITERAL IntegerLiteral)) $(LITERAL ';') * ;) */ VersionSpecification parseVersionSpecification() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!VersionSpecification; mixin(tokenCheck!"version"); mixin(tokenCheck!"="); if (!currentIsOneOf(tok!"identifier", tok!"intLiteral")) { error("Identifier or integer literal expected"); return null; } node.token = advance(); expect(tok!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a WhileStatement * * $(GRAMMAR $(RULEDEF whileStatement): * $(LITERAL 'while') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ WhileStatement parseWhileStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!WhileStatement; mixin(tokenCheck!"while"); if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; // this line makes DCD better } mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a WithStatement * * $(GRAMMAR $(RULEDEF withStatement): * $(LITERAL 'with') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ WithStatement parseWithStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!WithStatement; mixin(tokenCheck!"with"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; // this line makes DCD better } mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an XorExpression * * $(GRAMMAR $(RULEDEF xorExpression): * $(RULE andExpression) * | $(RULE xorExpression) $(LITERAL '^') $(RULE andExpression) * ;) */ ExpressionNode parseXorExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(XorExpression, AndExpression, tok!"^")(); } /** * Current error count */ uint errorCount; /** * Current warning count */ uint warningCount; /** * Name used when reporting warnings and errors */ string fileName; /** * Tokens to parse */ const(Token)[] tokens; /** * Allocator used for creating AST nodes */ RollbackAllocator* allocator; /** * Function or delegate that is called when a warning or error is encountered. * The parameters are the file name, line number, column number, * and the error or warning message. */ MessageFunction messageFunction; /// Ditto MessageDelegate messageDelegate; deprecated("Use messageDelegate instead") alias messageDg = messageDelegate; void setTokens(const(Token)[] tokens) { this.tokens = tokens; } /** * Returns: true if there are more tokens */ bool moreTokens() const nothrow pure @safe @nogc { return index < tokens.length; } protected: final: enum MAX_ERRORS = 500; void ownArray(T)(ref T[] arr, ref StackBuffer sb) { if (sb.length == 0) return; void[] a = allocator.allocate(sb.length); a[] = sb[]; arr = cast(T[]) a; } bool isCastQualifier() const { if (!moreTokens()) return false; switch (current.type) { case tok!"const": if (peekIs(tok!")")) return true; return startsWith(tok!"const", tok!"shared", tok!")"); case tok!"immutable": return peekIs(tok!")"); case tok!"inout": if (peekIs(tok!")")) return true; return startsWith(tok!"inout", tok!"shared", tok!")"); case tok!"shared": if (peekIs(tok!")")) return true; if (startsWith(tok!"shared", tok!"const", tok!")")) return true; return startsWith(tok!"shared", tok!"inout", tok!")"); default: return false; } } bool isAssociativeArrayLiteral() { mixin(traceEnterAndExit!(__FUNCTION__)); if (!moreTokens()) return false; if (auto p = current.index in cachedAAChecks) return *p; size_t currentIndex = current.index; immutable b = setBookmark(); scope(exit) goToBookmark(b); advance(); immutable bool result = !currentIs(tok!"]") && parseExpression() !is null && currentIs(tok!":"); cachedAAChecks[currentIndex] = result; return result; } bool hasMagicDelimiter(alias L, alias T)() { mixin(traceEnterAndExit!(__FUNCTION__)); immutable i = index; scope(exit) index = i; assert (currentIs(L)); advance(); while (moreTokens()) switch (current.type) { case tok!"{": skipBraces(); break; case tok!"(": skipParens(); break; case tok!"[": skipBrackets(); break; case tok!"]": case tok!"}": return false; case T: return true; default: advance(); break; } return false; } enum DecType { autoVar, autoFun, other } DecType isAutoDeclaration(ref size_t beginIndex) nothrow @safe { immutable b = setBookmark(); scope(exit) goToBookmark(b); loop: while (moreTokens()) switch (current().type) { case tok!"pragma": beginIndex = size_t.max; advance(); if (currentIs(tok!"(")) { skipParens(); break; } else return DecType.other; case tok!"package": case tok!"private": case tok!"protected": case tok!"public": case tok!"export": beginIndex = size_t.max; advance(); break; case tok!"@": beginIndex = min(beginIndex, index); advance(); if (currentIs(tok!"(")) skipParens(); else if (currentIs(tok!"identifier")) { advance(); if (currentIs(tok!"!")) { advance(); if (currentIs(tok!"(")) skipParens(); else if (moreTokens()) advance(); } if (currentIs(tok!"(")) skipParens(); } else return DecType.other; break; case tok!"deprecated": case tok!"align": case tok!"extern": beginIndex = min(beginIndex, index); advance(); if (currentIs(tok!"(")) skipParens(); break; case tok!"const": case tok!"immutable": case tok!"inout": case tok!"synchronized": if (peekIs(tok!"(")) return DecType.other; else { beginIndex = min(beginIndex, index); advance(); break; } case tok!"auto": case tok!"enum": case tok!"final": case tok!"__gshared": case tok!"nothrow": case tok!"override": case tok!"pure": case tok!"ref": case tok!"scope": case tok!"shared": case tok!"static": beginIndex = min(beginIndex, index); advance(); break; default: break loop; } if (index <= b) return DecType.other; if (startsWith(tok!"identifier", tok!"=")) return DecType.autoVar; if (startsWith(tok!"identifier", tok!"(")) { advance(); auto past = peekPastParens(); if (past is null) return DecType.other; else if (past.type == tok!"=") return DecType.autoVar; else return DecType.autoFun; } return DecType.other; } bool isDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); if (!moreTokens()) return false; switch (current.type) { case tok!("__traits"): if (peekIs(tok!"(")) { immutable b = setBookmark(); advance(); const ppp = peekPastParens(); goToBookmark(b); return ppp && ppp.type == tok!"identifier"; } goto default; case tok!("mixin"): return peekIs(tok!"("); case tok!"final": return !peekIs(tok!"switch"); case tok!"debug": if (peekIs(tok!":")) return true; goto case; case tok!"version": if (peekIs(tok!"=")) return true; if (peekIs(tok!"(")) goto default; return false; case tok!"synchronized": if (peekIs(tok!"(")) return false; else goto default; case tok!"static": if (peekIs(tok!"if")) return false; else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse")) goto default; goto case; case tok!"scope": if (peekIs(tok!"(")) return false; goto case; case tok!"@": case tok!"abstract": case tok!"alias": case tok!"align": case tok!"auto": case tok!"class": case tok!"deprecated": case tok!"enum": case tok!"export": case tok!"extern": case tok!"__gshared": case tok!"interface": case tok!"nothrow": case tok!"override": case tok!"package": case tok!"private": case tok!"protected": case tok!"public": case tok!"pure": case tok!"ref": case tok!"struct": case tok!"union": case tok!"unittest": return true; foreach (B; BasicTypes) { case B: } return !peekIsOneOf(tok!".", tok!"("); case tok!"asm": case tok!"break": case tok!"case": case tok!"continue": case tok!"default": case tok!"do": case tok!"for": case tok!"foreach": case tok!"foreach_reverse": case tok!"goto": case tok!"if": case tok!"return": case tok!"switch": case tok!"throw": case tok!"try": case tok!"while": case tok!"{": case tok!"assert": return false; default: immutable b = setBookmark(); scope(exit) goToBookmark(b); auto c = allocator.setCheckpoint(); scope(exit) allocator.rollback(c); return parseDeclaration(true, true) !is null; } } /// Only use this in template parameter parsing bool isType() { if (!moreTokens()) return false; immutable b = setBookmark(); scope (exit) goToBookmark(b); auto c = allocator.setCheckpoint(); scope (exit) allocator.rollback(c); if (parseType() is null) return false; return currentIsOneOf(tok!",", tok!")", tok!"="); } bool isStorageClass() { if (!moreTokens()) return false; switch (current.type) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": return !peekIs(tok!"("); case tok!"@": case tok!"deprecated": case tok!"abstract": case tok!"align": case tok!"auto": case tok!"enum": case tok!"extern": case tok!"final": case tok!"nothrow": case tok!"override": case tok!"pure": case tok!"ref": case tok!"__gshared": case tok!"scope": case tok!"static": case tok!"synchronized": return true; default: return false; } } bool isAttribute() { if (!moreTokens()) return false; switch (current.type) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"scope": return !peekIs(tok!"("); case tok!"static": return !peekIsOneOf(tok!"assert", tok!"this", tok!"if", tok!"~", tok!"foreach", tok!"foreach_reverse"); case tok!"shared": return !(startsWith(tok!"shared", tok!"static", tok!"this") || startsWith(tok!"shared", tok!"static", tok!"~") || peekIs(tok!"(")); case tok!"pragma": immutable b = setBookmark(); scope(exit) goToBookmark(b); advance(); const past = peekPastParens(); if (past is null || *past == tok!";") return false; return true; case tok!"deprecated": case tok!"private": case tok!"package": case tok!"protected": case tok!"public": case tok!"export": case tok!"final": case tok!"synchronized": case tok!"override": case tok!"abstract": case tok!"auto": case tok!"__gshared": case tok!"pure": case tok!"nothrow": case tok!"@": case tok!"ref": case tok!"extern": case tok!"align": case tok!"throw": return true; default: return false; } } static bool isMemberFunctionAttribute(IdType t) pure nothrow @nogc @safe { switch (t) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"@": case tok!"pure": case tok!"nothrow": case tok!"return": case tok!"scope": case tok!"throw": return true; default: return false; } } static bool isTypeCtor(IdType t) pure nothrow @nogc @safe { switch (t) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": return true; default: return false; } } bool currentIsMemberFunctionAttribute() const { return moreTokens && isMemberFunctionAttribute(current.type); } ExpressionNode parseLeftAssocBinaryExpression(alias ExpressionType, alias ExpressionPartType, Operators ...)(ExpressionNode part = null) { ExpressionNode node; auto startIndex = index; mixin ("node = part is null ? parse" ~ ExpressionPartType.stringof ~ "() : part;"); if (node is null) return null; while (currentIsOneOf(Operators)) { auto n = allocator.make!ExpressionType; n.line = current.line; n.column = current.column; static if (__traits(hasMember, ExpressionType, "operator")) n.operator = advance().type; else advance(); n.left = node; mixin (parseNodeQ!(`n.right`, ExpressionPartType.stringof)); node = n; } node.tokens = tokens[startIndex .. index]; return node; } ListType parseCommaSeparatedRule(alias ListType, alias ItemType, bool setLineAndColumn = false)(bool allowTrailingComma = false) { auto startIndex = index; auto node = allocator.make!ListType; static if (setLineAndColumn) { node.line = current.line; node.column = current.column; } StackBuffer items; while (moreTokens()) { if (!items.put(mixin("parse" ~ ItemType.stringof ~ "()"))) return null; if (currentIs(tok!",")) { advance(); if (allowTrailingComma && currentIsOneOf(tok!")", tok!"}", tok!"]")) break; else continue; } else break; } ownArray(node.items, items); node.tokens = tokens[startIndex .. index]; return node; } void warn(lazy string message) { import std.stdio : stderr; if (!suppressMessages.empty) return; ++warningCount; auto column = index < tokens.length ? tokens[index].column : 0; auto line = index < tokens.length ? tokens[index].line : 0; if (messageDelegate !is null) messageDelegate(fileName, line, column, message, false); else if (messageFunction !is null) messageFunction(fileName, line, column, message, false); else stderr.writefln("%s(%d:%d)[warn]: %s", fileName, line, column, message); } void error(string message, bool shouldAdvance = true) { import std.stdio : stderr; if (suppressMessages.empty) { ++errorCount; auto column = index < tokens.length ? tokens[index].column : tokens[$ - 1].column; auto line = index < tokens.length ? tokens[index].line : tokens[$ - 1].line; if (messageDelegate !is null) messageDelegate(fileName, line, column, message, true); else if (messageFunction !is null) messageFunction(fileName, line, column, message, true); else stderr.writefln("%s(%d:%d)[error]: %s", fileName, line, column, message); } else ++suppressMessages[$ - 1]; while (shouldAdvance && moreTokens()) { if (currentIsOneOf(tok!";", tok!"}", tok!")", tok!"]")) { advance(); break; } else advance(); } } /// Skips token if present and returns whether token was skipped bool skip(IdType token) { const found = currentIs(token); if (found) advance(); return found; } void skip(alias O, alias C)() { assert (currentIs(O), current().text); advance(); int depth = 1; while (moreTokens()) { switch (tokens[index].type) { case C: advance(); depth--; if (depth <= 0) return; break; case O: depth++; advance(); break; default: advance(); break; } } } void skipBraces() pure nothrow @safe @nogc { skip!(tok!"{", tok!"}")(); } void skipParens() pure nothrow @safe @nogc { skip!(tok!"(", tok!")")(); } void skipBrackets() pure nothrow @safe @nogc { skip!(tok!"[", tok!"]")(); } /** * Returns: a pointer to the token after the current one, or `null` if * there is none. */ const(Token)* peek() const pure nothrow @safe @nogc { return index + 1 < tokens.length ? &tokens[index + 1] : null; } const(Token)* peekPast(alias O, alias C)() const pure nothrow @safe @nogc { if (index >= tokens.length) return null; int depth = 1; size_t i = index; ++i; while (i < tokens.length) { if (tokens[i] == O) { ++depth; ++i; } else if (tokens[i] == C) { --depth; ++i; if (depth <= 0) break; } else ++i; } return i >= tokens.length ? null : depth == 0 ? &tokens[i] : null; } /** * Returns: a pointer to the token after a set of balanced parenthesis, or * `null` in the case that the current token is not an opening parenthesis * or an end of file is reached before a closing parenthesis is found, or * the closing parenthesis is the last token. */ const(Token)* peekPastParens() const pure nothrow @safe @nogc { return peekPast!(tok!"(", tok!")")(); } /** * See_also: peekPastParens */ const(Token)* peekPastBrackets() const pure nothrow @safe @nogc { return peekPast!(tok!"[", tok!"]")(); } /** * See_also: peekPastParens */ const(Token)* peekPastBraces() const pure nothrow @safe @nogc { return peekPast!(tok!"{", tok!"}")(); } /** * Returns: `true` if there is a next token and that token has the type `t`. */ bool peekIs(IdType t) const pure nothrow @safe @nogc { return peekNIs(t, 1); } /** * Returns: `true` if the token `offset` tokens ahead exists and is of type * `t`. */ bool peekNIs(IdType t, size_t offset) const pure nothrow @safe @nogc { return index + offset < tokens.length && tokens[index + offset].type == t; } /** * Returns: `true` if there are at least `types.length` tokens following the * current one and they have types matching the corresponding elements of * `types`. */ bool peekAre(IdType[] types...) const pure nothrow @safe @nogc { foreach (i, t; types) if (!peekNIs(t, i + 1)) return false; return true; } /** * Returns: `true` if there is a next token and its type is one of the given * `types`. */ bool peekIsOneOf(IdType[] types...) const pure nothrow @safe @nogc { return peekNIsOneOf(1, types); } /** * Returns: `true` if there is a token `offset` tokens after the current one * and its type is one of the given `types`. */ bool peekNIsOneOf(size_t offset, IdType[] types...) const pure nothrow @safe @nogc { if (index + offset >= tokens.length) return false; return canFind(types, tokens[index + offset].type); } /** * Returns: a pointer to a token of the specified type if it was the next * token, otherwise calls the error function and returns null. * * Advances the lexer by one token in the case that the token was the one * that was expected. Otherwise, only advances if the current token is not a * semicolon, right parenthesis, or closing curly brace. */ const(Token)* expect(IdType type) { if (index < tokens.length && tokens[index].type == type) return &tokens[index++]; else { string tokenString = str(type) is null ? to!string(type) : str(type); immutable bool shouldNotAdvance = index < tokens.length && (tokens[index].type == tok!")" || tokens[index].type == tok!";" || tokens[index].type == tok!"}"); auto token = (index < tokens.length ? (tokens[index].text is null ? str(tokens[index].type) : tokens[index].text) : "EOF"); error("Expected `" ~ tokenString ~ "` instead of `" ~ token ~ "`", !shouldNotAdvance); return null; } } /** * Returns: the _current token */ Token current() const pure nothrow @safe @nogc @property { return tokens[index]; } /** * Returns: the _previous token */ Token previous() const pure nothrow @safe @nogc @property { return tokens[index - 1]; } /** * Advances to the next token and returns the current token */ Token advance() pure nothrow @nogc @safe { return tokens[index++]; } /** * Returns: true if the current token has the given type */ bool currentIs(IdType type) const pure nothrow @safe @nogc { return index < tokens.length && tokens[index] == type; } /** * Returns: true if the current token is one of the given types */ bool currentIsOneOf(IdType[] types...) const pure nothrow @safe @nogc { if (index >= tokens.length) return false; return canFind(types, current.type); } /** * Returns: `true` if there are at least `types.length` tokens starting at * the current token, and the tokens have types corresponding to the * elements of `types`. */ bool startsWith(IdType[] types...) const pure nothrow @safe @nogc { if (index + types.length >= tokens.length) return false; for (size_t i = 0; (i < types.length) && ((index + i) < tokens.length); ++i) { if (tokens[index + i].type != types[i]) return false; } return true; } alias Bookmark = size_t; Bookmark setBookmark() pure nothrow @safe { // mixin(traceEnterAndExit!(__FUNCTION__)); suppressMessages ~= suppressedErrorCount(); return index; } void abandonBookmark(Bookmark) pure nothrow @safe @nogc { suppressMessages.popBack(); } /// Goes back to a parser bookmark and optionally invalidates it. /// Params: /// bookmark = bookmark to revert to /// popStack = if set to true, the bookmark is cleared, otherwise it must /// be manually cleared using $(LREF abandonBookmark). void goToBookmark(Bookmark bookmark, bool popStack = true) pure nothrow @safe @nogc { if (popStack) suppressMessages.popBack(); index = bookmark; } template simpleParse(NodeType, parts ...) { static if (__traits(hasMember, NodeType, "comment")) enum nodeComm = "node.comment = comment;\n" ~ "comment = null;\n"; else enum nodeComm = ""; static if (__traits(hasMember, NodeType, "line")) enum nodeLine = "node.line = current().line;\n"; else enum nodeLine = ""; static if (__traits(hasMember, NodeType, "column")) enum nodeColumn = "node.column = current().column;\n"; else enum nodeColumn = ""; static if (__traits(hasMember, NodeType, "location")) enum nodeLoc = "node.location = current().index;\n"; else enum nodeLoc = ""; enum simpleParse = "auto startIndex = index;\nauto node = allocator.make!" ~ NodeType.stringof ~ ";\n" ~ nodeComm ~ nodeLine ~ nodeColumn ~ nodeLoc ~ simpleParseItems!(parts) ~ "\nnode.tokens = tokens[startIndex .. index];\nreturn node;\n"; } template simpleParseItems(items ...) { static if (items.length > 1) enum simpleParseItems = simpleParseItem!(items[0]) ~ "\n" ~ simpleParseItems!(items[1 .. $]); else static if (items.length == 1) enum simpleParseItems = simpleParseItem!(items[0]); else enum simpleParseItems = ""; } template simpleParseItem(alias item) { static if (is (typeof(item) == string)) enum simpleParseItem = "if ((node." ~ item[0 .. item.countUntil("|")] ~ " = " ~ item[item.countUntil("|") + 1 .. $] ~ "()) is null) { return null; }"; else enum simpleParseItem = "if (expect(" ~ item.stringof ~ ") is null) { return null; }"; } template traceEnterAndExit(string fun) { enum traceEnterAndExit = `version (dparse_verbose) { _traceDepth++; trace("` ~ `\033[01;32m` ~ fun ~ `\033[0m"); }` ~ `version (dparse_verbose) scope(exit) { trace("` ~ `\033[01;31m` ~ fun ~ `\033[0m"); _traceDepth--; }`; } version (dparse_verbose) { import std.stdio : stderr; void trace(string message) { if (!suppressMessages.empty) return; auto depth = format("%4d ", _traceDepth); if (index < tokens.length) stderr.writeln(depth, message, "(", current.line, ":", current.column, ")"); else stderr.writeln(depth, message, "(EOF:0)"); } } else { void trace(lazy string) {} } template parseNodeQ(string VarName, string NodeName) { enum parseNodeQ = `{ if ((` ~ VarName ~ ` = parse` ~ NodeName ~ `()) is null) return null; }`; } template nullCheck(string exp) { enum nullCheck = "{if ((" ~ exp ~ ") is null) { return null; }}"; } template tokenCheck(string Tok) { enum tokenCheck = `{ if (expect(tok!"` ~ Tok ~ `") is null) { return null; } }`; } template tokenCheck(string Exp, string Tok) { enum tokenCheck = `{auto t = expect(tok!"` ~ Tok ~ `");` ~ `if (t is null) { return null;}` ~ `else {` ~ Exp ~ ` = *t; }}`; } void attachComment(T)(T node, string comment) { if (comment !is null) { if (node.comment is null) node.comment = comment; else { node.comment ~= "\n"; node.comment ~= comment; } } } T attachCommentFromSemicolon(T)(T node, size_t startIndex) { auto semicolon = expect(tok!";"); if (semicolon is null) return null; attachComment(node, semicolon.trailingComment); if (node) node.tokens = tokens[startIndex .. index]; return node; } // This list MUST BE MAINTAINED IN SORTED ORDER. static immutable string[] REGISTER_NAMES = [ "AH", "AL", "AX", "BH", "BL", "BP", "BPL", "BX", "CH", "CL", "CR0", "CR2", "CR3", "CR4", "CS", "CX", "DH", "DI", "DIL", "DL", "DR0", "DR1", "DR2", "DR3", "DR6", "DR7", "DS", "DX", "EAX", "EBP", "EBX", "ECX", "EDI", "EDX", "ES", "ESI", "ESP", "FS", "GS", "MM0", "MM1", "MM2", "MM3", "MM4", "MM5", "MM6", "MM7", "R10", "R10B", "R10D", "R10W", "R11", "R11B", "R11D", "R11W", "R12", "R12B", "R12D", "R12W", "R13", "R13B", "R13D", "R13W", "R14", "R14B", "R14D", "R14W", "R15", "R15B", "R15D", "R15W", "R8", "R8B", "R8D", "R8W", "R9", "R9B", "R9D", "R9W", "RAX", "RBP", "RBX", "RCX", "RDI", "RDX", "RSI", "RSP", "SI", "SIL", "SP", "SPL", "SS", "ST", "TR3", "TR4", "TR5", "TR6", "TR7", "XMM0", "XMM1", "XMM10", "XMM11", "XMM12", "XMM13", "XMM14", "XMM15", "XMM2", "XMM3", "XMM4", "XMM5", "XMM6", "XMM7", "XMM8", "XMM9", "YMM0", "YMM1", "YMM10", "YMM11", "YMM12", "YMM13", "YMM14", "YMM15", "YMM2", "YMM3", "YMM4", "YMM5", "YMM6", "YMM7", "YMM8", "YMM9" ]; N parseStaticCtorDtorCommon(N)(N node, size_t startIndex) { node.line = current.line; node.column = current.column; mixin(tokenCheck!"this"); mixin(tokenCheck!"("); mixin(tokenCheck!")"); StackBuffer attributes; while (moreTokens() && !currentIsOneOf(tok!"{", tok!"in", tok!"out", tok!"do", tok!";") && current.text != "body") if (!attributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, attributes); if (currentIs(tok!";")) advance(); else mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); if (node) node.tokens = tokens[startIndex .. index]; return node; } N parseInterfaceOrClass(N)(N node, size_t startIndex) { auto ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.name = *ident; node.comment = comment; comment = null; if (currentIs(tok!";")) goto emptyBody; if (currentIs(tok!"{")) goto structBody; if (currentIs(tok!"(")) { mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); if (currentIs(tok!";")) goto emptyBody; constraint: if (currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); if (node.baseClassList !is null) { if (currentIs(tok!"{")) goto structBody; else if (currentIs(tok!";")) goto emptyBody; else { error("Struct body or `;` expected"); return null; } } if (currentIs(tok!":")) goto baseClassList; if (currentIs(tok!"if")) goto constraint; if (currentIs(tok!";")) goto emptyBody; } if (currentIs(tok!":")) { baseClassList: advance(); // : if ((node.baseClassList = parseBaseClassList()) is null) return null; if (currentIs(tok!"if")) goto constraint; } structBody: mixin(parseNodeQ!(`node.structBody`, `StructBody`)); if (node.structBody.tokens.length) attachComment(node, node.structBody.tokens[$ - 1].trailingComment); node.tokens = tokens[startIndex .. index]; return node; emptyBody: advance(); node.tokens = tokens[startIndex .. index]; return node; } uint suppressedErrorCount() const pure nothrow @nogc @safe { return suppressMessages.empty ? 0 : suppressMessages.back(); } uint[] suppressMessages; size_t index; int _traceDepth; string comment; bool[typeof(Token.index)] cachedAAChecks; bool[typeof(Token.index)] cachedTypeChecks; }