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

Implemented support for comma-separated variable declarations.

This commit is contained in:
jespergravgaard 2019-04-15 23:13:15 +02:00
parent 0f5d8f906b
commit 24ca7e9c68
21 changed files with 1727 additions and 857 deletions

View File

@ -28,7 +28,16 @@ decl
;
declVariable
: directive* typeDecl directive* NAME ('=' expr)? ';'
: directive* typeDecl directive* declVariableList ';'
;
declVariableList
: declVariableInit
| declVariableList ',' declVariableInit
;
declVariableInit
: NAME ('=' expr)?
;
declFunction
@ -60,12 +69,12 @@ stmtSeq
stmt
: declVariable #stmtDeclVar
| '{' stmtSeq? '}' #stmtBlock
| expr ';' #stmtExpr
| 'if' '(' expr ')' stmt ( 'else' stmt )? #stmtIfElse
| directive* 'while' '(' expr ')' stmt #stmtWhile
| directive* 'do' stmt 'while' '(' expr ')' ';' #stmtDoWhile
| commaExpr ';' #stmtExpr
| 'if' '(' commaExpr ')' stmt ( 'else' stmt )? #stmtIfElse
| directive* 'while' '(' commaExpr ')' stmt #stmtWhile
| directive* 'do' stmt 'while' '(' commaExpr ')' ';' #stmtDoWhile
| directive* 'for' '(' forDeclaration? forIteration ')' stmt #stmtFor
| 'return' expr? ';' #stmtReturn
| 'return' commaExpr? ';' #stmtReturn
| 'break' ';' #stmtBreak
| 'continue' ';' #stmtContinue
| 'asm' asmDirectives? '{' asmLines '}' #stmtAsm

View File

@ -92,13 +92,13 @@ WS=91
COMMENT_LINE=92
COMMENT_BLOCK=93
'import'=1
'='=2
';'=3
'('=4
')'=5
'{'=6
'}'=7
','=8
';'=2
','=3
'='=4
'('=5
')'=6
'{'=7
'}'=8
'const'=9
'extern'=10
'align'=11

View File

@ -95,6 +95,30 @@ public class KickCBaseListener implements KickCListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitDeclVariable(KickCParser.DeclVariableContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterDeclVariableList(KickCParser.DeclVariableListContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitDeclVariableList(KickCParser.DeclVariableListContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterDeclVariableInit(KickCParser.DeclVariableInitContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitDeclVariableInit(KickCParser.DeclVariableInitContext ctx) { }
/**
* {@inheritDoc}
*

View File

@ -60,6 +60,20 @@ public class KickCBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitDeclVariable(KickCParser.DeclVariableContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitDeclVariableList(KickCParser.DeclVariableListContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitDeclVariableInit(KickCParser.DeclVariableInitContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*

View File

@ -56,7 +56,7 @@ public class KickCLexer extends Lexer {
};
private static final String[] _LITERAL_NAMES = {
null, "'import'", "'='", "';'", "'('", "')'", "'{'", "'}'", "','", "'const'",
null, "'import'", "';'", "','", "'='", "'('", "')'", "'{'", "'}'", "'const'",
"'extern'", "'align'", "'register'", "'inline'", "'volatile'", "'interrupt'",
"'if'", "'else'", "'while'", "'do'", "'for'", "'return'", "'break'", "'continue'",
"'asm'", "':'", "'..'", "'signed'", "'unsigned'", "'*'", "'['", "']'",
@ -240,10 +240,10 @@ public class KickCLexer extends Lexer {
"\u038f\3\2\2\2\u00c1\u039c\3\2\2\2\u00c3\u03a2\3\2\2\2\u00c5\u03ad\3\2"+
"\2\2\u00c7\u00c8\7k\2\2\u00c8\u00c9\7o\2\2\u00c9\u00ca\7r\2\2\u00ca\u00cb"+
"\7q\2\2\u00cb\u00cc\7t\2\2\u00cc\u00cd\7v\2\2\u00cd\4\3\2\2\2\u00ce\u00cf"+
"\7?\2\2\u00cf\6\3\2\2\2\u00d0\u00d1\7=\2\2\u00d1\b\3\2\2\2\u00d2\u00d3"+
"\7*\2\2\u00d3\n\3\2\2\2\u00d4\u00d5\7+\2\2\u00d5\f\3\2\2\2\u00d6\u00d7"+
"\7}\2\2\u00d7\16\3\2\2\2\u00d8\u00d9\7\177\2\2\u00d9\20\3\2\2\2\u00da"+
"\u00db\7.\2\2\u00db\22\3\2\2\2\u00dc\u00dd\7e\2\2\u00dd\u00de\7q\2\2\u00de"+
"\7=\2\2\u00cf\6\3\2\2\2\u00d0\u00d1\7.\2\2\u00d1\b\3\2\2\2\u00d2\u00d3"+
"\7?\2\2\u00d3\n\3\2\2\2\u00d4\u00d5\7*\2\2\u00d5\f\3\2\2\2\u00d6\u00d7"+
"\7+\2\2\u00d7\16\3\2\2\2\u00d8\u00d9\7}\2\2\u00d9\20\3\2\2\2\u00da\u00db"+
"\7\177\2\2\u00db\22\3\2\2\2\u00dc\u00dd\7e\2\2\u00dd\u00de\7q\2\2\u00de"+
"\u00df\7p\2\2\u00df\u00e0\7u\2\2\u00e0\u00e1\7v\2\2\u00e1\24\3\2\2\2\u00e2"+
"\u00e3\7g\2\2\u00e3\u00e4\7z\2\2\u00e4\u00e5\7v\2\2\u00e5\u00e6\7g\2\2"+
"\u00e6\u00e7\7t\2\2\u00e7\u00e8\7p\2\2\u00e8\26\3\2\2\2\u00e9\u00ea\7"+

View File

@ -92,13 +92,13 @@ WS=91
COMMENT_LINE=92
COMMENT_BLOCK=93
'import'=1
'='=2
';'=3
'('=4
')'=5
'{'=6
'}'=7
','=8
';'=2
','=3
'='=4
'('=5
')'=6
'{'=7
'}'=8
'const'=9
'extern'=10
'align'=11

View File

@ -77,6 +77,26 @@ public interface KickCListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitDeclVariable(KickCParser.DeclVariableContext ctx);
/**
* Enter a parse tree produced by {@link KickCParser#declVariableList}.
* @param ctx the parse tree
*/
void enterDeclVariableList(KickCParser.DeclVariableListContext ctx);
/**
* Exit a parse tree produced by {@link KickCParser#declVariableList}.
* @param ctx the parse tree
*/
void exitDeclVariableList(KickCParser.DeclVariableListContext ctx);
/**
* Enter a parse tree produced by {@link KickCParser#declVariableInit}.
* @param ctx the parse tree
*/
void enterDeclVariableInit(KickCParser.DeclVariableInitContext ctx);
/**
* Exit a parse tree produced by {@link KickCParser#declVariableInit}.
* @param ctx the parse tree
*/
void exitDeclVariableInit(KickCParser.DeclVariableInitContext ctx);
/**
* Enter a parse tree produced by {@link KickCParser#declFunction}.
* @param ctx the parse tree

File diff suppressed because it is too large Load Diff

View File

@ -52,6 +52,18 @@ public interface KickCVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitDeclVariable(KickCParser.DeclVariableContext ctx);
/**
* Visit a parse tree produced by {@link KickCParser#declVariableList}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitDeclVariableList(KickCParser.DeclVariableListContext ctx);
/**
* Visit a parse tree produced by {@link KickCParser#declVariableInit}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitDeclVariableInit(KickCParser.DeclVariableInitContext ctx);
/**
* Visit a parse tree produced by {@link KickCParser#declFunction}.
* @param ctx the parse tree

View File

@ -172,7 +172,8 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
SymbolType type = (SymbolType) this.visit(ctx.typeDecl());
VariableUnversioned param = new VariableUnversioned(ctx.NAME().getText(), getCurrentScope(), type);
// Add directives
addDirectives(type, param, ctx.directive());
List<Directive> directives = getDirectives(ctx.directive());
addDirectives(param, type, directives, new StatementSource(ctx));
return param;
}
@ -236,7 +237,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
/** will contain the address to generate the KickAssembler-code to. */
private RValue address;
public AsmDirectiveLocation(RValue address) {
AsmDirectiveLocation(RValue address) {
this.address = address;
}
@ -266,7 +267,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
/** bytes for the KickAssembler-code. */
private RValue bytes;
public AsmDirectiveBytes(RValue bytes) {
AsmDirectiveBytes(RValue bytes) {
this.bytes = bytes;
}
@ -299,7 +300,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
return uses;
}
public AsmDirectiveUses(SymbolVariableRef uses) {
AsmDirectiveUses(SymbolVariableRef uses) {
this.uses = uses;
}
@ -335,7 +336,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
/** cycles for the KickAssembler-code. */
private RValue cycles;
public AsmDirectiveCycles(RValue cycles) {
AsmDirectiveCycles(RValue cycles) {
this.cycles = cycles;
}
@ -378,7 +379,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
private class AsmDirectiveClobber implements AsmDirective {
private AsmClobber clobber;
public AsmDirectiveClobber(AsmClobber clobber) {
AsmDirectiveClobber(AsmClobber clobber) {
this.clobber = clobber;
}
@ -407,9 +408,41 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
return new AsmDirectiveClobber(clobber);
}
/** Holds the declared type when descending into a Variable Declaration. */
private SymbolType declVarType = null;
/** Holds the declared directives when descending into a Variable Declaration. */
private List<Directive> declVarDirectives = null;
/** Holds the declared comments when descending into a Variable Declaration. */
private List<Comment> declVarComments = null;
@Override
public Object visitDeclVariable(KickCParser.DeclVariableContext ctx) {
SymbolType type = (SymbolType) visit(ctx.typeDecl());
List<KickCParser.DirectiveContext> directive = ctx.directive();
this.declVarType = (SymbolType) visit(ctx.typeDecl());
this.declVarDirectives = getDirectives(directive);
this.declVarComments = getCommentsSymbol(ctx);
this.visit(ctx.declVariableList());
this.declVarType = null;
this.declVarDirectives = null;
this.declVarComments = null;
return null;
}
@Override
public Object visitDeclVariableList(KickCParser.DeclVariableListContext ctx) {
if(ctx.declVariableList()!=null) {
this.visit(ctx.declVariableList());
}
this.visit(ctx.declVariableInit());
return null;
}
@Override
public Object visitDeclVariableInit(KickCParser.DeclVariableInitContext ctx) {
List<Directive> directives = declVarDirectives;
SymbolType type = declVarType;
List<Comment> comments = declVarComments;
String varName = ctx.NAME().getText();
VariableUnversioned lValue;
try {
@ -418,12 +451,11 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
throw new CompileError(e.getMessage(), new StatementSource(ctx));
}
// Add directives
addDirectives(type, lValue, ctx.directive());
addDirectives(lValue, type, directives, new StatementSource(ctx));
// Array / String variables are implicitly constant
if(type instanceof SymbolTypeArray || type.equals(SymbolType.STRING)) {
lValue.setDeclaredConstant(true);
}
List<Comment> comments = getCommentsSymbol(ctx);
if(lValue.isDeclaredConstant()) {
// Add comments to constant
lValue.setComments(ensureUnusedComments(comments));
@ -434,7 +466,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
} else {
if(type instanceof SymbolTypeInteger) {
// Add an zero value initializer
ConstantInteger zero = new ConstantInteger(0l);
ConstantInteger zero = new ConstantInteger(0L);
Statement stmt = new StatementAssignment(lValue.getRef(), zero, new StatementSource(ctx), ensureUnusedComments(comments));
sequence.addStatement(stmt);
} else if(type instanceof SymbolTypeArray) {
@ -449,7 +481,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
} else if(type instanceof SymbolTypePointer) {
// Add an zero value initializer
SymbolTypePointer typePointer = (SymbolTypePointer) type;
ConstantValue zero = new ConstantPointer(0l, typePointer.getElementType());
ConstantValue zero = new ConstantPointer(0L, typePointer.getElementType());
Statement stmt = new StatementAssignment(lValue.getRef(), zero, new StatementSource(ctx), ensureUnusedComments(comments));
sequence.addStatement(stmt);
} else {
@ -457,23 +489,19 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
}
}
return null;
}
/**
* Add declared directives to an lValue (typically a variable).
*
* @param lValue The lValue
* @param type The type of the lValue
* @param lValue The lValue
* @param directivesCtx The directives to add
* @param directives The directives to add
*/
private void addDirectives(SymbolType type, SymbolVariable lValue, List<KickCParser.DirectiveContext> directivesCtx) {
List<Directive> directives = new ArrayList<>();
for(KickCParser.DirectiveContext directiveContext : directivesCtx) {
directives.add((Directive) this.visit(directiveContext));
}
private void addDirectives(SymbolVariable lValue, SymbolType type, List<Directive> directives, StatementSource source) {
for(Directive directive : directives) {
StatementSource source = new StatementSource(directivesCtx.get(0));
if(directive instanceof DirectiveConst) {
lValue.setDeclaredConstant(true);
} else if(directive instanceof DirectiveVolatile) {
@ -497,6 +525,19 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
}
}
/**
* Find the directives in the parse tree
* @param directivesCtx The directives in the parse tree to examine
* @return Objects representing the found directives
*/
private List<Directive> getDirectives(List<KickCParser.DirectiveContext> directivesCtx) {
List<Directive> directives = new ArrayList<>();
for(KickCParser.DirectiveContext directiveContext : directivesCtx) {
directives.add((Directive) this.visit(directiveContext));
}
return directives;
}
/**
* Add declared directives to a procedure.
*
@ -504,10 +545,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
* @param directivesCtx The directives to add
*/
private void addDirectives(Procedure procedure, List<KickCParser.DirectiveContext> directivesCtx) {
List<Directive> directives = new ArrayList<>();
for(KickCParser.DirectiveContext directiveContext : directivesCtx) {
directives.add((Directive) this.visit(directiveContext));
}
List<Directive> directives = getDirectives(directivesCtx);
for(Directive directive : directives) {
StatementSource source = new StatementSource(directivesCtx.get(0));
if(directive instanceof DirectiveInline) {
@ -527,10 +565,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
* @param directivesCtx The directives to add
*/
private void addDirectives(StatementConditionalJump conditional, List<KickCParser.DirectiveContext> directivesCtx) {
List<Directive> directives = new ArrayList<>();
for(KickCParser.DirectiveContext directiveContext : directivesCtx) {
directives.add((Directive) this.visit(directiveContext));
}
List<Directive> directives = getDirectives(directivesCtx);
for(Directive directive : directives) {
StatementSource source = new StatementSource(directivesCtx.get(0));
if(directive instanceof DirectiveInline) {
@ -603,9 +638,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
@Override
public Void visitStmtExpr(KickCParser.StmtExprContext ctx) {
PrePostModifierHandler.addPreModifiers(this, ctx.expr());
this.visit(ctx.expr());
PrePostModifierHandler.addPostModifiers(this, ctx.expr());
PrePostModifierHandler.addPreModifiers(this, ctx.commaExpr());
this.visit(ctx.commaExpr());
PrePostModifierHandler.addPostModifiers(this, ctx.commaExpr());
return null;
}
@ -613,15 +648,15 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
public Void visitStmtIfElse(KickCParser.StmtIfElseContext ctx) {
KickCParser.StmtContext ifStmt = ctx.stmt(0);
KickCParser.StmtContext elseStmt = ctx.stmt(1);
PrePostModifierHandler.addPreModifiers(this, ctx.expr());
RValue rValue = (RValue) this.visit(ctx.expr());
PrePostModifierHandler.addPreModifiers(this, ctx.commaExpr());
RValue rValue = (RValue) this.visit(ctx.commaExpr());
List<Comment> comments = ensureUnusedComments(getCommentsSymbol(ctx));
if(elseStmt == null) {
// If without else - skip the entire section if condition not met
VariableRef notExprVar = getCurrentScope().addVariableIntermediate().getRef();
sequence.addStatement(new StatementAssignment(notExprVar, null, Operators.LOGIC_NOT, rValue, new StatementSource(ctx), comments));
PrePostModifierHandler.addPostModifiers(this, ctx.expr());
PrePostModifierHandler.addPostModifiers(this, ctx.commaExpr());
Label endJumpLabel = getCurrentScope().addLabelIntermediate();
sequence.addStatement(new StatementConditionalJump(notExprVar, endJumpLabel.getRef(), new StatementSource(ctx), Comment.NO_COMMENTS));
this.visit(ifStmt);
@ -629,7 +664,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
sequence.addStatement(new StatementLabel(endJumpLabel.getRef(), new StatementSource(ctx), Comment.NO_COMMENTS));
} else {
// If with else - jump to if section if condition met - fall into else otherwise.
PrePostModifierHandler.addPostModifiers(this, ctx.expr());
PrePostModifierHandler.addPostModifiers(this, ctx.commaExpr());
Label ifJumpLabel = getCurrentScope().addLabelIntermediate();
sequence.addStatement(new StatementConditionalJump(rValue, ifJumpLabel.getRef(), new StatementSource(ctx), comments));
// Add else body
@ -661,26 +696,26 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
this.loopScope = loopScope;
}
public Label getBreakLabel() {
Label getBreakLabel() {
return breakLabel;
}
public Label getOrCreateBreakLabel() {
Label getOrCreateBreakLabel() {
if(breakLabel == null) {
breakLabel = loopScope.addLabelIntermediate();
}
return breakLabel;
}
public Label getContinueLabel() {
Label getContinueLabel() {
return continueLabel;
}
public void setContinueLabel(Label continueLabel) {
void setContinueLabel(Label continueLabel) {
this.continueLabel = continueLabel;
}
public Label getOrCreateContinueLabel() {
Label getOrCreateContinueLabel() {
if(continueLabel == null) {
continueLabel = loopScope.addLabelIntermediate();
}
@ -704,9 +739,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
List<Comment> comments = ensureUnusedComments(getCommentsSymbol(ctx));
StatementLabel beginJumpTarget = new StatementLabel(beginJumpLabel.getRef(), new StatementSource(ctx), comments);
sequence.addStatement(beginJumpTarget);
PrePostModifierHandler.addPreModifiers(this, ctx.expr());
RValue rValue = (RValue) this.visit(ctx.expr());
PrePostModifierHandler.addPostModifiers(this, ctx.expr());
PrePostModifierHandler.addPreModifiers(this, ctx.commaExpr());
RValue rValue = (RValue) this.visit(ctx.commaExpr());
PrePostModifierHandler.addPostModifiers(this, ctx.commaExpr());
StatementConditionalJump doJmpStmt = new StatementConditionalJump(rValue, doJumpLabel.getRef(), new StatementSource(ctx), Comment.NO_COMMENTS);
sequence.addStatement(doJmpStmt);
Statement endJmpStmt = new StatementJump(endJumpLabel.getRef(), new StatementSource(ctx), Comment.NO_COMMENTS);
@ -739,9 +774,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
sequence.addStatement(beginJumpTarget);
addLoopBody(ctx.stmt());
addLoopContinueLabel(loopStack.peek(), ctx);
PrePostModifierHandler.addPreModifiers(this, ctx.expr());
RValue rValue = (RValue) this.visit(ctx.expr());
PrePostModifierHandler.addPostModifiers(this, ctx.expr());
PrePostModifierHandler.addPreModifiers(this, ctx.commaExpr());
RValue rValue = (RValue) this.visit(ctx.commaExpr());
PrePostModifierHandler.addPostModifiers(this, ctx.commaExpr());
StatementConditionalJump doJmpStmt = new StatementConditionalJump(rValue, beginJumpLabel.getRef(), new StatementSource(ctx), Comment.NO_COMMENTS);
sequence.addStatement(doJmpStmt);
addDirectives(doJmpStmt, ctx.directive());
@ -893,7 +928,8 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
throw new CompileError(e.getMessage(), new StatementSource(forDeclCtx));
}
// Add directives
addDirectives(type, lValue, forDeclCtx.directive());
List<Directive> directives = getDirectives(forDeclCtx.directive());
addDirectives(lValue, type, directives, new StatementSource(forDeclCtx));
} else {
lValue = getCurrentScope().getVariable(varName);
}
@ -1018,7 +1054,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
@Override
public Void visitStmtReturn(KickCParser.StmtReturnContext ctx) {
Procedure procedure = getCurrentProcedure();
KickCParser.ExprContext exprCtx = ctx.expr();
KickCParser.CommaExprContext exprCtx = ctx.commaExpr();
RValue rValue;
if(exprCtx != null) {
PrePostModifierHandler.addPreModifiers(this, exprCtx);
@ -1285,14 +1321,12 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
@Override
public Object visitExprPreMod(KickCParser.ExprPreModContext ctx) {
RValue child = (RValue) this.visit(ctx.expr());
return child;
return this.visit(ctx.expr());
}
@Override
public Object visitExprPostMod(KickCParser.ExprPostModContext ctx) {
RValue child = (RValue) this.visit(ctx.expr());
return child;
return this.visit(ctx.expr());
}
@Override

View File

@ -37,15 +37,26 @@ public class TestPrograms {
// compileAndCompare("pointer-cast-3");
//}
@Test
public void testCommaDecl() throws IOException, URISyntaxException {
compileAndCompare("comma-decl");
}
@Test
public void testCommaExprFor() throws IOException, URISyntaxException {
compileAndCompare("comma-expr-for");
}
@Test
public void testCommaExpr2() throws IOException, URISyntaxException {
compileAndCompare("comma-expr-2");
}
@Test
public void testCommaExpr1() throws IOException, URISyntaxException {
compileAndCompare("comma-expr-1");
}
@Test
public void testForRangedNoVar() throws IOException, URISyntaxException {
assertError("for-ranged-novar", "Ranged for() must have iteration variable");

View File

@ -0,0 +1,9 @@
// Tests comma-separated declarations
void main() {
const byte* SCREEN = $400;
byte b = 'c', c = b+1, d = c+1;
SCREEN[0] = b;
SCREEN[1] = c;
SCREEN[2] = d;
}

View File

@ -0,0 +1,10 @@
// Tests simple comma-expressions (without parenthesis)
void main() {
const byte* SCREEN = $400;
byte b;
byte c;
b = 1,2,3;
c = 1+3,b+1;
SCREEN[1,0] = c;
}

View File

@ -0,0 +1,17 @@
// Tests comma-separated declarations
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
main: {
.label SCREEN = $400
.const b = 'c'
.const c = b+1
.const d = c+1
lda #b
sta SCREEN
lda #c
sta SCREEN+1
lda #d
sta SCREEN+2
rts
}

View File

@ -0,0 +1,17 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] *((const byte*) main::SCREEN#0) ← (const byte) main::b#0
[5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) main::c#0
[6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← (const byte) main::d#0
to:main::@return
main::@return: scope:[main] from main
[7] return
to:@return

274
src/test/ref/comma-decl.log Normal file
View File

@ -0,0 +1,274 @@
Identified constant variable (byte) main::b
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
to:@1
main: scope:[main] from @1
(byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400
(byte) main::b#0 ← (byte) 'c'
(byte/signed word/word/dword/signed dword~) main::$0 ← (byte) main::b#0 + (byte/signed byte/word/signed word/dword/signed dword) 1
(byte) main::c#0 ← (byte/signed word/word/dword/signed dword~) main::$0
(byte/signed word/word/dword/signed dword~) main::$1 ← (byte) main::c#0 + (byte/signed byte/word/signed word/dword/signed dword) 1
(byte) main::d#0 ← (byte/signed word/word/dword/signed dword~) main::$1
*((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 0) ← (byte) main::b#0
*((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 1) ← (byte) main::c#0
*((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 2) ← (byte) main::d#0
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
call main
to:@2
@2: scope:[] from @1
to:@end
@end: scope:[] from @2
SYMBOL TABLE SSA
(label) @1
(label) @2
(label) @begin
(label) @end
(void()) main()
(byte/signed word/word/dword/signed dword~) main::$0
(byte/signed word/word/dword/signed dword~) main::$1
(label) main::@return
(byte*) main::SCREEN
(byte*) main::SCREEN#0
(byte) main::b
(byte) main::b#0
(byte) main::c
(byte) main::c#0
(byte) main::d
(byte) main::d#0
Culled Empty Block (label) @2
Successful SSA optimization Pass2CullEmptyBlocks
Alias (byte) main::c#0 = (byte/signed word/word/dword/signed dword~) main::$0
Alias (byte) main::d#0 = (byte/signed word/word/dword/signed dword~) main::$1
Successful SSA optimization Pass2AliasElimination
Constant (const byte*) main::SCREEN#0 = ((byte*))$400
Constant (const byte) main::b#0 = 'c'
Successful SSA optimization Pass2ConstantIdentification
Constant (const byte) main::c#0 = main::b#0+1
Successful SSA optimization Pass2ConstantIdentification
Constant (const byte) main::d#0 = main::c#0+1
Successful SSA optimization Pass2ConstantIdentification
Consolidated array index constant in *(main::SCREEN#0+0)
Consolidated array index constant in *(main::SCREEN#0+1)
Consolidated array index constant in *(main::SCREEN#0+2)
Successful SSA optimization Pass2ConstantAdditionElimination
Simplifying constant plus zero main::SCREEN#0+0
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
CALL GRAPH
Calls in [] to main:2
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] *((const byte*) main::SCREEN#0) ← (const byte) main::b#0
[5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) main::c#0
[6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← (const byte) main::d#0
to:main::@return
main::@return: scope:[main] from main
[7] return
to:@return
VARIABLE REGISTER WEIGHTS
(void()) main()
(byte*) main::SCREEN
(byte) main::b
(byte) main::c
(byte) main::d
Initial phi equivalence classes
Complete equivalence classes
INITIAL ASM
//SEG0 File Comments
// Tests comma-separated declarations
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
//SEG2 Global Constants & labels
//SEG3 @begin
bbegin:
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
//SEG5 @1
b1:
//SEG6 [2] call main
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG8 @end
bend:
//SEG9 main
main: {
.label SCREEN = $400
.const b = 'c'
.const c = b+1
.const d = c+1
//SEG10 [4] *((const byte*) main::SCREEN#0) ← (const byte) main::b#0 -- _deref_pbuc1=vbuc2
lda #b
sta SCREEN
//SEG11 [5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) main::c#0 -- _deref_pbuc1=vbuc2
lda #c
sta SCREEN+1
//SEG12 [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← (const byte) main::d#0 -- _deref_pbuc1=vbuc2
lda #d
sta SCREEN+2
jmp breturn
//SEG13 main::@return
breturn:
//SEG14 [7] return
rts
}
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] *((const byte*) main::SCREEN#0) ← (const byte) main::b#0 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) main::c#0 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← (const byte) main::d#0 [ ] ( main:2 [ ] ) always clobbers reg byte a
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 39 combination
Uplifting [] best 39 combination
ASSEMBLER BEFORE OPTIMIZATION
//SEG0 File Comments
// Tests comma-separated declarations
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
//SEG2 Global Constants & labels
//SEG3 @begin
bbegin:
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
//SEG5 @1
b1:
//SEG6 [2] call main
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG8 @end
bend:
//SEG9 main
main: {
.label SCREEN = $400
.const b = 'c'
.const c = b+1
.const d = c+1
//SEG10 [4] *((const byte*) main::SCREEN#0) ← (const byte) main::b#0 -- _deref_pbuc1=vbuc2
lda #b
sta SCREEN
//SEG11 [5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) main::c#0 -- _deref_pbuc1=vbuc2
lda #c
sta SCREEN+1
//SEG12 [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← (const byte) main::d#0 -- _deref_pbuc1=vbuc2
lda #d
sta SCREEN+2
jmp breturn
//SEG13 main::@return
breturn:
//SEG14 [7] return
rts
}
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction bend_from_b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction bend:
Removing instruction breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
Updating BasicUpstart to call main directly
Removing instruction jsr main
Succesful ASM optimization Pass5SkipBegin
Removing instruction bbegin:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(void()) main()
(label) main::@return
(byte*) main::SCREEN
(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400
(byte) main::b
(const byte) main::b#0 b = (byte) 'c'
(byte) main::c
(const byte) main::c#0 c = (const byte) main::b#0+(byte/signed byte/word/signed word/dword/signed dword) 1
(byte) main::d
(const byte) main::d#0 d = (const byte) main::c#0+(byte/signed byte/word/signed word/dword/signed dword) 1
FINAL ASSEMBLER
Score: 24
//SEG0 File Comments
// Tests comma-separated declarations
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG2 Global Constants & labels
//SEG3 @begin
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
//SEG5 @1
//SEG6 [2] call main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
//SEG8 @end
//SEG9 main
main: {
.label SCREEN = $400
.const b = 'c'
.const c = b+1
.const d = c+1
//SEG10 [4] *((const byte*) main::SCREEN#0) ← (const byte) main::b#0 -- _deref_pbuc1=vbuc2
lda #b
sta SCREEN
//SEG11 [5] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) main::c#0 -- _deref_pbuc1=vbuc2
lda #c
sta SCREEN+1
//SEG12 [6] *((const byte*) main::SCREEN#0+(byte/signed byte/word/signed word/dword/signed dword) 2) ← (const byte) main::d#0 -- _deref_pbuc1=vbuc2
lda #d
sta SCREEN+2
//SEG13 main::@return
//SEG14 [7] return
rts
}

View File

@ -0,0 +1,14 @@
(label) @1
(label) @begin
(label) @end
(void()) main()
(label) main::@return
(byte*) main::SCREEN
(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400
(byte) main::b
(const byte) main::b#0 b = (byte) 'c'
(byte) main::c
(const byte) main::c#0 c = (const byte) main::b#0+(byte/signed byte/word/signed word/dword/signed dword) 1
(byte) main::d
(const byte) main::d#0 d = (const byte) main::c#0+(byte/signed byte/word/signed word/dword/signed dword) 1

View File

@ -0,0 +1,11 @@
// Tests simple comma-expressions (without parenthesis)
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
main: {
.label SCREEN = $400
.const c = 1+3
lda #c
sta SCREEN
rts
}

View File

@ -0,0 +1,15 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] *((const byte*) main::SCREEN#0) ← (const byte) main::c#1
to:main::@return
main::@return: scope:[main] from main
[5] return
to:@return

View File

@ -0,0 +1,226 @@
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
to:@1
main: scope:[main] from @1
(byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400
(byte) main::c#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0
(byte/signed byte/word/signed word/dword/signed dword~) main::$0 ← (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte/signed byte/word/signed word/dword/signed dword) 3
(byte) main::c#1 ← (byte/signed byte/word/signed word/dword/signed dword~) main::$0
*((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 0) ← (byte) main::c#1
to:main::@return
main::@return: scope:[main] from main
return
to:@return
@1: scope:[] from @begin
call main
to:@2
@2: scope:[] from @1
to:@end
@end: scope:[] from @2
SYMBOL TABLE SSA
(label) @1
(label) @2
(label) @begin
(label) @end
(void()) main()
(byte/signed byte/word/signed word/dword/signed dword~) main::$0
(label) main::@return
(byte*) main::SCREEN
(byte*) main::SCREEN#0
(byte) main::c
(byte) main::c#0
(byte) main::c#1
Culled Empty Block (label) @2
Successful SSA optimization Pass2CullEmptyBlocks
Alias (byte) main::c#1 = (byte/signed byte/word/signed word/dword/signed dword~) main::$0
Successful SSA optimization Pass2AliasElimination
Constant (const byte*) main::SCREEN#0 = ((byte*))$400
Constant (const byte) main::c#0 = 0
Constant (const byte) main::c#1 = 1+3
Successful SSA optimization Pass2ConstantIdentification
Consolidated array index constant in *(main::SCREEN#0+0)
Successful SSA optimization Pass2ConstantAdditionElimination
Successful SSA optimization PassNEliminateUnusedVars
Simplifying constant plus zero main::SCREEN#0+0
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
CALL GRAPH
Calls in [] to main:2
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
FINAL CONTROL FLOW GRAPH
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
main: scope:[main] from @1
[4] *((const byte*) main::SCREEN#0) ← (const byte) main::c#1
to:main::@return
main::@return: scope:[main] from main
[5] return
to:@return
VARIABLE REGISTER WEIGHTS
(void()) main()
(byte*) main::SCREEN
(byte) main::c
Initial phi equivalence classes
Complete equivalence classes
INITIAL ASM
//SEG0 File Comments
// Tests simple comma-expressions (without parenthesis)
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
//SEG2 Global Constants & labels
//SEG3 @begin
bbegin:
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
//SEG5 @1
b1:
//SEG6 [2] call main
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG8 @end
bend:
//SEG9 main
main: {
.label SCREEN = $400
.const c = 1+3
//SEG10 [4] *((const byte*) main::SCREEN#0) ← (const byte) main::c#1 -- _deref_pbuc1=vbuc2
lda #c
sta SCREEN
jmp breturn
//SEG11 main::@return
breturn:
//SEG12 [5] return
rts
}
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [4] *((const byte*) main::SCREEN#0) ← (const byte) main::c#1 [ ] ( main:2 [ ] ) always clobbers reg byte a
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 27 combination
Uplifting [] best 27 combination
ASSEMBLER BEFORE OPTIMIZATION
//SEG0 File Comments
// Tests simple comma-expressions (without parenthesis)
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
//SEG2 Global Constants & labels
//SEG3 @begin
bbegin:
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
//SEG5 @1
b1:
//SEG6 [2] call main
jsr main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
//SEG8 @end
bend:
//SEG9 main
main: {
.label SCREEN = $400
.const c = 1+3
//SEG10 [4] *((const byte*) main::SCREEN#0) ← (const byte) main::c#1 -- _deref_pbuc1=vbuc2
lda #c
sta SCREEN
jmp breturn
//SEG11 main::@return
breturn:
//SEG12 [5] return
rts
}
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction bend_from_b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction bend:
Removing instruction breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
Updating BasicUpstart to call main directly
Removing instruction jsr main
Succesful ASM optimization Pass5SkipBegin
Removing instruction bbegin:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(void()) main()
(label) main::@return
(byte*) main::SCREEN
(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400
(byte) main::c
(const byte) main::c#1 c = (byte/signed byte/word/signed word/dword/signed dword) 1+(byte/signed byte/word/signed word/dword/signed dword) 3
FINAL ASSEMBLER
Score: 12
//SEG0 File Comments
// Tests simple comma-expressions (without parenthesis)
//SEG1 Basic Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
//SEG2 Global Constants & labels
//SEG3 @begin
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
//SEG5 @1
//SEG6 [2] call main
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
//SEG8 @end
//SEG9 main
main: {
.label SCREEN = $400
.const c = 1+3
//SEG10 [4] *((const byte*) main::SCREEN#0) ← (const byte) main::c#1 -- _deref_pbuc1=vbuc2
lda #c
sta SCREEN
//SEG11 main::@return
//SEG12 [5] return
rts
}

View File

@ -0,0 +1,10 @@
(label) @1
(label) @begin
(label) @end
(void()) main()
(label) main::@return
(byte*) main::SCREEN
(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400
(byte) main::c
(const byte) main::c#1 c = (byte/signed byte/word/signed word/dword/signed dword) 1+(byte/signed byte/word/signed word/dword/signed dword) 3