return statement only has one single possible value

astvm can now more or less run all examples
This commit is contained in:
Irmen de Jong 2019-07-10 19:14:41 +02:00
parent 3d7a4bf81a
commit 845a99d623
19 changed files with 84 additions and 83 deletions

View File

@ -1,5 +1,5 @@
#!/usr/bin/env sh #!/usr/bin/env sh
rm *.jar *.asm *.prg *.vm.txt *.vice-mon-list rm *.jar *.asm *.prg *.vm.txt *.vice-mon-list
rm -r build rm -rf build out

View File

@ -1 +1 @@
1.10-dev 1.10

View File

@ -246,8 +246,7 @@ private fun prog8Parser.InlineasmContext.toAst() =
private fun prog8Parser.ReturnstmtContext.toAst() : Return { private fun prog8Parser.ReturnstmtContext.toAst() : Return {
val values = expression_list() return Return(expression()?.toAst(), toPosition())
return Return(values?.toAst() ?: emptyList(), toPosition())
} }
private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump { private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {

View File

@ -90,20 +90,20 @@ internal class AstChecker(private val program: Program,
override fun visit(returnStmt: Return): IStatement { override fun visit(returnStmt: Return): IStatement {
val expectedReturnValues = returnStmt.definingSubroutine()?.returntypes ?: emptyList() val expectedReturnValues = returnStmt.definingSubroutine()?.returntypes ?: emptyList()
if(expectedReturnValues.size != returnStmt.values.size) { if(expectedReturnValues.size>1) {
// if the return value is a function call, check the result of that call instead throw AstException("cannot use a return with one value in a subroutine that has multiple return values: $returnStmt")
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))
} }
for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) { if(expectedReturnValues.isEmpty() && returnStmt.value!=null) {
val valueDt=rv.second.inferType(program) checkResult.add(SyntaxError("invalid number of return values", returnStmt.position))
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.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) return super.visit(returnStmt)
} }

View File

@ -168,25 +168,23 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstMo
} }
override fun visit(returnStmt: Return): IStatement { 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 // possibly adjust any literal values returned, into the desired returning data type
val subroutine = returnStmt.definingSubroutine()!! 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. return returnStmt // mismatch in number of return values, error will be printed later.
val newValues = mutableListOf<IExpression>() val newValue: IExpression
for(returnvalue in returnStmt.values.zip(subroutine.returntypes)) { val lval = returnStmt.value as? LiteralValue
val lval = returnvalue.first as? LiteralValue if(lval!=null) {
if(lval!=null) { val adjusted = lval.cast(subroutine.returntypes.single())
val adjusted = lval.cast(returnvalue.second) if(adjusted!=null && adjusted !== lval)
if(adjusted!=null && adjusted !== lval) newValue = adjusted
newValues.add(adjusted)
else
newValues.add(lval)
}
else else
newValues.add(returnvalue.first) newValue = lval
} } else
returnStmt.values = newValues newValue = returnStmt.value!!
returnStmt.value = newValue
} }
return super.visit(returnStmt) return super.visit(returnStmt)
} }

View File

@ -149,7 +149,7 @@ interface IAstModifyingVisitor {
} }
fun visit(returnStmt: Return): IStatement { fun visit(returnStmt: Return): IStatement {
returnStmt.values = returnStmt.values.map { it.accept(this) } returnStmt.value = returnStmt.value?.accept(this)
return returnStmt return returnStmt
} }

View File

@ -111,7 +111,7 @@ interface IAstVisitor {
} }
fun visit(returnStmt: Return) { fun visit(returnStmt: Return) {
returnStmt.values.forEach { it.accept(this) } returnStmt.value?.accept(this)
} }
fun visit(arrayIndexedExpression: ArrayIndexedExpression) { fun visit(arrayIndexedExpression: ArrayIndexedExpression) {

View File

@ -89,7 +89,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
&& stmtBeforeFirstSub !is Jump && stmtBeforeFirstSub !is Jump
&& stmtBeforeFirstSub !is Subroutine && stmtBeforeFirstSub !is Subroutine
&& stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) { && stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) {
val ret = Return(emptyList(), stmtBeforeFirstSub.position) val ret = Return(null, stmtBeforeFirstSub.position)
ret.linkParents(block) ret.linkParents(block)
block.statements.add(block.statements.size - numSubroutinesAtEnd, ret) 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 // and if an assembly block doesn't contain a rts/rti
if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) { if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) {
if (subroutine.statements.lastOrNull {it !is VarDecl } !is Return) { if (subroutine.statements.lastOrNull {it !is VarDecl } !is Return) {
val returnStmt = Return(emptyList(), subroutine.position) val returnStmt = Return(null, subroutine.position)
returnStmt.linkParents(subroutine) returnStmt.linkParents(subroutine)
subroutine.statements.add(returnStmt) subroutine.statements.add(returnStmt)
} }

View File

@ -84,24 +84,24 @@ data class Label(val name: String, override val position: Position) : IStatement
val scopedname: String by lazy { makeScopedName(name) } val scopedname: String by lazy { makeScopedName(name) }
} }
open class Return(var values: List<IExpression>, override val position: Position) : IStatement { open class Return(var value: IExpression?, override val position: Position) : IStatement {
override lateinit var parent: Node 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) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
values.forEach {it.linkParents(this)} value?.linkParents(this)
} }
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun toString(): String { 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: IAstModifyingVisitor) = visitor.visit(this)
override fun accept(visitor: IAstVisitor) = visitor.visit(this) override fun accept(visitor: IAstVisitor) = visitor.visit(this)

View File

@ -327,11 +327,7 @@ class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
override fun visit(returnStmt: Return) { override fun visit(returnStmt: Return) {
output("return ") output("return ")
for(v in returnStmt.values) { returnStmt.value?.accept(this)
v.accept(this)
if(v!==returnStmt.values.last())
output(", ")
}
} }
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) { override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {

View File

@ -1517,9 +1517,8 @@ internal class Compiler(private val program: Program) {
private fun translate(stmt: Return) { private fun translate(stmt: Return) {
// put the return values on the stack, in reversed order. The caller will accept them. // put the return values on the stack, in reversed order. The caller will accept them.
for(value in stmt.values.reversed()) { if(stmt.value!=null)
translate(value) translate(stmt.value!!)
}
prog.line(stmt.position) prog.line(stmt.position)
prog.instr(Opcode.RETURN) prog.instr(Opcode.RETURN)
} }

View File

@ -88,7 +88,7 @@ fun compileProgram(filepath: Path,
programAst.checkValid(compilerOptions) // check if final tree is valid programAst.checkValid(compilerOptions) // check if final tree is valid
programAst.checkRecursion() // check if there are recursive subroutine calls programAst.checkRecursion() // check if there are recursive subroutine calls
printAst(programAst) // printAst(programAst)
// namespace.debugPrint() // namespace.debugPrint()
if(generateVmCode) { if(generateVmCode) {

View File

@ -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)} val returns = inlined.statements.withIndex().filter { iv -> iv.value is Return }.map { iv -> Pair(iv.index, iv.value as Return)}
for(returnIdx in returns) { for(returnIdx in returns) {
assert(returnIdx.second.values.isEmpty())
val jump = Jump(null, IdentifierReference(listOf(endlabel.name), returnIdx.second.position), null, returnIdx.second.position) val jump = Jump(null, IdentifierReference(listOf(endlabel.name), returnIdx.second.position), null, returnIdx.second.position)
inlined.statements[returnIdx.first] = jump inlined.statements[returnIdx.first] = jump
endLabelUsed = true endLabelUsed = true
@ -299,8 +298,8 @@ internal class StatementOptimizer(private val program: Program, private val opti
optimizationsDone++ optimizationsDone++
return FunctionCall(first.identifier, functionCall.arglist, functionCall.position) return FunctionCall(first.identifier, functionCall.arglist, functionCall.position)
} }
if(first is Return && first.values.size==1) { if(first is Return && first.value!=null) {
val constval = first.values[0].constValue(program) val constval = first.value?.constValue(program)
if(constval!=null) if(constval!=null)
return constval return constval
} }

View File

@ -261,11 +261,11 @@ class AstVm(val program: Program) {
class LoopControlBreak : Exception() class LoopControlBreak : Exception()
class LoopControlContinue : Exception() class LoopControlContinue : Exception()
class LoopControlReturn(val returnvalues: List<RuntimeValue>) : Exception() class LoopControlReturn(val returnvalue: RuntimeValue?) : Exception()
class LoopControlJump(val identifier: IdentifierReference?, val address: Int?, val generatedLabel: String?) : Exception() class LoopControlJump(val identifier: IdentifierReference?, val address: Int?, val generatedLabel: String?) : Exception()
internal fun executeSubroutine(sub: Subroutine, arguments: List<RuntimeValue>, startlabel: Label?=null): List<RuntimeValue> { internal fun executeSubroutine(sub: Subroutine, arguments: List<RuntimeValue>, startlabel: Label?=null): RuntimeValue? {
if(sub.isAsmSubroutine) { if(sub.isAsmSubroutine) {
return performSyscall(sub, arguments) return performSyscall(sub, arguments)
} }
@ -300,7 +300,7 @@ class AstVm(val program: Program) {
} }
} }
} catch (r: LoopControlReturn) { } catch (r: LoopControlReturn) {
return r.returnvalues return r.returnvalue
} }
throw VmTerminationException("instruction pointer overflow, is a return missing? $sub") 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 Continue -> throw LoopControlContinue()
is Break -> throw LoopControlBreak() is Break -> throw LoopControlBreak()
is Assignment -> { is Assignment -> {
@ -430,7 +437,7 @@ class AstVm(val program: Program) {
if (sub is Subroutine) { if (sub is Subroutine) {
val args = sub.parameters.map { runtimeVariables.get(sub, it.name) } val args = sub.parameters.map { runtimeVariables.get(sub, it.name) }
performSyscall(sub, args) performSyscall(sub, args)
throw LoopControlReturn(emptyList()) throw LoopControlReturn(null)
} }
throw VmExecutionException("can't execute inline assembly in $sub") throw VmExecutionException("can't execute inline assembly in $sub")
} }
@ -635,8 +642,8 @@ class AstVm(val program: Program) {
private fun evaluate(args: List<IExpression>) = args.map { evaluate(it, evalCtx) } private fun evaluate(args: List<IExpression>) = args.map { evaluate(it, evalCtx) }
private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>): List<RuntimeValue> { private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>): RuntimeValue? {
val result = mutableListOf<RuntimeValue>() var result: RuntimeValue? = null
when (sub.scopedname) { when (sub.scopedname) {
"c64scr.print" -> { "c64scr.print" -> {
// if the argument is an UWORD, consider it to be the "address" of the string (=heapId) // 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 origStr = program.heap.get(heapId).str!!
val paddedStr=inputStr.padEnd(origStr.length+1, '\u0000').substring(0, origStr.length) val paddedStr=inputStr.padEnd(origStr.length+1, '\u0000').substring(0, origStr.length)
program.heap.update(heapId, paddedStr) program.heap.update(heapId, paddedStr)
result.add(RuntimeValue(DataType.UBYTE, paddedStr.indexOf('\u0000'))) result = RuntimeValue(DataType.UBYTE, paddedStr.indexOf('\u0000'))
} }
"c64flt.print_f" -> { "c64flt.print_f" -> {
dialog.canvas.printText(args[0].floatval.toString(), true) dialog.canvas.printText(args[0].floatval.toString(), true)
@ -736,13 +743,13 @@ class AstVm(val program: Program) {
Thread.sleep(10) Thread.sleep(10)
} }
val char=dialog.keyboardBuffer.pop() val char=dialog.keyboardBuffer.pop()
result.add(RuntimeValue(DataType.UBYTE, char.toShort())) result = RuntimeValue(DataType.UBYTE, char.toShort())
} }
"c64utils.str2uword" -> { "c64utils.str2uword" -> {
val heapId = args[0].wordval!! val heapId = args[0].wordval!!
val argString = program.heap.get(heapId).str!! val argString = program.heap.get(heapId).str!!
val numericpart = argString.takeWhile { it.isDigit() } 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") else -> TODO("syscall ${sub.scopedname} $sub")
} }

View File

@ -16,7 +16,7 @@ import kotlin.math.abs
class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags, class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags,
val runtimeVars: RuntimeVariables, val runtimeVars: RuntimeVariables,
val performBuiltinFunction: (String, List<RuntimeValue>, StatusFlags) -> RuntimeValue?, val performBuiltinFunction: (String, List<RuntimeValue>, StatusFlags) -> RuntimeValue?,
val executeSubroutine: (sub: Subroutine, args: List<RuntimeValue>, startlabel: Label?) -> List<RuntimeValue>) val executeSubroutine: (sub: Subroutine, args: List<RuntimeValue>, startlabel: Label?) -> RuntimeValue?)
fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
val constval = expr.constValue(ctx.program) 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) } val args = expr.arglist.map { evaluate(it, ctx) }
return when(sub) { return when(sub) {
is Subroutine -> { is Subroutine -> {
val results = ctx.executeSubroutine(sub, args, null) val result = ctx.executeSubroutine(sub, args, null)
if(results.size!=1) ?: throw VmExecutionException("expected a result from functioncall $expr")
throw VmExecutionException("expected 1 result from functioncall $expr") result
results[0]
} }
is BuiltinFunctionStatementPlaceholder -> { is BuiltinFunctionStatementPlaceholder -> {
val result = ctx.performBuiltinFunction(sub.name, args, ctx.statusflags) val result = ctx.performBuiltinFunction(sub.name, args, ctx.statusflags)

View File

@ -32,7 +32,8 @@
ubyte guess = lsb(c64utils.str2uword(input)) ubyte guess = lsb(c64utils.str2uword(input))
if guess==secretnumber { if guess==secretnumber {
return ending(true) ending(true)
return
} else { } else {
c64scr.print("\n\nThat is too ") c64scr.print("\n\nThat is too ")
if guess<secretnumber if guess<secretnumber
@ -42,7 +43,8 @@
} }
} }
return ending(false) ; @todo error ending(false)
return
sub ending(ubyte success) { sub ending(ubyte success) {

View File

@ -4,16 +4,18 @@
~ main { ~ main {
sub start() { sub start() {
str naam = " "
while true { foo(42)
c64scr.print("naam: ") return
ubyte length = c64scr.input_chars(naam) }
c64scr.print_ub(length)
c64.CHROUT(':') sub foo(ubyte arg) -> ubyte {
c64scr.print(naam) bar(arg)
c64.CHROUT('\n') return 33
} }
sub bar(ubyte a2) {
;nothing
} }
} }

View File

@ -183,7 +183,7 @@ expression_list :
expression (',' EOL? expression)* // you can split the expression list over several lines expression (',' EOL? expression)* // you can split the expression list over several lines
; ;
returnstmt : 'return' expression_list? ; returnstmt : 'return' expression? ;
breakstmt : 'break'; breakstmt : 'break';

View File

@ -2265,8 +2265,8 @@ public class prog8Parser extends Parser {
} }
public static class ReturnstmtContext extends ParserRuleContext { public static class ReturnstmtContext extends ParserRuleContext {
public Expression_listContext expression_list() { public ExpressionContext expression() {
return getRuleContext(Expression_listContext.class,0); return getRuleContext(ExpressionContext.class,0);
} }
public ReturnstmtContext(ParserRuleContext parent, int invokingState) { public ReturnstmtContext(ParserRuleContext parent, int invokingState) {
super(parent, invokingState); super(parent, invokingState);
@ -2288,7 +2288,7 @@ public class prog8Parser extends Parser {
case 1: case 1:
{ {
setState(426); setState(426);
expression_list(); expression(0);
} }
break; 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"+ "\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"+ "\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"+ "\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"+ "\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\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"+ "\7t\2\2\u01b8\u01b6\3\2\2\2\u01b9\u01bc\3\2\2\2\u01ba\u01b8\3\2\2\2\u01ba"+