From 845a99d6230d01ef4ecdbaedabe1a9ceb22431ee Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 10 Jul 2019 19:14:41 +0200 Subject: [PATCH] return statement only has one single possible value astvm can now more or less run all examples --- clean.sh | 2 +- compiler/res/version.txt | 2 +- compiler/src/prog8/ast/antlr/Antr2Kotlin.kt | 3 +- .../src/prog8/ast/processing/AstChecker.kt | 24 ++++++++-------- .../ast/processing/AstIdentifiersChecker.kt | 28 +++++++++---------- .../ast/processing/IAstModifyingVisitor.kt | 2 +- .../src/prog8/ast/processing/IAstVisitor.kt | 2 +- .../ast/processing/StatementReorderer.kt | 4 +-- .../src/prog8/ast/statements/AstStatements.kt | 10 +++---- .../src/prog8/compiler/AstToSourceCode.kt | 6 +--- compiler/src/prog8/compiler/Compiler.kt | 5 ++-- compiler/src/prog8/compiler/Main.kt | 2 +- .../src/prog8/optimizer/StatementOptimizer.kt | 5 ++-- compiler/src/prog8/vm/astvm/AstVm.kt | 27 +++++++++++------- compiler/src/prog8/vm/astvm/Expressions.kt | 9 +++--- examples/numbergame.p8 | 6 ++-- examples/test.p8 | 20 +++++++------ parser/antlr/prog8.g4 | 2 +- parser/src/prog8/parser/prog8Parser.java | 8 +++--- 19 files changed, 84 insertions(+), 83 deletions(-) diff --git a/clean.sh b/clean.sh index 4444cfa45..bb26b47e6 100755 --- a/clean.sh +++ b/clean.sh @@ -1,5 +1,5 @@ #!/usr/bin/env sh rm *.jar *.asm *.prg *.vm.txt *.vice-mon-list -rm -r build +rm -rf build out diff --git a/compiler/res/version.txt b/compiler/res/version.txt index 1a9d86ec7..c044b1a32 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -1.10-dev +1.10 diff --git a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt index 21333cc43..f762f8117 100644 --- a/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt +++ b/compiler/src/prog8/ast/antlr/Antr2Kotlin.kt @@ -246,8 +246,7 @@ private fun prog8Parser.InlineasmContext.toAst() = private fun prog8Parser.ReturnstmtContext.toAst() : Return { - val values = expression_list() - return Return(values?.toAst() ?: emptyList(), toPosition()) + return Return(expression()?.toAst(), toPosition()) } private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump { diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 300d877ab..53a50e5b8 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -90,20 +90,20 @@ internal class AstChecker(private val program: Program, override fun visit(returnStmt: Return): IStatement { val expectedReturnValues = returnStmt.definingSubroutine()?.returntypes ?: emptyList() - if(expectedReturnValues.size != returnStmt.values.size) { - // if the return value is a function call, check the result of that call instead - if(returnStmt.values.size==1 && returnStmt.values[0] is FunctionCall) { - val dt = (returnStmt.values[0] as FunctionCall).inferType(program) - if(dt!=null && expectedReturnValues.isEmpty()) - checkResult.add(SyntaxError("invalid number of return values", returnStmt.position)) - } else - checkResult.add(SyntaxError("invalid number of return values", returnStmt.position)) + if(expectedReturnValues.size>1) { + throw AstException("cannot use a return with one value in a subroutine that has multiple return values: $returnStmt") } - for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) { - val valueDt=rv.second.inferType(program) - if(rv.first.value!=valueDt) - checkResult.add(ExpressionError("type $valueDt of return value #${rv.first.index + 1} doesn't match subroutine return type ${rv.first.value}", rv.second.position)) + if(expectedReturnValues.isEmpty() && returnStmt.value!=null) { + checkResult.add(SyntaxError("invalid number of return values", returnStmt.position)) + } + if(expectedReturnValues.isNotEmpty() && returnStmt.value==null) { + checkResult.add(SyntaxError("invalid number of return values", returnStmt.position)) + } + if(expectedReturnValues.size==1 && returnStmt.value!=null) { + val valueDt = returnStmt.value!!.inferType(program) + if(expectedReturnValues[0]!=valueDt) + checkResult.add(ExpressionError("type $valueDt of return value doesn't match subroutine's return type", returnStmt.value!!.position)) } return super.visit(returnStmt) } diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index 3e5551f13..afca7da20 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -168,25 +168,23 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo } override fun visit(returnStmt: Return): IStatement { - if(returnStmt.values.isNotEmpty()) { + if(returnStmt.value!=null) { // possibly adjust any literal values returned, into the desired returning data type val subroutine = returnStmt.definingSubroutine()!! - if(subroutine.returntypes.size!=returnStmt.values.size) + if(subroutine.returntypes.size!=1) return returnStmt // mismatch in number of return values, error will be printed later. - val newValues = mutableListOf() - for(returnvalue in returnStmt.values.zip(subroutine.returntypes)) { - val lval = returnvalue.first as? LiteralValue - if(lval!=null) { - val adjusted = lval.cast(returnvalue.second) - if(adjusted!=null && adjusted !== lval) - newValues.add(adjusted) - else - newValues.add(lval) - } + val newValue: IExpression + val lval = returnStmt.value as? LiteralValue + if(lval!=null) { + val adjusted = lval.cast(subroutine.returntypes.single()) + if(adjusted!=null && adjusted !== lval) + newValue = adjusted else - newValues.add(returnvalue.first) - } - returnStmt.values = newValues + newValue = lval + } else + newValue = returnStmt.value!! + + returnStmt.value = newValue } return super.visit(returnStmt) } diff --git a/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt b/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt index de3f9a3b3..07751adc6 100644 --- a/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt @@ -149,7 +149,7 @@ interface IAstModifyingVisitor { } fun visit(returnStmt: Return): IStatement { - returnStmt.values = returnStmt.values.map { it.accept(this) } + returnStmt.value = returnStmt.value?.accept(this) return returnStmt } diff --git a/compiler/src/prog8/ast/processing/IAstVisitor.kt b/compiler/src/prog8/ast/processing/IAstVisitor.kt index 80c711cbb..7a885efa1 100644 --- a/compiler/src/prog8/ast/processing/IAstVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstVisitor.kt @@ -111,7 +111,7 @@ interface IAstVisitor { } fun visit(returnStmt: Return) { - returnStmt.values.forEach { it.accept(this) } + returnStmt.value?.accept(this) } fun visit(arrayIndexedExpression: ArrayIndexedExpression) { diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index ba10dd9e1..2623a659f 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -89,7 +89,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi && stmtBeforeFirstSub !is Jump && stmtBeforeFirstSub !is Subroutine && stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) { - val ret = Return(emptyList(), stmtBeforeFirstSub.position) + val ret = Return(null, stmtBeforeFirstSub.position) ret.linkParents(block) block.statements.add(block.statements.size - numSubroutinesAtEnd, ret) } @@ -137,7 +137,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi // and if an assembly block doesn't contain a rts/rti if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) { if (subroutine.statements.lastOrNull {it !is VarDecl } !is Return) { - val returnStmt = Return(emptyList(), subroutine.position) + val returnStmt = Return(null, subroutine.position) returnStmt.linkParents(subroutine) subroutine.statements.add(returnStmt) } diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 80ffe7f5a..eca01777b 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -84,24 +84,24 @@ data class Label(val name: String, override val position: Position) : IStatement val scopedname: String by lazy { makeScopedName(name) } } -open class Return(var values: List, override val position: Position) : IStatement { +open class Return(var value: IExpression?, override val position: Position) : IStatement { override lateinit var parent: Node - override val expensiveToInline = values.any { it !is LiteralValue } + override val expensiveToInline = value!=null && value !is LiteralValue override fun linkParents(parent: Node) { this.parent = parent - values.forEach {it.linkParents(this)} + value?.linkParents(this) } override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun toString(): String { - return "Return(values: $values, pos=$position)" + return "Return($value, pos=$position)" } } -class ReturnFromIrq(override val position: Position) : Return(emptyList(), position) { +class ReturnFromIrq(override val position: Position) : Return(null, position) { override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this) diff --git a/compiler/src/prog8/compiler/AstToSourceCode.kt b/compiler/src/prog8/compiler/AstToSourceCode.kt index e4b53959d..75a5654c4 100644 --- a/compiler/src/prog8/compiler/AstToSourceCode.kt +++ b/compiler/src/prog8/compiler/AstToSourceCode.kt @@ -327,11 +327,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor { override fun visit(returnStmt: Return) { output("return ") - for(v in returnStmt.values) { - v.accept(this) - if(v!==returnStmt.values.last()) - output(", ") - } + returnStmt.value?.accept(this) } override fun visit(arrayIndexedExpression: ArrayIndexedExpression) { diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 88b6164c0..9ae7118a8 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -1517,9 +1517,8 @@ internal class Compiler(private val program: Program) { private fun translate(stmt: Return) { // put the return values on the stack, in reversed order. The caller will accept them. - for(value in stmt.values.reversed()) { - translate(value) - } + if(stmt.value!=null) + translate(stmt.value!!) prog.line(stmt.position) prog.instr(Opcode.RETURN) } diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index cf92d4cda..1e9917632 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -88,7 +88,7 @@ fun compileProgram(filepath: Path, programAst.checkValid(compilerOptions) // check if final tree is valid programAst.checkRecursion() // check if there are recursive subroutine calls - printAst(programAst) + // printAst(programAst) // namespace.debugPrint() if(generateVmCode) { diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index 5ddb5b3de..6bcbe7a18 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -79,7 +79,6 @@ internal class StatementOptimizer(private val program: Program, private val opti } val returns = inlined.statements.withIndex().filter { iv -> iv.value is Return }.map { iv -> Pair(iv.index, iv.value as Return)} for(returnIdx in returns) { - assert(returnIdx.second.values.isEmpty()) val jump = Jump(null, IdentifierReference(listOf(endlabel.name), returnIdx.second.position), null, returnIdx.second.position) inlined.statements[returnIdx.first] = jump endLabelUsed = true @@ -299,8 +298,8 @@ internal class StatementOptimizer(private val program: Program, private val opti optimizationsDone++ return FunctionCall(first.identifier, functionCall.arglist, functionCall.position) } - if(first is Return && first.values.size==1) { - val constval = first.values[0].constValue(program) + if(first is Return && first.value!=null) { + val constval = first.value?.constValue(program) if(constval!=null) return constval } diff --git a/compiler/src/prog8/vm/astvm/AstVm.kt b/compiler/src/prog8/vm/astvm/AstVm.kt index 6b14da1fa..281820b50 100644 --- a/compiler/src/prog8/vm/astvm/AstVm.kt +++ b/compiler/src/prog8/vm/astvm/AstVm.kt @@ -261,11 +261,11 @@ class AstVm(val program: Program) { class LoopControlBreak : Exception() class LoopControlContinue : Exception() - class LoopControlReturn(val returnvalues: List) : Exception() + class LoopControlReturn(val returnvalue: RuntimeValue?) : Exception() class LoopControlJump(val identifier: IdentifierReference?, val address: Int?, val generatedLabel: String?) : Exception() - internal fun executeSubroutine(sub: Subroutine, arguments: List, startlabel: Label?=null): List { + internal fun executeSubroutine(sub: Subroutine, arguments: List, startlabel: Label?=null): RuntimeValue? { if(sub.isAsmSubroutine) { return performSyscall(sub, arguments) } @@ -300,7 +300,7 @@ class AstVm(val program: Program) { } } } catch (r: LoopControlReturn) { - return r.returnvalues + return r.returnvalue } throw VmTerminationException("instruction pointer overflow, is a return missing? $sub") } @@ -355,7 +355,14 @@ class AstVm(val program: Program) { } } } - is Return -> throw LoopControlReturn(stmt.values.map { evaluate(it, evalCtx) }) + is Return -> { + val value = + if(stmt.value==null) + null + else + evaluate(stmt.value!!, evalCtx) + throw LoopControlReturn(value) + } is Continue -> throw LoopControlContinue() is Break -> throw LoopControlBreak() is Assignment -> { @@ -430,7 +437,7 @@ class AstVm(val program: Program) { if (sub is Subroutine) { val args = sub.parameters.map { runtimeVariables.get(sub, it.name) } performSyscall(sub, args) - throw LoopControlReturn(emptyList()) + throw LoopControlReturn(null) } throw VmExecutionException("can't execute inline assembly in $sub") } @@ -635,8 +642,8 @@ class AstVm(val program: Program) { private fun evaluate(args: List) = args.map { evaluate(it, evalCtx) } - private fun performSyscall(sub: Subroutine, args: List): List { - val result = mutableListOf() + private fun performSyscall(sub: Subroutine, args: List): RuntimeValue? { + var result: RuntimeValue? = null when (sub.scopedname) { "c64scr.print" -> { // if the argument is an UWORD, consider it to be the "address" of the string (=heapId) @@ -720,7 +727,7 @@ class AstVm(val program: Program) { val origStr = program.heap.get(heapId).str!! val paddedStr=inputStr.padEnd(origStr.length+1, '\u0000').substring(0, origStr.length) program.heap.update(heapId, paddedStr) - result.add(RuntimeValue(DataType.UBYTE, paddedStr.indexOf('\u0000'))) + result = RuntimeValue(DataType.UBYTE, paddedStr.indexOf('\u0000')) } "c64flt.print_f" -> { dialog.canvas.printText(args[0].floatval.toString(), true) @@ -736,13 +743,13 @@ class AstVm(val program: Program) { Thread.sleep(10) } val char=dialog.keyboardBuffer.pop() - result.add(RuntimeValue(DataType.UBYTE, char.toShort())) + result = RuntimeValue(DataType.UBYTE, char.toShort()) } "c64utils.str2uword" -> { val heapId = args[0].wordval!! val argString = program.heap.get(heapId).str!! val numericpart = argString.takeWhile { it.isDigit() } - result.add(RuntimeValue(DataType.UWORD, numericpart.toInt() and 65535)) + result = RuntimeValue(DataType.UWORD, numericpart.toInt() and 65535) } else -> TODO("syscall ${sub.scopedname} $sub") } diff --git a/compiler/src/prog8/vm/astvm/Expressions.kt b/compiler/src/prog8/vm/astvm/Expressions.kt index 1afbbc66c..8d954dfc5 100644 --- a/compiler/src/prog8/vm/astvm/Expressions.kt +++ b/compiler/src/prog8/vm/astvm/Expressions.kt @@ -16,7 +16,7 @@ import kotlin.math.abs class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags, val runtimeVars: RuntimeVariables, val performBuiltinFunction: (String, List, StatusFlags) -> RuntimeValue?, - val executeSubroutine: (sub: Subroutine, args: List, startlabel: Label?) -> List) + val executeSubroutine: (sub: Subroutine, args: List, startlabel: Label?) -> RuntimeValue?) fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { val constval = expr.constValue(ctx.program) @@ -117,10 +117,9 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { val args = expr.arglist.map { evaluate(it, ctx) } return when(sub) { is Subroutine -> { - val results = ctx.executeSubroutine(sub, args, null) - if(results.size!=1) - throw VmExecutionException("expected 1 result from functioncall $expr") - results[0] + val result = ctx.executeSubroutine(sub, args, null) + ?: throw VmExecutionException("expected a result from functioncall $expr") + result } is BuiltinFunctionStatementPlaceholder -> { val result = ctx.performBuiltinFunction(sub.name, args, ctx.statusflags) diff --git a/examples/numbergame.p8 b/examples/numbergame.p8 index 68cef5c6e..5324eee7d 100644 --- a/examples/numbergame.p8 +++ b/examples/numbergame.p8 @@ -32,7 +32,8 @@ ubyte guess = lsb(c64utils.str2uword(input)) if guess==secretnumber { - return ending(true) + ending(true) + return } else { c64scr.print("\n\nThat is too ") if guess ubyte { + bar(arg) + return 33 + } + + sub bar(ubyte a2) { + ;nothing } } diff --git a/parser/antlr/prog8.g4 b/parser/antlr/prog8.g4 index d448cb599..b442201cb 100644 --- a/parser/antlr/prog8.g4 +++ b/parser/antlr/prog8.g4 @@ -183,7 +183,7 @@ expression_list : expression (',' EOL? expression)* // you can split the expression list over several lines ; -returnstmt : 'return' expression_list? ; +returnstmt : 'return' expression? ; breakstmt : 'break'; diff --git a/parser/src/prog8/parser/prog8Parser.java b/parser/src/prog8/parser/prog8Parser.java index ebb00de1a..0a2e18f18 100644 --- a/parser/src/prog8/parser/prog8Parser.java +++ b/parser/src/prog8/parser/prog8Parser.java @@ -2265,8 +2265,8 @@ public class prog8Parser extends Parser { } public static class ReturnstmtContext extends ParserRuleContext { - public Expression_listContext expression_list() { - return getRuleContext(Expression_listContext.class,0); + public ExpressionContext expression() { + return getRuleContext(ExpressionContext.class,0); } public ReturnstmtContext(ParserRuleContext parent, int invokingState) { super(parent, invokingState); @@ -2288,7 +2288,7 @@ public class prog8Parser extends Parser { case 1: { setState(426); - expression_list(); + expression(0); } break; } @@ -5147,7 +5147,7 @@ public class prog8Parser extends Parser { "\3\2\2\2\u01a3\u01a4\3\2\2\2\u01a4\u01a5\3\2\2\2\u01a5\u01a7\5&\24\2\u01a6"+ "\u01a1\3\2\2\2\u01a7\u01aa\3\2\2\2\u01a8\u01a6\3\2\2\2\u01a8\u01a9\3\2"+ "\2\2\u01a9\65\3\2\2\2\u01aa\u01a8\3\2\2\2\u01ab\u01ad\7C\2\2\u01ac\u01ae"+ - "\5\64\33\2\u01ad\u01ac\3\2\2\2\u01ad\u01ae\3\2\2\2\u01ae\67\3\2\2\2\u01af"+ + "\5&\24\2\u01ad\u01ac\3\2\2\2\u01ad\u01ae\3\2\2\2\u01ae\67\3\2\2\2\u01af"+ "\u01b0\7D\2\2\u01b09\3\2\2\2\u01b1\u01b2\7E\2\2\u01b2;\3\2\2\2\u01b3\u01b4"+ "\7t\2\2\u01b4=\3\2\2\2\u01b5\u01ba\7t\2\2\u01b6\u01b7\7F\2\2\u01b7\u01b9"+ "\7t\2\2\u01b8\u01b6\3\2\2\2\u01b9\u01bc\3\2\2\2\u01ba\u01b8\3\2\2\2\u01ba"+