• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tbklang / tlang / #973

03 Jan 2025 09:39AM UTC coverage: 79.97% (-0.4%) from 80.342%
#973

push

coveralls-ruby

deavmi
Dep-gen

- Added missing import

4839 of 6051 relevant lines covered (79.97%)

368.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.99
/source/tlang/compiler/parsing/core.d
1
module tlang.compiler.parsing.core;
2

3
import tlang.misc.logging;
4
import std.conv : to, ConvException;
5
import std.string : isNumeric, cmp;
6
import tlang.compiler.symbols.check;
7
import tlang.compiler.symbols.data;
8
import tlang.compiler.lexer.core;
9
import core.stdc.stdlib;
10
import tlang.misc.exceptions : TError;
11
import tlang.compiler.parsing.exceptions;
12
import tlang.compiler.core : Compiler;
13
import std.string : format;
14
import tlang.compiler.modman;
15
import tlang.compiler.symbols.comments;
16

17
// TODO: Technically we could make a core parser etc
18
public final class Parser
19
{
20
    /** 
21
     * Tokens management
22
     */
23
    private LexerInterface lexer;
24

25
    /** 
26
     * Stores the previous token
27
     * 
28
     * This is updated everytime
29
     * `nextToken()` is called
30
     */
31
    private Token prevToken;
32

33
    /** 
34
     * The associated compiler
35
     */
36
    private Compiler compiler;
37

38
    /**
39
    * Crashes the program if the given token is not a symbol
40
    * the same as the givne expected one
41
    */
42
    public void expect(SymbolType symbol, Token token)
43
    {
44
        /* TODO: Do checking here to see if token is a type of given symbol */
45
        SymbolType actualType = getSymbolType(token);
1,284✔
46
        bool isFine = actualType == symbol;
1,284✔
47

48
        /* TODO: Crash program if not */
49
        if (!isFine)
1,284✔
50
        {
51
            throw new SyntaxError(this, symbol, token);
×
52
            // expect("Expected symbol of type " ~ to!(string)(symbol) ~ " but got " ~ to!(
53
                    // string)(actualType) ~ " with " ~ token.toString());
54
        }
55
    }
56

57
    /** 
58
     * Crashes the parser with an expectation message
59
     * by throwing a new `ParserException`.
60
     *
61
     * Params:
62
     *   message = the expectation message
63
     */
64
    public void expect(string message)
65
    {
66
        ERROR(message);
3✔
67

68
        throw new ParserException(this, ParserException.ParserErrorType.GENERAL_ERROR, message);
3✔
69
    }
70

71
    /** 
72
     * Constructs a new parser with the given lexer
73
     * from which tokens can be sourced from
74
     *
75
     * Params:
76
     *   lexer = the token source
77
     *   compiler = the compiler to be using
78
     *
79
     * FIXME: Remove null for `compiler`
80
     */
81
    this(LexerInterface lexer, Compiler compiler = null)
85✔
82
    {
83
        this.lexer = lexer;
85✔
84
        this.compiler = compiler;
85✔
85
    }
86

87
    /** 
88
     * Given a type of `Statement` to look for and a `Container` of
89
     * which to search with in. This method will recursively search
90
     * down the given container and look for any statements which
91
     * are a kind-of (`isBaseOf`) the requested type. it will return
92
     * an array of `Statement` (`Statement[]`) of the matches.
93
     *
94
     * The container itself is not considered in this type check.
95
     *
96
     * Params:
97
     *   statementType = the kind-of statement to look for
98
     *   from = the `Container` to search within
99
     * Returns: a `Statement[]` of matches
100
     */
101
    private static Statement[] findOfType(TypeInfo_Class statementType, Container from)
102
    {
103
        Statement[] matches;
61✔
104

105
        Statement[] bodyStatements = from.getStatements();
61✔
106
        foreach(Statement bodyStmt; bodyStatements)
696✔
107
        {
108
            if(cast(Container)bodyStmt)
171✔
109
            {
110
                matches ~= findOfType(statementType, cast(Container)bodyStmt);
11✔
111
            }
112

113
            if(statementType.isBaseOf(typeid(bodyStmt)))
171✔
114
            {
115
                matches ~= [bodyStmt];
49✔
116
            }
117
        }
118

119
        return matches;
61✔
120
    }
121

122
    /** 
123
     * Given a type of `Statement` to look for and a `Container` of
124
     * which to search with in. This method will recursively search
125
     * down the given container and look for any statements which
126
     * are a kind-of (`isBaseOf`) the requested type. It will return
127
     * `true` if any macthes are found.
128
     *
129
     * The container itself is not considered in this type check.
130
     *
131
     * Params:
132
     *   statementType = the kind-of statement to look for
133
     *   from = the `Container` to search within
134
     * Returns: `true` if at least one match is found, `false`
135
     * otherwise
136
     */
137
    private static bool existsWithin(TypeInfo_Class statementType, Container from)
138
    {
139
        return findOfType(statementType, from).length != 0;
50✔
140
    }
141

142
    
143
    /** 
144
     * Returns the current token
145
     *
146
     * This is a simple proxy method
147
     *
148
     * Returns: the `Token`
149
     */
150
    private Token getCurrentToken()
151
    {
152
        return this.lexer.getCurrentToken();
14,601✔
153
    }
154

155
    /** 
156
     * Moves the token pointer back
157
     *
158
     * This is a simple proxy method
159
     */
160
    private void previousToken()
161
    {
162
        this.lexer.previousToken();
354✔
163
    }
164
    
165
    /** 
166
     * Moves the token pointer forwards
167
     *
168
     * This is a proxy method BUT prior
169
     * to doing the proxy call it first
170
     * saves the current token into `prevToken`
171
     */
172
    private void nextToken()
173
    {
174
        // Save current token as previous token
175
        this.prevToken = this.getCurrentToken();
3,529✔
176

177
        // Move onto next token
178
        this.lexer.nextToken();
3,529✔
179
    }
180

181
    /** 
182
     * Sets the `Comment` if and only if
183
     * the previous token was a comment
184
     *
185
     * Params:
186
     *   comment = the found comment
187
     * Returns: `true` if a comment was
188
     * found, otherwise `false`
189
     */
190
    private bool getAssociatedComment(ref Comment comment)
191
    {
192
        // TODO: null check? on this.prevToken
193

194
        // If the previous token was a comment
195
        if
307✔
196
        (
197
            getSymbolType(this.prevToken) == SymbolType.SINGLE_LINE_COMMENT ||
198
            getSymbolType(this.prevToken) == SymbolType.MULTI_LINE_COMMENT
306✔
199
        )
200
        {
201
            DEBUG(format("Parsing a comment from token: '%s'", this.prevToken));
2✔
202
            comment = Comment.fromToken(this.prevToken);
2✔
203
            return true;
2✔
204
        }
205

206
        return false;
305✔
207
    }
208

209

210
    /**
211
    * Parses if statements
212
    *
213
    * TODO: Check kanban
214
    * TOOD: THis should return something
215
    */
216
    private IfStatement parseIf()
217
    {
218
        WARN("parseIf(): Enter");
9✔
219

220
        IfStatement ifStmt;
9✔
221
        Branch[] branches;
9✔
222

223
        while (lexer.hasTokens())
23✔
224
        {
225
            Expression currentBranchCondition;
23✔
226
            Statement[] currentBranchBody;
23✔
227

228
            /* This will only be called once (it is what caused a call to parseIf()) */
229
            if (getSymbolType(getCurrentToken()) == SymbolType.IF)
23✔
230
            {
231
                /* Pop off the `if` */
232
                nextToken();
9✔
233

234
                /* Expect an opening brace `(` */
235
                expect(SymbolType.LBRACE, getCurrentToken());
9✔
236
                nextToken();
9✔
237

238
                /* Parse an expression (for the condition) */
239
                currentBranchCondition = parseExpression();
9✔
240
                expect(SymbolType.RBRACE, getCurrentToken());
9✔
241

242
                /* Opening { */
243
                nextToken();
9✔
244
                expect(SymbolType.OCURLY, getCurrentToken());
9✔
245

246
                /* Parse the if' statement's body AND expect a closing curly */
247
                currentBranchBody = parseBody();
9✔
248
                expect(SymbolType.CCURLY, getCurrentToken());
9✔
249
                nextToken();
9✔
250

251
                /* Create a branch node */
252
                Branch branch = new Branch(currentBranchCondition, currentBranchBody);
9✔
253
                parentToContainer(branch, currentBranchBody);
9✔
254
                branches ~= branch;
9✔
255
            }
256
            /* If we get an else as the next symbol */
257
            else if (getSymbolType(getCurrentToken()) == SymbolType.ELSE)
14✔
258
            {
259
                /* Pop off the `else` */
260
                nextToken();
10✔
261

262
                /* Check if we have an `if` after the `{` (so an "else if" statement) */
263
                if (getSymbolType(getCurrentToken()) == SymbolType.IF)
10✔
264
                {
265
                    /* Pop off the `if` */
266
                    nextToken();
5✔
267

268
                    /* Expect an opening brace `(` */
269
                    expect(SymbolType.LBRACE, getCurrentToken());
5✔
270
                    nextToken();
5✔
271

272
                    /* Parse an expression (for the condition) */
273
                    currentBranchCondition = parseExpression();
5✔
274
                    expect(SymbolType.RBRACE, getCurrentToken());
5✔
275

276
                    /* Opening { */
277
                    nextToken();
5✔
278
                    expect(SymbolType.OCURLY, getCurrentToken());
5✔
279

280
                    /* Parse the if' statement's body AND expect a closing curly */
281
                    currentBranchBody = parseBody();
5✔
282
                    expect(SymbolType.CCURLY, getCurrentToken());
5✔
283
                    nextToken();
5✔
284

285
                    /* Create a branch node */
286
                    Branch branch = new Branch(currentBranchCondition, currentBranchBody);
5✔
287
                    parentToContainer(branch, currentBranchBody);
5✔
288
                    branches ~= branch;
5✔
289
                }
290
                /* Check for opening curly (just an "else" statement) */
291
                else if (getSymbolType(getCurrentToken()) == SymbolType.OCURLY)
5✔
292
                {
293
                    /* Parse the if' statement's body (starting with `{` AND expect a closing curly */
294
                    currentBranchBody = parseBody();
5✔
295
                    expect(SymbolType.CCURLY, getCurrentToken());
5✔
296
                    nextToken();
5✔
297

298
                    /* Create a branch node */
299
                    Branch branch = new Branch(null, currentBranchBody);
5✔
300
                    parentToContainer(branch, currentBranchBody);
5✔
301
                    branches ~= branch;
5✔
302

303
                    /* Exit, this is the end of the if statement as an else is reached */
304
                    break;
5✔
305
                }
306
                /* Error out if no `{` or `if` */
307
                else
308
                {
309
                    expect("Expected either if (for else if) or { for (else)");
×
310
                }
311
            }
312
            /* If we get anything else, then we are done with if statement */
313
            else
314
            {
315
                break;
4✔
316
            }
317
        }
318

319
        WARN("parseIf(): Leave");
9✔
320

321
        /* Create the if statement with the branches */
322
        ifStmt = new IfStatement(branches);
9✔
323

324
        /* Parent the branches to the IfStatement */
325
        parentToContainer(ifStmt, cast(Statement[])branches);
9✔
326

327
        return ifStmt;
9✔
328
    }
329

330
    private WhileLoop parseWhile()
331
    {
332
        WARN("parseWhile(): Enter");
3✔
333

334
        Expression branchCondition;
3✔
335
        Statement[] branchBody;
3✔
336

337
        /* Pop off the `while` */
338
        nextToken();
3✔
339

340
        /* Expect an opening brace `(` */
341
        expect(SymbolType.LBRACE, getCurrentToken());
3✔
342
        nextToken();
3✔
343

344
        /* Parse an expression (for the condition) */
345
        branchCondition = parseExpression();
3✔
346
        expect(SymbolType.RBRACE, getCurrentToken());
3✔
347

348
        /* Opening { */
349
        nextToken();
3✔
350
        expect(SymbolType.OCURLY, getCurrentToken());
3✔
351

352
        /* Parse the while' statement's body AND expect a closing curly */
353
        branchBody = parseBody();
3✔
354
        expect(SymbolType.CCURLY, getCurrentToken());
3✔
355
        nextToken();
3✔
356

357

358
        /* Create a Branch node coupling the condition and body statements */
359
        Branch branch = new Branch(branchCondition, branchBody);
3✔
360

361
        /* Parent the branchBody to the branch */
362
        parentToContainer(branch, branchBody);
3✔
363

364
        /* Create the while loop with the single branch */
365
        WhileLoop whileLoop = new WhileLoop(branch);
3✔
366

367
        /* Parent the branch to the WhileLoop */
368
        parentToContainer(whileLoop, [branch]);
3✔
369

370
        WARN("parseWhile(): Leave");
3✔
371

372
        return whileLoop;
3✔
373
    }
374

375
    private WhileLoop parseDoWhile()
376
    {
377
        WARN("parseDoWhile(): Enter");
×
378

379
        Expression branchCondition;
×
380
        Statement[] branchBody;
×
381

382
        /* Pop off the `do` */
383
        nextToken();
×
384

385
        /* Expect an opening curly `{` */
386
        expect(SymbolType.OCURLY, getCurrentToken());
×
387

388
        /* Parse the do-while statement's body AND expect a closing curly */
389
        branchBody = parseBody();
×
390
        expect(SymbolType.CCURLY, getCurrentToken());
×
391
        nextToken();
×
392

393
        /* Expect a `while` */
394
        expect(SymbolType.WHILE, getCurrentToken());
×
395
        nextToken();
×
396

397
        /* Expect an opening brace `(` */
398
        expect(SymbolType.LBRACE, getCurrentToken());
×
399
        nextToken();
×
400

401
        /* Parse the condition */
402
        branchCondition = parseExpression();
×
403
        expect(SymbolType.RBRACE, getCurrentToken());
×
404
        nextToken();
×
405

406
        /* Expect a semicolon */
407
        expect(SymbolType.SEMICOLON, getCurrentToken());
×
408
        nextToken();
×
409

410
        /* Create a Branch node coupling the condition and body statements */
411
        Branch branch = new Branch(branchCondition, branchBody);
×
412

413
        /* Parent the branchBody to the branch */
414
        parentToContainer(branch, branchBody);
×
415

416
        /* Create the while loop with the single branch and marked as a do-while loop */
417
        WhileLoop whileLoop = new WhileLoop(branch, true);
×
418

419
        /* Parent the branch to the WhileLoop */
420
        parentToContainer(whileLoop, [branch]);
×
421

422
        WARN("parseDoWhile(): Leave");
×
423

424
        return whileLoop;
×
425
    }
426

427
    // TODO: Finish implementing this
428
    // TODO: We need to properly parent and build stuff
429
    // TODO: We ASSUME there is always pre-run, condition and post-iteration
430
    public ForLoop parseFor()
431
    {
432
        WARN("parseFor(): Enter");
5✔
433

434
        Expression branchCondition;
5✔
435
        Statement[] branchBody;
5✔
436

437
        /* Pop of the token `for` */
438
        nextToken();
5✔
439

440
        /* Expect an opening smooth brace `(` */
441
        expect(SymbolType.LBRACE, getCurrentToken());
5✔
442
        nextToken();
5✔
443

444
        /* Expect a single Statement */
445
        // TODO: Make optional, add parser lookahead check
446
        Statement preRunStatement = parseStatement();
5✔
447
        
448
        /* Expect an expression */
449
        // TODO: Make optional, add parser lookahead check
450
        branchCondition = parseExpression();
5✔
451

452
        /* Expect a semi-colon, then move on */
453
        expect(SymbolType.SEMICOLON, getCurrentToken());
5✔
454
        nextToken();
5✔
455

456
        /* Expect a post-iteration statement with `)` as terminator */
457
        // TODO: Make optional, add parser lookahead check
458
        Statement postIterationStatement = parseStatement(SymbolType.RBRACE);
5✔
459
        
460
        /* Expect an opening curly `{` and parse the body */
461
        expect(SymbolType.OCURLY, getCurrentToken());
5✔
462
        branchBody = parseBody();
5✔
463

464
        /* Expect a closing curly and move on */
465
        expect(SymbolType.CCURLY, getCurrentToken());
5✔
466
        nextToken();
5✔
467

468
        DEBUG("Yo: "~getCurrentToken().toString());
5✔
469

470
        /* Create the Branch coupling the body statements (+post iteration statement) and condition */
471
        Branch forBranch = new Branch(branchCondition, branchBody~postIterationStatement);
5✔
472

473
        /* Create the for loop */
474
        ForLoop forLoop = new ForLoop(forBranch, preRunStatement);
5✔
475

476
        // TODO: Set `forLoop.hasPostIterate`
477

478
        /* Parent the pre-run statement to its for loop */
479
        parentToContainer(forLoop, [preRunStatement]);
5✔
480

481
        /* Parent the body of the branch (body statements + post iteration statement) */
482
        parentToContainer(forBranch, branchBody~postIterationStatement);
5✔
483

484
        /* Parent the Branch to its for loop */
485
        parentToContainer(forLoop, [forBranch]);
5✔
486

487
        WARN("parseFor(): Leave");
5✔
488

489
        return forLoop;
5✔
490
    }
491

492
    public VariableAssignmentStdAlone parseAssignment(SymbolType terminatingSymbol = SymbolType.SEMICOLON)
493
    {
494
        /* Generated Assignment statement */
495
        VariableAssignmentStdAlone assignment;
56✔
496

497
        /* The identifier being assigned to */
498
        string identifier = getCurrentToken().getToken();
56✔
499
        nextToken();
56✔
500
        nextToken();
56✔
501
        DEBUG(getCurrentToken());
56✔
502

503
        /* Expression */
504
        Expression assignmentExpression = parseExpression();
56✔
505

506

507
        assignment = new VariableAssignmentStdAlone(identifier, assignmentExpression);
56✔
508

509
        /* TODO: Support for (a=1)? */
510
        /* Expect a the terminating symbol */
511
        // expect(SymbolType.SEMICOLON, getCurrentToken());
512
        expect(terminatingSymbol, getCurrentToken());
56✔
513

514
        /* Move off terminating symbol */
515
        nextToken();
56✔
516
        
517

518
        return assignment;
56✔
519
    }
520

521
    public Statement parseName(SymbolType terminatingSymbol = SymbolType.SEMICOLON)
522
    {
523
        Statement ret;
307✔
524

525
        /* If there are any comments available then pop them off now */
526
        Comment potComment;
307✔
527
        if(getAssociatedComment(potComment))
307✔
528
        {
529
            DEBUG(format("Found associated comment: %s", potComment));
2✔
530
        }
531

532
        scope(exit)
533
        {
534
            if(potComment)
307✔
535
            {
536
                ret.setComment(potComment);
2✔
537
            }
538
        }
539

540
        /* Save the name or type */
541
        string nameTYpe = getCurrentToken().getToken();
307✔
542
        DEBUG("parseName(): Current token: "~getCurrentToken().toString());
307✔
543

544
        /* TODO: The problem here is I don't want to progress the token */
545

546
        /* Get next token */
547
        nextToken();
307✔
548
        SymbolType type = getSymbolType(getCurrentToken());
307✔
549

550
        /* If we have `(` then function call */
551
        if(type == SymbolType.LBRACE)
307✔
552
        {
553
            previousToken();
4✔
554
            FunctionCall funcCall = parseFuncCall();
4✔
555
            ret = funcCall;
4✔
556

557
            /* Set the flag to say this is a statement-level function call */
558
            funcCall.makeStatementLevel();
4✔
559

560
             /* Expect a semi-colon */
561
            expect(SymbolType.SEMICOLON, getCurrentToken());
4✔
562
            nextToken();
4✔
563
        }
564
        /**
565
        * Either we have:
566
        *
567
        * 1. `int ptr` (and we looked ahead to `ptr`)
568
        * 2. `int* ptr` (and we looked ahead to `*`)
569
        * 3. `int[] thing` (and we looked ahead to `[`)
570
        */
571
        /* If we have an identifier/type then declaration */
572
        else if(type == SymbolType.IDENT_TYPE || type == SymbolType.STAR || type == SymbolType.OBRACKET)
508✔
573
        {
574
            previousToken();
247✔
575
            ret = parseTypedDeclaration();
247✔
576

577
            /* If it is a function definition, then do nothing */
578
            if(cast(Function)ret)
245✔
579
            {
580
                // The ending `}` would have already been consumed
581
            }
582
            /* If it is a variable declaration then */
583
            else if(cast(Variable)ret)
150✔
584
            {
585
                /* Expect a semicolon and consume it */
586
                expect(SymbolType.SEMICOLON, getCurrentToken());
126✔
587
                nextToken();
126✔
588
            }
589
            /* If it is an arrau assignment */
590
            else if(cast(ArrayAssignment)ret)
24✔
591
            {
592
                /* Expect a semicolon and consume it */
593
                expect(SymbolType.SEMICOLON, getCurrentToken());
24✔
594
                nextToken();
24✔
595
            }
596
            /* This should never happen */
597
            else
598
            {
599
                assert(false);
600
            }
601
        }
602
        /* Assignment */
603
        else if(type == SymbolType.ASSIGN)
56✔
604
        {
605
            previousToken();
56✔
606
            ret = parseAssignment(terminatingSymbol);
56✔
607
        }
608
        /* Any other case */
609
        else
610
        {
611
            DEBUG(getCurrentToken());
×
612
            expect("Error expected ( for var/func def");
×
613
        }
614
       
615

616

617

618
        return ret;
305✔
619
    }
620

621
    /* TODO: Implement me, and call me */
622
    private Struct parseStruct()
623
    {
624
        WARN("parseStruct(): Enter");
×
625

626
        Struct generatedStruct;
×
627
        Statement[] statements;
×
628

629
        /* Consume the `struct` that caused `parseStruct` to be called */
630
        nextToken();
×
631

632
        /* Expect an identifier here (no dot) */
633
        string structName = getCurrentToken().getToken();
×
634
        expect(SymbolType.IDENT_TYPE, getCurrentToken());
×
635
        if(!isIdentifier_NoDot(getCurrentToken()))
×
636
        {
637
            expect("Identifier (for struct declaration) cannot be dotted");
×
638
        }
639
        
640
        /* Consume the name */
641
        nextToken();
×
642

643
        /* TODO: Here we will do a while loop */
644
        expect(SymbolType.OCURLY, getCurrentToken());
×
645
        nextToken();
×
646

647
        while(true)
×
648
        {
649
            /* Get current token */
650
            SymbolType symbolType = getSymbolType(getCurrentToken());
×
651

652
            /* The possibly valid returned struct member (Entity) */
653
            Statement structMember;
×
654

655
            /** TODO:
656
            * We only want to allow function definitions and variable
657
            * declarations here (WIP: for now without assignments)
658
            *
659
            * parseAccessor() supports those BUT it will also allow classes
660
            * and further structs - this we do not want and hence we should
661
            * filter out those (raise an error) on checking the type of
662
            * Entity returned by `parseAccessor()`
663
            */
664

665

666
            /* If it is a type */
667
            if (symbolType == SymbolType.IDENT_TYPE)
×
668
            {
669
                /* Might be a function definition or variable declaration */
670
                structMember = parseTypedDeclaration();
×
671
                
672
                /* Should have a semi-colon and consume it */
673
                expect(SymbolType.SEMICOLON, getCurrentToken());
×
674
                nextToken();
×
675
            }
676
            /* If it is an accessor */
677
            else if (isAccessor(getCurrentToken()))
×
678
            {
679
                structMember = parseAccessor();
×
680
            }
681
            /* If is is a modifier */
682
            else if(isModifier(getCurrentToken()))
×
683
            {
684
                structMember = parseInitScope();
×
685
            }
686
            /* If closing brace then exit */
687
            else if(symbolType == SymbolType.CCURLY)
×
688
            {
689
                break;
×
690
            }
691

692
            /* Ensure only function declaration or variable declaration */
693
            if(cast(Function)structMember)
×
694
            {
695

696
            }
697
            else if(cast(Variable)structMember)
×
698
            {
699
                /* Ensure that there is (WIP: for now) no assignment in the variable declaration */
700
                Variable variableDeclaration = cast(Variable)structMember;
×
701

702
                /* Raise error if an assignment is present */
703
                if(variableDeclaration.getAssignment())
×
704
                {
705
                    expect("Assignments not allowed in struct body");
×
706
                }
707
            }
708
            /**
709
            * Anything else that isn't a assignment-less variable declaration
710
            * or a function definition is an error
711
            */
712
            else
713
            {
714
                expect("Only function definitions and variable declarations allowed in struct body");
×
715
            }
716
            
717
            /* Append to struct's body */
718
            statements ~= structMember;
×
719
            
720
            
721

722
            
723

724
            /* TODO: Only allow variables here */
725
            /* TODO: Only allowe VariableDeclarations (maybe assignments idk) */
726
            /* TODO: Might, do what d does and allow function */
727
            /* TODO: Which is just a codegen trick and implicit thing really */
728
            /* TODO: I mean isn't OOP too lmao */
729

730
            
731
        }
732

733

734
        /* Generate a new Struct with the given body Statement(s) */
735
        generatedStruct = new Struct(structName);
×
736
        generatedStruct.addStatements(statements);
×
737
        
738
        /* Expect closing brace (sanity) */
739
        expect(SymbolType.CCURLY, getCurrentToken());
×
740

741
        /* Consume the closing curly brace */
742
        nextToken();
×
743

744

745
        WARN("parseStruct(): Leave");
×
746

747
        return generatedStruct;
×
748
    }
749

750
    private ReturnStmt parseReturn()
751
    {
752
        ReturnStmt returnStatement;
50✔
753

754
        /* Move from `return` onto start of expression */
755
        nextToken();
50✔
756

757
        // TODO: Check if semicolon here (no expression) else expect expression
758

759
        /* If the next token after `return` is a `;` then it is an expressionless return */
760
        if(getSymbolType(getCurrentToken()) == SymbolType.SEMICOLON)
50✔
761
        {
762
            /* Create the ReturnStmt (without an expression) */
763
            returnStatement = new ReturnStmt();
1✔
764
        }
765
        /* Else, then look for an expression */
766
        else
767
        {
768
            /* Parse the expression till termination */
769
            Expression returnExpression = parseExpression();
49✔
770

771
            /* Expect a semi-colon as the terminator */
772
            WARN(getCurrentToken());
49✔
773
            expect(SymbolType.SEMICOLON, getCurrentToken());
49✔
774

775
            /* Create the ReturnStmt */
776
            returnStatement = new ReturnStmt(returnExpression);
49✔
777
        }
778

779
        /* Move off of the terminator */
780
        nextToken();
50✔
781

782
        return returnStatement;
50✔
783
    }
784

785
    private Statement[] parseBody()
786
    {
787
        WARN("parseBody(): Enter");
124✔
788

789
        /* TODO: Implement body parsing */
790
        Statement[] statements;
124✔
791

792
        /* Consume the `{` symbol */
793
        nextToken();
124✔
794

795
        /**
796
        * If we were able to get a closing token, `}`, then
797
        * this will be set to true, else it will be false by
798
        * default which implies we ran out of tokens before
799
        * we could close te body which is an error we do throw
800
        */
801
        bool closedBeforeExit;
124✔
802

803
        while (lexer.hasTokens())
358✔
804
        {
805
            /* Get the token */
806
            Token tok = getCurrentToken();
358✔
807
            SymbolType symbol = getSymbolType(tok);
358✔
808

809
            DEBUG("parseBody(): SymbolType=" ~ to!(string)(symbol));
358✔
810

811
    
812
            /* If it is a class definition */
813
            if(symbol == SymbolType.CLASS)
358✔
814
            {
815
                /* Parsgprintlne the class and add its statements */
816
                statements ~= parseClass();
×
817
            }
818
            /* If it is a struct definition */
819
            else if(symbol == SymbolType.STRUCT)
358✔
820
            {
821
                /* Parse the struct and add it to the statements */
822
                statements ~= parseStruct();
×
823
            }
824
            /* If it is closing the body `}` */
825
            else if(symbol == SymbolType.CCURLY)
358✔
826
            {
827
                WARN("parseBody(): Exiting body by }");
124✔
828

829
                closedBeforeExit = true;
124✔
830
                break;
124✔
831
            }
832
            else
833
            {
834
                statements ~= parseStatement();
234✔
835
            }
836
        }
837

838
        /* TODO: We can sometimes run out of tokens before getting our closing brace, we should fix that here */
839
        if (!closedBeforeExit)
124✔
840
        {
841
            expect("Expected closing } but ran out of tokens");
×
842
        }
843

844
        WARN("parseBody(): Leave");
124✔
845

846
        return statements;
124✔
847
    }
848

849
    private AccessorType getAccessorType(Token token)
850
    {
851
        if(getSymbolType(token) == SymbolType.PUBLIC)
8✔
852
        {
853
            return AccessorType.PUBLIC;
4✔
854
        }
855
        else if(getSymbolType(token) == SymbolType.PROTECTED)
4✔
856
        {
857
            return AccessorType.PROTECTED;
×
858
        }
859
        else if(getSymbolType(token) == SymbolType.PRIVATE)
4✔
860
        {
861
            return AccessorType.PRIVATE;
4✔
862
        }
863
        else
864
        {
865
            return AccessorType.UNKNOWN;
×
866
        }
867
    }
868

869
    private InitScope getInitScope(Token token)
870
    {
871
        if(getSymbolType(token) == SymbolType.STATIC)
×
872
        {
873
            return InitScope.STATIC;
×
874
        }
875
        else
876
        {
877
            return InitScope.UNKNOWN;
×
878
        }
879
    }
880

881

882
    /* STATUS: Not being used yet */
883
    /**
884
    * Called in an occurence of the: `static x`
885
    */
886
    /* TODO: Anything that isn't static, is non-static => the false boolean should imply non-static */
887
    private Entity parseInitScope()
888
    {
889
        Entity entity;
×
890

891
        /* Save and consume the init-scope */
892
        InitScope initScope = getInitScope(getCurrentToken());
×
893
        nextToken();
×
894

895
        /* Get the current token's symbol type */
896
        SymbolType symbolType = getSymbolType(getCurrentToken());
×
897

898
        /**
899
        * TODO
900
        *
901
        * Topic of discussion: "What can be static?"
902
        *
903
        * Structs!
904
        *   As we might want them to be initted on class load or not (on instance initialization)
905
        * Classes
906
        *   Likewise a class in a class could be initted if static then on outer class load so would inner
907
        *   If not then only inner class loads on outer instantiation
908
        * Variables
909
        *   Initialize on class reference if static, however if not, then on instance initialization
910
        *
911
        *   Note: There are two meanings for static (if you take C for example, I might add a word for that, `global` rather)
912
        * Functions
913
        *   Journal entry describes this.
914
        *
915
        * Journal entry also describes this (/journal/static_keyword_addition/)
916
        */
917
        /* If class */
918
        if(symbolType == SymbolType.CLASS)
×
919
        {
920
            /* TODO: Set accessor on returned thing */
921
            entity = parseClass();
×
922
        }
923
        /* If struct */
924
        else if(symbolType == SymbolType.STRUCT)
×
925
        {
926
            /* TODO: Set accessor on returned thing */
927
            entity = parseStruct();
×
928
            DEBUG("Poes"~to!(string)(entity));
×
929
        }
930
        /* If typed-definition (function or variable) */
931
        else if(symbolType == SymbolType.IDENT_TYPE)
×
932
        {
933
            /* TODO: Set accesor on returned thing */
934
            entity = cast(Entity)parseName();
×
935

936
            if(!entity)
×
937
            {
938
                expect("Accessor got func call when expecting var/func def");
×
939
            }
940
        }
941
        /* Error out */
942
        else
943
        {
944
            expect("Expected either function definition, variable declaration, struct definition or class definition");
×
945
        }
946

947
        entity.setModifierType(initScope);
×
948

949
        return entity;
×
950
    }
951

952
    /* STATUS: Not being used yet */
953
    private Entity parseAccessor()
954
    {
955
        Entity entity;
8✔
956

957
        /* Save and consume the accessor */
958
        AccessorType accessorType = getAccessorType(getCurrentToken());
8✔
959
        nextToken();
8✔
960

961
        /* TODO: Only allow, private, public, protected */
962
        /* TODO: Pass this to call for class prsewr or whatever comes after the accessor */
963

964
        /* Get the current token's symbol type */
965
        SymbolType symbolType = getSymbolType(getCurrentToken());
8✔
966

967
        /* If class */
968
        if(symbolType == SymbolType.CLASS)
8✔
969
        {
970
            /* TODO: Set accessor on returned thing */
971
            entity = parseClass();
×
972
        }
973
        /* If struct */
974
        else if(symbolType == SymbolType.STRUCT)
8✔
975
        {
976
            /* TODO: Set accessor on returned thing */
977
            entity = parseStruct();
×
978
            DEBUG("Poes"~to!(string)(entity));
×
979
        }
980
        /* If typed-definition (function or variable) */
981
        else if(symbolType == SymbolType.IDENT_TYPE)
8✔
982
        {
983
            /* TODO: Set accesor on returned thing */
984
            entity = cast(Entity)parseName();
8✔
985

986
            if(!entity)
8✔
987
            {
988
                expect("Accessor got func call when expecting var/func def");
×
989
            }
990
        }
991
        /* If static */
992
        else if(symbolType == SymbolType.STATIC)
×
993
        {
994
            entity = parseInitScope();
×
995
        }
996
        /* Error out */
997
        else
998
        {
999
            expect("Expected either function definition, variable declaration, struct definition or class definition");
×
1000
        }
1001

1002
        entity.setAccessorType(accessorType);
8✔
1003

1004
        return entity;
8✔
1005
    }
1006

1007
    private void parseFunctionArguments()
1008
    {
1009
        /* TODO: Use later */
1010
        /* TODO: Add support for default values for function arguments */
1011
    }
1012

1013
    private struct funcDefPair
1014
    {
1015
        Statement[] bodyStatements;
1016
        VariableParameter[] params;
1017
    }
1018

1019
    private funcDefPair parseFuncDef(bool wantsBody = true)
1020
    {
1021
        WARN("parseFuncDef(): Enter");
97✔
1022

1023
        Statement[] statements;
97✔
1024
        VariableParameter[] parameterList;
97✔
1025
        funcDefPair bruh;
97✔
1026
        
1027

1028
        /* Consume the `(` token */
1029
        nextToken();
97✔
1030

1031
        /* Count for number of parameters processed */
1032
        ulong parameterCount;
97✔
1033

1034
        /* Expecting more arguments */
1035
        bool moreArgs;
97✔
1036

1037
        /* Get command-line arguments */
1038
        while (lexer.hasTokens())
156✔
1039
        {
1040
            /* Check if the first thing is a type */
1041
            if(getSymbolType(getCurrentToken()) == SymbolType.IDENT_TYPE)
156✔
1042
            {
1043
                /* Get the type */
1044
                TypedEntity bogusEntity = cast(TypedEntity)parseTypedDeclaration(false, false, false, true);
49✔
1045
                string type = bogusEntity.getType();
49✔
1046

1047
                /* Get the identifier (This CAN NOT be dotted) */
1048
                expect(SymbolType.IDENT_TYPE, getCurrentToken());
49✔
1049
                if(!isIdentifier_NoDot(getCurrentToken()))
49✔
1050
                {
1051
                    expect("Identifier can not be path");
×
1052
                }
1053
                string identifier = getCurrentToken().getToken();
49✔
1054
                nextToken();
49✔
1055

1056

1057
                /* Add the local variable (parameter variable) */
1058
                parameterList ~= new VariableParameter(type, identifier);
49✔
1059

1060
                moreArgs = false;
49✔
1061

1062
                parameterCount++;
49✔
1063
            }
1064
            /* If we get a comma */
1065
            else if(getSymbolType(getCurrentToken()) == SymbolType.COMMA)
107✔
1066
            {
1067
                /* Consume the `,` */
1068
                nextToken();
10✔
1069

1070
                moreArgs = true;
10✔
1071
            }
1072
            /* Check if it is a closing brace */
1073
            else if(getSymbolType(getCurrentToken()) == SymbolType.RBRACE)
97✔
1074
            {
1075
                /* Make sure we were not expecting more arguments */
1076
                if(!moreArgs)
97✔
1077
                {
1078
                    /* Consume the `)` */
1079
                    nextToken();
97✔
1080
                    break;
97✔
1081
                }
1082
                /* Error out if we were and we prematurely ended */
1083
                else
1084
                {
1085
                    expect(SymbolType.IDENT_TYPE, getCurrentToken());
×
1086
                }
1087
            }
1088
            /* Error out */
1089
            else
1090
            {
1091
                expect("Expected either type or )");
×
1092
            }
1093
        }
1094

1095
        /* If a body is required then allow it */
1096
        if(wantsBody)
97✔
1097
        {
1098
            expect(SymbolType.OCURLY, getCurrentToken());
97✔
1099

1100
            /* Parse the body (and it leaves ONLY when it gets the correct symbol, no expect needed) */
1101
            statements = parseBody();
97✔
1102

1103
            /* TODO: We should now run through the statements in the body and check for return */
1104
            for(ulong i = 0; i < statements.length; i++)
598✔
1105
            {
1106
                Statement curStatement = statements[i];
202✔
1107

1108
                /* If we find a return statement */
1109
                if(cast(ReturnStmt)curStatement)
202✔
1110
                {
1111
                    /* If it is not the last statement, throw an error */
1112
                    if(i != statements.length-1)
48✔
1113
                    {
1114
                        expect("A return statement must be the last statement of a function's body");
×
1115
                    }
1116
                }
1117
            }
1118

1119
            nextToken();
97✔
1120
        }
1121
        /* If no body is requested */
1122
        else
1123
        {
1124
            expect(SymbolType.SEMICOLON, getCurrentToken());
×
1125
        }
1126

1127
        DEBUG("ParseFuncDef: Parameter count: " ~ to!(string)(parameterCount));
97✔
1128
        WARN("parseFuncDef(): Leave");
97✔
1129

1130
        bruh.bodyStatements = statements;
97✔
1131
        bruh.params = parameterList;
97✔
1132

1133
        return bruh;
97✔
1134
    }
1135

1136

1137
    /**
1138
    * Only a subset of expressions are parsed without coming after
1139
    * an assignment, functioncall parameters etc
1140
    *
1141
    * Therefore instead of mirroring a lot fo what is in expression, for now atleast
1142
    * I will support everything using discard
1143
    *
1144
    * TODO: Remove discard and implement the needed mirrors
1145
    */
1146
    private DiscardStatement parseDiscard()
1147
    {
1148
        /* Consume the `discard` */
1149
        nextToken();
13✔
1150

1151
        /* Parse the following expression */
1152
        Expression expression = parseExpression();
13✔
1153

1154
        /* Expect a semi-colon */
1155
        expect(SymbolType.SEMICOLON, getCurrentToken());
13✔
1156
        nextToken();
13✔
1157

1158
        /* Create a `discard` statement */
1159
        DiscardStatement discardStatement = new DiscardStatement(expression);
13✔
1160

1161
        return discardStatement;
13✔
1162
    }
1163

1164
    /**
1165
    * Parses the `new Class()` expression
1166
    */
1167

1168

1169
    private CastedExpression parseCast()
1170
    {
1171
        CastedExpression castedExpression;
10✔
1172

1173
        /* Consume the `cast` */
1174
        nextToken();
10✔
1175

1176
        /* Expect an `(` open brace */
1177
        expect(SymbolType.LBRACE, getCurrentToken());
10✔
1178
        nextToken();
10✔
1179

1180
        /** 
1181
         * Expect a type
1182
         *
1183
         * The way we do this is to re-use the logic
1184
         * that `parseTypedDeclaration()` uses but we
1185
         * ask it to not parse further than the last token
1186
         * constituting the type (i.e. before starting to
1187
         * parse the identifier token).
1188
         *
1189
         * It then will return a bogus `TypedEntity` with
1190
         * a verfiable bogus name `BOGUS_NAME_STOP_SHORT_OF_IDENTIFIER_TYPE_FETCH` (TODO: Make sure we use this)
1191
         * which means we can call `getType()` and extract
1192
         * the type string
1193
         */
1194
        TypedEntity bogusEntity = cast(TypedEntity)parseTypedDeclaration(false, false, false, true);
10✔
1195
        assert(bogusEntity);
10✔
1196
        string toType = bogusEntity.getType();
10✔
1197

1198
        /* Expect a `)` closing brace */
1199
        expect(SymbolType.RBRACE, getCurrentToken());
10✔
1200
        nextToken();
10✔
1201

1202
        /* Get the expression to cast */
1203
        Expression uncastedExpression = parseExpression();
10✔
1204

1205
        
1206
        castedExpression = new CastedExpression(toType, uncastedExpression);
10✔
1207

1208
        return castedExpression;
10✔
1209
    }
1210

1211
    /**
1212
    * Parses an expression
1213
    *
1214
    * TODO:
1215
    *
1216
    * I think we need a loop here to move till we hit a terminator like `)`
1217
    * in the case of a condition's/function's argument expression or `;` in
1218
    * the case of a assignment's expression.
1219
    *
1220
    * This means we will be able to get the `+` token and process it
1221
    * We will also terminate on `;` or `)` and that means our `else` can be
1222
    * left to error out for unknowns then
1223
    */
1224
    private Expression parseExpression()
1225
    {
1226
        WARN("parseExpression(): Enter");
545✔
1227

1228
        import tlang.compiler.symbols.strings : StringExpression;
1229

1230

1231
        /** 
1232
         * Helper methods
1233
         *
1234
         * (TODO: These should be moved elsewhere)
1235
         */
1236
        bool isFloatLiteral(string numberLiteral)
1237
        {
1238
            import std.string : indexOf;
1239
            bool isFloat = indexOf(numberLiteral, ".") > -1; 
258✔
1240
            return isFloat;
258✔
1241
        }
1242

1243

1244
        /* The expression to be returned */
1245
        Expression[] retExpression;
545✔
1246

1247
        void addRetExp(Expression e)
1248
        {
1249
            retExpression ~= e;
688✔
1250
        }
1251

1252
        Expression removeExp()
1253
        {
1254
            Expression poppedExp = retExpression[retExpression.length-1];
143✔
1255
            retExpression.length--;
143✔
1256

1257
            return poppedExp;
143✔
1258
        }
1259

1260
        bool hasExp()
1261
        {
1262
            return retExpression.length != 0;
126✔
1263
        }
1264

1265
        void expressionStackSanityCheck()
1266
        {
1267
            /* If we don't have 1 on the stack */
1268
            if(retExpression.length != 1)
545✔
1269
            {
1270
                DEBUG(retExpression);
×
1271
                expect("Expression parsing failed as we had remaining items on the expression parser stack or zero");
×
1272
            }
1273
        }
1274

1275
        /* TODO: Unless I am wrong we can do a check that retExp should always be length 1 */
1276
        /* TODO: Makes sure that expressions like 1 1 don't wortk */
1277
        /* TODO: It must always be consumed */
1278

1279
        /* TODO: Implement expression parsing */
1280

1281
        /**
1282
        * We loop here until we hit something that closes
1283
        * an expression, in other words an expression
1284
        * appears in variable assignments which end with a
1285
        * `;`, they also appear in conditions which end in
1286
        * a `)`
1287
        */
1288
        while (true)
1,233✔
1289
        {
1290
            SymbolType symbol = getSymbolType(getCurrentToken());
1,233✔
1291

1292
            DEBUG(retExpression);
1,233✔
1293

1294
            /* If it is a number literal */
1295
            if (symbol == SymbolType.NUMBER_LITERAL)
1,233✔
1296
            { 
1297
                string numberLiteralStr = getCurrentToken().getToken();
258✔
1298
                NumberLiteral numberLiteral;
258✔
1299

1300
                // If floating point literal
1301
                if(isFloatLiteral(numberLiteralStr))
258✔
1302
                {
1303
                    // TODO: Issue #94, siiliar to below for integers
1304
                    numberLiteral = new FloatingLiteral(getCurrentToken().getToken());
×
1305
                }
1306
                // Else, then an integer literal
1307
                else
1308
                {
1309
                    // TODO: Issue #94, we should be checking the range here
1310
                    // ... along with any explicit encoders and setting it
1311
                    // ... for now default to SIGNED_INTEGER.
1312
                    IntegerLiteralEncoding chosenEncoding;
258✔
1313
                    // TODO (X-platform): Use `size_t` here
1314
                    ulong literalValue;
258✔
1315

1316

1317
                    
1318
                    
1319
                    // TODO: Add a check for the `U`, `UL` stuff here
1320
                    import std.algorithm.searching : canFind;
1321
                    // Explicit integer encoding (unsigned long)
1322
                    if(canFind(numberLiteralStr, "UL"))
258✔
1323
                    {
1324
                        chosenEncoding = IntegerLiteralEncoding.UNSIGNED_LONG;
4✔
1325

1326
                        // Strip the `UL` away
1327
                        numberLiteralStr = numberLiteralStr[0..numberLiteralStr.length-2];
4✔
1328
                    }
1329
                    // Explicit integer encoding (signed long)
1330
                    else if(canFind(numberLiteralStr, "L"))
254✔
1331
                    {
1332
                        chosenEncoding = IntegerLiteralEncoding.SIGNED_LONG;
3✔
1333

1334
                        // Strip the `L` away
1335
                        numberLiteralStr = numberLiteralStr[0..numberLiteralStr.length-1];
3✔
1336
                    }
1337
                    // Explicit integer encoding (unsigned int)
1338
                    else if(canFind(numberLiteralStr, "UI"))
251✔
1339
                    {
1340
                        chosenEncoding = IntegerLiteralEncoding.UNSIGNED_INTEGER;
3✔
1341

1342
                        // Strip the `UI` away
1343
                        numberLiteralStr = numberLiteralStr[0..numberLiteralStr.length-2];
3✔
1344
                    }
1345
                    // Explicit integer encoding (signed int)
1346
                    else if(canFind(numberLiteralStr, "I"))
248✔
1347
                    {
1348
                        chosenEncoding = IntegerLiteralEncoding.SIGNED_INTEGER;
3✔
1349

1350
                        // Strip the `I` away
1351
                        numberLiteralStr = numberLiteralStr[0..numberLiteralStr.length-1];
3✔
1352
                    }
1353
                    else
1354
                    {
1355
                        try
1356
                        {
1357
                            // TODO (X-platform): Use `size_t` here
1358
                            literalValue = to!(ulong)(numberLiteralStr);
245✔
1359
                            
1360

1361
                            // Signed integer range [0, 2_147_483_647]
1362
                            if(literalValue >= 0 && literalValue <= 2_147_483_647)
490✔
1363
                            {
1364
                                chosenEncoding = IntegerLiteralEncoding.SIGNED_INTEGER;
244✔
1365
                            }
1366
                            // Signed long range [2_147_483_648, 9_223_372_036_854_775_807]
1367
                            else if(literalValue >= 2_147_483_648 && literalValue <= 9_223_372_036_854_775_807)
2✔
1368
                            {
1369
                                chosenEncoding = IntegerLiteralEncoding.SIGNED_LONG;
×
1370
                            }
1371
                            // Unsigned long range [9_223_372_036_854_775_808, 18_446_744_073_709_551_615]
1372
                            else
1373
                            {
1374
                                chosenEncoding = IntegerLiteralEncoding.UNSIGNED_LONG;
1✔
1375
                            }
1376
                        }
1377
                        catch(ConvException e)
1378
                        {
1379
                            throw new ParserException(this, ParserException.ParserErrorType.LITERAL_OVERFLOW, "Literal '"~numberLiteralStr~"' would overflow");
×
1380
                        }
1381
                    }
1382

1383
                    numberLiteral = new IntegerLiteral(numberLiteralStr, chosenEncoding);
258✔
1384
                }
1385
                
1386
                /* Add expression to stack */
1387
                addRetExp(numberLiteral);
258✔
1388

1389
                /* Get the next token */
1390
                nextToken();
258✔
1391
            }
1392
            /* If it is a cast operator */
1393
            else if(symbol == SymbolType.CAST)
975✔
1394
            {
1395
                CastedExpression castedExpression = parseCast();
10✔
1396
                addRetExp(castedExpression);
10✔
1397
            }
1398
            /* If it is a maths operator */
1399
            /* TODO: Handle all operators here (well most), just include bit operators */
1400
            else if (isMathOp(getCurrentToken()) || isBinaryOp(getCurrentToken()))
1,835✔
1401
            {
1402
                SymbolType operatorType = getSymbolType(getCurrentToken());
126✔
1403

1404
                /* TODO: Save operator, also pass to constructor */
1405
                /* TODO: Parse expression or pass arithemetic (I think latter) */
1406
                nextToken();
126✔
1407

1408
                OperatorExpression opExp;
126✔
1409

1410
                /* Check if unary or not (if so no expressions on stack) */
1411
                if(!hasExp())
126✔
1412
                {
1413
                    /* Only `*`, `+` and `-` are valid or `~` */
1414
                    if(operatorType == SymbolType.STAR || operatorType == SymbolType.ADD || operatorType == SymbolType.SUB || operatorType == SymbolType.TILDE)
90✔
1415
                    {
1416
                        /* Parse the expression following the unary operator */
1417
                        Expression rhs = parseExpression();
12✔
1418

1419
                        /* Create UnaryExpression comprised of the operator and the right-hand side expression */
1420
                        opExp = new UnaryOperatorExpression(operatorType, rhs);
12✔
1421
                    }
1422
                    /* Support for ampersand (&) */
1423
                    else if(operatorType == SymbolType.AMPERSAND)
15✔
1424
                    {
1425
                        /* Expression can only be a `VariableExpression` which accounts for Function Handles and Variable Identifiers */
1426
                        Expression rhs = parseExpression();
15✔
1427
                        DEBUG("hhshhshshsh");
15✔
1428
                        if(cast(VariableExpression)rhs)
15✔
1429
                        {
1430
                            /* Create UnaryExpression comprised of the operator and the right-hand side expression */
1431
                            opExp = new UnaryOperatorExpression(operatorType, rhs);
15✔
1432
                        }
1433
                        else
1434
                        {
1435
                            expect("& operator can only be followed by a variable expression");
×
1436
                        }
1437
                    }
1438
                    else
1439
                    {
1440
                        expect("Expected *, + or - as unary operators but got "~to!(string)(operatorType));
×
1441
                    }
1442
                }
1443
                /* If has, then binary */
1444
                else
1445
                {
1446
                    /* Pop left-hand side expression */
1447
                    /* TODO: We should have error checking for `removeExp()` */
1448
                    /* TODO: Make it automatically exit if not enough exps */
1449
                    Expression lhs = removeExp();
99✔
1450

1451
                    /* Parse expression (the right-hand side) */
1452
                    Expression rhs = parseExpression();
99✔
1453

1454
                    /* Create BinaryOpertaor Expression */
1455
                    opExp = new BinaryOperatorExpression(operatorType, lhs, rhs);
99✔
1456
                }
1457

1458
                /* Add operator expression to stack */
1459
                addRetExp(opExp);
126✔
1460
            }
1461
            /* If it is a string literal */
1462
            else if (symbol == SymbolType.STRING_LITERAL)
839✔
1463
            {
1464
                // TODO: Add different string encoding support
1465
                
1466
                /* Add the string to the stack */
1467
                string str_lit = getCurrentToken().getToken();
×
1468
                addRetExp(StringExpression.buildUTF8FromLiteral(str_lit));
×
1469

1470
                /* Get the next token */
1471
                nextToken();
×
1472
            }
1473
            /* If we have a `[` (array index/access) */
1474
            else if(symbol == SymbolType.OBRACKET)
839✔
1475
            {
1476
                // Pop off an expression which will be `indexTo`
1477
                Expression indexTo = removeExp();
44✔
1478
                DEBUG("indexTo: "~indexTo.toString());
44✔
1479

1480
                /* Get the index expression */
1481
                nextToken();
44✔
1482
                Expression index = parseExpression();
44✔
1483
                nextToken();
44✔
1484
                DEBUG("IndexExpr: "~index.toString());
44✔
1485
                // gprintln(getCurrentToken());
1486

1487
                ArrayIndex arrayIndexExpr = new ArrayIndex(indexTo, index);
44✔
1488
                addRetExp(arrayIndexExpr);
44✔
1489
            }
1490
            /* If it is an identifier */
1491
            else if (symbol == SymbolType.IDENT_TYPE)
795✔
1492
            {
1493
                string identifier = getCurrentToken().getToken();
240✔
1494

1495
                nextToken();
240✔
1496

1497
                Expression toAdd;
240✔
1498

1499
                /* If the symbol is `(` then function call */
1500
                if (getSymbolType(getCurrentToken()) == SymbolType.LBRACE)
240✔
1501
                {
1502
                    /* TODO: Implement function call parsing */
1503
                    previousToken();
47✔
1504
                    toAdd = parseFuncCall();
47✔
1505
                }
1506
                else
1507
                {
1508
                    /* TODO: Leave the token here */
1509
                    /* TODO: Just leave it, yeah */
1510
                    // expect("poes");
1511
                    toAdd = new VariableExpression(identifier);
193✔
1512

1513
                    /**
1514
                    * FIXME: To properly support function handles I think we are going to need a new type
1515
                    * Well not here, this should technically be IdentExpression.
1516
                    */
1517
                }
1518

1519
                /* TODO: Change this later, for now we doing this */
1520
                addRetExp(toAdd);
240✔
1521
            }
1522
            /* Detect if this expression is coming to an end, then return */
1523
            else if (symbol == SymbolType.SEMICOLON || symbol == SymbolType.RBRACE ||
804✔
1524
                    symbol == SymbolType.COMMA || symbol == SymbolType.ASSIGN ||
267✔
1525
                    symbol == SymbolType.CBRACKET)
96✔
1526
            {
1527
                break;
545✔
1528
            }
1529
            /**
1530
            * For ()
1531
            */
1532
            else if (symbol == SymbolType.LBRACE)
10✔
1533
            {
1534
                /* Consume the `(` */
1535
                nextToken();
10✔
1536

1537
                /* Parse the inner expression till terminator */
1538
                addRetExp(parseExpression());
10✔
1539

1540
                /* Consume the terminator */
1541
                nextToken();
10✔
1542
            }
1543
            /**
1544
            * `new` operator
1545
            */
1546
            else if(symbol == SymbolType.NEW)
×
1547
            {
1548
                /* Cosume the `new` */
1549
                nextToken();
×
1550

1551
                /* Get the identifier */
1552
                string identifier = getCurrentToken().getToken();
×
1553
                nextToken();
×
1554

1555

1556
                NewExpression toAdd;
×
1557
                FunctionCall functionCallPart;
×
1558

1559
                /* If the symbol is `(` then function call */
1560
                if (getSymbolType(getCurrentToken()) == SymbolType.LBRACE)
×
1561
                {
1562
                    /* TODO: Implement function call parsing */
1563
                    previousToken();
×
1564
                    functionCallPart = parseFuncCall();
×
1565
                }
1566
                /* If not an `(` */
1567
                else
1568
                {
1569
                    /* Raise a syntax error */
1570
                    expect(SymbolType.LBRACE, getCurrentToken());
×
1571
                }
1572

1573
                /* Create a NewExpression with the associated FunctionCall */
1574
                toAdd = new NewExpression(functionCallPart);
×
1575

1576
                /* Add the expression */
1577
                addRetExp(toAdd);
×
1578
            }
1579
            /* TODO: New addition (UNTESTED, remove if problem causer) */
1580
            else if(symbol == SymbolType.DOT)
×
1581
            {
1582
                /* Pop the previous expression */
1583
                Expression previousExpression = removeExp();
×
1584

1585
                /* TODO: Get next expression */
1586
                nextToken();
×
1587
                Expression item = parseExpression();
×
1588

1589
                /* TODO: Construct accessor expression from both and addRetExp */
1590

1591
                BinaryOperatorExpression binOp = new BinaryOperatorExpression(SymbolType.DOT, previousExpression, item);
×
1592

1593
                addRetExp(binOp);
×
1594
            }
1595
            else
1596
            {
1597
                //gprintln("parseExpression(): NO MATCH", DebugType.ERROR);
1598
                /* TODO: Something isn't right here */
1599
                expect("Expected expression terminator ) or ;");
×
1600
            }
1601
        }
1602

1603

1604
        DEBUG(retExpression);
545✔
1605
        WARN("parseExpression(): Leave");
545✔
1606

1607
        /* TODO: DO check here for retExp.length = 1 */
1608
        expressionStackSanityCheck();
545✔
1609

1610
        return retExpression[0];
545✔
1611
    }
1612

1613
    
1614

1615
    // TODO: Update to `Statement` as this can return an ArrayAssignment now
1616
    private Statement parseTypedDeclaration(bool wantsBody = true, bool allowVarDec = true, bool allowFuncDef = true, bool onlyType = false)
1617
    {
1618
        WARN("parseTypedDeclaration(): Enter");
313✔
1619

1620

1621
        /* Generated object */
1622
        Statement generated;
313✔
1623

1624

1625
        /* TODO: Save type */
1626
        string type = getCurrentToken().getToken();
313✔
1627
        string identifier;
313✔
1628
        nextToken();
313✔
1629

1630
      
1631

1632
        /* Potential array index expressions (assignment) */
1633
        // Think myArray[i][1] -> [`i`, `1`]
1634
        Expression[] arrayIndexExprs;
313✔
1635

1636
        // We are currently 1 past the "type" (the identifier) so go back one
1637
        ulong arrayAssignTokenBeginPos = lexer.getCursor()-1;
313✔
1638

1639
        /* Potential stack-array type size (declaration) */
1640
        string potentialStackSize;
313✔
1641

1642
        /* Handling of pointer and array types */
1643
        while(getSymbolType(getCurrentToken()) == SymbolType.STAR || getSymbolType(getCurrentToken()) == SymbolType.OBRACKET)
816✔
1644
        {
1645
            /* If we have `[` then expect a number and/or a `]` */
1646
            if(getSymbolType(getCurrentToken()) == SymbolType.OBRACKET)
120✔
1647
            {
1648
                nextToken();
70✔
1649
                SymbolType nextType = getSymbolType(getCurrentToken());
70✔
1650
                
1651

1652
                /* Check if the next symbol is NOT a `]` */
1653
                if(nextType != SymbolType.CBRACKET)
70✔
1654
                {
1655
                    
1656

1657
                    arrayIndexExprs ~= parseExpression();
42✔
1658

1659
                    /**
1660
                     * If it is the case it is a number literal then save it
1661
                     * anyways just for the case whereby we may be declaring
1662
                     * a stack-array type
1663
                     *
1664
                     * TODO: Double check any error checking here which should be deferred to later
1665
                     */
1666
                    if(nextType == SymbolType.NUMBER_LITERAL)
42✔
1667
                    {
1668
                        // TODO: Ensure the returned thing is a number
1669
                        // TODO: Ensure said number is non-negative
1670
                        // TODO: May as well now start adding `]` as a seperator or stopper or something
1671
                        IntegerLiteral stackArraySize = cast(IntegerLiteral)arrayIndexExprs[$-1];
37✔
1672

1673
                        // If the expression is an integer (which it should be)
1674
                        if(stackArraySize)
37✔
1675
                        {
1676
                            DEBUG("StackArraySize: "~stackArraySize.toString());
37✔
1677
                            potentialStackSize = stackArraySize.getNumber();
37✔
1678
                        }
1679
                        // If not, then error
1680
                        else
1681
                        {
1682
                            ERROR("Expected an integer as stack-array size but got iets ander");
×
1683
                            // TODO: Rather throw a parsing error
1684
                            assert(false);
1685
                        }
1686
                    }
1687
                }
1688

1689
                
1690

1691
                expect(SymbolType.CBRACKET, getCurrentToken());
70✔
1692
                type=type~"["~potentialStackSize~"]";
70✔
1693
            }
1694
            /* If we have `*` */
1695
            else
1696
            {
1697
                type=type~"*";
50✔
1698
            }
1699
            
1700
            nextToken();
120✔
1701
        }
1702

1703
        /* If were requested to only find a type, then stop here and return it */
1704
        if(onlyType)
313✔
1705
        {
1706
            /* Create a bogus TypedEntity for the sole purpose of returning the type */
1707
            generated = new TypedEntity("BOGUS_NAME_STOP_SHORT_OF_IDENTIFIER_TYPE_FETCH", type);
59✔
1708

1709
            return generated;
59✔
1710
        }
1711

1712
        /* If we are going to be assigning into an array (indexed) */
1713
        bool arrayIndexing = false;
254✔
1714

1715

1716
        /* If the current token is ASSIGN then array indexing is occuring */
1717
        if(getSymbolType(getCurrentToken()) == SymbolType.ASSIGN)
254✔
1718
        {
1719
            // Then we are doing an array-indexed assignment
1720
            arrayIndexing = true;
24✔
1721
        }
1722
        /* If we have an identifier the a declaration is occuring */
1723
        else if(getSymbolType(getCurrentToken()) == SymbolType.IDENT_TYPE)
230✔
1724
        {
1725
            /* Expect an identifier (CAN NOT be dotted) */
1726
            expect(SymbolType.IDENT_TYPE, getCurrentToken());
229✔
1727
            if(!isIdentifier_NoDot(getCurrentToken()))
229✔
1728
            {
1729
                expect("Identifier cannot be dotted");
×
1730
            }
1731
            identifier = getCurrentToken().getToken();
229✔
1732

1733
            nextToken();
229✔
1734
            DEBUG("ParseTypedDec: DecisionBtwn FuncDef/VarDef: " ~ getCurrentToken().getToken());
229✔
1735
        }
1736
        /* Anything else is an error */
1737
        else
1738
        {
1739
            expect("Either a identity or an assignment symbol is expected");
1✔
1740
        }
1741

1742

1743
       
1744

1745
        /* Check if it is `(` (func dec) */
1746
        SymbolType symbolType = getSymbolType(getCurrentToken());
253✔
1747
        DEBUG("ParseTypedDec: SymbolType=" ~ to!(string)(symbolType));
253✔
1748
        if (symbolType == SymbolType.LBRACE)
253✔
1749
        {
1750
            // Only continue is function definitions are allowed
1751
            if(allowFuncDef)
97✔
1752
            {
1753
                /* Will consume the `}` (or `;` if wantsBody-false) */
1754
                funcDefPair pair = parseFuncDef(wantsBody);
97✔
1755

1756
                
1757

1758
                generated = new Function(identifier, type, pair.bodyStatements, pair.params);
97✔
1759

1760
                /**
1761
                 * If this function definition has a body (i.e. `wantsBody == true`)
1762
                 * and if the return type is non-void, THEN ensure we have a `ReturnStmt`
1763
                 * (return statement)
1764
                 */
1765
                if(wantsBody && type != "void")
194✔
1766
                {
1767
                    /* Recurse down to find a `ReturnStmt` */
1768
                    bool hasReturn = existsWithin(typeid(ReturnStmt), cast(Container)generated);
50✔
1769

1770
                    // Error if no return statement exists
1771
                    if(!hasReturn)
50✔
1772
                    {
1773
                        expect("Function '"~identifier~"' declared with return type does not contain a return statement");
2✔
1774
                    }
1775
                }
1776
                
1777
                import std.stdio;
1778
                writeln(to!(string)((cast(Function)generated).getVariables()));
95✔
1779

1780
                // Parent the parameters of the function to the Function
1781
                parentToContainer(cast(Container)generated, cast(Statement[])pair.params);
95✔
1782

1783
                // Parent the statements that make up the function to the Function
1784
                parentToContainer(cast(Container)generated, pair.bodyStatements);
95✔
1785
            }
1786
            else
1787
            {
1788
                expect("Function definitions not allowed");
×
1789
            }
1790
        }
1791
        /* Check for semi-colon (var dec) */
1792
        else if (symbolType == SymbolType.SEMICOLON)
156✔
1793
        {
1794
            // Only continue if variable declarations are allowed
1795
            if(allowVarDec)
66✔
1796
            {
1797
                DEBUG("Semi: "~to!(string)(getCurrentToken()));
66✔
1798
                DEBUG("Semi: "~to!(string)(getCurrentToken()));
66✔
1799
                WARN("ParseTypedDec: VariableDeclaration: (Type: " ~ type ~ ", Identifier: " ~ identifier ~ ")");
66✔
1800

1801
                generated = new Variable(type, identifier);
66✔
1802
            }
1803
            else
1804
            {
1805
                expect("Variables declarations are not allowed.");
×
1806
            }
1807
        }
1808
        /* Check for `=` (var dec) */
1809
        else if (symbolType == SymbolType.ASSIGN && (arrayIndexing == false))
180✔
1810
        {
1811
            // Only continue if variable declarations are allowed
1812
            if(allowVarDec)
66✔
1813
            {
1814
                // Only continue if assignments are allowed
1815
                if(wantsBody)
66✔
1816
                {
1817
                    /* Consume the `=` token */
1818
                    nextToken();
66✔
1819

1820
                    /* Now parse an expression */
1821
                    Expression expression = parseExpression();
66✔
1822

1823
                    VariableAssignment varAssign = new VariableAssignment(expression);
66✔
1824

1825
                    WARN("ParseTypedDec: VariableDeclarationWithAssingment: (Type: "
66✔
1826
                            ~ type ~ ", Identifier: " ~ identifier ~ ")");
1827
                    
1828
                    Variable variable = new Variable(type, identifier);
66✔
1829
                    variable.addAssignment(varAssign);
66✔
1830

1831
                    generated = variable;
66✔
1832
                }
1833
                else
1834
                {
1835
                    expect("Variable assignments+declarations are not allowed.");
×
1836
                }
1837
            }
1838
            else
1839
            {
1840
                expect("Variables declarations are not allowed.");
×
1841
            }
1842
        }
1843
        /* Check for `=` (array indexed assignment) */
1844
        else if (symbolType == SymbolType.ASSIGN && (arrayIndexing == true))
48✔
1845
        {
1846
            // Set the token pointer back to the beginning
1847
            lexer.setCursor(arrayAssignTokenBeginPos);
24✔
1848
            DEBUG("Looking at: "~to!(string)(getCurrentToken()));
24✔
1849

1850
            // TODO: Move all below code to the branch below that handles this case
1851
            WARN("We have an array assignment, here is the indexers: "~to!(string)(arrayIndexExprs));
24✔
1852

1853
            // Our identifier will be some weird malformed-looking `mrArray[][1]` (because os atck array size declarations no-number literal)
1854
            // ... expressions don't make it in (we have arrayIndexExprs for that). Therefore what we must do is actually
1855
            // strip the array bracket syntax away to get the name
1856
            import std.string : indexOf;
1857
            long firstBracket = indexOf(type, "[");
24✔
1858
            assert(firstBracket > -1);
24✔
1859
            identifier = type[0..firstBracket];
24✔
1860
            DEBUG("Then identifier is type actually: "~identifier);
24✔
1861

1862

1863
            ERROR("We are still implenenting array assignments");
24✔
1864

1865
            ArrayIndex muhIndex = cast(ArrayIndex)parseExpression();
24✔
1866
            DEBUG("Expback: "~muhIndex.toString());
24✔
1867

1868
            /* Expect a `=` and consume it */
1869
            DEBUG(getCurrentToken());
24✔
1870
            expect(SymbolType.ASSIGN, getCurrentToken());
24✔
1871
            nextToken();
24✔
1872

1873
            /* Parse the expression being assigned followed by a semi-colon `;` */
1874
            Expression expressionBeingAssigned = parseExpression();
24✔
1875
            expect(SymbolType.SEMICOLON, getCurrentToken());
24✔
1876

1877
            // TODO: Get the expression after the `=`
1878
            ArrayAssignment arrayAssignment = new ArrayAssignment(muhIndex, expressionBeingAssigned);
24✔
1879
            DEBUG("Created array assignment: "~arrayAssignment.toString());
24✔
1880
            // assert(false);
1881

1882
            generated = arrayAssignment;
24✔
1883
        }
1884
        else
1885
        {
1886
            expect("Expected one of the following: (, ; or =");
×
1887
        }
1888

1889
        WARN("parseTypedDeclaration(): Leave");
251✔
1890

1891
        return generated;
251✔
1892
    }
1893

1894
    /**
1895
    * Parses a class definition
1896
    *
1897
    * This is called when there is an occurrence of
1898
    * a token `class`
1899
    */
1900
    private Clazz parseClass()
1901
    {
1902
        WARN("parseClass(): Enter");
33✔
1903

1904
        Clazz generated;
33✔
1905

1906
        /* Pop off the `class` */
1907
        nextToken();
33✔
1908

1909
        /* Get the class's name (CAN NOT be dotted) */
1910
        expect(SymbolType.IDENT_TYPE, getCurrentToken());
33✔
1911
        if(!isIdentifier_NoDot(getCurrentToken()))
33✔
1912
        {
1913
            expect("Class name in declaration cannot be path");
×
1914
        }
1915
        string className = getCurrentToken().getToken();
33✔
1916
        DEBUG("parseClass(): Class name found '" ~ className ~ "'");
33✔
1917
        nextToken();
33✔
1918

1919
        generated = new Clazz(className);
33✔
1920

1921
        string[] inheritList;
33✔
1922

1923
        /* TODO: If we have the inherit symbol `:` */
1924
        if(getSymbolType(getCurrentToken()) == SymbolType.INHERIT_OPP)
33✔
1925
        {
1926
            /* TODO: Loop until `}` */
1927

1928
            /* Consume the inheritance operator `:` */
1929
            nextToken();
×
1930

1931
            while(true)
×
1932
            {
1933
                /* Check if it is an identifier (may be dotted) */
1934
                expect(SymbolType.IDENT_TYPE, getCurrentToken());
×
1935
                inheritList ~= getCurrentToken().getToken();
×
1936
                nextToken();
×
1937

1938
                /* Check if we have ended with a `{` */
1939
                if(getSymbolType(getCurrentToken()) == SymbolType.OCURLY)
×
1940
                {
1941
                    /* Exit */
1942
                    break;
×
1943
                }
1944
                /* If we get a comma */
1945
                else if(getSymbolType(getCurrentToken()) == SymbolType.COMMA)
×
1946
                {
1947
                    /* Consume */
1948
                    nextToken();
×
1949
                }
1950
                /* Error out if we get anything else */
1951
                else
1952
                {
1953
                    expect("Expected either { or ,");
×
1954
                }
1955
            }
1956
        }
1957

1958

1959

1960

1961

1962

1963

1964

1965
        /* TODO: Here we will do a while loop */
1966
        expect(SymbolType.OCURLY, getCurrentToken());
33✔
1967
        nextToken();
33✔
1968

1969
        Statement[] statements;
33✔
1970

1971
        while(true)
59✔
1972
        {
1973
            /* Get current token */
1974
            SymbolType symbolType = getSymbolType(getCurrentToken());
59✔
1975

1976
            /* The possibly valid returned struct member (Entity) */
1977
            Statement structMember;
59✔
1978

1979
            /** TODO:
1980
            * We only want to allow function definitions and variable
1981
            * declarations here (WIP: for now without assignments)
1982
            *
1983
            * parseAccessor() supports those BUT it will also allow classes
1984
            * and further structs - this we do not want and hence we should
1985
            * filter out those (raise an error) on checking the type of
1986
            * Entity returned by `parseAccessor()`
1987
            */
1988

1989

1990
            /* If it is a type */
1991
            if (symbolType == SymbolType.IDENT_TYPE)
59✔
1992
            {
1993
                /* Might be a function definition or variable declaration */
1994
                structMember = parseTypedDeclaration();
7✔
1995
                
1996
                /* Should have a semi-colon and consume it */
1997
                expect(SymbolType.SEMICOLON, getCurrentToken());
6✔
1998
                nextToken();
6✔
1999
            }
2000
            /* If it is a class */
2001
            else if(symbolType == SymbolType.CLASS)
52✔
2002
            {
2003
                structMember = parseClass();   
20✔
2004
            }
2005
            /* If it is a struct */
2006
            else if(symbolType == SymbolType.STRUCT)
32✔
2007
            {
2008
                structMember = parseStruct();
×
2009
            }
2010
            /* If it is an accessor */
2011
            else if (isAccessor(getCurrentToken()))
32✔
2012
            {
2013
                structMember = parseAccessor();
×
2014
            }
2015
            /* If is is a modifier */
2016
            else if(isModifier(getCurrentToken()))
32✔
2017
            {
2018
                structMember = parseInitScope();
×
2019
            }
2020
            /* If closing brace then exit */
2021
            else if(symbolType == SymbolType.CCURLY)
32✔
2022
            {
2023
                break;
32✔
2024
            }
2025
            else
2026
            {
2027
                expect("Only classes, structs, instance fields, static fields, functions allowed in class");
×
2028
            }
2029

2030
            
2031
            
2032
            /* Append to struct's body */
2033
            statements ~= structMember;
26✔
2034
            
2035
            
2036

2037
            
2038

2039
            /* TODO: Only allow variables here */
2040
            /* TODO: Only allowe VariableDeclarations (maybe assignments idk) */
2041
            /* TODO: Might, do what d does and allow function */
2042
            /* TODO: Which is just a codegen trick and implicit thing really */
2043
            /* TODO: I mean isn't OOP too lmao */
2044

2045
            
2046
        }
2047

2048

2049

2050

2051

2052
        /* Add inherit list */
2053
        generated.addInherit(inheritList);
32✔
2054

2055

2056

2057

2058

2059
        // /* TODO: Technically we should be more specific, this does too much */
2060
        // /* Parse a body */
2061
        // Statement[] statements = parseBody();
2062
        generated.addStatements(statements);
32✔
2063

2064
        /* Parent each Statement to the container */
2065
        parentToContainer(generated, statements);
32✔
2066

2067
        /* Pop off the ending `}` */
2068
        nextToken();
32✔
2069

2070
        WARN("parseClass(): Leave");
32✔
2071

2072
        return generated;
32✔
2073
    }
2074

2075
    private void parentToContainer(Container container, Statement[] statements, bool allowRecursivePainting = true)
2076
    {
2077
        foreach(Statement statement; statements)
7,599✔
2078
        {
2079
            if(statement !is null)
1,460✔
2080
            {
2081
                statement.parentTo(container);
1,444✔
2082

2083
                if(allowRecursivePainting)
1,444✔
2084
                {
2085
                    // TODO: Add specifics handling here to same-level parent
2086

2087
                    /** 
2088
                    * If we have a `Variable` (a vardec)
2089
                    * then, if it has an assignment,
2090
                    * parent its expression to the
2091
                    * same `Container`
2092
                    */
2093
                    if(cast(Variable)statement)
1,444✔
2094
                    {
2095
                        Variable variable = cast(Variable)statement;
191✔
2096
                        
2097
                        VariableAssignment assignment = variable.getAssignment();
191✔
2098
                        if(assignment)
191✔
2099
                        {
2100
                            Expression assExp = assignment.getExpression();
76✔
2101
                            parentToContainer(container, [assExp]);
76✔
2102
                        }
2103
                    }
2104
                    /** 
2105
                    * If we have an `BinaryOperatorExpression`
2106
                    * then we must parent its left and right
2107
                    * hand side expressions
2108
                    */
2109
                    else if(cast(BinaryOperatorExpression)statement)
1,253✔
2110
                    {
2111
                        BinaryOperatorExpression binOpExp = cast(BinaryOperatorExpression)statement;
193✔
2112

2113
                        parentToContainer(container, [binOpExp.getLeftExpression(), binOpExp.getRightExpression()]);
193✔
2114
                    }
2115
                    /** 
2116
                     * If we have a `VariableAssignmentStdAlone`
2117
                     * then we must parent its expression
2118
                     * (the assignment) to the same `Container`
2119
                     */
2120
                    else if(cast(VariableAssignmentStdAlone)statement)
1,060✔
2121
                    {
2122
                        VariableAssignmentStdAlone varAss = cast(VariableAssignmentStdAlone)statement;
119✔
2123
                        Expression varAssExp = varAss.getExpression();
119✔
2124
                        
2125
                        parentToContainer(container, [varAssExp]);
119✔
2126
                    }
2127
                    /**
2128
                     * If we have a `PointerDereferenceAssignment`
2129
                     * then we must parent its left-hand and right-hand
2130
                     * side expressions (the expression the address
2131
                     * is derived from) and (the expression being
2132
                     * assigned)
2133
                     */
2134
                    else if(cast(PointerDereferenceAssignment)statement)
941✔
2135
                    {
2136
                        PointerDereferenceAssignment ptrDerefAss = cast(PointerDereferenceAssignment)statement;
7✔
2137
                        Expression addrExp = ptrDerefAss.getPointerExpression();
7✔
2138
                        Expression assExp = ptrDerefAss.getExpression();
7✔
2139
                        
2140
                        parentToContainer(container, [addrExp, assExp]);
7✔
2141
                    }
2142
                    /** 
2143
                     * If we have a `FunctionCall`
2144
                     * expression
2145
                     */
2146
                    else if(cast(FunctionCall)statement)
934✔
2147
                    {
2148
                        FunctionCall funcCall = cast(FunctionCall)statement;
56✔
2149

2150
                        Expression[] actualArguments = funcCall.getCallArguments();
56✔
2151
                        parentToContainer(container, cast(Statement[])actualArguments);
56✔
2152
                    }
2153
                    /** 
2154
                     * If we have a `ReturnStmt`
2155
                     * then we must process its
2156
                     * contained expression (if any)
2157
                     */
2158
                    else if(cast(ReturnStmt)statement)
878✔
2159
                    {
2160
                        ReturnStmt retStmt = cast(ReturnStmt)statement;
54✔
2161

2162
                        if(retStmt.hasReturnExpression())
54✔
2163
                        {
2164
                            parentToContainer(container, [retStmt.getReturnExpression()]);
53✔
2165
                        }
2166
                    }
2167
                    /**
2168
                     * If we have a `DiscardStatement`
2169
                     * then we must process its
2170
                     * contained expression
2171
                     */
2172
                    else if(cast(DiscardStatement)statement)
824✔
2173
                    {
2174
                        DiscardStatement dcrdStmt = cast(DiscardStatement)statement;
13✔
2175

2176
                        parentToContainer(container, [dcrdStmt.getExpression()]);
13✔
2177
                    }
2178
                    /**
2179
                     * If we have an `IfStatement`
2180
                     * then extract its `Branch`
2181
                     * object
2182
                     */
2183
                    else if(cast(IfStatement)statement)
811✔
2184
                    {
2185
                        IfStatement ifStmt = cast(IfStatement)statement;
23✔
2186
                        Branch[] branches = ifStmt.getBranches();
23✔
2187
                        
2188
                        // Parent the branches to the if-statement
2189
                        parentToContainer(ifStmt, cast(Statement[])branches);
23✔
2190
                    }
2191
                    /**
2192
                     * If we have an `WhileLoop`
2193
                     * then extract its `Branch`
2194
                     * object
2195
                     */
2196
                    else if(cast(WhileLoop)statement)
788✔
2197
                    {
2198
                        WhileLoop whileLoop = cast(WhileLoop)statement;
5✔
2199

2200
                        // Parent the branch to the while-statement
2201
                        parentToContainer(whileLoop, [whileLoop.getBranch()]);
5✔
2202
                    }
2203
                    /**
2204
                     * If we have an `ForLoop`
2205
                     * then extract its `Branch`
2206
                     * object
2207
                     */
2208
                    else if(cast(ForLoop)statement)
783✔
2209
                    {
2210
                        ForLoop forLoop = cast(ForLoop)statement;
7✔
2211

2212
                        // Parent the branch to the for-loop-statement
2213
                        parentToContainer(forLoop, [forLoop.getBranch()]);
7✔
2214
                    }
2215
                    /** 
2216
                     * If we have a `Branch` then
2217
                     * process its conditions
2218
                     * expression
2219
                     */
2220
                    else if(cast(Branch)statement)
776✔
2221
                    {
2222
                        Branch branch = cast(Branch)statement;
84✔
2223
                        // TODO: See if this is okay, because I recall
2224
                        // ... atleast for for-loops that we had to
2225
                        // ... place things (body-container wise)
2226
                        // ... into certain locations
2227

2228
                        // FIXME: This doesn't look right, if it is a Container
2229
                        // ... then it should stay as such
2230
                        parentToContainer(branch, [branch.getCondition()]);
84✔
2231

2232
                        // NOTE: I don't want to recurse on to
2233
                        // ... body as that would entail
2234
                        // ... reparenting things whereas
2235
                        // ... they SHOULD (Body statements)
2236
                        // ... remain ALWAYS parented to
2237
                        // ... their branch's body
2238
                        Statement[] branchBody = branch.getBody();
84✔
2239

2240
                        // UPDATE: The above _can_ be done
2241
                        // ... it probably isn't needed as 
2242
                        // explicit depth calls are made 
2243
                        // already BUT we can do it for
2244
                        // safety and we can just make the 
2245
                        // branch the container as we have
2246
                        // done so above
2247
                        parentToContainer(branch, branchBody);
84✔
2248
                    }
2249
                }
2250
            }
2251
        }
2252
    }
2253

2254
    private Statement parseDerefAssignment()
2255
    {
2256
        WARN("parseDerefAssignment(): Enter");
7✔
2257

2258
        Statement statement;
7✔
2259

2260
        /* Consume the star `*` */
2261
        nextToken();
7✔
2262
        ulong derefCnt = 1;
7✔
2263

2264
        /* Check if there is another star */
2265
        while(getSymbolType(getCurrentToken()) == SymbolType.STAR)
7✔
2266
        {
2267
            derefCnt+=1;
×
2268
            nextToken();
×
2269
        }
2270

2271
        /* Expect an expression */
2272
        Expression pointerExpression = parseExpression();
7✔
2273

2274
        /* Expect an assignment operator */
2275
        expect(SymbolType.ASSIGN, getCurrentToken());
7✔
2276
        nextToken();
7✔
2277

2278
        /* Expect an expression */
2279
        Expression assigmentExpression = parseExpression();
7✔
2280

2281
        /* Expect a semicolon */
2282
        expect(SymbolType.SEMICOLON, getCurrentToken());
7✔
2283
        nextToken();
7✔
2284

2285
        // FIXME: We should make a LHSPiinterAssignmentThing
2286
        statement = new PointerDereferenceAssignment(pointerExpression, assigmentExpression, derefCnt);
7✔
2287

2288
        WARN("parseDerefAssignment(): Leave");
7✔
2289

2290
        return statement;
7✔
2291
    }
2292
    
2293
    private void parseComment()
2294
    {
2295
        WARN("parseComment(): Enter");
5✔
2296

2297
        Token curCommentToken = getCurrentToken();
5✔
2298

2299
        // pushComment(curCommentToken);
2300

2301
        // TODO: Do something here like placing it on some kind of stack
2302
        DEBUG("Comment is: '"~curCommentToken.getToken()~"'");
5✔
2303
        nextToken(); // Move off comment
5✔
2304

2305
        WARN("parseComment(): Leave");
5✔
2306
    }
2307

2308
    // TODO: We need to add `parseComment()`
2309
    // support here (see issue #84)
2310
    // TODO: This ic currently dead code and ought to be used/implemented
2311
    private Statement parseStatement(SymbolType terminatingSymbol = SymbolType.SEMICOLON)
2312
    {
2313
        WARN("parseStatement(): Enter");
244✔
2314

2315
        /* Get the token */
2316
        Token tok = getCurrentToken();
244✔
2317
        SymbolType symbol = getSymbolType(tok);
244✔
2318

2319
        DEBUG("parseStatement(): SymbolType=" ~ to!(string)(symbol));
244✔
2320

2321
        Statement statement;
244✔
2322

2323
        /* If it is a type */
2324
        if(symbol == SymbolType.IDENT_TYPE)
244✔
2325
        {
2326
            /* Might be a function, might be a variable, or assignment */
2327
            statement = parseName(terminatingSymbol);
157✔
2328
        }
2329
        /* If it is an accessor */
2330
        else if(isAccessor(tok))
87✔
2331
        {
2332
            statement = parseAccessor();
×
2333
        }
2334
        /* If it is a modifier */
2335
        else if(isModifier(tok))
87✔
2336
        {
2337
            statement = parseInitScope();
×
2338
        }
2339
        /* If it is a branch */
2340
        else if(symbol == SymbolType.IF)
87✔
2341
        {
2342
            statement = parseIf();
9✔
2343
        }
2344
        /* If it is a while loop */
2345
        else if(symbol == SymbolType.WHILE)
78✔
2346
        {
2347
            statement = parseWhile();
3✔
2348
        }
2349
        /* If it is a do-while loop */
2350
        else if(symbol == SymbolType.DO)
75✔
2351
        {
2352
            statement = parseDoWhile();
×
2353
        }
2354
        /* If it is a for loop */
2355
        else if(symbol == SymbolType.FOR)
75✔
2356
        {
2357
            statement = parseFor();
5✔
2358
        }
2359
        /* If it is a function call (further inspection needed) */
2360
        else if(symbol == SymbolType.IDENT_TYPE)
70✔
2361
        {
2362
            /* Function calls can have dotted identifiers */
2363
            parseFuncCall();
×
2364
        }
2365
        /* If it is the return keyword */
2366
        //TODO: We should add a flag to prevent return being used in generla bodies? or wait we have a non parseBiody already
2367
        else if(symbol == SymbolType.RETURN)
70✔
2368
        {
2369
            /* Parse the return statement */
2370
            statement = parseReturn();
50✔
2371
        }
2372
        /* If it is a `discard` statement */
2373
        else if(symbol == SymbolType.DISCARD)
20✔
2374
        {
2375
            /* Parse the discard statement */
2376
            statement = parseDiscard();
13✔
2377
        }
2378
        /* If it is a dereference assigment (a `*`) */
2379
        else if(symbol == SymbolType.STAR)
7✔
2380
        {
2381
            statement = parseDerefAssignment();
7✔
2382
        }
2383
        /* If it is a kind-of comment */
2384
        else if(symbol == SymbolType.SINGLE_LINE_COMMENT || symbol == SymbolType.MULTI_LINE_COMMENT)
×
2385
        {
2386
            ERROR("COMMENTS NOT YET PROPERLY SUPOORTED");
×
2387
            parseComment();
×
2388
        }
2389
        /* Error out */
2390
        else
2391
        {
2392
            expect("parseStatement(): Unknown symbol: " ~ getCurrentToken().getToken());
×
2393
        }
2394

2395
        // // TODO: Check if we should pop anything off of the comment
2396
        // // stack here
2397
        // if(hasCommentsOnStack())
2398
        // {
2399
        //     statement.setComment(popComment());
2400
        // }
2401
        
2402

2403
        WARN("parseStatement(): Leave");
244✔
2404

2405
        return statement;
244✔
2406
    }
2407

2408
    private FunctionCall parseFuncCall()
2409
    {
2410
        WARN("parseFuncCall(): Enter");
51✔
2411

2412
        /* TODO: Save name */
2413
        string functionName = getCurrentToken().getToken();
51✔
2414

2415
        Expression[] arguments;
51✔
2416

2417
        nextToken();
51✔
2418

2419
        /* Expect an opening brace `(` */
2420
        expect(SymbolType.LBRACE, getCurrentToken());
51✔
2421
        nextToken();
51✔
2422

2423
        /* If next token is RBRACE we don't expect arguments */
2424
        if(getSymbolType(getCurrentToken()) == SymbolType.RBRACE)
51✔
2425
        {
2426
            
2427
        }
2428
        /* If not expect arguments */
2429
        else
2430
        {
2431
            while(true)
45✔
2432
            {
2433
                /* Get the Expression */
2434
                Expression exp = parseExpression();
45✔
2435

2436
                /* Add it to list */
2437
                arguments ~= exp;
45✔
2438

2439
                /* Check if we exiting */
2440
                if(getSymbolType(getCurrentToken()) == SymbolType.RBRACE)
45✔
2441
                {
2442
                    break;
33✔
2443
                }
2444
                /* If comma expect more */
2445
                else if(getSymbolType(getCurrentToken()) == SymbolType.COMMA)
12✔
2446
                {
2447
                    nextToken();
12✔
2448
                    /* TODO: If rbrace after then error, so save boolean */
2449
                }
2450
                /* TODO: Add else, could have exited on `;` which is invalid closing */
2451
                else
2452
                {
2453
                    expect("Function call closed on ;, invalid");
×
2454
                }
2455
            }
2456
        }
2457

2458
       
2459
        nextToken();
51✔
2460

2461
        WARN("parseFuncCall(): Leave");
51✔
2462

2463
        return new FunctionCall(functionName, arguments);
51✔
2464
    }
2465

2466
    private ExternStmt parseExtern()
2467
    {
2468
        ExternStmt externStmt;
×
2469

2470
        /* Consume the `extern` token */
2471
        nextToken();
×
2472

2473
        /* Expect the next token to be either `efunc` or `evariable` */
2474
        SymbolType externType = getSymbolType(getCurrentToken());
×
2475
        nextToken();
×
2476

2477
        /* Pseudo-entity */
2478
        Entity pseudoEntity;
×
2479

2480
        /* External function symbol */
2481
        if(externType == SymbolType.EXTERN_EFUNC)
×
2482
        {
2483
            // TODO: (For one below)(we should also disallow somehow assignment) - evar
2484

2485
            // We now parse function definition but with `wantsBody` set to false
2486
            // indicating no body should be allowed.
2487
            pseudoEntity = cast(TypedEntity)parseTypedDeclaration(false, false, true);
×
2488

2489
            // TODO: Add a check for this cast (AND parse wise if it is evan possible)
2490
            assert(pseudoEntity);
×
2491
        }
2492
        /* External variable symbol */
2493
        else if(externType == SymbolType.EXTERN_EVAR)
×
2494
        {
2495
            // We now parse a variable declaration but with the `wantsBody` set to false
2496
            // indicating no assignment should be allowed.
2497
            pseudoEntity = cast(TypedEntity)parseTypedDeclaration(false, true, false);
×
2498

2499
            // TODO: Add a check for this cast (AND parse wise if it is evan possible)
2500
            assert(pseudoEntity);
×
2501
        }
2502
        /* Anything else is invalid */
2503
        else
2504
        {
2505
            expect("Expected either extern function (efunc) or extern variable (evar)");
×
2506
        }
2507

2508
        /* Expect a semicolon to end it all and then consume it */
2509
        expect(SymbolType.SEMICOLON, getCurrentToken());
×
2510
        nextToken();
×
2511

2512
        externStmt = new ExternStmt(pseudoEntity, externType);
×
2513

2514
        /* Mark the Entity as external */
2515
        pseudoEntity.makeExternal();
×
2516

2517
        return externStmt;
×
2518
    }
2519

2520
    /** 
2521
     * Performs an import of the given
2522
     * modules by their respective names
2523
     *
2524
     * Params:
2525
     *   modules = the names of the modules
2526
     * to import
2527
     */
2528
    private void doImport(string[] modules)
2529
    {
2530
        DEBUG(format("modules[]: %s", modules));
6✔
2531

2532
        // Print out some information about the current program
2533
        Program prog = this.compiler.getProgram();
6✔
2534
        DEBUG(format("Program currently: '%s'", prog));
6✔
2535

2536
        // Get the module manager
2537
        ModuleManager modMan = compiler.getModMan();
6✔
2538

2539
        // Search for all the module entries
2540
        ModuleEntry[] foundEnts;
6✔
2541
        foreach(string mod; modules)
42✔
2542
        {
2543
            DEBUG(format("Module wanting to be imported: %s", mod));
8✔
2544

2545
            // Search for the module entry
2546
            ModuleEntry foundEnt = modMan.find(mod);
8✔
2547
            DEBUG("Found module entry: "~to!(string)(foundEnt));
8✔
2548
            foundEnts ~= foundEnt;
8✔
2549
        }
2550
        
2551
        // For each module entry, only import
2552
        // it if not already in the process
2553
        // of being visited
2554
        foreach(ModuleEntry modEnt; foundEnts)
42✔
2555
        {
2556
            // Check here if already present, if so,
2557
            // then skip
2558
            if(prog.isEntryPresent(modEnt))
8✔
2559
            {
2560
                DEBUG(format("Not parsing module '%s' as already marked as visited", modEnt));
4✔
2561
                continue;
4✔
2562
            }
2563

2564
            // Mark it as visited
2565
            prog.markEntryAsVisited(modEnt);
4✔
2566

2567
            // Read in the module's contents
2568
            string moduleSource = modMan.readModuleData_throwable(modEnt);
4✔
2569
            DEBUG("Module has "~to!(string)(moduleSource.length)~" many bytes");
4✔
2570

2571
            // Parse the module
2572
            import tlang.compiler.lexer.kinds.basic : BasicLexer;
2573
            LexerInterface lexerInterface = new BasicLexer(moduleSource);
4✔
2574
            (cast(BasicLexer)lexerInterface).performLex();
4✔
2575
            Parser parser = new Parser(lexerInterface, this.compiler);
4✔
2576
            Module pMod = parser.parse(modEnt.getPath());
4✔
2577

2578
            // Map parsed module to its entry
2579
            prog.setEntryModule(modEnt, pMod);
4✔
2580
        }   
2581
    }
2582

2583
    /** 
2584
     * Parses module import statements
2585
     */
2586
    private void parseImport()
2587
    {
2588
        WARN("parseImport(): Enter");
6✔
2589

2590
        /* Consume the `import` keyword */
2591
        nextToken();
6✔
2592

2593
        /* Get the module's name */
2594
        expect(SymbolType.IDENT_TYPE, getCurrentToken());
6✔
2595
        string moduleName = getCurrentToken().getToken();
6✔
2596

2597
        /* Consume the token */
2598
        nextToken();
6✔
2599

2600
        /* All modules to be imported */
2601
        string[] collectedModuleNames = [moduleName];
6✔
2602

2603
        /* Try process multi-line imports (if any) */
2604
        while(getSymbolType(getCurrentToken()) == SymbolType.COMMA)
8✔
2605
        {
2606
            /* Consume the comma `,` */
2607
            nextToken();
2✔
2608

2609
            /* Get the module's name */
2610
            expect(SymbolType.IDENT_TYPE, getCurrentToken());
2✔
2611
            string curModuleName = getCurrentToken().getToken();
2✔
2612
            collectedModuleNames ~= curModuleName;
2✔
2613

2614
            /* Consume the name */
2615
            nextToken();
2✔
2616
        }
2617

2618
        /* Expect a semi-colon and consume it */
2619
        expect(SymbolType.SEMICOLON, getCurrentToken());
6✔
2620
        nextToken();
6✔
2621

2622
        /* Perform the actual import */
2623
        doImport(collectedModuleNames);
6✔
2624

2625
        WARN("parseImport(): Leave");
6✔
2626
    }
2627

2628
    /* Almost like parseBody but has more */
2629
    /**
2630
    * TODO: For certain things like `parseClass` we should
2631
    * keep track of what level we are at as we shouldn't allow
2632
    * one to define classes within functions
2633
    */
2634
    /* TODO: Variables should be allowed to have letters in them and underscores */
2635
    public Module parse(string moduleFilePath, bool isEntrypoint = false)
2636
    {
2637
        WARN("parse(): Enter");
85✔
2638

2639
        Module modulle;
85✔
2640

2641
        /* Expect `module` and module name and consume them (and `;`) */
2642
        expect(SymbolType.MODULE, getCurrentToken());
85✔
2643
        nextToken();
85✔
2644

2645
        /* Module name may NOT be dotted (TODO: Maybe it should be yeah) */
2646
        expect(SymbolType.IDENT_TYPE, getCurrentToken());
85✔
2647
        string moduleName = getCurrentToken().getToken();
85✔
2648
        nextToken();
85✔
2649

2650
        expect(SymbolType.SEMICOLON, getCurrentToken());
85✔
2651
        nextToken();
85✔
2652

2653
        /* Initialize Module */
2654
        modulle = new Module(moduleName);
85✔
2655

2656
        /* Set the file system path of this module */
2657
        modulle.setFilePath(moduleFilePath);
85✔
2658

2659
        /**
2660
         * As a rule, the tail end of a filename should match
2661
         * the name of the module (in its header)
2662
         *
2663
         * i.e. `niks/c.t` should have a module name
2664
         * (declared in the header `module <name>;`)
2665
         * of `c`
2666
         *
2667
         * Only checked is enabled (TODO: make that a thing)
2668
         */
2669
        import std.string : replace, split;
2670
        // TODO: use a PATH SPLITTER rather
2671
        import std.path : pathSplitter;
2672
        if
85✔
2673
        (
2674
            compiler.getConfig().hasConfig("modman:strict_headers") &&
2675
            compiler.getConfig().getConfig("modman:strict_headers").flag() &&
85✔
2676
            cmp(moduleName, replace(pathSplitter(moduleFilePath).back(), ".t", "")) != 0)
×
2677
        {
2678
            expect(format("The module's name '%s' does not match the file name for it at '%s'", moduleName, moduleFilePath));
×
2679
        }
2680

2681

2682
        /**
2683
         * If this is an entrypoint module (i.e. one
2684
         * specified on the command-line) then store
2685
         * it as visited
2686
         */
2687
        if(isEntrypoint)
85✔
2688
        {
2689
            DEBUG
81✔
2690
            (
2691
                format
2692
                (
2693
                    "parse(): Yes, this IS your entrypoint module '%s' about to be parsed",
2694
                    moduleName
2695
                )
2696
            );
2697

2698
            ModuleEntry curModEnt = ModuleEntry(moduleFilePath, moduleName);
81✔
2699
            Program prog = this.compiler.getProgram();
81✔
2700

2701
            prog.markEntryAsVisited(curModEnt); // TODO: Could not call?
81✔
2702
            prog.setEntryModule(curModEnt, modulle);
81✔
2703
        }
2704

2705
        /* TODO: We should add `lexer.hasTokens()` to the `nextToken()` */
2706
        /* TODO: And too the `getCurrentTokem()` and throw an error when we have ran out rather */
2707

2708
        /* We can have an import or vardef or funcdef */
2709
        while (lexer.hasTokens())
256✔
2710
        {
2711
            /* Get the token */
2712
            Token tok = getCurrentToken();
174✔
2713
            SymbolType symbol = getSymbolType(tok);
174✔
2714

2715
            DEBUG("parse(): Token: " ~ tok.getToken());
174✔
2716

2717
            /* If it is a type */
2718
            if (symbol == SymbolType.IDENT_TYPE)
174✔
2719
            {
2720
                /* Might be a function, might be a variable, or assignment */
2721
                Statement statement = parseName();
142✔
2722
                
2723
                /**
2724
                * If it is an Entity then mark it as static
2725
                * as all Entities at module-level are static
2726
                */
2727
                if(cast(Entity)statement)
140✔
2728
                {
2729
                    Entity entity = cast(Entity)statement;
137✔
2730
                    entity.setModifierType(InitScope.STATIC);
137✔
2731
                }
2732

2733
                modulle.addStatement(statement);
140✔
2734
            }
2735
            /* If it is an accessor */
2736
            else if (isAccessor(tok))
32✔
2737
            {
2738
                Entity entity = parseAccessor();
8✔
2739

2740
                /* Everything at the Module level is static */
2741
                entity.setModifierType(InitScope.STATIC);
8✔
2742

2743
                /* TODO: Tets case has classes which null statement, will crash */
2744
                modulle.addStatement(entity);
8✔
2745
            }
2746
            /* If it is a class */
2747
            else if (symbol == SymbolType.CLASS)
24✔
2748
            {
2749
                Clazz clazz = parseClass();
13✔
2750

2751
                /* Everything at the Module level is static */
2752
                clazz.setModifierType(InitScope.STATIC);
12✔
2753

2754
                /* Add the class definition to the program */
2755
                modulle.addStatement(clazz);
12✔
2756
            }
2757
            /* If it is a struct definition */
2758
            else if(symbol == SymbolType.STRUCT)
11✔
2759
            {
2760
                Struct ztruct = parseStruct();
×
2761

2762
                /* Everything at the Module level is static */
2763
                ztruct.setModifierType(InitScope.STATIC);
×
2764

2765
                /* Add the struct definition to the program */
2766
                modulle.addStatement(ztruct);
×
2767
            }
2768
            /* If it is an extern */
2769
            else if(symbol == SymbolType.EXTERN)
11✔
2770
            {
2771
                ExternStmt externStatement = parseExtern();
×
2772

2773
                modulle.addStatement(externStatement);
×
2774
            }
2775
            /* If it is an import */
2776
            else if(symbol == SymbolType.IMPORT)
11✔
2777
            {
2778
                parseImport();
6✔
2779
            }
2780
            /* If it is a kind-of comment */
2781
            else if(symbol == SymbolType.SINGLE_LINE_COMMENT || symbol == SymbolType.MULTI_LINE_COMMENT)
6✔
2782
            {
2783
                ERROR("COMMENTS NOT YET PROPERLY SUPOORTED");
5✔
2784
                parseComment();
5✔
2785
            }
2786
            else
2787
            {
2788
                expect("parse(): Unknown '" ~ tok.getToken() ~ "'");
×
2789
            }
2790
        }
2791

2792
        WARN("parse(): Leave");
82✔
2793

2794
        /* Parent each Statement to the container (the module) */
2795
        parentToContainer(modulle, modulle.getStatements());
82✔
2796

2797

2798
        DEBUG("Done parsing module '"~modulle.getName()~"' from file '"~modulle.getFilePath()~"'");
82✔
2799

2800
        return modulle;
82✔
2801
    }
2802
}
2803

2804

2805
version(unittest)
2806
{
2807
    import std.file;
2808
    import std.stdio;
2809
    import tlang.compiler.lexer.core;
2810
    import tlang.compiler.lexer.kinds.basic : BasicLexer;
2811
    import tlang.compiler.typecheck.core;
2812
    import tlang.compiler.typecheck.resolution : Resolver;
2813
    import tlang.compiler.core : gibFileData;
2814
}
2815

2816
/**
2817
 * Basic Module test case
2818
 */
2819
unittest
2820
{
2821
    string sourceCode = `
1✔
2822
module myModule;
2823
`;
2824

2825
    File dummyFile;
2✔
2826
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
2827
    try
2828
    {
2829
        compiler.doLex();
1✔
2830
        compiler.doParse();
1✔
2831
        Module modulle = compiler.getProgram().getModules()[0];
1✔
2832

2833
        assert(cmp(modulle.getName(), "myModule")==0);
1✔
2834
    }
2835
    catch(TError e)
2836
    {
2837
        assert(false);
2838
    }
2839
}
2840

2841
/**
2842
* Naming test for Entity recognition
2843
*/
2844
unittest
2845
{
2846
    string sourceCode = `
1✔
2847
module myModule;
2848

2849
class myClass1
2850
{
2851
    class myClass1_1
2852
    {
2853
        int entity;
2854
    }
2855

2856
    class myClass2
2857
    {
2858
        int inner;
2859
    }
2860
}
2861

2862
class myClass2
2863
{
2864
    int outer;
2865
}
2866
`;
2867

2868
    File dummyFile;
2✔
2869
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
2870

2871
    try
2872
    {
2873
        compiler.doLex();
1✔
2874
        assert(true);
1✔
2875
    }
2876
    catch(LexerException e)
2877
    {
2878
        assert(false);
2879
    }
2880
    
2881
    try
2882
    {
2883
        compiler.doParse();
1✔
2884
        Program program = compiler.getProgram();
1✔
2885

2886
        // There is only a single module in this program
2887
        Module modulle = program.getModules()[0];
1✔
2888

2889
        /* Module name must be myModule */
2890
        assert(cmp(modulle.getName(), "myModule")==0);
1✔
2891
        TypeChecker tc = new TypeChecker(compiler);
1✔
2892

2893
        /**
2894
        * There should exist two module-level classes
2895
        *
2896
        * 1. Attempt resolving the two without a full-path (relative to module)
2897
        * 2. Attempt resolving the two with a full-path
2898
        */
2899

2900
        /* There should exist two Module-level classes named `myClass1` and `myClass2` */
2901
        Entity entity1_rel = tc.getResolver().resolveBest(modulle, "myClass1");
1✔
2902
        Entity entity2_rel = tc.getResolver().resolveBest(modulle, "myClass2");
1✔
2903
        assert(entity1_rel);
1✔
2904
        assert(entity2_rel);
1✔
2905

2906
        /* Resolve using full-path instead */
2907
        Entity entity1_fp = tc.getResolver().resolveBest(modulle, "myModule.myClass1");
1✔
2908
        Entity entity2_fp = tc.getResolver().resolveBest(modulle, "myModule.myClass2");
1✔
2909
        assert(entity1_fp);
1✔
2910
        assert(entity2_fp);
1✔
2911

2912
        /* These should match respectively */
2913
        assert(entity1_rel == entity1_fp);
1✔
2914
        assert(entity2_rel == entity2_fp);
1✔
2915

2916
        /* These should all be classes */
2917
        Clazz clazz1 = cast(Clazz)entity1_fp;
1✔
2918
        Clazz clazz2 = cast(Clazz)entity2_fp;
1✔
2919
        assert(clazz1);
1✔
2920
        assert(clazz1);
1✔
2921
        
2922
        
2923

2924

2925
        /**
2926
        * Resolve members of myClass1
2927
        *
2928
        * 1. Resolve full-path
2929
        * 2. Resolve relative to myClass1
2930
        * 3. Resolve relative to module (incorrect)
2931
        * 4. Resolve relative to module (correct)
2932
        * 5. Resolve relative to myClass2 (resolves upwards)
2933
        */
2934
        Entity myClass1_myClass2_1 = tc.getResolver().resolveBest(modulle, "myModule.myClass1.myClass2");
1✔
2935
        Entity myClass1_myClass2_2 = tc.getResolver().resolveBest(clazz1, "myClass2");
1✔
2936
        Entity myClass2 = tc.getResolver().resolveBest(modulle, "myClass2");
1✔
2937
        Entity myClass1_myClass2_4 = tc.getResolver().resolveBest(modulle, "myClass1.myClass2");
1✔
2938
        Entity myClass1_myClass2_5 = tc.getResolver().resolveBest(clazz2, "myClass1.myClass2");
1✔
2939
        
2940
        /**
2941
        * All the above should exist
2942
        */
2943
        assert(myClass1_myClass2_1);
1✔
2944
        assert(myClass1_myClass2_2);
1✔
2945
        assert(myClass2);
1✔
2946
        assert(myClass1_myClass2_4);
1✔
2947
        assert(myClass1_myClass2_5);
1✔
2948

2949
        /**
2950
        * They should all be classes
2951
        */
2952
        Clazz c_myClass1_myClass2_1 = cast(Clazz)myClass1_myClass2_1;
1✔
2953
        Clazz c_myClass1_myClass2_2 = cast(Clazz)myClass1_myClass2_2;
1✔
2954
        Clazz c_myClass2 = cast(Clazz)myClass2;
1✔
2955
        Clazz c_myClass1_myClass2_4 = cast(Clazz)myClass1_myClass2_4;
1✔
2956
        Clazz c_myClass1_myClass2_5 = cast(Clazz)myClass1_myClass2_5;
1✔
2957

2958
        /**
2959
        * These should all be equal `myClass1.myClass2`
2960
        */
2961
        assert(c_myClass1_myClass2_1 == c_myClass1_myClass2_2);
1✔
2962
        assert(c_myClass1_myClass2_2 == myClass1_myClass2_4);
1✔
2963
        assert(myClass1_myClass2_4 == myClass1_myClass2_5);
1✔
2964

2965
        /**
2966
        * myClass1.myClass2 != myClass2
2967
        *
2968
        * myClass1.myClass2.inner should exist in myClass1.myClass2
2969
        * myClass2.outer should exist in myClass2
2970
        *
2971
        * Vice-versa of the above should not be true
2972
        *
2973
        * Both should be variables
2974
        */
2975
        assert(myClass1_myClass2_5 != myClass2);
1✔
2976

2977
        Entity innerVariable = tc.getResolver().resolveBest(c_myClass1_myClass2_5, "inner");
1✔
2978
        Entity outerVariable = tc.getResolver().resolveBest(c_myClass2, "outer");
1✔
2979
        assert(innerVariable !is null);
1✔
2980
        assert(outerVariable !is null);
1✔
2981
        assert(cast(Variable)innerVariable);
1✔
2982
        assert(cast(Variable)outerVariable);
1✔
2983

2984

2985
        innerVariable = tc.getResolver().resolveBest(c_myClass2, "inner");
1✔
2986
        outerVariable = tc.getResolver().resolveBest(c_myClass1_myClass2_5, "outer");
1✔
2987
        assert(innerVariable is null);
1✔
2988
        assert(outerVariable is null); 
1✔
2989

2990
        /**
2991
        * myClass1_1.entity should exist
2992
        *
2993
        * 1. Resolve from myClass1.myClass2 relative position
2994
        */
2995
        Entity variableEntity = tc.getResolver().resolveBest(c_myClass1_myClass2_5, "myClass1_1.entity");
1✔
2996
        assert(variableEntity);
1✔
2997

2998
        /* Should be a variable */
2999
        assert(cast(Variable)variableEntity);
1✔
3000
    }
3001
    catch(TError)
3002
    {
3003
        assert(false);
3004
    }
3005
}
3006

3007
/**
3008
 * Discard statement test case
3009
 */
3010
unittest
3011
{
3012
    string sourceCode = `
1✔
3013
module parser_discard;
3014

3015
void function()
3016
{
3017
    discard function();
3018
}
3019
`;
3020

3021
    File dummyFile;
2✔
3022
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
3023

3024
    try
3025
    {
3026
        compiler.doLex();
1✔
3027
        assert(true);
1✔
3028
    }
3029
    catch(LexerException e)
3030
    {
3031
        assert(false);
3032
    }
3033
    
3034
    try
3035
    {
3036
        compiler.doParse();
1✔
3037
        Program program = compiler.getProgram();
1✔
3038

3039
        // There is only a single module in this program
3040
        Module modulle = program.getModules()[0];
1✔
3041

3042
        /* Module name must be parser_discard */
3043
        assert(cmp(modulle.getName(), "parser_discard")==0);
1✔
3044
        TypeChecker tc = new TypeChecker(compiler);
1✔
3045

3046
        
3047
        /* Find the function named `function` */
3048
        Entity func = tc.getResolver().resolveBest(modulle, "function");
1✔
3049
        assert(func);
1✔
3050
        assert(cast(Function)func); // Ensure it is a Funciton
1✔
3051

3052
        /* Get the function's body */
3053
        Container funcContainer = cast(Container)func;
1✔
3054
        assert(funcContainer);
1✔
3055
        Statement[] functionStatements = funcContainer.getStatements();
1✔
3056
        assert(functionStatements.length == 1);
1✔
3057

3058
        /* First statement should be a discard */
3059
        DiscardStatement discard = cast(DiscardStatement)functionStatements[0];
1✔
3060
        assert(discard);
1✔
3061
        
3062
        /* The statement being discarded should be a function call */
3063
        FunctionCall functionCall = cast(FunctionCall)discard.getExpression();
1✔
3064
        assert(functionCall);
1✔
3065
    }
3066
    catch(TError e)
3067
    {
3068
        assert(false);
3069
    }
3070
}
3071

3072
/**
3073
 * Function definition test case
3074
 */
3075
unittest
3076
{
3077
    string sourceCode = `
1✔
3078
module parser_function_def;
3079

3080
int myFunction(int i, int j)
3081
{
3082
    int k = i + j;
3083

3084
    return k+1;
3085
}
3086
`;
3087

3088

3089
    File dummyFile;
2✔
3090
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
3091

3092
    try
3093
    {
3094
        compiler.doLex();
1✔
3095
        assert(true);
1✔
3096
    }
3097
    catch(LexerException e)
3098
    {
3099
        assert(false);
3100
    }
3101
    
3102
    try
3103
    {
3104
        compiler.doParse();
1✔
3105
        Program program = compiler.getProgram();
1✔
3106

3107
        // There is only a single module in this program
3108
        Module modulle = program.getModules()[0];
1✔
3109

3110
        /* Module name must be parser_function_def */
3111
        assert(cmp(modulle.getName(), "parser_function_def")==0);
1✔
3112
        TypeChecker tc = new TypeChecker(compiler);
1✔
3113

3114
        
3115
        /* Find the function named `myFunction` */
3116
        Entity func = tc.getResolver().resolveBest(modulle, "myFunction");
1✔
3117
        assert(func);
1✔
3118
        assert(cast(Function)func); // Ensure it is a Funciton
1✔
3119

3120
        /* Get the function's body */
3121
        Container funcContainer = cast(Container)func;
1✔
3122
        assert(funcContainer);
1✔
3123
        Statement[] functionStatements = funcContainer.getStatements();
1✔
3124

3125
        // Two parameters, 1 local and a return
3126
        assert(functionStatements.length == 4);
1✔
3127

3128
        /* First statement should be a variable (param) */
3129
        VariableParameter varPar1 = cast(VariableParameter)functionStatements[0];
1✔
3130
        assert(varPar1);
1✔
3131
        assert(cmp(varPar1.getName(), "i") == 0);
1✔
3132

3133
        /* Second statement should be a variable (param) */
3134
        VariableParameter varPar2 = cast(VariableParameter)functionStatements[1];
1✔
3135
        assert(varPar2);
1✔
3136
        assert(cmp(varPar2.getName(), "j") == 0);
1✔
3137

3138
        /* ThirdFirst statement should be a variable (local) */
3139
        Variable varLocal = cast(Variable)functionStatements[2];
1✔
3140
        assert(varLocal);
1✔
3141
        assert(cmp(varLocal.getName(), "k") == 0);
1✔
3142

3143
        /* Last statement should be a return */
3144
        assert(cast(ReturnStmt)functionStatements[3]);
1✔
3145
    }
3146
    catch(TError e)
3147
    {
3148
        assert(false);
3149
    }
3150
}
3151

3152
/**
3153
 * While loop test case (nested)
3154
 */
3155
unittest
3156
{
3157
    string sourceCode = `
1✔
3158
module parser_while;
3159

3160
void function()
3161
{
3162
    int i = 0;
3163
    while(i)
3164
    {
3165
        int p = i;
3166

3167
        while(i)
3168
        {
3169
            int f;
3170
        }
3171
    }
3172
}
3173
`;
3174

3175

3176
    File dummyFile;
2✔
3177
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
3178

3179
    try
3180
    {
3181
        compiler.doLex();
1✔
3182
        assert(true);
1✔
3183
    }
3184
    catch(LexerException e)
3185
    {
3186
        assert(false);
3187
    }
3188
    
3189
    try
3190
    {
3191
        compiler.doParse();
1✔
3192
        Program program = compiler.getProgram();
1✔
3193

3194
        // There is only a single module in this program
3195
        Module modulle = program.getModules()[0];
1✔
3196

3197
        /* Module name must be parser_while */
3198
        assert(cmp(modulle.getName(), "parser_while")==0);
1✔
3199
        TypeChecker tc = new TypeChecker(compiler);
1✔
3200

3201
        
3202
        /* Find the function named `function` */
3203
        Entity func = tc.getResolver().resolveBest(modulle, "function");
1✔
3204
        assert(func);
1✔
3205
        assert(cast(Function)func); // Ensure it is a Funciton
1✔
3206

3207
        /* Get the function's body */
3208
        Container funcContainer = cast(Container)func;
1✔
3209
        assert(funcContainer);
1✔
3210
        Statement[] functionStatements = funcContainer.getStatements();
1✔
3211
        assert(functionStatements.length == 2);
1✔
3212

3213
        /* Find the while loop within the function's body */
3214
        WhileLoop potentialWhileLoop;
1✔
3215
        foreach(Statement curStatement; functionStatements)
7✔
3216
        {
3217
            potentialWhileLoop = cast(WhileLoop)curStatement;
2✔
3218

3219
            if(potentialWhileLoop)
2✔
3220
            {
3221
                break;
1✔
3222
            }
3223
        }
3224

3225
        /* This must pass if we found the while loop */
3226
        assert(potentialWhileLoop);
1✔
3227

3228
        /* Grab the branch of the while loop */
3229
        Branch whileBranch = potentialWhileLoop.getBranch();
1✔
3230
        assert(whileBranch);
1✔
3231

3232
        /* Ensure that we have one statement in this branch's body and that it is a Variable and next is a while loop */
3233
        Statement[] whileBranchStatements = whileBranch.getStatements();
1✔
3234
        assert(whileBranchStatements.length == 2);
1✔
3235
        assert(cast(Variable)whileBranchStatements[0]);
1✔
3236
        assert(cast(WhileLoop)whileBranchStatements[1]);
1✔
3237

3238
        /* The inner while loop also has a similiar structure, only one variable */
3239
        WhileLoop innerLoop = cast(WhileLoop)whileBranchStatements[1];
1✔
3240
        Branch innerWhileBranch = innerLoop.getBranch();
1✔
3241
        assert(innerWhileBranch);
1✔
3242
        Statement[] innerWhileBranchStatements = innerWhileBranch.getStatements();
1✔
3243
        assert(innerWhileBranchStatements.length == 1);
1✔
3244
        assert(cast(Variable)innerWhileBranchStatements[0]);
1✔
3245
    }
3246
    catch(TError e)
3247
    {
3248
        assert(false);
3249
    }
3250
}
3251

3252
/**
3253
 *
3254
 */
3255
unittest
3256
{
3257
    string sourceCode = `
1✔
3258
module simple_pointer;
3259

3260
int j;
3261

3262
void myFunc(int* ptr, int** ptrPtr, int*** ptrPtrPtr)
3263
{
3264

3265
}
3266

3267
int** funcPtr()
3268
{
3269
    return 1;
3270
}
3271

3272
int function(int* ptr)
3273
{
3274
    *ptr = 2+2;
3275

3276
    return 0;
3277
}
3278

3279
int thing()
3280
{
3281
    int discardExpr = function(&j);
3282
    int** l;
3283

3284
    return 0;
3285
}
3286
`;
3287
    File dummyFile;
2✔
3288
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
3289

3290
    try
3291
    {
3292
        compiler.doLex();
1✔
3293
        assert(true);
1✔
3294
    }
3295
    catch(LexerException e)
3296
    {
3297
        assert(false);
3298
    }
3299
    
3300
    try
3301
    {
3302
        compiler.doParse();
1✔
3303
        Program program = compiler.getProgram();
1✔
3304

3305
        // There is only a single module in this program
3306
        Module modulle = program.getModules()[0];
1✔
3307

3308
        /* Module name must be simple_pointer */
3309
        assert(cmp(modulle.getName(), "simple_pointer")==0);
1✔
3310
        TypeChecker tc = new TypeChecker(compiler);
1✔
3311

3312
        /* Find the function named `function` */
3313
        Entity funcFunction = tc.getResolver().resolveBest(modulle, "function");
1✔
3314
        assert(funcFunction);
1✔
3315
        assert(cast(Function)funcFunction); // Ensure it is a Function
1✔
3316

3317
        /* Find the function named `thing` */
3318
        Entity funcThing = tc.getResolver().resolveBest(modulle, "thing");
1✔
3319
        assert(funcThing);
1✔
3320
        assert(cast(Function)funcThing); // Ensure it is a Function
1✔
3321

3322
        /* Find the variable named `j` */
3323
        Entity variableJ = tc.getResolver().resolveBest(modulle, "j");
1✔
3324
        assert(variableJ);
1✔
3325
        assert(cast(Variable)variableJ);
1✔
3326

3327

3328
        /* Get the `function`'s body */
3329
        Container funcFunctionContainer = cast(Container)funcFunction;
1✔
3330
        assert(funcFunctionContainer);
1✔
3331
        Statement[] funcFunctionStatements = funcFunctionContainer.getStatements();
1✔
3332
        assert(funcFunctionStatements.length == 3); // Remember this includes the parameters
1✔
3333

3334
        /* Get the `thing`'s body */
3335
        Container funcThingContainer = cast(Container)funcThing;
1✔
3336
        assert(funcThingContainer);
1✔
3337
        Statement[] funcThingStatements = funcThingContainer.getStatements();
1✔
3338
        assert(funcThingStatements.length == 3);
1✔
3339

3340
        // TODO: Finish this
3341
        // TODO: Add a check for the Statement types in the bodies, the arguments and the parameters
3342
    }
3343
    catch(TError e)
3344
    {
3345
        assert(false);
3346
    }
3347
}
3348

3349
/**
3350
 * Do-while loop tests (TODO: Add this)
3351
 */
3352

3353
/**
3354
 * For loop tests (TODO: FInish this)
3355
 */
3356
unittest
3357
{
3358
    string sourceCode = `
1✔
3359
module parser_for;
3360

3361
void function()
3362
{
3363
    int i = 0;
3364
    for(int idx = i; idx < i; idx=idx+1)
3365
    {
3366
        i = i + 1;
3367

3368
        for(int idxInner = idx; idxInner < idx; idxInner = idxInner +1)
3369
        {
3370

3371
        }
3372
    }
3373
}
3374
`;
3375

3376
    File dummyFile;
2✔
3377
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
3378

3379
    try
3380
    {
3381
        compiler.doLex();
1✔
3382
        assert(true);
1✔
3383
    }
3384
    catch(LexerException e)
3385
    {
3386
        assert(false);
3387
    }
3388
    
3389
    try
3390
    {
3391
        compiler.doParse();
1✔
3392
        Program program = compiler.getProgram();
1✔
3393

3394
        // There is only a single module in this program
3395
        Module modulle = program.getModules()[0];
1✔
3396

3397
        /* Module name must be parser_for */
3398
        assert(cmp(modulle.getName(), "parser_for")==0);
1✔
3399
        TypeChecker tc = new TypeChecker(compiler);
1✔
3400

3401
        
3402
        /* Find the function named `function` */
3403
        Entity func = tc.getResolver().resolveBest(modulle, "function");
1✔
3404
        assert(func);
1✔
3405
        assert(cast(Function)func); // Ensure it is a Funciton
1✔
3406

3407
        /* Get the function's body */
3408
        Container funcContainer = cast(Container)func;
1✔
3409
        assert(funcContainer);
1✔
3410
        Statement[] functionStatements = funcContainer.getStatements();
1✔
3411
        assert(functionStatements.length == 2);
1✔
3412

3413
        /* First statement should be a variable declaration */
3414
        assert(cast(Variable)functionStatements[0]);
1✔
3415

3416
        /* Next statement should be a for loop */
3417
        ForLoop outerLoop = cast(ForLoop)functionStatements[1];
1✔
3418
        assert(outerLoop);
1✔
3419

3420
        /* Get the body of the for-loop which should be [preRun, Branch] */
3421
        Statement[] outerLoopBody = outerLoop.getStatements();
1✔
3422
        assert(outerLoopBody.length == 2);
1✔
3423

3424
        /* We should have a preRun Statement */
3425
        assert(outerLoop.hasPreRunStatement());
1✔
3426

3427
        /* The first should be the [preRun, ] which should be a Variable (declaration) */
3428
        Variable preRunVarDec = cast(Variable)(outerLoopBody[0]);
1✔
3429
        assert(preRunVarDec);
1✔
3430

3431
        /* Next up is the branch */
3432
        Branch outerLoopBranch = cast(Branch)outerLoopBody[1];
1✔
3433
        assert(outerLoopBranch);
1✔
3434

3435
        /* The branch should have a condition */
3436
        Expression outerLoopBranchCondition = outerLoopBranch.getCondition();
1✔
3437
        assert(outerLoopBranchCondition);
1✔
3438

3439
        /* The branch should have a body made up of [varAssStdAlone, forLoop, postIteration] */
3440
        Statement[] outerLoopBranchBody = outerLoopBranch.getStatements();
1✔
3441
        assert(outerLoopBranchBody.length == 3);
1✔
3442

3443
        /* Check for [varAssStdAlone, ] */
3444
        VariableAssignmentStdAlone outerLoopBranchBodyStmt1 = cast(VariableAssignmentStdAlone)outerLoopBranchBody[0];
1✔
3445
        assert(outerLoopBranchBodyStmt1);
1✔
3446

3447
        /* Check for [, forLoop, ] */
3448
        ForLoop innerLoop = cast(ForLoop)outerLoopBranchBody[1];
1✔
3449
        assert(innerLoop);
1✔
3450

3451
        /* Check for [, postIteration] */
3452
        VariableAssignmentStdAlone outerLoopBranchBodyStmt3 = cast(VariableAssignmentStdAlone)outerLoopBranchBody[2];
1✔
3453
        assert(outerLoopBranchBodyStmt3);
1✔
3454

3455
        /* Start examining the inner for-loop */
3456
        Branch innerLoopBranch = innerLoop.getBranch();
1✔
3457
        assert(innerLoopBranch);
1✔
3458

3459
        /* The branch should have a condition */
3460
        Expression innerLoopBranchCondition = innerLoopBranch.getCondition();
1✔
3461
        assert(innerLoopBranchCondition);
1✔
3462

3463
        /* The branch should have a body made up of [postIteration] */
3464
        Statement[] innerLoopBranchBody = innerLoopBranch.getStatements();
1✔
3465
        assert(innerLoopBranchBody.length == 1);
1✔
3466
    }
3467
    catch(TError e)
3468
    {
3469
        assert(false);
3470
    }
3471
}
3472

3473
/**
3474
 * If statement tests
3475
 */
3476
unittest
3477
{
3478
    string sourceCode = `
1✔
3479
module parser_if;
3480

3481
void function()
3482
{
3483
    int i = 0;
3484
    if(i)
3485
    {
3486
        int p = -i;
3487
    }
3488
    else if(i)
3489
    {
3490
        int p = 3+(i*9);
3491
    }
3492
    else if(i)
3493
    {
3494

3495
    }
3496
    else
3497
    {
3498

3499
    }
3500
}
3501
`;
3502

3503
    File dummyFile;
2✔
3504
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
3505

3506
    try
3507
    {
3508
        compiler.doLex();
1✔
3509
        assert(true);
1✔
3510
    }
3511
    catch(LexerException e)
3512
    {
3513
        assert(false);
3514
    }
3515
    
3516
    try
3517
    {
3518
        compiler.doParse();
1✔
3519
        Program program = compiler.getProgram();
1✔
3520

3521
        // There is only a single module in this program
3522
        Module modulle = program.getModules()[0];
1✔
3523

3524
        /* Module name must be parser_if */
3525
        assert(cmp(modulle.getName(), "parser_if")==0);
1✔
3526
        TypeChecker tc = new TypeChecker(compiler);
1✔
3527

3528
        /* Find the function named `function` */
3529
        Entity func = tc.getResolver().resolveBest(modulle, "function");
1✔
3530
        assert(func);
1✔
3531
        assert(cast(Function)func); // Ensure it is a Funciton
1✔
3532

3533
        /* Get the function's body */
3534
        Container funcContainer = cast(Container)func;
1✔
3535
        assert(funcContainer);
1✔
3536
        Statement[] functionStatements = funcContainer.getStatements();
1✔
3537
        assert(functionStatements.length == 2);
1✔
3538

3539
        /* Second statement is an if statemnet */
3540
        IfStatement ifStatement = cast(IfStatement)functionStatements[1];
1✔
3541
        assert(ifStatement);
1✔
3542
        
3543
        /* Extract the branches (should be 4) */
3544
        Branch[] ifStatementBranches = ifStatement.getBranches();
1✔
3545
        assert(ifStatementBranches.length == 4);
1✔
3546

3547
        /* First branch should have one statement which is a variable declaration */
3548
        Statement[] firstBranchBody = ifStatementBranches[0].getStatements();
1✔
3549
        assert(firstBranchBody.length == 1);
1✔
3550
        assert(cast(Variable)firstBranchBody[0]);
1✔
3551

3552
        /* Second branch should have one statement which is a variable declaration */
3553
        Statement[] secondBranchBody = ifStatementBranches[1].getStatements();
1✔
3554
        assert(secondBranchBody.length == 1);
1✔
3555
        assert(cast(Variable)secondBranchBody[0]);
1✔
3556

3557
        /* Third branch should have no statements */
3558
        Statement[] thirdBranchBody = ifStatementBranches[2].getStatements();
1✔
3559
        assert(thirdBranchBody.length == 0);
1✔
3560

3561
        /* Forth branch should have no statements */
3562
        Statement[] fourthBranchBody = ifStatementBranches[3].getStatements();
1✔
3563
        assert(fourthBranchBody.length == 0);
1✔
3564

3565
        // TODO: @Tristan Add this
3566
    }
3567
    catch(TError e)
3568
    {
3569
        assert(false);
3570
    }
3571
}
3572

3573
/**
3574
 * Function test case
3575
 *
3576
 * Test: A function of a non-void return type
3577
 * must have a return statement
3578
 */
3579
unittest
3580
{
3581
    string sourceCode = `
1✔
3582
module myModule;
3583

3584
int wrongFunction()
3585
{
3586

3587
}
3588
`;
3589

3590
    File dummyFile;
2✔
3591
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
3592

3593
    try
3594
    {
3595
        compiler.doLex();
1✔
3596
        assert(true);
1✔
3597
    }
3598
    catch(LexerException e)
3599
    {
3600
        assert(false);
3601
    }
3602
    
3603
    try
3604
    {
3605
        compiler.doParse();
1✔
3606

3607
        assert(false);
3608
    }
3609
    catch(ParserException)
3610
    {
3611
        assert(true);
1✔
3612
    }
3613
    catch(TError)
3614
    {
3615
        assert(false);
3616
    }
3617
}
3618

3619
/**
3620
 * Importing of modules test
3621
 */
3622
unittest
3623
{
3624
    string inputFilePath = "source/tlang/testing/modules/a.t";
1✔
3625
    string sourceCode = gibFileData(inputFilePath);
1✔
3626

3627
    File dummyFile;
2✔
3628
    Compiler compiler = new Compiler(sourceCode, inputFilePath, dummyFile);
1✔
3629

3630
    try
3631
    {
3632
        compiler.doLex();
1✔
3633
    }
3634
    catch(LexerException e)
3635
    {
3636
        assert(false);
3637
    }
3638
    
3639
    try
3640
    {
3641
        compiler.doParse();
1✔
3642

3643

3644
        Program program = compiler.getProgram();
1✔
3645

3646
        // There should be 3 modules in this program
3647
        Module[] modules = program.getModules();
1✔
3648
        assert(modules.length == 3);
1✔
3649

3650
        TypeChecker tc = new TypeChecker(compiler);
1✔
3651
        Resolver resolver = tc.getResolver();
1✔
3652

3653
        // There should be modules named `a`, `b` and `c`
3654
        Module module_a = cast(Module)resolver.resolveBest(program, "a");
1✔
3655
        assert(module_a);
1✔
3656
        Module module_b = cast(Module)resolver.resolveBest(program, "b");
1✔
3657
        assert(module_b);
1✔
3658
        Module module_c = cast(Module)resolver.resolveBest(program, "c");
1✔
3659
        assert(module_c);
1✔
3660

3661
        // There should be a function named `main` in module `a`
3662
        Function a_func = cast(Function)resolver.resolveBest(module_a, "main");
1✔
3663
        assert(a_func);
1✔
3664

3665
        // There should be a function named `doThing` in module `b`
3666
        Function b_func = cast(Function)resolver.resolveBest(module_b, "doThing");
1✔
3667
        assert(b_func);
1✔
3668

3669
        // There should be a function named `k` in module `c`
3670
        Function c_func = cast(Function)resolver.resolveBest(module_c, "k");
1✔
3671
        assert(c_func);
1✔
3672
    }
3673
    catch(TError e)
3674
    {
3675
        assert(false);
3676
    }
3677
}
3678

3679
/**
3680
 * Comments tests
3681
 */
3682
unittest
3683
{
3684
    string sourceCode = `
1✔
3685
module comments;
3686

3687
// Comment for no one
3688

3689
// Comment for Elise
3690
int p;
3691

3692
/**
3693
 * This is a comment on a function
3694
 *  
3695
 *
3696
 * @param i This is the i
3697
 * @param p This is the p
3698
 * @throws Exception worst exception eva
3699
 * @return Void, so nothing
3700
 */
3701
void function(int i, int p)
3702
{
3703

3704
}
3705
`;
3706

3707
    File dummyFile;
2✔
3708
    Compiler compiler = new Compiler(sourceCode, "legitidk.t", dummyFile);
1✔
3709

3710
    try
3711
    {
3712
        compiler.doLex();
1✔
3713
        assert(true);
1✔
3714
    }
3715
    catch(LexerException e)
3716
    {
3717
        assert(false);
3718
    }
3719
    
3720
    try
3721
    {
3722
        compiler.doParse();
1✔
3723
        Program program = compiler.getProgram();
1✔
3724

3725
        // There is only a single module in this program
3726
        Module modulle = program.getModules()[0];
1✔
3727

3728
        TypeChecker tc = new TypeChecker(compiler);
1✔
3729

3730
        /* Find the variable named `p` and get its comment */
3731
        Entity varEnt = tc.getResolver().resolveBest(modulle, "p");
1✔
3732
        Variable var = cast(Variable)varEnt;
1✔
3733
        Comment varComment = var.getComment();
1✔
3734
        assert(varComment);
1✔
3735
        assert(varComment.getContent() == "Comment for Elise");
1✔
3736

3737
        /* Find the function named `function` and get its comment */
3738
        Entity funcEnt = tc.getResolver().resolveBest(modulle, "function");
1✔
3739
        Function func = cast(Function)funcEnt;
1✔
3740
        Comment funcComment = func.getComment();
1✔
3741
        assert(funcComment);
1✔
3742

3743
        /* Ensure the comment is as we want */
3744
        DEBUG(funcComment.getContent());
1✔
3745
        assert(funcComment.getContent() == "This is a comment on a function  ");
1✔
3746

3747
        ParamDoc* iPDoc, pPDoc;
2✔
3748
        ParamDoc[string] paramDocs = funcComment.getAllParamDocs();
1✔
3749
        assert((iPDoc = "i" in paramDocs) !is null);
1✔
3750
        assert((pPDoc = "p" in paramDocs) !is null);
1✔
3751
        assert(iPDoc.getParam() == "i");
1✔
3752
        assert(iPDoc.getDescription() == "This is the i");
1✔
3753
        assert(pPDoc.getParam() == "p");
1✔
3754
        assert(pPDoc.getDescription() == "This is the p");
1✔
3755

3756
        ExceptionDoc eDoc;
1✔
3757
        bool found;
1✔
3758
        foreach(DocStr dc; funcComment.getDocStrings())
10✔
3759
        {
3760
            if((found = dc.getExceptionDoc(eDoc)) == true)
3✔
3761
            {
3762
                break;
1✔
3763
            }
3764
        }
3765
        assert(found);
1✔
3766
        assert(eDoc.getException() == "Exception");
1✔
3767
        assert(eDoc.getDescription() == "worst exception eva");
1✔
3768

3769
        ReturnsDoc retDoc;
1✔
3770
        assert(funcComment.getReturnDoc(retDoc));
1✔
3771
        assert(retDoc.getDescription() == "Void, so nothing");
1✔
3772
    }
3773
    catch(TError e)
3774
    {
3775
        assert(false);
3776
    }
3777
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc