• 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

82.14
/source/tlang/compiler/codegen/emit/dgen.d
1
module tlang.compiler.codegen.emit.dgen;
2

3
import tlang.compiler.codegen.emit.core;
4
import tlang.compiler.typecheck.core;
5
import std.container.slist : SList;
6
import tlang.compiler.codegen.instruction;
7
import std.stdio;
8
import std.file;
9
import std.conv : to;
10
import std.string : cmp;
11
import tlang.misc.logging;
12
import std.range : walkLength;
13
import std.string : wrap;
14
import std.process : spawnProcess, Pid, ProcessException, wait;
15
import tlang.compiler.typecheck.dependency.core : Context, FunctionData, DNode;
16
import tlang.compiler.codegen.mapper.core;
17
import tlang.compiler.symbols.data : SymbolType, Variable, Function, VariableParameter;
18
import tlang.compiler.symbols.check : getCharacter;
19
import tlang.misc.utils : Stack;
20
import tlang.compiler.symbols.typing.core;
21
import tlang.compiler.configuration : CompilerConfiguration;
22
import tlang.compiler.symbols.containers : Module;
23
import std.format : format;
24
import std.datetime.stopwatch : StopWatch, AutoStart;
25
import std.datetime.stopwatch : Duration, dur;
26
import tlang.compiler.codegen.emit.dgen_types : DGenException;
27

28
public final class DCodeEmitter : CodeEmitter
29
{
30
    /** 
31
     * Whether or not symbol mappi g should
32
     * apply to identifiers
33
     */
34
    private bool symbolMapping;
35

36
    // NOTE: In future store the mapper in the config please
37
    this(TypeChecker typeChecker, File file, CompilerConfiguration config, SymbolMapper mapper)
23✔
38
    {
39
        super(typeChecker, file, config, mapper);
23✔
40

41
        // By default symbols will be mapped
42
        enableSymbolMapping();
23✔
43
    }
44

45
    /** 
46
     * Enables symbol mapping
47
     */
48
    private void enableSymbolMapping()
49
    {
50
             this.symbolMapping = true;
23✔
51
    }
52

53
    /** 
54
     * Disables symbol mapping
55
     */
56
    private void disableSymbolMapping()
57
    {
58
             this.symbolMapping = false;
×
59
    }
60

61
    private ulong transformDepth = 0;
62

63
    private string genTabs(ulong count)
64
    {
65
        string tabStr;
65✔
66

67
        /* Only generate tabs if enabled in compiler config */
68
        if(config.getConfig("dgen:pretty_code").flag())
65✔
69
        {
70
            for(ulong i = 0; i < count; i++)
348✔
71
            {
72
                tabStr~="\t";
109✔
73
            }
74
        }
75
        
76
        return tabStr;
65✔
77
    }
78

79
    /** 
80
     * Given an instance of a Type this will transform it to a string
81
     *
82
     * Params:
83
     *   typeIn = The Type to transform
84
     *
85
     * Returns:  The string representation of the transformed type
86
     */
87
    public string typeTransform(Type typeIn)
88
    {
89
        string stringRepr;
223✔
90

91
        // TODO: Some types will ident transform
92

93
        /* Pointer types */
94
        if(cast(Pointer)typeIn)
223✔
95
        {
96
            /* Extract type being pointed to */
97
            Pointer pointerType = cast(Pointer)typeIn;       
45✔
98
            Type referType = pointerType.getReferredType();
45✔
99

100
            /* The type is then `transform(<refertype>)*` */
101
            return typeTransform(referType)~"*";
45✔
102
        }
103
        /* Integral types transformation */
104
        else if(cast(Integer)typeIn)
178✔
105
        {
106
            Integer integralType = cast(Integer)typeIn;
146✔
107

108
            /* u<>_t or <>_t (Determine signedness) */
109
            string typeString = integralType.isSigned() ? "int" : "uint";
292✔
110

111
            /* Width of integer */
112
            typeString ~= to!(string)(integralType.getSize()*8);
146✔
113

114
            /* Trailing `_t` */
115
            typeString ~= "_t";
146✔
116

117
            return typeString;
146✔
118
        }
119
        /* Void type */
120
        else if(cast(Void)typeIn)
32✔
121
        {
122
            return "void";
26✔
123
        }
124
        /* Stack-based array type */
125
        else if(cast(StackArray)typeIn)
6✔
126
        {
127
            // TODO: Still implement stakc-based arrays
128
            // we won't be able to tyoe transform just here
129
            // ... as we need <componentType> <varName>[<arraySize>]
130
            // ... hence this must be performed in avriable declaration
131
            StackArray stackArray = cast(StackArray)typeIn;
6✔
132
            
133
            return typeTransform(stackArray.getComponentType());
6✔
134
            // return "KAK TODO";
135
        }
136

137
        ERROR("Type transform unimplemented for type '"~to!(string)(typeIn)~"'");
×
138
        assert(false);
139
        // return stringRepr;
140
    }
141

142

143
    public override string transform(const Instruction instruction)
144
    {
145
        writeln("\n");
449✔
146
        DEBUG("transform(): "~to!(string)(instruction));
449✔
147
        transformDepth++;
449✔
148

149
        // The data to emit
150
        string emmmmit;
449✔
151

152
        // At any return decrement the depth
153
        scope(exit)
154
        {
155
            transformDepth--;
449✔
156
        }
157

158
        /* VariableAssignmentInstr */
159
        if(cast(VariableAssignmentInstr)instruction)
449✔
160
        {
161
            DEBUG("type: VariableAssignmentInstr");
25✔
162

163
            VariableAssignmentInstr varAs = cast(VariableAssignmentInstr)instruction;
25✔
164
            Context context = varAs.getContext();
25✔
165

166
            DEBUG("Is ContextNull?: "~to!(string)(context is null));
25✔
167
            DEBUG("Wazza contect: "~to!(string)(context.container));
25✔
168
            auto typedEntityVariable = typeChecker.getResolver().resolveBest(context.getContainer(), varAs.varName); //TODO: Remove `auto`
25✔
169
            DEBUG("Hi"~to!(string)(varAs));
25✔
170
            DEBUG("Hi"~to!(string)(varAs.data));
25✔
171
            DEBUG("Hi"~to!(string)(varAs.data.getInstrType()));
25✔
172

173
            // NOTE: For tetsing issue #94 coercion (remove when done)
174
            string typeName = (cast(Type)varAs.data.getInstrType()).getName();
25✔
175
            DEBUG("VariableAssignmentInstr: The data to assign's type is: "~typeName);
25✔
176

177

178
            /* If it is not external */
179
            if(!typedEntityVariable.isExternal())
25✔
180
            {
181
                // FIXME: Set proper scope type
182
                string renamedSymbol = mapper.map(typedEntityVariable, ScopeType.GLOBAL);
25✔
183

184
                emmmmit = renamedSymbol~" = "~transform(varAs.data)~";";
25✔
185
            }
186
            /* If it is external */
187
            else
188
            {
189
                emmmmit = typedEntityVariable.getName()~" = "~transform(varAs.data)~";";
×
190
            }
191
        }
192
        /* VariableDeclaration */
193
        else if(cast(VariableDeclaration)instruction)
424✔
194
        {
195
            DEBUG("type: VariableDeclaration");
52✔
196

197
            VariableDeclaration varDecInstr = cast(VariableDeclaration)instruction;
52✔
198
            Context context = varDecInstr.getContext();
52✔
199

200
            Variable typedEntityVariable = cast(Variable)typeChecker.getResolver().resolveBest(context.getContainer(), varDecInstr.varName); //TODO: Remove `auto`
52✔
201

202
            /* If the variable is not external */
203
            if(!typedEntityVariable.isExternal())
52✔
204
            {
205
                //NOTE: We should remove all dots from generated symbol names as it won't be valid C (I don't want to say C because
206
                // a custom CodeEmitter should be allowed, so let's call it a general rule)
207
                //
208
                //simple_variables.x -> simple_variables_x
209
                //NOTE: We may need to create a symbol table actually and add to that and use that as these names
210
                //could get out of hand (too long)
211
                // NOTE: Best would be identity-mapping Entity's to a name
212
                // FIXME: Set proper scope type
213
                string renamedSymbol = mapper.map(typedEntityVariable, ScopeType.GLOBAL);
52✔
214

215

216
                // Check if the type is a stack-based array
217
                // ... if so then take make symbolName := `<symbolName>[<stackArraySize>]`
218
                if(cast(StackArray)varDecInstr.varType)
52✔
219
                {
220
                    StackArray stackArray = cast(StackArray)varDecInstr.varType;
6✔
221
                    renamedSymbol~="["~to!(string)(stackArray.getAllocatedSize())~"]";
6✔
222
                }
223

224
                // Check to see if this declaration has an assignment attached
225
                if(typedEntityVariable.getAssignment())
52✔
226
                {
227
                    Value varAssInstr = varDecInstr.getAssignmentInstr();
30✔
228
                    DEBUG("VarDec(with assignment): My assignment type is: "~varAssInstr.getInstrType().getName());
30✔
229

230
                    // Generate the code to emit
231
                    emmmmit = typeTransform(cast(Type)varDecInstr.varType)~" "~renamedSymbol~" = "~transform(varAssInstr)~";";
30✔
232
                }
233
                else
234
                {
235
                    emmmmit = typeTransform(cast(Type)varDecInstr.varType)~" "~renamedSymbol~";";
22✔
236
                }
237
            }
238
            /* If the variable is external */
239
            else
240
            {
241
                emmmmit = "extern "~typeTransform(cast(Type)varDecInstr.varType)~" "~typedEntityVariable.getName()~";";
×
242
            }
243
        }
244
        /* LiteralValue */
245
        else if(cast(LiteralValue)instruction)
372✔
246
        {
247
            DEBUG("type: LiteralValue");
124✔
248

249
            LiteralValue literalValueInstr = cast(LiteralValue)instruction;
124✔
250

251
            emmmmit = to!(string)(literalValueInstr.getLiteralValue());
124✔
252
        }
253
        /* FetchValueVar */
254
        else if(cast(FetchValueVar)instruction)
248✔
255
        {
256
            DEBUG("type: FetchValueVar");
79✔
257

258
            FetchValueVar fetchValueVarInstr = cast(FetchValueVar)instruction;
79✔
259
            Context context = fetchValueVarInstr.getContext();
79✔
260

261
            Variable typedEntityVariable = cast(Variable)typeChecker.getResolver().resolveBest(context.getContainer(), fetchValueVarInstr.varName); //TODO: Remove `auto`
79✔
262

263
            /* If it is not external */
264
            if(!typedEntityVariable.isExternal())
79✔
265
            {
266
                //TODO: THis is giving me kak (see issue #54), it's generating name but trying to do it for the given container, relative to it
267
                //TODO: We might need a version of generateName that is like generatenamebest (currently it acts like generatename, within)
268

269
                // FIXME: Set proper scope type
270
                string renamedSymbol = mapper.map(typedEntityVariable, ScopeType.GLOBAL);
79✔
271

272
                emmmmit = renamedSymbol;
79✔
273
            }
274
            /* If it is external */
275
            else
276
            {
277
                emmmmit = typedEntityVariable.getName();
×
278
            }
279
        }
280
        /* BinOpInstr */
281
        else if(cast(BinOpInstr)instruction)
169✔
282
        {
283
            DEBUG("type: BinOpInstr");
61✔
284

285
            BinOpInstr binOpInstr = cast(BinOpInstr)instruction;
61✔
286

287
            // TODO: I like having `lhs == rhs` for `==` or comparators but not spaces for `lhs+rhs`
288

289
            /**
290
             * C compiler's do this thing where:
291
             *
292
             * If `<a>` is a pointer and `<b>` is an integer then the
293
             * following pointer arithmetic is allowed:
294
             *
295
             * int* a = (int*)2;
296
             * a = a + b;
297
             *
298
             * But it's WRONG if you do
299
             *
300
             * a = a + (int*)b;
301
             *
302
             * Even though it makes logical sense coercion wise.
303
             *
304
             * Therefore we need to check such a case and yank
305
             * the cast out me thinks.
306
             * 
307
             * See issue #140 (https://deavmi.assigned.network/git/tlang/tlang/issues/140#issuecomment-1892)
308
             */
309
            Type leftHandOpType = (cast(Value)binOpInstr.getLHSInstr()).getInstrType();
61✔
310
            Type rightHandOpType = (cast(Value)binOpInstr.getRHSInstr()).getInstrType();
61✔
311

312
            if(typeChecker.isPointerType(leftHandOpType))
61✔
313
            {
314
                // Sanity check the other side should have been coerced to CastedValueInstruction
315
                CastedValueInstruction cvInstr = cast(CastedValueInstruction)binOpInstr.getRHSInstr();
5✔
316
                assert(cvInstr);
5✔
317

318
                DEBUG("CastedValueInstruction relax setting: Da funk RIGHT ");
5✔
319

320
                // Relax the CV-instr to prevent it from emitting explicit cast code
321
                cvInstr.setRelax(true);
5✔
322
            }
323
            else if(typeChecker.isPointerType(rightHandOpType))
56✔
324
            {
325
                // Sanity check the other side should have been coerced to CastedValueInstruction
326
                CastedValueInstruction cvInstr = cast(CastedValueInstruction)binOpInstr.getLHSInstr();
×
327
                assert(cvInstr);
×
328

329
                DEBUG("CastedValueInstruction relax setting: Da funk LEFT ");
×
330

331
                // Relax the CV-instr to prevent it from emitting explicit cast code
332
                cvInstr.setRelax(true);
×
333
            }
334

335
            emmmmit = transform(binOpInstr.getLHSInstr())~to!(string)(getCharacter(binOpInstr.getOperator()))~transform(binOpInstr.getRHSInstr());
61✔
336
        }
337
        /* FuncCallInstr */
338
        else if(cast(FuncCallInstr)instruction)
108✔
339
        {
340
            DEBUG("type: FuncCallInstr");
15✔
341

342
            FuncCallInstr funcCallInstr = cast(FuncCallInstr)instruction;
15✔
343
            Context context = funcCallInstr.getContext();
15✔
344
            assert(context);
15✔
345

346
            Function functionToCall = cast(Function)typeChecker.getResolver().resolveBest(context.getContainer(), funcCallInstr.functionName); //TODO: Remove `auto`
15✔
347

348
            // TODO: SymbolLookup?
349

350
            string emit = functionToCall.getName()~"(";
15✔
351

352
            //TODO: Insert argument passimng code here
353
            //NOTE: Typechecker must have checked for passing arguments to a function that doesn't take any, for example
354

355
            //NOTE (Behaviour): We may want to actually have an preinliner for these arguments
356
            //such to enforce a certain ordering. I believe this should be done in the emitter stage,
357
            //so it is best placed here
358
            if(functionToCall.hasParams())
15✔
359
            {
360
                Value[] argumentInstructions = funcCallInstr.getEvaluationInstructions();
15✔
361
                string argumentString;
15✔
362
                
363
                for(ulong argIdx = 0; argIdx < argumentInstructions.length; argIdx++)
72✔
364
                {
365
                    Value currentArgumentInstr = argumentInstructions[argIdx];
21✔
366
                    argumentString~=transform(currentArgumentInstr);
21✔
367

368
                    if(argIdx != (argumentInstructions.length-1))
21✔
369
                    {
370
                        argumentString~=", ";
6✔
371
                    }
372
                }
373

374
                emit~=argumentString;
15✔
375
            }
376

377
            emit ~= ")";
15✔
378

379
            // If this is a statement-level function call then tack on a `;`
380
            if(funcCallInstr.isStatementLevel())
15✔
381
            {
382
                emit ~= ";";
2✔
383
            }
384

385
            emmmmit = emit;
15✔
386
        }
387
        /* ReturnInstruction */
388
        else if(cast(ReturnInstruction)instruction)
93✔
389
        {
390
            DEBUG("type: ReturnInstruction");
21✔
391

392
            ReturnInstruction returnInstruction = cast(ReturnInstruction)instruction;
21✔
393
            Context context = returnInstruction.getContext();
21✔
394
            assert(context);
21✔
395

396
            /* If there is an expression returned */
397
            if(returnInstruction.hasReturnExpInstr())
21✔
398
            {
399
                /* Get the return expression instruction */
400
                Value returnExpressionInstr = returnInstruction.getReturnExpInstr();
21✔
401
                emmmmit = "return "~transform(returnExpressionInstr)~";";
21✔
402
            }
403
            /* Expression-less return */
404
            else
405
            {
406
                emmmmit = "return;";
×
407
            }
408
        }
409
        /**
410
        * If statements (IfStatementInstruction)
411
        */
412
        else if(cast(IfStatementInstruction)instruction)
72✔
413
        {
414
            IfStatementInstruction ifStatementInstruction = cast(IfStatementInstruction)instruction;
8✔
415

416
            BranchInstruction[] branchInstructions = ifStatementInstruction.getBranchInstructions();
8✔
417
            DEBUG("Holla"~to!(string)(branchInstructions));
8✔
418

419
            string emit;
8✔
420

421
            for(ulong i = 0; i < branchInstructions.length; i++)
46✔
422
            {
423
                BranchInstruction curBranchInstr = branchInstructions[i];
15✔
424

425
                if(curBranchInstr.hasConditionInstr())
15✔
426
                {
427
                    Value conditionInstr = cast(Value)curBranchInstr.getConditionInstr();
11✔
428

429
                    string hStr = (i == 0) ? "if" : genTabs(transformDepth)~"else if";
22✔
430

431
                    emit~=hStr~"("~transform(conditionInstr)~")\n";
11✔
432

433
                    emit~=genTabs(transformDepth)~"{\n";
11✔
434

435
                    foreach(Instruction branchBodyInstr; curBranchInstr.getBodyInstructions())
78✔
436
                    {
437
                        emit~=genTabs(transformDepth)~"\t"~transform(branchBodyInstr)~"\n";
15✔
438
                    }
439

440
                    emit~=genTabs(transformDepth)~"}\n";
11✔
441
                }
442
                else
443
                {
444
                    emit~=genTabs(transformDepth)~"else\n";
4✔
445

446
                    emit~=genTabs(transformDepth)~"{\n";
4✔
447

448
                    foreach(Instruction branchBodyInstr; curBranchInstr.getBodyInstructions())
18✔
449
                    {
450
                        emit~=genTabs(transformDepth)~"\t"~transform(branchBodyInstr)~"\n";
2✔
451
                    }
452

453
                    emit~=genTabs(transformDepth)~"}\n";
4✔
454
                }
455
            }
456

457
            emmmmit = emit;
8✔
458
        }
459
        /**
460
        * While loops (WhileLoopInstruction)
461
        *
462
        * TODO: Add do-while check
463
        */
464
        else if(cast(WhileLoopInstruction)instruction)
64✔
465
        {
466
            WhileLoopInstruction whileLoopInstr = cast(WhileLoopInstruction)instruction;
1✔
467

468
            BranchInstruction branchInstr = whileLoopInstr.getBranchInstruction();
1✔
469
            Value conditionInstr = branchInstr.getConditionInstr();
1✔
470
            Instruction[] bodyInstructions = branchInstr.getBodyInstructions();
1✔
471

472
            string emit;
1✔
473

474
            /* Generate the `while(<expr>)` and opening curly brace */
475
            emit = "while("~transform(conditionInstr)~")\n";
1✔
476
            emit~=genTabs(transformDepth)~"{\n"; 
1✔
477

478
            /* Transform each body statement */
479
            foreach(Instruction curBodyInstr; bodyInstructions)
18✔
480
            {
481
                emit~=genTabs(transformDepth)~"\t"~transform(curBodyInstr)~"\n";
5✔
482
            }
483

484
            /* Closing curly brace */
485
            emit~=genTabs(transformDepth)~"}";
1✔
486

487
            emmmmit = emit;
1✔
488
        }
489
        /**
490
        * For loops (ForLoopInstruction)
491
        */
492
        else if(cast(ForLoopInstruction)instruction)
63✔
493
        {
494
            ForLoopInstruction forLoopInstr = cast(ForLoopInstruction)instruction;
1✔
495

496
            BranchInstruction branchInstruction = forLoopInstr.getBranchInstruction();
1✔
497
            Value conditionInstr = branchInstruction.getConditionInstr();
1✔
498
            Instruction[] bodyInstructions = branchInstruction.getBodyInstructions();
1✔
499

500
            string emit = "for(";
1✔
501

502
            // Emit potential pre-run instruction
503
            emit ~= forLoopInstr.hasPreRunInstruction() ? transform(forLoopInstr.getPreRunInstruction()) : ";";
2✔
504

505
            // Condition
506
            emit ~= transform(conditionInstr)~";";
1✔
507

508
            // NOTE: We are leaving the post-iteration blank due to us including it in the body
509
            // TODO: We can hoist bodyInstructions[$] maybe if we want to generate it as C-for-loops
510
            // if(forLoopInstr.hasPostIterationInstruction())
511
            emit ~= ")\n";
1✔
512

513
            // Open curly (begin body)
514
            emit~=genTabs(transformDepth)~"{\n"; 
1✔
515

516
            /* Transform each body statement */
517
            foreach(Instruction curBodyInstr; bodyInstructions)
9✔
518
            {
519
                emit~=genTabs(transformDepth)~"\t"~transform(curBodyInstr)~"\n";
2✔
520
            }
521

522
            // Close curly (body end)
523
            emit~=genTabs(transformDepth)~"}"; 
1✔
524

525
            emmmmit = emit;
1✔
526
        }
527
        /**
528
        * Unary operators (UnaryOpInstr)
529
        */
530
        else if(cast(UnaryOpInstr)instruction)
62✔
531
        {
532
            UnaryOpInstr unaryOpInstr = cast(UnaryOpInstr)instruction;
8✔
533
            Value operandInstruction = cast(Value)unaryOpInstr.getOperand();
8✔
534
            assert(operandInstruction);
8✔
535

536
            string emit;
8✔
537
            
538
            /* The operator's symbol */
539
            emit ~= getCharacter(unaryOpInstr.getOperator());
8✔
540

541
            /* Transform the operand */
542
            emit ~= transform(operandInstruction);
8✔
543

544
            emmmmit = emit;
8✔
545
        }
546
        /**
547
        * Pointer dereference assignment (PointerDereferenceAssignmentInstruction)
548
        */
549
        else if(cast(PointerDereferenceAssignmentInstruction)instruction)
54✔
550
        {
551
            PointerDereferenceAssignmentInstruction pointerDereferenceAssignmentInstruction = cast(PointerDereferenceAssignmentInstruction)instruction;
6✔
552
            Value lhsPtrAddrExprInstr = pointerDereferenceAssignmentInstruction.getPointerEvalInstr();
6✔
553
            assert(lhsPtrAddrExprInstr);
6✔
554
            Value rhsAssExprInstr = pointerDereferenceAssignmentInstruction.getAssExprInstr();
6✔
555
            assert(rhsAssExprInstr);
6✔
556

557
            string emit;
6✔
558

559
            /* Star followed by transformation of the pointer address expression */
560
            string starsOfLiberty;
6✔
561
            for(ulong i = 0; i < pointerDereferenceAssignmentInstruction.getDerefCount(); i++)
24✔
562
            {
563
                starsOfLiberty ~= "*";
6✔
564
            }
565
            emit ~= starsOfLiberty~"("~transform(lhsPtrAddrExprInstr)~")";
6✔
566

567
            /* Assignment operator follows */
568
            emit ~= " = ";
6✔
569

570
            /* Expression to be assigned on the right hand side */
571
            emit ~= transform(rhsAssExprInstr)~";";
6✔
572

573

574
            emmmmit = emit;
6✔
575
        }
576
        /**
577
        * Discard instruction (DiscardInstruction)
578
        */
579
        else if(cast(DiscardInstruction)instruction)
48✔
580
        {
581
            DiscardInstruction discardInstruction = cast(DiscardInstruction)instruction;
3✔
582
            Value valueInstruction = discardInstruction.getExpressionInstruction();
3✔
583

584
            string emit;
3✔
585

586
            /* Transform the expression */
587
            emit ~= transform(valueInstruction)~";";
3✔
588

589
            emmmmit = emit;
3✔
590
        }
591
        /**
592
        * Type casting instruction (CastedValueInstruction)
593
        */
594
        else if(cast(CastedValueInstruction)instruction)
45✔
595
        {
596
            CastedValueInstruction castedValueInstruction = cast(CastedValueInstruction)instruction;
17✔
597
            Type castingTo = castedValueInstruction.getCastToType();
17✔
598

599
            // TODO: Dependent on type being casted one must handle different types, well differently (as is case for atleast OOP)
600

601
            Value uncastedInstruction = castedValueInstruction.getEmbeddedInstruction();
17✔
602

603

604
            string emit;
17✔
605

606

607
            /**
608
             * Issue #140
609
             *
610
             * If relaxed then just emit the uncasted instruction
611
             */
612
            if(castedValueInstruction.isRelaxed())
17✔
613
            {
614
                /* The original expression */
615
                emit ~= transform(uncastedInstruction);
5✔
616
            }
617
            else
618
            {
619
                /* Handling of primitive types */
620
                if(cast(Primitive)castingTo)
12✔
621
                {
622
                    /* Add the actual cast */
623
                    emit ~= "("~typeTransform(castingTo)~")";
12✔
624

625
                    /* The expression being casted */
626
                    emit ~= "("~transform(uncastedInstruction)~")";
12✔
627
                }
628
                else
629
                {
630
                    // TODO: Implement this
631
                    ERROR("Non-primitive type casting not yet implemented");
×
632
                    assert(false);
633
                }
634
            }
635

636
            emmmmit = emit;
17✔
637
        }
638
        /** 
639
         * Array indexing (pointer-based arrays)
640
         *
641
         * Handles `myArray[<index>]` where `myArray` is
642
         * of type `int[]` (i.e. `int*`)
643
         */
644
        else if(cast(ArrayIndexInstruction)instruction)
28✔
645
        {
646
            ArrayIndexInstruction arrAssInstr = cast(ArrayIndexInstruction)instruction;
10✔
647

648
            ERROR("TODO: Implement Pointer-array index emit");
10✔
649

650
            DEBUG("ArrayInstr: "~arrAssInstr.getIndexedToInstr().toString());
10✔
651
            DEBUG("ArrayIndexInstr: "~to!(string)(arrAssInstr.getIndexInstr()));
10✔
652

653
            
654
            /* Obtain the entity being indexed */
655
            Value indexed = arrAssInstr.getIndexedToInstr();
10✔
656

657
            /* Obtain the index */
658
            Value index = arrAssInstr.getIndexInstr();
10✔
659

660
            
661
            /** 
662
             * Emit *(<indexedEval>+<index>)
663
             */
664
            string emit;
10✔
665
            emit ~= "*(";
10✔
666

667
            
668
            emit ~= transform(indexed);
10✔
669
            emit ~= "+";
10✔
670
            emit ~= transform(index);
10✔
671
            emit ~= ")";
10✔
672

673

674

675
            // return "*("~transform(indexed)~"+"~transform(index)~")";
676
            emmmmit = emit;
10✔
677
        }
678
        /** 
679
         * Array assignments (pointer-based arrays)
680
         *
681
         * Handles `myArray[<index>] = <expression>` where `myArray`
682
         * is of type `int[]` (i.e. `int*`)
683
         */
684
        else if(cast(ArrayIndexAssignmentInstruction)instruction)
18✔
685
        {
686
            ArrayIndexAssignmentInstruction arrayAssignmentInstr = cast(ArrayIndexAssignmentInstruction)instruction;
6✔
687

688
            /** 
689
             * Obtain the array pointer evaluation
690
             */
691
            ArrayIndexInstruction arrayPtrEval = arrayAssignmentInstr.getArrayPtrEval();
6✔
692

693
            // NOTE: See above
694
            // /** 
695
            //  * Obtain the index being assigned at
696
            //  */
697
            // Value index = arrayAssignmentInstr.getIndexInstr();
698

699
            /** 
700
             * Obtain the expression being assigned
701
             */
702
            Value assignmentInstr = arrayAssignmentInstr.getAssignmentInstr();
6✔
703

704

705
            /** 
706
             * Emit *(<arrayPtrVal>+<indexInstr>) = <assignmentInstr>;
707
             */
708
            string emit;
6✔
709
            // NOTE: Below is done by ArrayIndexInstruction
710
            // emit ~= "*(";
711
            // emit ~= transform(arrayPtrEval);
712
            // emit ~= "+";
713
            // emit ~= transform(index);
714
            // emit ~= ")";
715
            emit ~= transform(arrayPtrEval);
6✔
716

717
            emit ~= " = ";
6✔
718
            emit ~= transform(assignmentInstr);
6✔
719
            emit ~= ";";
6✔
720

721

722
            emmmmit = emit; 
6✔
723
        }
724
        /** 
725
         * Array indexing (stack-based arrays)
726
         *
727
         * Handles `myArray[<index>]` where `myArray` is
728
         * of type `int[<size>]` (i.e. a stack-array)
729
         */
730
        else if(cast(StackArrayIndexInstruction)instruction)
12✔
731
        {
732
            StackArrayIndexInstruction stackArrInstr = cast(StackArrayIndexInstruction)instruction;
8✔
733
            Context context = stackArrInstr.getContext();
8✔
734
            
735
            /* Obtain the stack array variable being indexed */
736
            // TODO: Investigate, nroamlly we do a `FetchValueVar` as like the instr which is fine actually
737
            FetchValueVar array = cast(FetchValueVar)stackArrInstr.getIndexedToInstr();
8✔
738
            assert(array);
8✔
739
            Variable arrayVariable = cast(Variable)typeChecker.getResolver().resolveBest(context.getContainer(), array.varName);
8✔
740

741
            /* Perform symbol mapping */
742
            // FIXME: Set proper scope type
743
            string arrayName = mapper.map(arrayVariable, ScopeType.GLOBAL);
8✔
744

745
            /* Obtain the index expression */
746
            Value indexInstr = stackArrInstr.getIndexInstr();
8✔
747

748
            /** 
749
             * Emit <arrayName>[<index>]
750
             */
751
            string emit = arrayName;
8✔
752
            emit ~= "[";
8✔
753
            emit ~= transform(indexInstr);
8✔
754
            emit ~= "]";
8✔
755

756

757
            ERROR("TODO: Implement Stack-array index emit");
8✔
758

759
            
760
            
761

762
            // return "(TODO: Stack-array index emit)";
763
            emmmmit = emit;
8✔
764
        }
765
        /** 
766
         * Array assignments (stack-based arrays)
767
         *
768
         * Handles `myArray[<index>] = <expression>` where `myArray`
769
         * is of type `int[<size>]` (i.e. a stack-array)
770
         */
771
        else if(cast(StackArrayIndexAssignmentInstruction)instruction)
4✔
772
        {
773
            StackArrayIndexAssignmentInstruction stackArrAssInstr = cast(StackArrayIndexAssignmentInstruction)instruction;
4✔
774
            Context context = stackArrAssInstr.getContext();
4✔
775
            assert(context);
4✔
776

777
            /** 
778
             * Obtain the stack array being assigned to
779
             */
780
            string arrayName = stackArrAssInstr.getArrayName();
4✔
781
            Variable arrayVariable = cast(Variable)typeChecker.getResolver().resolveBest(context.getContainer(), arrayName);
4✔
782

783
            /* Perform symbol mapping */
784
            // FIXME: Set proper scope type
785
            string arrayNameMapped = mapper.map(arrayVariable, ScopeType.GLOBAL);
4✔
786

787
            /* Obtain the index expression */
788
            Value indexInstr = stackArrAssInstr.getIndexInstr();
4✔
789

790
            /* Obtain the expresison being assigned */
791
            Value assignmentInstr = stackArrAssInstr.getAssignedValue();
4✔
792

793
            /** 
794
             * Emit <arrayName>[<index>] = <expression>;
795
             */
796
            string emit = arrayNameMapped;
4✔
797
            emit ~= "[";
4✔
798
            emit ~= transform(indexInstr);
4✔
799
            emit ~= "]";
4✔
800

801
            emit ~= " = ";
4✔
802
            emit ~= transform(assignmentInstr);
4✔
803
            emit ~= ";";
4✔
804

805

806
            // return "(StackArrAssignmentInstr: TODO)";
807

808
            emmmmit = emit;
4✔
809
        }
810
        /**
811
         * String literals
812
         *
813
         * Instructions containing string literals
814
         */
815
        else if(cast(StringLiteral)instruction)
×
816
        {
817
            // TODO: Do pool-based emit instead of this direct stuff
818

819
            import tlang.compiler.symbols.strings : StringInfo;
820
            StringLiteral sl_instr = cast(StringLiteral)instruction;
×
821
            StringInfo* sl_info = sl_instr.str();
×
822
            assert(sl_info.width() == 1); // TODO: Add support for other string types
×
823

824
            
825
            // C-string literal is `"<my content>"`
826
            string emit = `"`~sl_info.utf8()~`"`;
×
827

828
            emmmmit = emit;
×
829
        }
830
        /** 
831
         * Unsupported instruction
832
         *
833
         * If you get here then normally it's because
834
         * you didn't implement a transformation for
835
         * an instruction yet.
836
         */
837
        else
838
        {
839
            if(instruction is null)
×
840
            {
841
                emmmmit = "<TODO: transform() called but instruction was null>";
×
842
            }
843
            else
844
            {
845
                emmmmit = "<TODO: Base emit: "~to!(string)(instruction)~">";
×
846
            }
847
        }
848

849
        return emmmmit;
449✔
850
    }
851

852

853
    public override void emit()
854
    {
855
        // TODO: We must figure out how we decide to generate
856
        // multiple emits here for the many modules within the
857
        // `Program`
858
        import tlang.compiler.symbols.data : Program;
859
        import tlang.compiler.symbols.containers : Module;
860
        Program program = this.typeChecker.getProgram();
23✔
861
        Module[] programsModules = program.getModules();
23✔
862
        DEBUG("emit() has found modules '"~to!(string)(programsModules)~"'");
23✔
863

864
        foreach(Module curMod; programsModules)
138✔
865
        {
866
            DEBUG("Begin emit process for module '"~to!(string)(curMod)~"'...");
23✔
867

868
            File modOut;
46✔
869
            modOut.open(format("%s.c", curMod.getName()), "w");
23✔
870

871
            // Emit header comment (NOTE: Change this to a useful piece of text)
872
            emitHeaderComment(modOut, curMod, "Place any extra information by code generator here"); // NOTE: We can pass a string with extra information to it if we want to
23✔
873

874
            // Emit standard integer header import
875
            emitStdint(modOut, curMod);
23✔
876

877
            // Emit make-available's (externs)
878
            emitExterns(modOut, curMod);
23✔
879

880
            // Emit static allocation code
881
            emitStaticAllocations(modOut, curMod);
23✔
882

883
            // Emit globals
884
            emitCodeQueue(modOut, curMod);
23✔
885

886
            // Emit function definitions
887
            emitFunctionPrototypes(modOut, curMod);
23✔
888
            emitFunctionDefinitions(modOut, curMod);
23✔
889

890
            // Close (and flush anything not yet written)
891
            modOut.close();
23✔
892
            DEBUG("Emit for '"~to!(string)(curMod)~"'");
23✔
893
        }
894
        
895
        // If enabled (default: yes) then emit entry point (TODO: change later)
896
        Module mainModule;
23✔
897
        Function mainFunction;
23✔
898
        if(findEntrypoint(mainModule, mainFunction))
23✔
899
        {
900
            // FIXME: Disable (needed "a", because "w" overwrote previous writes)
901
            File entryModOut;
2✔
902
            entryModOut.open(format("%s.c", mainModule.getName()), "a");
1✔
903

904
            // Emit entry point
905
            emitEntrypoint(entryModOut, mainModule);
1✔
906

907
            entryModOut.close();
1✔
908
        }
909
        else
910
        {
911
            // If enabled (default: yes) then emit a testing
912
            // entrypoint (if one if available for the given
913
            // test case)
914
            //
915
            // In such test cases we assume that the first module
916
            // is the one we care about
917
            if(config.getConfig("dgen:emit_entrypoint_test").flag())
22✔
918
            {
919
                WARN("Generating a testcase entrypoint for this program");
22✔
920

921
                Module firstMod = programsModules[0];
22✔
922
                File firstModOut;
44✔
923
                firstModOut.open(format("%s.c", firstMod.getName()), "a");
22✔
924
                
925
                // Emit testing entrypoint
926
                emitTestingEntrypoint(firstModOut, firstMod);
22✔
927

928
                firstModOut.close();
22✔
929
            }
930
            else
931
            {
932
                ERROR("Could not find an entry point module and function. Missing a main() maybe?");
×
933
            }
934
        }   
935
    }
936

937
    /** 
938
     * Attempts to find an entry point within the `Program`,
939
     * when it is found the ref parameters are filled in
940
     * and `true` is returned, else they are left untouched
941
     * and `false` is returned
942
     *
943
     * Params:
944
     *   mainModule = the found main `Module` (if any)
945
     *   mainFunc = the found main `Function` (if any)
946
     * Returns: `true` if an entrypoint is found, else
947
     * `false`
948
     */
949
    private bool findEntrypoint(ref Module mainModule, ref Function mainFunc)
950
    {
951
        import tlang.compiler.symbols.data : Program, Entity;
952
        import tlang.compiler.typecheck.resolution : Resolver;
953
        Program program = this.typeChecker.getProgram();
23✔
954
        Resolver resolver = this.typeChecker.getResolver();
23✔
955
        foreach(Module curMod; program.getModules())
136✔
956
        {
957
            Entity potentialMain = resolver.resolveWithin(curMod, "main");
23✔
958

959
            if(potentialMain !is null)
23✔
960
            {
961
                Function potentialMainFunc = cast(Function)potentialMain;
1✔
962
                if(potentialMainFunc !is null)
1✔
963
                {
964
                    // TODO: Ensure that it is void or int? (Our decision)
965
                    // TODO: Ensure arguments (choose what we allow)
966
                    mainModule = curMod;
1✔
967
                    mainFunc = potentialMainFunc;
1✔
968
                    return true;
1✔
969
                }
970
            }
971
        }
972

973
        return false;
22✔
974
    }
975

976
    /** 
977
     * Emits the header comment which contains information about the source
978
     * file and the generated code file
979
     *
980
     * Params:
981
     *   modFile = the `File` to write the emitted source code to
982
     *   mod = the current `Module` being processed
983
     *   headerPhrase = Optional additional string information to add to the header comment
984
     */
985
    private void emitHeaderComment(File modFile, Module mod, string headerPhrase = "")
986
    {
987
        // NOTE: We could maybe fetch input fiel info too? Although it would have to be named similiarly in any case
988
        // so perhaps just appending a `.t` to the module name below would be fine
989
        string moduleName = typeChecker.getResolver().generateName(mod, mod); //TODO: Lookup actual module name (I was lazy)
23✔
990
        string outputCFilename = modFile.name();
23✔
991

992
        modFile.write(`/**
23✔
993
 * TLP compiler generated code
994
 *
995
 * Module name: `);
996
        modFile.writeln(moduleName);
23✔
997
        modFile.write(" * Output C file: ");
23✔
998
        modFile.writeln(outputCFilename);
23✔
999

1000
        if(headerPhrase.length)
23✔
1001
        {
1002
            modFile.write(wrap(headerPhrase, 40, " *\n * ", " * "));
23✔
1003
        }
1004
        
1005
        modFile.write(" */\n");
23✔
1006
    }
1007

1008
    private struct ModuleExternSet
1009
    {
1010
        private Module originator;
1011
        private Variable[] pubVars;
1012
        private Function[] pubFns;
1013

1014
        this(Module originator, Variable[] publicVars, Function[] publicFunctions)
×
1015
        {
1016
            this.originator = originator;
×
1017
            this.pubVars = publicVars;
×
1018
            this.pubFns =  publicFunctions;
×
1019
        }
1020

1021
        public Variable[] vars()
1022
        {
1023
            return this.pubVars;
×
1024
        }
1025

1026
        public Function[] funcs()
1027
        {
1028
            return this.pubFns;
×
1029
        }
1030

1031
        public Module mod()
1032
        {
1033
            return this.originator;
×
1034
        }
1035
    }
1036

1037
    private ModuleExternSet generateExternsForModule(Module mod)
1038
    {
1039
        DEBUG(format("Generating extern statements for module '%s'", mod.getName()));
×
1040

1041
        Entity[] allPubFunc;
×
1042
        Entity[] allPubVar;
×
1043

1044
        import tlang.compiler.typecheck.resolution : Resolver;
1045

1046
        Resolver resolver = this.typeChecker.getResolver();
×
1047

1048
        auto funcAccPred = derive_functionAccMod(AccessorType.PUBLIC);
×
1049
        auto varAccPred = derive_variableAccMod(AccessorType.PUBLIC);
×
1050

1051
        bool allPubFuncsAndVars(Entity entity)
1052
        {
1053
            return funcAccPred(entity) || varAccPred(entity);
×
1054
        }
1055

1056
        Entity[] entities;
×
1057
        resolver.resolveWithin(mod, &allPubFuncsAndVars, entities);
×
1058
        DEBUG(format("Got %d many entities needing extern statements emitted", entities.length));
×
1059
        import niknaks.arrays : filter;
1060
        import niknaks.functional : predicateOf;
1061

1062
        // Filter variables
1063
        bool onlyVar(Entity entity) { return cast(Variable)entity !is null; }
×
1064
        filter!(Entity)(entities, predicateOf!(onlyVar), allPubVar);
×
1065

1066
        // Filter functions
1067
        bool onlyFunc(Entity entity) { return cast(Function)entity !is null; }
×
1068
        filter!(Entity)(entities, predicateOf!(onlyFunc), allPubFunc);
×
1069
        
1070
        ModuleExternSet modExtSet = ModuleExternSet(mod, cast(Variable[])allPubVar, cast(Function[])allPubFunc);
×
1071
        return modExtSet;
×
1072
    }
1073

1074
    /** 
1075
     * TODO: Re-do
1076
     *
1077
     * Generates a bunch of extern statements
1078
     * for symbols such as variables and
1079
     * function which are to be exposed
1080
     * in the generated object file such
1081
     * that they can be linked externally
1082
     * to other object files.
1083
     *
1084
     * The method for this is to resolve
1085
     * all `Entity`(s) which are either
1086
     * a `Function` or `Variable` which
1087
     * have an access modifier of `public`
1088
     * and lastly which are only at the
1089
     * module-level in terms of declaration
1090
     *
1091
     * Params:
1092
     *   modOut = the `File` to write the
1093
     * emitted source code to
1094
     *   mod = the current `Module` being
1095
     * processed
1096
     */
1097
    private void emitExterns(File modOut, Module mod)
1098
    {
1099
        // Find all modules except ourselves
1100
        import tlang.compiler.typecheck.resolution : Resolver;
1101
        import niknaks.arrays : filter;
1102
        Module[] everyoneElse;
23✔
1103
        bool justNotMe(Module modI) { return modI !is mod; }
23✔
1104
        filter!(Module)(this.typeChecker.getProgram().getModules(), predicateOf!(justNotMe), everyoneElse);
23✔
1105

1106
        // Now grab each other modules' extern data
1107
        ModuleExternSet[] externSets;
23✔
1108
        foreach(Module omod; everyoneElse)
69✔
1109
        {
1110
            externSets ~= generateExternsForModule(omod);
×
1111
        }
1112

1113
        /**
1114
         * Emit for each
1115
         */
1116
        foreach(ModuleExternSet mos; externSets)
69✔
1117
        {
1118
            DEBUG(format("Emitting extern(...) statements for module %s...", mos.mod()));
×
1119

1120
            // Emit public functions
1121
            foreach(Function func; mos.funcs())
×
1122
            {
1123
                // Generate signature
1124
                string signature = generateSignature(func);
×
1125

1126
                // Decide whether or not `extern` is needed
1127
                string externPart = func.isExternal() ? "" : "extern ";
×
1128

1129
                // Generate the emit
1130
                string externEmit = format("%s%s;", externPart, signature);
×
1131

1132
                DEBUG(format("FuncExternEmit: '%s'", externEmit));
×
1133
                modOut.writeln(externEmit);
×
1134
            }
1135

1136
            // Emit public variables
1137
            foreach(Variable var; mos.vars())
×
1138
            {
1139
                // Generate signature
1140
                string signature = generateSignature_Variable(var);
×
1141

1142
                // Decide whether or not `extern` is needed
1143
                string externPart = var.isExternal() ? "" : "extern ";
×
1144

1145
                // Generate the emit
1146
                string externEmit = format("%s%s;", externPart, signature);
×
1147

1148
                DEBUG(format("VarExternEmit: '%s'", externEmit));
×
1149
                modOut.writeln(externEmit);
×
1150
            }
1151
        }
1152
    }
1153

1154
    /** 
1155
     * Emits the static allocations provided
1156
     *
1157
     * Params:
1158
     *   modFile = the `File` to write the emitted source code to
1159
     *   mod = the current `Module` being processed
1160
     */
1161
    private void emitStaticAllocations(File modOut, Module mod)
1162
    {
1163
        // Select the static initializations code queue for
1164
        // the given module
1165
        selectQueue(mod, QueueType.ALLOC_QUEUE);
23✔
1166
        DEBUG("Static allocations needed: "~to!(string)(getQueueLength()));
23✔
1167

1168
        modOut.writeln();
23✔
1169
    }
1170

1171
    /** 
1172
     * Emits the function prototypes
1173
     *
1174
     * Params:
1175
     *   modFile = the `File` to write the emitted source code to
1176
     *   mod = the current `Module` being processed
1177
     */
1178
    private void emitFunctionPrototypes(File modOut, Module mod)
1179
    {
1180
        DEBUG("Function definitions needed: "~to!(string)(getFunctionDefinitionsCount(mod)));
23✔
1181

1182
        // Get complete map (should we bypass anything in CodeEmitter for this? Guess it is fair?)
1183
        Instruction[][string] functionBodyInstrs = typeChecker.getFunctionBodyCodeQueues(mod);
23✔
1184
        string[] functionNames = getFunctionDefinitionNames(mod);
23✔
1185

1186
        DEBUG("WOAH: "~to!(string)(functionNames));
23✔
1187

1188
        foreach(string currentFunctioName; functionNames)
168✔
1189
        {
1190
            emitFunctionPrototype(modOut, mod, currentFunctioName);
33✔
1191
            modOut.writeln();
33✔
1192
        }
1193
    }
1194

1195
    /** 
1196
     * Emits the function definitions
1197
     *
1198
     * Params:
1199
     *   modFile = the `File` to write the emitted source code to
1200
     *   mod = the current `Module` being processed
1201
     */
1202
    private void emitFunctionDefinitions(File modOut, Module mod)
1203
    {
1204
        DEBUG("Function definitions needed: "~to!(string)(getFunctionDefinitionsCount(mod)));
23✔
1205

1206
        // Get the function definitions of the current module
1207
        Instruction[][string] functionBodyInstrs = typeChecker.getFunctionBodyCodeQueues(mod);
23✔
1208

1209
        string[] functionNames = getFunctionDefinitionNames(mod);
23✔
1210

1211
        DEBUG("WOAH: "~to!(string)(functionNames));
23✔
1212

1213
        foreach(string currentFunctioName; functionNames)
168✔
1214
        {
1215
            emitFunctionDefinition(modOut, mod, currentFunctioName);
33✔
1216
            modOut.writeln();
33✔
1217
        }
1218
    }
1219

1220
    /** 
1221
     * Generates the signature emit for a given
1222
     * variable.
1223
     *
1224
     * This is something of the form:
1225
     *
1226
     *    `<type> <name>`
1227
     *
1228
     * Where the `<name>` has been symbol
1229
     * mapped.
1230
     *
1231
     * Params:
1232
     *   var = the `Variable`
1233
     * Returns: a string
1234
     */
1235
    private string generateSignature_Variable(Variable var)
1236
    {
1237
        string signature;
×
1238

1239
        // Extract the Variable's type
1240
        Type varType = typeChecker.getType(var.context.container, var.getType());
×
1241

1242
        // Decide on the symbol's name
1243
        string symbolName;
×
1244

1245
        // If it is NOT extern then map it
1246
        if(!var.isExternal())
×
1247
        {
1248
            // FIXME: Set proper scope type
1249
            symbolName = mapper.map(var, ScopeType.GLOBAL);
×
1250
        }
1251
        // If it is extern, then leave it as such
1252
        else
1253
        {
1254
            symbolName = var.getName();
×
1255
        }
1256

1257
        // <type> <name>
1258
        signature = typeTransform(varType)~" "~symbolName;
×
1259

1260
        // If if is external then it needs `extern ...`
1261
        if(var.isExternal())
×
1262
        {
1263
            signature = "extern "~signature;
×
1264
        }
1265

1266
        return signature;
×
1267
    }
1268

1269
    private string generateSignature(Function func)
1270
    {
1271
        string signature;
66✔
1272

1273
        // Extract the Function's return Type
1274
        Type returnType = typeChecker.getType(func.context.container, func.getType());
66✔
1275

1276
        // <type> <functionName> (
1277
        signature = typeTransform(returnType)~" "~func.getName()~"(";
66✔
1278

1279
        // Generate parameter list
1280
        if(func.hasParams())
66✔
1281
        {
1282
            VariableParameter[] parameters = func.getParams();
36✔
1283
            string parameterString;
36✔
1284
            
1285
            for(ulong parIdx = 0; parIdx < parameters.length; parIdx++)
156✔
1286
            {
1287
                Variable currentParameter = parameters[parIdx];
42✔
1288

1289
                // Extract the variable's type
1290
                Type parameterType = typeChecker.getType(currentParameter.context.container, currentParameter.getType());
42✔
1291

1292
                // Generate the symbol-mapped names for the parameters
1293
                Variable typedEntityVariable = cast(Variable)typeChecker.getResolver().resolveBest(func, currentParameter.getName()); //TODO: Remove `auto`
42✔
1294
                // FIXME: Set proper scope type
1295
                string renamedSymbol = mapper.map(typedEntityVariable, ScopeType.GLOBAL);
42✔
1296

1297

1298
                // Generate <type> <parameter-name (symbol mapped)>
1299
                parameterString~=typeTransform(parameterType)~" "~renamedSymbol;
42✔
1300

1301
                if(parIdx != (parameters.length-1))
42✔
1302
                {
1303
                    parameterString~=", ";
6✔
1304
                }
1305
            }
1306

1307
            signature~=parameterString;
36✔
1308
        }
1309

1310
        // )
1311
        signature~=")";
66✔
1312

1313
        // If the function is marked as external then place `extern` infront
1314
        if(func.isExternal())
66✔
1315
        {
1316
            signature = "extern "~signature;
×
1317
        }
1318

1319
        return signature;
66✔
1320

1321
    }
1322

1323
    /** 
1324
     * Emits the function prototype for the `Function`
1325
     * of the given name
1326
     *
1327
     * Params:
1328
     *   modFile = the `File` to write the emitted source code to
1329
     *   mod = the current `Module` being processed
1330
     *   functionName = the name of the function
1331
     */
1332
    private void emitFunctionPrototype(File modOut, Module mod, string functionName)
1333
    {
1334
        // Select the function definition code queue by module and function name
1335
        // TODO: Is this needed for protptype def? I think not (REMOVE PLEASE)
1336
        selectQueue(mod, QueueType.FUNCTION_DEF_QUEUE, functionName);
33✔
1337

1338
        DEBUG("emotFunctionDefinition(): Function: "~functionName~", with "~to!(string)(getSelectedQueueLength())~" many instructions");
33✔
1339
    
1340
        //TODO: Look at nested definitions or nah? (Context!!)
1341
        //TODO: And what about methods defined in classes? Those should technically be here too
1342
        Function functionEntity = cast(Function)typeChecker.getResolver().resolveBest(mod, functionName); //TODO: Remove `auto`
33✔
1343
        
1344
        // Emit the function signature
1345
        modOut.writeln(generateSignature(functionEntity)~";");
33✔
1346
    }
1347

1348
    /** 
1349
     * Emits the function definition for the `Function`
1350
     * of the given name
1351
     *
1352
     * Params:
1353
     *   modFile = the `File` to write the emitted source code to
1354
     *   mod = the current `Module` being processed
1355
     *   functionName = the name of the function
1356
     */
1357
    private void emitFunctionDefinition(File modOut, Module mod, string functionName)
1358
    {
1359
        // Select the function definition code queue by module and function name
1360
        selectQueue(mod, QueueType.FUNCTION_DEF_QUEUE, functionName);
33✔
1361

1362
        DEBUG("emotFunctionDefinition(): Function: "~functionName~", with "~to!(string)(getSelectedQueueLength())~" many instructions");
33✔
1363
    
1364
        //TODO: Look at nested definitions or nah? (Context!!)
1365
        //TODO: And what about methods defined in classes? Those should technically be here too
1366
        Function functionEntity = cast(Function)typeChecker.getResolver().resolveBest(mod, functionName); //TODO: Remove `auto`
33✔
1367
        
1368
        // If the Entity is NOT external then emit the signature+body
1369
        if(!functionEntity.isExternal())
33✔
1370
        {
1371
            // Emit the function signature
1372
            modOut.writeln(generateSignature(functionEntity));
33✔
1373

1374
            // Emit opening curly brace
1375
            modOut.writeln(getCharacter(SymbolType.OCURLY));
33✔
1376

1377
            // Emit body
1378
            while(hasInstructions())
122✔
1379
            {
1380
                Instruction curFuncBodyInstr = getCurrentInstruction();
89✔
1381

1382
                string emit = transform(curFuncBodyInstr);
89✔
1383
                DEBUG("emitFunctionDefinition("~functionName~"): Emit: "~emit);
89✔
1384
                modOut.writeln("\t"~emit);
89✔
1385
                
1386
                nextInstruction();
89✔
1387
            }
1388

1389
            // Emit closing curly brace
1390
            modOut.writeln(getCharacter(SymbolType.CCURLY));
33✔
1391
        }
1392
        // If the Entity IS external then don't emit anything as the signature would have been emitted via a prorotype earlier with `emitPrototypes()`
1393
        else
1394
        {
1395
            // Do nothing
1396
        }
1397
    }
1398

1399
    /** 
1400
     * Emits the code queue of the given `Module`
1401
     *
1402
     * Params:
1403
     *   modFile = the `File` to write the emitted source code to
1404
     *   mod = the current `Module` being processed
1405
     */
1406
    private void emitCodeQueue(File modOut, Module mod)
1407
    {
1408
        // Select the global code queue of the current module
1409
        selectQueue(mod, QueueType.GLOBALS_QUEUE);
23✔
1410
        DEBUG("Code emittings needed: "~to!(string)(getQueueLength()));
23✔
1411

1412
        while(hasInstructions())
38✔
1413
        {
1414
            Instruction currentInstruction = getCurrentInstruction();
15✔
1415
            modOut.writeln(transform(currentInstruction));
15✔
1416

1417
            nextInstruction();
15✔
1418
        }
1419

1420
        modOut.writeln();
23✔
1421
    }
1422

1423
    /** 
1424
     * Emits the standard imports of the given
1425
     * `Module`
1426
     *
1427
     * Params:
1428
     *   modFile = the `File` to write the emitted source code to
1429
     *   mod = the current `Module` being processed
1430
     */
1431
    private void emitStdint(File modOut, Module mod)
1432
    {
1433
        modOut.writeln("#include<stdint.h>");
23✔
1434
    }
1435

1436
    private void emitEntrypoint(File modOut, Module mod)
1437
    {
1438
        ERROR("IMPLEMENT ME");
1✔
1439
        ERROR("IMPLEMENT ME");
1✔
1440
        ERROR("IMPLEMENT ME");
1✔
1441
        ERROR("IMPLEMENT ME");
1✔
1442
        ERROR("We have NOT YET implemented the init method");
1✔
1443

1444
        // modOut.writeln("fok");
1445

1446
        // TODO: In future, for runtime init,
1447
        // I will want to co-opt main(int, args)
1448
        // for use for runtime init to then
1449
        // call ANOTHER REAL main (specified)
1450
        // by the user
1451

1452
        // Therefore there must be some sort
1453
        // of renaming stage somewhere
1454
    }
1455

1456
    private void emitTestingEntrypoint(File modOut, Module mod)
1457
    {
1458
        // TODO: Implement me
1459

1460
        // Test for `simple_functions.t` (function call testing)
1461
        if(cmp(mod.getName(), "simple_functions") == 0)
22✔
1462
        {
1463
            modOut.writeln(`
1✔
1464
#include<stdio.h>
1465
#include<assert.h>
1466
int main()
1467
{
1468
    assert(t_7b6d477c5859059f16bc9da72fc8cc3b == 22);
1469
    printf("k: %u\n", t_7b6d477c5859059f16bc9da72fc8cc3b);
1470
    
1471
    banana(1);
1472
    assert(t_7b6d477c5859059f16bc9da72fc8cc3b == 72);
1473
    printf("k: %u\n", t_7b6d477c5859059f16bc9da72fc8cc3b);
1474

1475
    return 0;
1476
}`);
1477
        }
1478
        // Test for `simple_function_recursion_factorial.t` (recursive function call testing)
1479
        else if(cmp(mod.getName(), "simple_function_recursion_factorial") == 0)
21✔
1480
        {
1481
            modOut.writeln(`
1✔
1482
#include<stdio.h>
1483
#include<assert.h>
1484
int main()
1485
{
1486
    int result = factorial(3);
1487
    assert(result == 6);
1488
    printf("factorial: %u\n", result);
1489
    
1490
    return 0;
1491
}`);
1492
        }
1493
        // Test for `simple_direct_func_call.t` (statement-level function call)
1494
        else if(cmp(mod.getName(), "simple_direct_func_call") == 0)
20✔
1495
        {
1496
            modOut.writeln(`
1✔
1497
#include<stdio.h>
1498
#include<assert.h>
1499
int main()
1500
{
1501
    // Before it should be 0
1502
    assert(t_de44aff5a74865c97c4f8701d329f28d == 0);
1503

1504
    // Call the function
1505
    function();
1506

1507
    // After it it should be 69
1508
    assert(t_de44aff5a74865c97c4f8701d329f28d == 69);
1509
    
1510
    return 0;
1511
}`);
1512
        }
1513
        else if(cmp(mod.getName(), "simple_while") == 0)
19✔
1514
        {
1515
            modOut.writeln(`
1✔
1516
#include<stdio.h>
1517
#include<assert.h>
1518
int main()
1519
{
1520
    int result = function(3);
1521
    printf("result: %d\n", result);
1522
    assert(result == 3);
1523

1524
    return 0;
1525
}`);
1526
        }
1527
        else if(cmp(mod.getName(), "simple_for_loops") == 0)
18✔
1528
        {
1529
            modOut.writeln(`
1✔
1530
#include<stdio.h>
1531
#include<assert.h>
1532
int main()
1533
{
1534
    int result = function(3);
1535
    printf("result: %d\n", result);
1536
    assert(result == 3);
1537

1538
    return 0;
1539
}`);
1540
        }
1541
        else if(cmp(mod.getName(), "simple_pointer") == 0)
17✔
1542
        {
1543
            modOut.writeln(`
1✔
1544
#include<stdio.h>
1545
#include<assert.h>
1546
int main()
1547
{
1548
    int retValue = thing();
1549
    assert(t_87bc875d0b65f741b69fb100a0edebc7 == 4);
1550
    assert(retValue == 6);
1551

1552
    return 0;
1553
}`);
1554
        }
1555
        else if(cmp(mod.getName(), "simple_pointer_array_syntax") == 0)
16✔
1556
        {
1557
            modOut.writeln(`
1✔
1558
#include<stdio.h>
1559
#include<assert.h>
1560
int main()
1561
{
1562
    int retValue = thing();
1563
    assert(t_9d01d71b858651e520c9b503122a1b7a == 4);
1564
    assert(retValue == 6);
1565

1566
    return 0;
1567
}`);
1568
        }
1569
        else if(cmp(mod.getName(), "simple_pointer_cast_le") == 0)
15✔
1570
        {
1571
            modOut.writeln(`
1✔
1572
#include<stdio.h>
1573
#include<assert.h>
1574
int main()
1575
{
1576
    int retValue = thing();
1577
    assert(t_e159019f766be1a175186a13f16bcfb7 == 256+4);
1578
    assert(retValue == 256+4+2);
1579

1580
    return 0;
1581
}`);
1582
        }
1583
        else if(cmp(mod.getName(), "simple_pointer_malloc") == 0)
14✔
1584
        {
1585
            modOut.writeln(`
×
1586
#include<stdio.h>
1587
#include<assert.h>
1588
int main()
1589
{
1590
    test();
1591
    
1592
    // TODO: Test the value
1593

1594
    return 0;
1595
}`);
1596
        }
1597
        else if(cmp(mod.getName(), "simple_extern") == 0)
14✔
1598
        {
1599
            modOut.writeln(`
×
1600
#include<stdio.h>
1601
#include<assert.h>
1602
int main()
1603
{
1604
    test();
1605
   
1606
    
1607

1608
    return 0;
1609
}`);
1610
        }
1611
        else if(cmp(mod.getName(), "simple_stack_array_coerce") == 0)
14✔
1612
        {
1613
            modOut.writeln(`
1✔
1614
#include<stdio.h>
1615
#include<assert.h>
1616
int main()
1617
{
1618
    int result = function();
1619
    assert(result == 420+69);
1620
    printf("stackArr sum: %d\n", result);
1621

1622
    return 0;
1623
}`);
1624
        }
1625
        else if(cmp(mod.getName(), "complex_stack_array_coerce") == 0)
13✔
1626
        {
1627
            modOut.writeln(`
1✔
1628
#include<stdio.h>
1629
#include<assert.h>
1630
int main()
1631
{
1632
    int result = function();
1633
    assert(result == 69+420);
1634

1635
    printf("val1: %d\n", t_596f49b2a2784a3c1b073ccfe174caa0);
1636
    printf("val2: %d\n", t_4233b83329676d70ab4afaa00b504564);
1637
    printf("stackArr sum: %d\n", result);
1638

1639
    return 0;
1640
}`);
1641
        }
1642
        else if(cmp(mod.getName(), "simple_stack_array_coerce_ptr_syntax") == 0)
12✔
1643
        {
1644
            modOut.writeln(`
1✔
1645
#include<stdio.h>
1646
#include<assert.h>
1647
int main()
1648
{
1649
    int result = function();
1650
    assert(result == 420+69);
1651
    printf("stackArr sum: %d\n", result);
1652

1653
    return 0;
1654
}`);
1655
        }
1656
        else if(cmp(mod.getName(), "simple_stack_arrays4") == 0)
11✔
1657
        {
1658
            modOut.writeln(`
1✔
1659
#include<stdio.h>
1660
#include<assert.h>
1661
int main()
1662
{
1663
    int result = function();
1664
    assert(result == 61);
1665

1666
    return 0;
1667
}`);
1668
        }
1669
        else
1670
        {
1671
            modOut.writeln(`
10✔
1672
int main()
1673
{
1674
    return 0;
1675
}
1676
`);
1677
        }
1678
    }
1679

1680
    /** 
1681
     * Performs the compilation step
1682
     *
1683
     * This requires that the `emit()`
1684
     * step must have already been completed
1685
     */
1686
    public override EmitResult finalize()
1687
    {
1688
        import tlang.compiler.symbols.data : Program;
1689
        Program program = this.typeChecker.getProgram();
23✔
1690
        Module[] programModules = program.getModules();
23✔
1691

1692
        string[] srcFiles;
23✔
1693
        string[] objectFiles;
23✔
1694

1695
        // import tlang.compiler.configuration;
1696
        // config.addConfig(ConfigEntry("dgen:afterexit:clean_c_files", true));
1697
        // config.addConfig(ConfigEntry("dgen:afterexit:clean_obj_files", true));
1698

1699
        scope(exit)
1700
        {
1701
            // Clean up all generated C files
1702
            if(config.hasConfig("dgen:afterexit:clean_c_files") && config.getConfig("dgen:afterexit:clean_c_files").flag())
23✔
1703
            {
1704
                foreach(string srcFile; srcFiles)
×
1705
                {
1706
                    DEBUG("Cleaning up source file '"~srcFile~"'...");
×
1707
                    import std.stdio : remove;
1708
                    remove(srcFile.ptr);
×
1709

1710
                    if(!remove(srcFile.ptr))
×
1711
                    {
1712
                        ERROR("There was an error cleaning up source file '"~srcFile~"'"); // TODO: Add error code
×
1713
                    }
1714
                }
1715
            }
1716

1717
            // Clean up all generates object files
1718
            if(config.hasConfig("dgen:afterexit:clean_obj_files") && config.getConfig("dgen:afterexit:clean_obj_files").flag())
23✔
1719
            {
1720
                foreach(string objFile; objectFiles)
×
1721
                {
1722
                    DEBUG("Cleaning up object file '"~objFile~"'...");
×
1723
                    import std.stdio : remove;
1724
                    remove(objFile.ptr);
×
1725

1726
                    if(!remove(objFile.ptr))
×
1727
                    {
1728
                        ERROR("There was an error cleaning up object file '"~objFile~"'"); // TODO: Add error code
×
1729
                    }
1730
                }
1731
            }
1732
        }
1733

1734
        try
1735
        {
1736
            string systemCompiler = config.getConfig("dgen:compiler").text();
23✔
1737
            INFO("Using system C compiler '"~systemCompiler~"' for compilation");
23✔
1738

1739
            // Check for object files to be linked in
1740
            string[] objectFilesLink;
23✔
1741
            if(config.hasConfig("linker:link_files"))
23✔
1742
            {
1743
                objectFilesLink = config.getConfig("linker:link_files").array();
×
1744
                INFO("Object files to be linked in: "~to!(string)(objectFilesLink));
×
1745
            }
1746
            else
1747
            {
1748
                INFO("No files to link in");
23✔
1749
            }
1750

1751
            // Total compilation time
1752
            StopWatch watch = StopWatch(AutoStart.yes);
23✔
1753
            Duration total_c = Duration.zero();
23✔
1754

1755
            // TODO: Do for-each generation of `.o` files here with `-c`
1756
            foreach(Module curMod; programModules)
138✔
1757
            {
1758
                scope(exit)
1759
                {
1760
                    watch.reset();
23✔
1761
                }
1762
                
1763
                string modFileSrcPath = format("%s.c", curMod.getName());
23✔
1764
                srcFiles ~= modFileSrcPath;
23✔
1765
                string modFileObjPath = format("%s.o", curMod.getName());
23✔
1766

1767
                string[] args = [systemCompiler, "-c", modFileSrcPath, "-o", modFileObjPath];
23✔
1768

1769
                INFO("Compiling now with arguments: "~to!(string)(args));
23✔
1770

1771
                Pid ccPID = spawnProcess(args);
23✔
1772
                int code = wait(ccPID);
23✔
1773
                if(code)
23✔
1774
                {
1775
                    throw new DGenException("The CC exited with a non-zero exit code (%d)", code);
×
1776
                }
1777

1778
                Duration compTime = watch.peek();
23✔
1779
                INFO(format("Compiled %s in %sms", curMod.getName(), compTime.total!("msecs")()));
23✔
1780
                total_c = dur!("msecs")(total_c.total!("msecs")()+compTime.total!("msecs")());
23✔
1781

1782
                // Only add it to the list of files if it was generated
1783
                // (this guards against the clean up routines spitting out errors
1784
                // for object files which were never generated in the first place)
1785
                objectFiles ~= modFileObjPath;
23✔
1786
            }
1787

1788
            INFO(format("Total compilation time took %s", total_c));
23✔
1789

1790
            // Now determine the entry point module
1791
            // Module entryModule;
1792
            // Function _;
1793
            // if(findEntrypoint(entryModule, _))
1794
            // {
1795
                
1796
            // }
1797

1798
            // Perform linking
1799
            string[] args = [systemCompiler];
23✔
1800

1801
            // Tack on all generated object files
1802
            args ~= objectFiles;
23✔
1803

1804
            // Tack on any objects to link that were specified in Config
1805
            args ~= objectFilesLink;
23✔
1806

1807
            // Tack on the output filename
1808
            string executableOutput;
23✔
1809
            if(config.hasConfig("emit:executable_output"))
23✔
1810
            {
1811
                executableOutput = config.getConfig("emit:executable_output").text();
23✔
1812
            }
1813
            else
1814
            {
1815
                throw new DGenException("Missing the `emit:executableOutput` option");
×
1816
            }
1817
            args ~= ["-o", executableOutput]; 
23✔
1818

1819
            
1820
            // Total linking time
1821
            Duration total_l = Duration.zero();
23✔
1822
            watch.reset();
23✔
1823

1824
            // Now link all object files (the `.o`'s) together
1825
            // and perform linking
1826
            INFO("Linking args: ", args);
23✔
1827
            Pid ccPID = spawnProcess(args);
23✔
1828
            int code = wait(ccPID);
23✔
1829
            total_l = watch.peek();
23✔
1830

1831
            if(code)
23✔
1832
            {
1833
                throw new DGenException("The CC exited with a non-zero exit code (%d)", code);
×
1834
            }
1835

1836
            INFO(format("Total linking time took %s", total_l));
23✔
1837

1838
            return EmitResult("./tlang.out", total_c+total_l);
23✔
1839
        }
1840
        catch(ProcessException e)
1841
        {
1842
            ERROR("NOTE: Case where it exited and Pid now invalid (if it happens it would throw processexception surely)?");
×
1843
            assert(false);
1844
        }
1845
    }
1846
}
1847

1848
import tlang.compiler.symbols.data : Entity, AccessorType;
1849
import niknaks.functional : Predicate, predicateOf;
1850

1851
/** 
1852
 * Derives a closure predicate which captires
1853
 * the provided access modifier type and will
1854
 * apply a logic which disregards any non-`Function`
1855
 * `Entity`, however if a `Function`-typed entity
1856
 * IS found then it will determine if its access
1857
 * modifier matches that of the provided one
1858
 *
1859
 * Params:
1860
 *   accModType = the access modifier to filter
1861
 * by
1862
 *
1863
 * Returns: a `Predicate!(Entity)`
1864
 */
1865
private Predicate!(Entity) derive_functionAccMod(AccessorType accModType)
1866
{
1867
    bool match(Entity entity)
1868
    {
1869
        Function func = cast(Function)entity;
×
1870

1871
        // Disregard any non-Function
1872
        if(func is null)
×
1873
        {
1874
            return false;
×
1875
        }
1876
        // Only care about those with a matching
1877
        // modifier
1878
        else
1879
        {
1880
            return func.getAccessorType() == accModType;
×
1881
        }
1882
    }
1883

1884
    return &match;
×
1885
}
1886

1887
/** 
1888
 * Derives a closure predicate which captires
1889
 * the provided access modifier type and will
1890
 * apply a logic which disregards any non-`Variable`
1891
 * `Entity`, however if a `Variable`-typed entity
1892
 * IS found then it will determine if its access
1893
 * modifier matches that of the provided one
1894
 *
1895
 * Params:
1896
 *   accModType = the access modifier to filter
1897
 * by
1898
 *
1899
 * Returns: a `Predicate!(Entity)`
1900
 */
1901
private Predicate!(Entity) derive_variableAccMod(AccessorType accModType)
1902
{
1903
    bool match(Entity entity)
1904
    {
1905
        Variable var = cast(Variable)entity;
×
1906

1907
        // Disregard any non-Variable
1908
        if(var is null)
×
1909
        {
1910
            return false;
×
1911
        }
1912
        // Only care about those with a matching
1913
        // modifier
1914
        else
1915
        {
1916
            return var.getAccessorType() == accModType;
×
1917
        }
1918
    }
1919

1920
    return &match;
×
1921
}
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