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
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 {
val values = expression_list()
return Return(values?.toAst() ?: emptyList(), toPosition())
return Return(expression()?.toAst(), toPosition())
}
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 {
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)
}

View File

@ -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<IExpression>()
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)
}

View File

@ -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
}

View File

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

View File

@ -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)
}

View File

@ -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<IExpression>, 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)

View File

@ -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) {

View File

@ -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)
}

View File

@ -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) {

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)}
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
}

View File

@ -261,11 +261,11 @@ class AstVm(val program: Program) {
class LoopControlBreak : 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()
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) {
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<IExpression>) = args.map { evaluate(it, evalCtx) }
private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>): List<RuntimeValue> {
val result = mutableListOf<RuntimeValue>()
private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>): 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")
}

View File

@ -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<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 {
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)

View File

@ -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<secretnumber
@ -42,7 +43,8 @@
}
}
return ending(false) ; @todo error
ending(false)
return
sub ending(ubyte success) {

View File

@ -4,16 +4,18 @@
~ main {
sub start() {
str naam = " "
while true {
c64scr.print("naam: ")
ubyte length = c64scr.input_chars(naam)
c64scr.print_ub(length)
c64.CHROUT(':')
c64scr.print(naam)
c64.CHROUT('\n')
}
foo(42)
return
}
sub foo(ubyte arg) -> ubyte {
bar(arg)
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
;
returnstmt : 'return' expression_list? ;
returnstmt : 'return' expression? ;
breakstmt : 'break';

View File

@ -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"+