mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-04-08 14:37:40 +00:00
Recusion now detected, resulting in CompileError. Closes #88
This commit is contained in:
parent
a34f77d319
commit
34909f47d8
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
14
src/test/java/dk/camelot64/kickc/test/kc/recursion-error.kc
Normal file
14
src/test/java/dk/camelot64/kickc/test/kc/recursion-error.kc
Normal 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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user