mirror of
https://github.com/irmen/prog8.git
synced 2025-02-16 22:30:46 +00:00
return statement only has one single possible value
astvm can now more or less run all examples
This commit is contained in:
parent
3d7a4bf81a
commit
845a99d623
2
clean.sh
2
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
|
||||
|
||||
|
@ -1 +1 @@
|
||||
1.10-dev
|
||||
1.10
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ interface IAstVisitor {
|
||||
}
|
||||
|
||||
fun visit(returnStmt: Return) {
|
||||
returnStmt.values.forEach { it.accept(this) }
|
||||
returnStmt.value?.accept(this)
|
||||
}
|
||||
|
||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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"+
|
||||
|
Loading…
x
Reference in New Issue
Block a user