1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-08-02 09:29:35 +00:00

Added missing line number to error when encountering symbol that has already been declared.

This commit is contained in:
jespergravgaard 2019-03-10 09:29:02 +01:00
parent 241f8bec4d
commit 3090fe4849
9 changed files with 69 additions and 28 deletions

View File

@ -479,17 +479,17 @@ class AsmFragmentTemplateSynthesisRule {
synths.add(new AsmFragmentTemplateSynthesisRule("(.*)vw(.)c1(.*)", null, null , "$1vd$2c1$3", null, null));
synths.add(new AsmFragmentTemplateSynthesisRule("(.*)vw(.)c2(.*)", null, null , "$1vd$2c2$3", null, null));
/* Removed pending optimization!
/* Removed awaiting optimization
// Rewrite constant unsigned word values to constant signed dword values
synths.add(new AsmFragmentTemplateSynthesisRule("(.*)vwuc1(.*)", null, null , "$1vdsc1$2", null, null));
synths.add(new AsmFragmentTemplateSynthesisRule("(.*)vwuc2(.*)", null, null , "$1vdsc2$2", null, null));
*/
/*
/* Removed awaiting optimization
// Rewrite any zeropage pointer as an unsigned word zeropage values
synths.add(new AsmFragmentTemplateSynthesisRule("(.*)p..z(.)(.*)", null, null , "$1vwuz$2$3", null, null));
synths.add(new AsmFragmentTemplateSynthesisRule("(.*)p..z(.)(.*)", ".*p..z._deref.*", null , "$1vwuz$2$3", null, null));
// Rewrite any constant pointer as an constant unsigned word
synths.add(new AsmFragmentTemplateSynthesisRule("(.*)p..c(.)(.*)", null, null , "$1vwuc$2$3", null, null));
synths.add(new AsmFragmentTemplateSynthesisRule("(.*)p..c(.)(.*)", ".*p..z._deref.*", null , "$1vwuc$2$3", null, null));
*/
// Synthesize some constant pointers as constant words (remove when the above section can be included)

View File

@ -2,6 +2,7 @@ package dk.camelot64.kickc.model.statements;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.Interval;
/** Contains information about the source of a program statement */
@ -15,9 +16,13 @@ public class StatementSource {
@Override
public String toString() {
CharStream stream = context.getStart().getInputStream();
Interval interval = new Interval(context.getStart().getStartIndex(), context.getStop().getStopIndex());
String sourceMessage = "File "+stream.getSourceName()+"\nLine "+context.getStart().getLine()+ "\n" +stream.getText(interval);
return sourceMessage;
Token contextStart = context.getStart();
if(contextStart != null) {
CharStream stream = contextStart.getInputStream();
Interval interval = new Interval(contextStart.getStartIndex(), context.getStop().getStopIndex());
return "File " + stream.getSourceName() + "\nLine " + contextStart.getLine() + "\n" + stream.getText(interval);
} else {
return "";
}
}
}

View File

@ -1,5 +1,6 @@
package dk.camelot64.kickc.model.symbols;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.Registers;
import dk.camelot64.kickc.model.VariableRegisterWeights;
@ -97,7 +98,7 @@ public abstract class Scope implements Symbol {
public Symbol add(Symbol symbol) {
if(symbols.get(symbol.getLocalName()) != null) {
throw new RuntimeException("Symbol already declared " + symbol.getLocalName());
throw new CompileError("Symbol already declared " + symbol.getLocalName());
}
symbols.put(symbol.getLocalName(), symbol);
return symbol;

View File

@ -299,7 +299,12 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
public Object visitDeclVariable(KickCParser.DeclVariableContext ctx) {
SymbolType type = (SymbolType) visit(ctx.typeDecl());
String varName = ctx.NAME().getText();
VariableUnversioned lValue = getCurrentSymbols().addVariable(varName, type);
VariableUnversioned lValue;
try {
lValue = getCurrentSymbols().addVariable(varName, type);
} catch(CompileError e) {
throw new CompileError(e.getMessage(), new StatementSource(ctx));
}
// Add directives
addDirectives(type, lValue, ctx.directive());
// Array / String variables are implicitly constant
@ -669,7 +674,11 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
Variable lValue;
if(forDeclCtx.typeDecl() != null) {
SymbolType type = (SymbolType) visit(forDeclCtx.typeDecl());
try {
lValue = getCurrentSymbols().addVariable(varName, type);
} catch(CompileError e) {
throw new CompileError(e.getMessage(), new StatementSource(forDeclCtx));
}
// Add directives
addDirectives(type, lValue, forDeclCtx.directive());
} else {

View File

@ -68,7 +68,8 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
throw new CompileError(
"Constant variable has a non-matching type \n variable: " + variable.toString(getProgram()) +
"\n value: (" + valueType.toString() + ") " + constVal.calculateLiteral(getScope()) +
"\n value definition: " + constVal.toString(getProgram()));
"\n value definition: " + constVal.toString(getProgram())
);
}
}

View File

@ -4,6 +4,7 @@ import dk.camelot64.kickc.asm.AsmClobber;
import dk.camelot64.kickc.asm.AsmProgram;
import dk.camelot64.kickc.asm.AsmSegment;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.StatementSource;
import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.model.statements.Statement;
@ -110,16 +111,17 @@ public class Pass4AssertNoCpuClobber extends Pass2Base {
// Alive and not assigned to - clobber not allowed!
if(clobberRegisters.contains(aliveVarRegister)) {
if(verbose) {
getLog().append("Error! Alive variable " + aliveVar + " register " + aliveVarRegister + " clobbered by the ASM generated by statement " + statement);
throw new CompileError(
"Error! Generated ASM has register clobber problem. " +
"Alive variable "+aliveVar + " register " + aliveVarRegister + " clobbered by the ASM generated by statement "+statement.toString(getProgram(), true),
statement.getSource()
);
}
clobberProblem = true;
}
}
}
}
if(verbose && clobberProblem) {
getLog().append(asm.toString(true));
}
return clobberProblem;
}

View File

@ -44,6 +44,11 @@ public class TestPrograms {
AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false);
}
@Test
public void testNoLocalScope() throws IOException, URISyntaxException {
assertError("nolocalscope", "Symbol already declared");
}
@Test
public void testTypeMix() throws IOException, URISyntaxException {
compileAndCompare("type-mix");
@ -288,7 +293,7 @@ public class TestPrograms {
@Test
public void testUnrollInfinite() throws IOException, URISyntaxException {
assertError("unroll-infinite", "Loop cannot be unrolled.");
assertError("unroll-infinite", "Loop cannot be unrolled.", false);
}
@Test
@ -1143,7 +1148,7 @@ public class TestPrograms {
@Test
public void testNoReturn() throws IOException, URISyntaxException {
assertError("noreturn", "Method must end with a return statement");
assertError("noreturn", "Method must end with a return statement", false);
}
@Test
@ -1158,7 +1163,7 @@ public class TestPrograms {
@Test
public void testInvalidConstType() throws IOException, URISyntaxException {
assertError("invalid-consttype", "Constant variable has a non-matching type");
assertError("invalid-consttype", "Constant variable has a non-matching type", false);
}
@Test
@ -1173,12 +1178,12 @@ public class TestPrograms {
@Test
public void testArrayLengthMismatch() throws IOException, URISyntaxException {
assertError("array-length-mismatch", "Array length mismatch");
assertError("array-length-mismatch", "Array length mismatch", false);
}
@Test
public void testStringLengthMismatch() throws IOException, URISyntaxException {
assertError("string-length-mismatch", "Array length mismatch");
assertError("string-length-mismatch", "Array length mismatch", false);
}
@Test
@ -1188,17 +1193,17 @@ public class TestPrograms {
@Test
public void testRegisterClobber() throws IOException, URISyntaxException {
assertError("register-clobber", "CLOBBER ERROR");
assertError("register-clobber", "register clobber problem", false);
}
@Test
public void testRecursionError() throws IOException, URISyntaxException {
assertError("recursion-error", "Recursion");
assertError("recursion-error", "Recursion", false);
}
@Test
public void testRecursionComplexError() throws IOException, URISyntaxException {
assertError("recursion-error-complex", "Recursion");
assertError("recursion-error-complex", "Recursion", false);
}
@Test
@ -1223,7 +1228,7 @@ public class TestPrograms {
@Test
public void testNoInlineInterrupt() throws IOException, URISyntaxException {
assertError("no-inlineinterrupt", "Interrupts cannot be inlined");
assertError("no-inlineinterrupt", "Interrupts cannot be inlined", false);
}
@Test
@ -1233,12 +1238,12 @@ public class TestPrograms {
@Test
public void testNoParamInterrupt() throws IOException, URISyntaxException {
assertError("no-paraminterrupt", "Interrupts cannot have parameters.");
assertError("no-paraminterrupt", "Interrupts cannot have parameters.", false);
}
@Test
public void testNoReturnInterrupt() throws IOException, URISyntaxException {
assertError("no-returninterrupt", "Interrupts cannot return anything.");
assertError("no-returninterrupt", "Interrupts cannot return anything.", false);
}
@Test
@ -1247,12 +1252,20 @@ public class TestPrograms {
}
private void assertError(String kcFile, String expectError) throws IOException, URISyntaxException {
assertError(kcFile, expectError, true);
}
private void assertError(String kcFile, String expectError, boolean expectLineNumber) throws IOException, URISyntaxException {
try {
compileAndCompare(kcFile);
} catch(CompileError e) {
System.out.println("Got error: " + e.getMessage());
// expecting error!
assertTrue("Error message expected '" + expectError + "' - was:" + e.getMessage(), e.getMessage().contains(expectError));
if(expectLineNumber) {
// expecting line number!
assertTrue("Error message expected line number - was:" + e.getMessage(), e.getMessage().contains("Line"));
}
return;
}
fail("Expected compile error.");

View File

@ -0,0 +1,10 @@
// Illustrates problem with not introducing local scopes inside loops etc
import "print"
void main() {
for (byte i: 0..5)
print_ln();
for (byte i: 0..5)
print_str_ln(" xxxxx@");
}