1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-09-08 17:54:40 +00:00

Recusion now detected, resulting in CompileError. Closes #88

This commit is contained in:
jespergravgaard 2018-04-22 23:46:26 +02:00
parent a34f77d319
commit 34909f47d8
5 changed files with 135 additions and 0 deletions

View File

@ -120,6 +120,7 @@ public class Compiler {
new Pass1AssertNoLValueIntermediate(program).execute();
new Pass1AddTypePromotions(program).execute();
new Pass1AssertArrayLengths(program).execute();
new Pass1AssertNoRecursion(program).execute();
getLog().append("INITIAL CONTROL FLOW GRAPH");
getLog().append(program.getGraph().toString(program));

View File

@ -0,0 +1,74 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.ControlFlowGraph;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.SymbolRef;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
/** Asserts that the program has no recursive calls */
public class Pass1AssertNoRecursion extends Pass1Base {
public Pass1AssertNoRecursion(Program program) {
super(program);
}
@Override
public boolean step() {
ControlFlowBlock firstBlock = getGraph().getFirstBlock();
Deque<ControlFlowBlock> path = new LinkedList<>();
assertNoRecursion(firstBlock, path);
return false;
}
/**
* Asserts that no methods perform recursive calls
* @param block The block to check
* @param path The call-path taken to the block
*/
private void assertNoRecursion(ControlFlowBlock block, Deque<ControlFlowBlock> path) {
// Detect recursion
if(path.contains(block)) {
StringBuffer msg = new StringBuffer();
Iterator<ControlFlowBlock> pathIt = path.descendingIterator();
while(pathIt.hasNext()) {
ControlFlowBlock pathBlock = pathIt.next();
msg.append(pathBlock.getLabel()).append(" > ");
}
msg.append(block.getLabel());
throw new CompileError("ERROR! Recursion not allowed! "+msg);
}
path.push(block);
// Follow all calls
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementCall) {
ProcedureRef procedureRef = ((StatementCall) statement).getProcedure();
ControlFlowBlock procedureBlock = getGraph().getBlock(procedureRef.getLabelRef());
assertNoRecursion(procedureBlock, path);
}
}
// Follow successors
if(block.getConditionalSuccessor()!=null && !block.getConditionalSuccessor().isProcExit()) {
ControlFlowBlock conditionalSuccessor = getGraph().getConditionalSuccessor(block);
if(!path.contains(conditionalSuccessor)) {
assertNoRecursion(conditionalSuccessor, path);
}
}
if(block.getDefaultSuccessor()!=null && !block.getDefaultSuccessor().isProcExit()) {
ControlFlowBlock defaultSuccessor = getGraph().getDefaultSuccessor(block);
if(!path.contains(defaultSuccessor)) {
assertNoRecursion(defaultSuccessor, path);
}
}
path.pop();
}
}

View File

@ -710,6 +710,17 @@ public class TestPrograms {
assertError("register-clobber", "CLOBBER ERROR");
}
@Test
public void testRecursionError() throws IOException, URISyntaxException {
assertError("recursion-error", "Recursion");
}
@Test
public void testRecursionComplexError() throws IOException, URISyntaxException {
assertError("recursion-error-complex", "Recursion");
}
private void assertError(String kcFile, String expectError) throws IOException, URISyntaxException {
try {
compileAndCompare(kcFile);

View File

@ -0,0 +1,35 @@
// Test that a complex recursion results in a CompileError
void main() {
byte* screen = $400;
byte f = fa(8);
*screen = f;
}
// Recursive function
byte fa(byte n) {
if(n<10) {
return fb(n-1);
} else {
return fc(n+1);
}
}
// Recursive function
byte fb(byte n) {
if((n&1)==0) {
return fc(n);
} else {
return n;
}
}
// Recursive function
byte fc(byte n) {
if((n&1)==0) {
return n;
} else {
return fa(n>>1);
}
}

View File

@ -0,0 +1,14 @@
// Test that recursion results in a CompileError, as it is not supported
void main() {
byte* screen = $400;
byte f = fib(8);
*screen = f;
}
// Fibonacci implementation
byte fib(byte n) {
if(n==0) return 0;
if(n==1) return 1;
return fib(n-1)+fib(n-2);
}