1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-04-16 08:37:47 +00:00

Closes Pointer: Support calculated pointers eg. *(ptr+i)

This commit is contained in:
jespergravgaard 2017-11-13 21:52:38 +01:00
parent 365357596b
commit 8462d75265
17 changed files with 2292 additions and 330 deletions

@ -129,7 +129,7 @@ Testing
- Test single passes by configuring compiler to only log ICL before & after the tested step!
- Implement ICL syntax (printer & parser). Drop the JSON.
- Test that the parse tree for specific KC syntax is as expected. Use a print function for the parse tree to generate output for comparison.
- Test the ICL result of a specific phase on a specific ICL input. Create an ICL syntax for parsing directly to ICL and a printer for the syntax.
- Test that the parse tree for specific KC syntax is as expected. Use a print function for the parse tree to generate output for comparison.
- Emulate/Run the ASM for a specific KC program compiled. Compare the emulated ASM output to output calculated by executing directly on the KC tree.
- Add assert statements to the language. Create KC programs that test the compiler by compiling, running and testing assertions.

@ -126,6 +126,22 @@ public class AsmFragmentManager {
return result;
}
}
if (signature.startsWith("_deref_zpptrby1=")) {
String subSignature = regexpRewriteSignature(signature, "_deref_zpptrby1=(.*)", "aby=$1").replace("zpptrby2", "zpptrby1").replace("zpptrby3", "zpptrby2");
CharStream subCharStream = loadOrSynthesizeFragment(subSignature);
if (subCharStream != null) {
CharStream result = CharStreams.fromString(subCharStream.toString().replace("zpptrby2", "zpptrby3").replace("zpptrby1", "zpptrby2") + "\n"+"ldy #0\n" + "sta ({zpptrby1}),y\n");
return result;
}
}
if (signature.startsWith("_deref_cowo1=")) {
String subSignature = regexpRewriteSignature(signature, "_deref_cowo1=(.*)", "aby=$1").replace("cowo2", "cowo1").replace("cowo3", "cowo2").replace("coby2", "coby1").replace("coby3", "coby2");
CharStream subCharStream = loadOrSynthesizeFragment(subSignature);
if (subCharStream != null) {
CharStream result = CharStreams.fromString(subCharStream.toString().replace("cowo2", "cowo3").replace("cowo1", "cowo2").replace("coby2", "coby3").replace("coby1", "coby2") + "\nsta {cowo1}\n");
return result;
}
}
if (signature.startsWith("_deref_cowo1_neq_") && !signature.contains("aby")) {
String subSignature = regexpRewriteSignature(signature, "_deref_cowo1_neq_(.*)", "aby_neq_$1").replace("cowo2", "cowo1").replace("cowo3", "cowo2").replace("coby2", "coby1").replace("coby3", "coby2");
CharStream subCharStream = loadOrSynthesizeFragment(subSignature);
@ -167,7 +183,7 @@ public class AsmFragmentManager {
}
}
if (signature.contains("=_deref_zpptrby1") && !signature.matches(".*=.*aby.*")&& !signature.matches(".*=.*yby.*")) {
String subSignature = regexpRewriteSignature(signature, "(.*)=_deref_zpptrby1_(.*)", "$1=aby_$2").replace("zpptrby2", "zpptrby1").replace("zpptrby3", "zpptrby2");
String subSignature = regexpRewriteSignature(signature, "(.*)=_deref_zpptrby1(.*)", "$1=aby$2").replace("zpptrby2", "zpptrby1").replace("zpptrby3", "zpptrby2");
CharStream subCharStream = loadOrSynthesizeFragment(subSignature);
if (subCharStream != null) {
CharStream result = CharStreams.fromString("ldy #0\n"+"lda ({zpptrby1}),y\n"+subCharStream.toString().replace("zpptrby2", "zpptrby3").replace("zpptrby1", "zpptrby2"));

@ -54,9 +54,9 @@ initializer
;
lvalue
: '(' lvalue ')' #lvaluePar
| NAME #lvalueName
| '*' lvalue #lvaluePtr
: NAME #lvalueName
| '*' NAME #lvaluePtr
| '*' '(' expr ')' #lvaluePtrExpr
| ('<' | '>' ) lvalue #lvalueLoHi
| lvalue '[' expr ']' #lvalueArray
;

@ -323,6 +323,18 @@ public class KickCBaseListener implements KickCListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLvaluePtr(KickCParser.LvaluePtrContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterLvaluePtrExpr(KickCParser.LvaluePtrExprContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLvaluePtrExpr(KickCParser.LvaluePtrExprContext ctx) { }
/**
* {@inheritDoc}
*
@ -335,18 +347,6 @@ public class KickCBaseListener implements KickCListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLvalueArray(KickCParser.LvalueArrayContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterLvaluePar(KickCParser.LvalueParContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitLvaluePar(KickCParser.LvalueParContext ctx) { }
/**
* {@inheritDoc}
*

@ -199,14 +199,14 @@ public class KickCBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLvalueArray(KickCParser.LvalueArrayContext ctx) { return visitChildren(ctx); }
@Override public T visitLvaluePtrExpr(KickCParser.LvaluePtrExprContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitLvaluePar(KickCParser.LvalueParContext ctx) { return visitChildren(ctx); }
@Override public T visitLvalueArray(KickCParser.LvalueArrayContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*

@ -309,6 +309,18 @@ public interface KickCListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitLvaluePtr(KickCParser.LvaluePtrContext ctx);
/**
* Enter a parse tree produced by the {@code lvaluePtrExpr}
* labeled alternative in {@link KickCParser#lvalue}.
* @param ctx the parse tree
*/
void enterLvaluePtrExpr(KickCParser.LvaluePtrExprContext ctx);
/**
* Exit a parse tree produced by the {@code lvaluePtrExpr}
* labeled alternative in {@link KickCParser#lvalue}.
* @param ctx the parse tree
*/
void exitLvaluePtrExpr(KickCParser.LvaluePtrExprContext ctx);
/**
* Enter a parse tree produced by the {@code lvalueArray}
* labeled alternative in {@link KickCParser#lvalue}.
@ -321,18 +333,6 @@ public interface KickCListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitLvalueArray(KickCParser.LvalueArrayContext ctx);
/**
* Enter a parse tree produced by the {@code lvaluePar}
* labeled alternative in {@link KickCParser#lvalue}.
* @param ctx the parse tree
*/
void enterLvaluePar(KickCParser.LvalueParContext ctx);
/**
* Exit a parse tree produced by the {@code lvaluePar}
* labeled alternative in {@link KickCParser#lvalue}.
* @param ctx the parse tree
*/
void exitLvaluePar(KickCParser.LvalueParContext ctx);
/**
* Enter a parse tree produced by the {@code lvalueLoHi}
* labeled alternative in {@link KickCParser#lvalue}.

File diff suppressed because it is too large Load Diff

@ -187,6 +187,13 @@ public interface KickCVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitLvaluePtr(KickCParser.LvaluePtrContext ctx);
/**
* Visit a parse tree produced by the {@code lvaluePtrExpr}
* labeled alternative in {@link KickCParser#lvalue}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLvaluePtrExpr(KickCParser.LvaluePtrExprContext ctx);
/**
* Visit a parse tree produced by the {@code lvalueArray}
* labeled alternative in {@link KickCParser#lvalue}.
@ -194,13 +201,6 @@ public interface KickCVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitLvalueArray(KickCParser.LvalueArrayContext ctx);
/**
* Visit a parse tree produced by the {@code lvaluePar}
* labeled alternative in {@link KickCParser#lvalue}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLvaluePar(KickCParser.LvalueParContext ctx);
/**
* Visit a parse tree produced by the {@code lvalueLoHi}
* labeled alternative in {@link KickCParser#lvalue}.

@ -170,7 +170,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
// Create and assign declared loop variable
String varName = forDeclCtx.NAME().getText();
Variable lValue;
if(forDeclCtx.typeDecl()!=null) {
if (forDeclCtx.typeDecl() != null) {
SymbolType type = (SymbolType) visit(forDeclCtx.typeDecl());
lValue = getCurrentSymbols().addVariable(varName, type);
} else {
@ -189,7 +189,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
this.visit(stmtForCtx.stmt());
}
// Add increment
if(ctx.expr(1)!=null) {
if (ctx.expr(1) != null) {
PrePostModifierHandler.addPreModifiers(this, ctx.expr(1));
this.visit(ctx.expr(1));
PrePostModifierHandler.addPostModifiers(this, ctx.expr(1));
@ -211,7 +211,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
// Create declared loop variable
String varName = forDeclCtx.NAME().getText();
Variable lValue;
if(forDeclCtx.typeDecl()!=null) {
if (forDeclCtx.typeDecl() != null) {
SymbolType type = (SymbolType) visit(forDeclCtx.typeDecl());
lValue = getCurrentSymbols().addVariable(varName, type);
} else {
@ -236,21 +236,21 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
}
// Add increment
ConstantInteger beyondLastVal;
if(rangeFirst.getNumber()>rangeLast.getNumber()) {
if (rangeFirst.getNumber() > rangeLast.getNumber()) {
Statement stmtInc = new StatementAssignment(lValue.getRef(), Operator.DECREMENT, lValue.getRef());
sequence.addStatement(stmtInc);
if(rangeLast.getNumber()==0) {
if (rangeLast.getNumber() == 0) {
beyondLastVal = new ConstantInteger(255);
} else {
beyondLastVal = new ConstantInteger(rangeLast.getNumber()-1);
beyondLastVal = new ConstantInteger(rangeLast.getNumber() - 1);
}
} else {
} else {
Statement stmtInc = new StatementAssignment(lValue.getRef(), Operator.INCREMENT, lValue.getRef());
sequence.addStatement(stmtInc);
if(rangeLast.getNumber()==255) {
if (rangeLast.getNumber() == 255) {
beyondLastVal = new ConstantInteger(0);
} else {
beyondLastVal = new ConstantInteger(rangeLast.getNumber()+1);
beyondLastVal = new ConstantInteger(rangeLast.getNumber() + 1);
}
}
@ -290,7 +290,9 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
sequence.addStatement(new StatementAssignment(returnVar, returnVar));
}
VariableRef returnVarRef = null;
if(returnVar!=null) {returnVarRef = returnVar.getRef();}
if (returnVar != null) {
returnVarRef = returnVar.getRef();
}
sequence.addStatement(new StatementReturn(returnVarRef));
scopeStack.pop();
sequence.addStatement(new StatementProcedureEnd(procedure.getRef()));
@ -378,18 +380,17 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
}
@Override
public LValue visitLvaluePar(KickCParser.LvalueParContext ctx) {
return (LValue) visit(ctx.lvalue());
public LValue visitLvaluePtr(KickCParser.LvaluePtrContext ctx) {
Variable variable = getCurrentSymbols().getVariable(ctx.NAME().getText());
return new PointerDereferenceSimple(variable.getRef());
}
@Override
public LValue visitLvaluePtr(KickCParser.LvaluePtrContext ctx) {
LValue lval = (LValue) visit(ctx.lvalue());
if (lval instanceof VariableRef) {
return new PointerDereferenceSimple(lval);
} else {
throw new RuntimeException("Not implemented");
}
public Object visitLvaluePtrExpr(KickCParser.LvaluePtrExprContext ctx) {
PrePostModifierHandler.addPreModifiers(this, ctx.expr());
RValue rValue = (RValue) this.visit(ctx.expr());
PrePostModifierHandler.addPostModifiers(this, ctx.expr());
return new PointerDereferenceSimple(rValue);
}
@Override
@ -402,7 +403,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
} else if (opTxt.equals(">")) {
return new LvalueLoHiByte(Operator.SET_HIBYTE, (VariableRef) lval);
} else {
throw new RuntimeException("Not implemented - lo/hi-lvalue operator "+opTxt);
throw new RuntimeException("Not implemented - lo/hi-lvalue operator " + opTxt);
}
}
throw new RuntimeException("Not implemented - lo/hi lvalues of non-variables");
@ -444,7 +445,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
@Override
public SymbolType visitTypeArray(KickCParser.TypeArrayContext ctx) {
SymbolType elementType = (SymbolType) visit(ctx.typeDecl());
if(ctx.expr()!=null) {
if (ctx.expr() != null) {
ConstantValue size = ParseTreeConstantEvaluator.evaluate(ctx.expr());
if (size instanceof ConstantInteger) {
return new SymbolTypeArray(elementType, ((ConstantInteger) size).getNumber());
@ -512,7 +513,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
@Override
public RValue visitExprString(KickCParser.ExprStringContext ctx) {
String text = ctx.getText();
return new ConstantString(text.substring(1, text.length()-1));
return new ConstantString(text.substring(1, text.length() - 1));
}
@Override
@ -544,7 +545,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
RValue child = (RValue) this.visit(ctx.expr());
String op = ((TerminalNode) ctx.getChild(0)).getSymbol().getText();
Operator operator = Operator.getUnary(op);
if(Operator.STAR.equals(operator)) {
if (Operator.STAR.equals(operator)) {
return new PointerDereferenceSimple(child);
} else {
VariableIntermediate tmpVar = getCurrentSymbols().addVariableIntermediate();
@ -575,9 +576,9 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
@Override
public RValue visitExprId(KickCParser.ExprIdContext ctx) {
Variable variable = getCurrentSymbols().getVariable(ctx.NAME().getText());
if(variable == null) {
program.getLog().append("ERROR! Line "+ctx.getStart().getLine()+". Unknown variable "+ctx.NAME().getText());
throw new CompileError("ERROR! Line "+ctx.getStart().getLine()+". Unknown variable "+ctx.NAME().getText());
if (variable == null) {
program.getLog().append("ERROR! Line " + ctx.getStart().getLine() + ". Unknown variable " + ctx.NAME().getText());
throw new CompileError("ERROR! Line " + ctx.getStart().getLine() + ". Unknown variable " + ctx.NAME().getText());
}
return variable.getRef();
}
@ -626,7 +627,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor<Object> {
for (PrePostModifier mod : modifiers) {
Statement stmt = new StatementAssignment((LValue) mod.child, mod.operator, mod.child);
parser.sequence.addStatement(stmt);
parser.program.getLog().append("Adding pre/post-modifier "+stmt.toString(parser.program, true));
parser.program.getLog().append("Adding pre/post-modifier " + stmt.toString(parser.program, true));
}
}

@ -24,6 +24,10 @@ public class TestPrograms extends TestCase {
helper = new ReferenceHelper("dk/camelot64/kickc/test/ref/");
}
public void testPtrComplex() throws IOException, URISyntaxException {
compileAndCompare("ptr-complex");
}
public void testIncD020() throws IOException, URISyntaxException {
compileAndCompare("incd020");
}

@ -6,7 +6,7 @@ void main() {
byte* cursor = SCREEN;
byte i=0;
do {
(*cursor) = TEXT[i];
*cursor = TEXT[i];
if(++i==8) {
i = 0;
}

@ -0,0 +1,28 @@
// Test some complex pointers
void main() {
byte* screen = $0400;
// RValue pointer expression (constant)
byte a = *(screen+80);
// RValue pointer expression (variable)
for(byte i : 0..10) {
screen[i] = *(screen+40+i);
}
// LValue pointer expression (constant - through tmp variable)
byte* sc2 = screen+81;
*sc2 = *(screen+121);
// LValue pointer expression (constant - directly)
*(screen+82) = *(screen+122);
// LValue pointer expression (variable - directly)
for(byte j : 0..10) {
*(screen+160+j) = *(screen+200+j);
}
}

@ -6,7 +6,7 @@ void main() {
byte* cursor = SCREEN;
byte i=0;
do {
(*cursor) = TEXT[i];
*cursor = TEXT[i];
if(++i==8) {
i = 0;
}

@ -0,0 +1,54 @@
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
jsr main
main: {
.const screen = $400
.const sc2 = screen+$51
.label _2 = 2
.label _9 = 2
.label _11 = 4
lda screen+$50
ldx #0
b1:
txa
clc
adc #<screen+$28
sta _2
lda #0
adc #>screen+$28
sta _2+1
ldy #0
lda (_2),y
sta screen,x
inx
cpx #$b
bne b1
lda screen+$79
sta sc2
lda screen+$7a
sta screen+$52
ldx #0
b2:
txa
clc
adc #<screen+$a0
sta _9
lda #0
adc #>screen+$a0
sta _9+1
txa
clc
adc #<screen+$c8
sta _11
lda #0
adc #>screen+$c8
sta _11+1
ldy #0
lda (_11),y
sta (_9),y
inx
cpx #$b
bne b2
rts
}

@ -0,0 +1,31 @@
@begin: scope:[] from
to:@1
@1: scope:[] from @begin
[0] call main param-assignment [ ] ( )
to:@end
@end: scope:[] from @1
main: scope:[main] from @1
[1] (byte) main::a#0 ← *((const byte*) main::screen#0+(byte) 80) [ ] ( main:0 [ ] )
to:main::@1
main::@1: scope:[main] from main main::@1
[2] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 ) [ main::i#2 ] ( main:0 [ main::i#2 ] )
[3] (byte*~) main::$2 ← (const byte*) main::screen#0+(byte) 40 + (byte) main::i#2 [ main::i#2 main::$2 ] ( main:0 [ main::i#2 main::$2 ] )
[4] *((const byte*) main::screen#0 + (byte) main::i#2) ← *((byte*~) main::$2) [ main::i#2 ] ( main:0 [ main::i#2 ] )
[5] (byte) main::i#1 ← ++ (byte) main::i#2 [ main::i#1 ] ( main:0 [ main::i#1 ] )
[6] if((byte) main::i#1!=(byte) 11) goto main::@1 [ main::i#1 ] ( main:0 [ main::i#1 ] )
to:main::@3
main::@3: scope:[main] from main::@1
[7] *((const byte*) main::sc2#0) ← *((const byte*) main::screen#0+(byte) 121) [ ] ( main:0 [ ] )
[8] *((const byte*) main::screen#0+(byte) 82) ← *((const byte*) main::screen#0+(byte) 122) [ ] ( main:0 [ ] )
to:main::@2
main::@2: scope:[main] from main::@2 main::@3
[9] (byte) main::j#2 ← phi( main::@2/(byte) main::j#1 main::@3/(byte) 0 ) [ main::j#2 ] ( main:0 [ main::j#2 ] )
[10] (byte*~) main::$9 ← (const byte*) main::screen#0+(byte) 160 + (byte) main::j#2 [ main::j#2 main::$9 ] ( main:0 [ main::j#2 main::$9 ] )
[11] (byte*~) main::$11 ← (const byte*) main::screen#0+(byte) 200 + (byte) main::j#2 [ main::j#2 main::$9 main::$11 ] ( main:0 [ main::j#2 main::$9 main::$11 ] )
[12] *((byte*~) main::$9) ← *((byte*~) main::$11) [ main::j#2 ] ( main:0 [ main::j#2 ] )
[13] (byte) main::j#1 ← ++ (byte) main::j#2 [ main::j#1 ] ( main:0 [ main::j#1 ] )
[14] if((byte) main::j#1!=(byte) 11) goto main::@2 [ main::j#1 ] ( main:0 [ main::j#1 ] )
to:main::@return
main::@return: scope:[main] from main::@2
[15] return [ ] ( main:0 [ ] )
to:@return

File diff suppressed because it is too large Load Diff

@ -0,0 +1,29 @@
(label) @1
(label) @begin
(label) @end
(void()) main()
(byte*~) main::$11 $11 zp ZP_PTR_BYTE:4 11.0
(byte*~) main::$2 $2 zp ZP_PTR_BYTE:2 11.0
(byte*~) main::$9 $9 zp ZP_PTR_BYTE:2 11.0
(label) main::@1
(label) main::@2
(label) main::@3
(label) main::@return
(byte) main::a
(byte) main::a#0 reg byte a 20.0
(byte) main::i
(byte) main::i#1 reg byte x 16.5
(byte) main::i#2 reg byte x 14.666666666666666
(byte) main::j
(byte) main::j#1 reg byte x 16.5
(byte) main::j#2 reg byte x 11.0
(byte*) main::sc2
(const byte*) main::sc2#0 sc2 = (const byte*) main::screen#0+(byte) 81
(byte*) main::screen
(const byte*) main::screen#0 screen = (word) 1024
reg byte x [ main::i#2 main::i#1 ]
reg byte x [ main::j#2 main::j#1 ]
reg byte a [ main::a#0 ]
zp ZP_PTR_BYTE:2 [ main::$2 main::$9 ]
zp ZP_PTR_BYTE:4 [ main::$11 ]