diff --git a/.github/workflows/d.yml b/.github/workflows/d.yml index b152f687..3a35e332 100644 --- a/.github/workflows/d.yml +++ b/.github/workflows/d.yml @@ -330,6 +330,17 @@ jobs: run: | ./tlang compile source/tlang/testing/simple_functions.t ./tlang.out + - name: Simple functions (statement-level) + run: | + ./tlang compile source/tlang/testing/simple_direct_func_call.t + ./tlang.out + - name: Simple functions (recursive) + run: | + ./tlang compile source/tlang/testing/simple_function_recursion_factorial.t + ./tlang.out + + + # TODO: Re-enable when we support the `discard` keyword again #- name: Simple variables # run: | diff --git a/source/tlang/compiler/codegen/emit/dgen.d b/source/tlang/compiler/codegen/emit/dgen.d index ce68ba85..aa612ec3 100644 --- a/source/tlang/compiler/codegen/emit/dgen.d +++ b/source/tlang/compiler/codegen/emit/dgen.d @@ -293,6 +293,12 @@ public final class DCodeEmitter : CodeEmitter emit ~= ")"; + // If this is a statement-level function call then tack on a `;` + if(funcCallInstr.isStatementLevel()) + { + emit ~= ";"; + } + return emit; } /* ReturnInstruction */ @@ -963,6 +969,41 @@ int main() assert(t_7b6d477c5859059f16bc9da72fc8cc3b == 72); printf("k: %u\n", t_7b6d477c5859059f16bc9da72fc8cc3b); + return 0; +}`); + } + // Test for `simple_function_recursion_factorial.t` (recursive function call testing) + else if(cmp(typeChecker.getModule().getName(), "simple_function_recursion_factorial") == 0) + { + file.writeln(` +#include +#include +int main() +{ + int result = factorial(3); + assert(result == 6); + printf("factorial: %u\n", result); + + return 0; +}`); + } + // Test for `simple_direct_func_call.t` (statement-level function call) + else if(cmp(typeChecker.getModule().getName(), "simple_direct_func_call") == 0) + { + file.writeln(` +#include +#include +int main() +{ + // Before it should be 0 + assert(t_de44aff5a74865c97c4f8701d329f28d == 0); + + // Call the function + function(); + + // After it it should be 69 + assert(t_de44aff5a74865c97c4f8701d329f28d == 69); + return 0; }`); } diff --git a/source/tlang/compiler/codegen/instruction.d b/source/tlang/compiler/codegen/instruction.d index 208da01e..70451860 100644 --- a/source/tlang/compiler/codegen/instruction.d +++ b/source/tlang/compiler/codegen/instruction.d @@ -297,6 +297,15 @@ public class CallInstr : Value public class FuncCallInstr : CallInstr { + /** + * This is described in the corresponding AST node + * `FunctionCall`. See that. For short, function calls + * from within expressions and those as appearing as statements + * require a tiny different code gen but for Instructions + * their emit also needs a tiny difference + */ + private bool statementLevel = false; + /* Per-argument instrructions */ private Value[] evaluationInstructions; @@ -328,6 +337,26 @@ public class FuncCallInstr : CallInstr { return evaluationInstructions; } + + /** + * Determines whether this function call instruction + * is within an expression or a statement itself + * + * Returns: true if statement-level, false otherwise + */ + public bool isStatementLevel() + { + return statementLevel; + } + + /** + * Marks this function call instruction as statement + * level + */ + public void markStatementLevel() + { + statementLevel = true; + } } diff --git a/source/tlang/compiler/parsing/core.d b/source/tlang/compiler/parsing/core.d index 099962a8..9b63af4a 100644 --- a/source/tlang/compiler/parsing/core.d +++ b/source/tlang/compiler/parsing/core.d @@ -442,9 +442,12 @@ public final class Parser /* If we have `(` then function call */ if(type == SymbolType.LBRACE) { - /* TODO: Collect and return */ previousToken(); - parseFuncCall(); + FunctionCall funcCall = parseFuncCall(); + ret = funcCall; + + /* Set the flag to say this is a statement-level function call */ + funcCall.makeStatementLevel(); /* Expect a semi-colon */ expect(SymbolType.SEMICOLON, getCurrentToken()); diff --git a/source/tlang/compiler/symbols/data.d b/source/tlang/compiler/symbols/data.d index a11d37b8..04016ba7 100644 --- a/source/tlang/compiler/symbols/data.d +++ b/source/tlang/compiler/symbols/data.d @@ -598,7 +598,15 @@ public class Call : IdentExpression public final class FunctionCall : Call { - + /* Whether this is statement-level function call or not */ + + /** + * Function calls either appear as part of an expression + * (i.e. from `parseExpression()`) or directly as a statement + * in the body of a `Container`. This affects how code generation + * works and hence one needs to disambiguate between the two. + */ + private bool isStatementLevel = false; /* Argument list */ private Expression[] arguments; @@ -618,6 +626,24 @@ public final class FunctionCall : Call { return arguments; } + + /** + * Mark this function call as statement-level + */ + public void makeStatementLevel() + { + this.isStatementLevel = true; + } + + /** + * Determines if this function call is statement-level + * + * Returns: true if so, false otherwise + */ + public bool isStatementLevelFuncCall() + { + return isStatementLevel; + } } /** diff --git a/source/tlang/compiler/typecheck/core.d b/source/tlang/compiler/typecheck/core.d index 0dd78120..1972bddd 100644 --- a/source/tlang/compiler/typecheck/core.d +++ b/source/tlang/compiler/typecheck/core.d @@ -1265,17 +1265,34 @@ public final class TypeChecker /** - * TODO: + * Codegen * * 1. Create FuncCallInstr * 2. Evaluate args and process them?! wait done elsewhere yeah!!! - * 3. Pop arts into here - * 4. AddInstr(combining those args) - * 5. DOne + * 3. Pop args into here + * 4. addInstr(combining those args) + * 4.1. If this is a statement-level function then `addInstrB()` is used + * 5. Done */ funcCallInstr.setContext(funcCall.getContext()); - addInstr(funcCallInstr); + // If not a statement-level function call then it is an expression + // ... and ought to be placed at the top of the stack for later consumption + if(!funcCall.isStatementLevelFuncCall()) + { + addInstr(funcCallInstr); + } + // If this IS a statement-level function call then it is not meant + // ... to be placed on the top of the stack as it won't be consumed later, + // ... rather it is finalised and should be added to the back of the code queue + else + { + addInstrB(funcCallInstr); + + // We also, for emitter, must transfer this flag over by + // ... marking this function call instruction as statement-level + funcCallInstr.markStatementLevel(); + } /* Set the Value instruction's type */ Type funcCallInstrType = getType(func.parentOf(), func.getType()); diff --git a/source/tlang/compiler/typecheck/dependency/core.d b/source/tlang/compiler/typecheck/dependency/core.d index ddca33fb..9ecd59be 100644 --- a/source/tlang/compiler/typecheck/dependency/core.d +++ b/source/tlang/compiler/typecheck/dependency/core.d @@ -1680,6 +1680,23 @@ public class DNodeGenerator /* NOTE: If anything we ought to remove these ExternSTmt nodes during such a process */ return null; } + /** + * Function call (statement-level) + */ + else if(cast(FunctionCall)entity) + { + FunctionCall funcCall = cast(FunctionCall)entity; + funcCall.setContext(context); + + // It MUST be if we are processing it in `generalPass()` + assert(funcCall.isStatementLevelFuncCall()); + gprintln("Function calls (at statement level)", DebugType.INFO); + + // The FunctionCall is an expression, so to get a DNode from it `expressionPass()` it + DNode funcCallDNode = expressionPass(funcCall, context); + + return funcCallDNode; + } return null; } diff --git a/source/tlang/testing/simple_direct_func_call.t b/source/tlang/testing/simple_direct_func_call.t new file mode 100644 index 00000000..09082483 --- /dev/null +++ b/source/tlang/testing/simple_direct_func_call.t @@ -0,0 +1,13 @@ +module simple_direct_func_call; + +int myVar = 0; + +void otherFunction(int i) +{ + myVar = i; +} + +void function() +{ + otherFunction(69); +} \ No newline at end of file diff --git a/source/tlang/testing/simple_function_recursion_factorial.t b/source/tlang/testing/simple_function_recursion_factorial.t new file mode 100644 index 00000000..f1e16ba7 --- /dev/null +++ b/source/tlang/testing/simple_function_recursion_factorial.t @@ -0,0 +1,13 @@ +module simple_function_recursion_factorial; + +ubyte factorial(ubyte i) +{ + if(i == cast(ubyte)0) + { + return cast(ubyte)1; + } + else + { + return i*factorial(i-cast(ubyte)1); + } +} \ No newline at end of file