From 1e9586f635b3d9e5b9c2a28f882ed8eb131473d9 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 12 Jul 2019 12:41:08 +0200 Subject: [PATCH] Structs can be compiled and executed in the vm! structs are just syntactic sugar for a set of variables for now. --- .../src/prog8/ast/processing/AstChecker.kt | 23 +++++++++++--- .../src/prog8/functions/BuiltinFunctions.kt | 6 +++- compiler/src/prog8/vm/astvm/AstVm.kt | 6 ++-- compiler/src/prog8/vm/astvm/Expressions.kt | 12 +++++-- .../src/prog8/vm/astvm/VariablesCreator.kt | 31 ++++++++++++------- examples/test.p8 | 1 + 6 files changed, 57 insertions(+), 22 deletions(-) diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index 64c22f350..0cf27b3b3 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -1,6 +1,5 @@ package prog8.ast.processing -import com.sun.org.apache.bcel.internal.generic.ISTORE import prog8.ast.* import prog8.ast.base.* import prog8.ast.base.printWarning @@ -426,7 +425,7 @@ internal class AstChecker(private val program: Program, checkResult.add(ExpressionError("assignment value is invalid or has no proper datatype", assignment.value.position)) } else - checkAssignmentCompatible(targetDatatype, sourceDatatype, assignment.value, assignment.position) + checkAssignmentCompatible(targetDatatype, sourceDatatype, assignment.value, target, assignment.position) } } return assignment @@ -1138,7 +1137,11 @@ internal class AstChecker(private val program: Program, } return err("invalid float array initialization value ${value.type}, expected $targetDt") } - DataType.STRUCT -> TODO("struct dt") + DataType.STRUCT -> { + if(value.type!=DataType.STRUCT) + return err("cannot assign a normal value to a struct") + TODO("check struct, $value ") + } } return true } @@ -1195,10 +1198,11 @@ internal class AstChecker(private val program: Program, private fun checkAssignmentCompatible(targetDatatype: DataType, sourceDatatype: DataType, sourceValue: IExpression, + target: AssignTarget, position: Position) : Boolean { if(sourceValue is RangeExpr) - checkResult.add(SyntaxError("can't assign a range value", position)) + checkResult.add(SyntaxError("can't assign a range value to something else", position)) val result = when(targetDatatype) { DataType.BYTE -> sourceDatatype== DataType.BYTE @@ -1208,6 +1212,17 @@ internal class AstChecker(private val program: Program, DataType.FLOAT -> sourceDatatype in NumericDatatypes DataType.STR -> sourceDatatype== DataType.STR DataType.STR_S -> sourceDatatype== DataType.STR_S + DataType.STRUCT -> { + // for now we've decided you cannot assign struct by-value. +// if(sourceDatatype==DataType.STRUCT) { +// val sourcename = (sourceValue as IdentifierReference).nameInSource +// val vd1 = program.namespace.lookup(sourcename, target) as? VarDecl +// val targetname = target.identifier!!.nameInSource +// val vd2 = program.namespace.lookup(targetname, target) as? VarDecl +// return vd1?.struct == vd2?.struct +// } + false + } else -> checkResult.add(SyntaxError("cannot assign new value to variable of type $targetDatatype", position)) } diff --git a/compiler/src/prog8/functions/BuiltinFunctions.kt b/compiler/src/prog8/functions/BuiltinFunctions.kt index 9891a24db..f56dd21e5 100644 --- a/compiler/src/prog8/functions/BuiltinFunctions.kt +++ b/compiler/src/prog8/functions/BuiltinFunctions.kt @@ -12,10 +12,14 @@ import kotlin.math.* class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set) + +typealias ConstExpressionCaller = (args: List, position: Position, program: Program) -> LiteralValue + + class FunctionSignature(val pure: Boolean, // does it have side effects? val parameters: List, val returntype: DataType?, - val constExpressionFunc: ((args: List, position: Position, program: Program) -> LiteralValue)? = null) + val constExpressionFunc: ConstExpressionCaller? = null) val BuiltinFunctions = mapOf( diff --git a/compiler/src/prog8/vm/astvm/AstVm.kt b/compiler/src/prog8/vm/astvm/AstVm.kt index de01898b6..f0919a7c6 100644 --- a/compiler/src/prog8/vm/astvm/AstVm.kt +++ b/compiler/src/prog8/vm/astvm/AstVm.kt @@ -265,7 +265,7 @@ class AstVm(val program: Program) { class LoopControlJump(val identifier: IdentifierReference?, val address: Int?, val generatedLabel: String?) : Exception() - internal fun executeSubroutine(sub: Subroutine, arguments: List, startlabel: Label?=null): RuntimeValue? { + internal fun executeSubroutine(sub: Subroutine, arguments: List, startAtLabel: Label?=null): RuntimeValue? { if(sub.isAsmSubroutine) { return performSyscall(sub, arguments) } @@ -282,10 +282,10 @@ class AstVm(val program: Program) { } val statements = sub.statements.iterator() - if(startlabel!=null) { + if(startAtLabel!=null) { do { val stmt = statements.next() - } while(stmt!==startlabel) + } while(stmt!==startAtLabel) } try { diff --git a/compiler/src/prog8/vm/astvm/Expressions.kt b/compiler/src/prog8/vm/astvm/Expressions.kt index 8d954dfc5..ed9c43b11 100644 --- a/compiler/src/prog8/vm/astvm/Expressions.kt +++ b/compiler/src/prog8/vm/astvm/Expressions.kt @@ -13,10 +13,15 @@ import prog8.vm.RuntimeValue import prog8.vm.RuntimeValueRange import kotlin.math.abs + +typealias BuiltinfunctionCaller = (name: String, args: List, flags: StatusFlags) -> RuntimeValue? +typealias SubroutineCaller = (sub: Subroutine, args: List, startAtLabel: Label?) -> RuntimeValue? + + 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?) -> RuntimeValue?) + val performBuiltinFunction: BuiltinfunctionCaller, + val executeSubroutine: SubroutineCaller) fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { val constval = expr.constValue(ctx.program) @@ -96,6 +101,9 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue { if(variable is VarDecl) { if(variable.type==VarDeclType.VAR) return ctx.runtimeVars.get(variable.definingScope(), variable.name) + else if(variable.type==VarDeclType.STRUCT) { + throw VmExecutionException("cannot process structs by-value. at ${expr.position}") + } else { val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name) return when(variable.datatype) { diff --git a/compiler/src/prog8/vm/astvm/VariablesCreator.kt b/compiler/src/prog8/vm/astvm/VariablesCreator.kt index 34e9a2c20..04a6f1c1e 100644 --- a/compiler/src/prog8/vm/astvm/VariablesCreator.kt +++ b/compiler/src/prog8/vm/astvm/VariablesCreator.kt @@ -4,6 +4,7 @@ import prog8.ast.* import prog8.ast.base.* import prog8.ast.expressions.LiteralValue import prog8.ast.processing.IAstModifyingVisitor +import prog8.ast.statements.StructDecl import prog8.ast.statements.VarDecl import prog8.compiler.HeapValues import prog8.vm.RuntimeValue @@ -34,19 +35,25 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v } override fun visit(decl: VarDecl): IStatement { - when(decl.type) { - // we can assume the value in the vardecl already has been converted into a constant LiteralValue here. - VarDeclType.VAR -> { - val value = RuntimeValue.from(decl.value as LiteralValue, heap) - runtimeVariables.define(decl.definingScope(), decl.name, value) + // if the decl is part of a struct, just skip it + if(decl.parent !is StructDecl) { + when (decl.type) { + // we can assume the value in the vardecl already has been converted into a constant LiteralValue here. + VarDeclType.VAR -> { + println("$decl") + val value = RuntimeValue.from(decl.value as LiteralValue, heap) + runtimeVariables.define(decl.definingScope(), decl.name, value) + } + VarDeclType.MEMORY -> { + runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as LiteralValue).asIntegerValue!!) + } + VarDeclType.CONST -> { + // consts should have been const-folded away + } + VarDeclType.STRUCT -> { + // struct vardecl can be skipped because its members have been flattened out + } } - VarDeclType.MEMORY -> { - runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as LiteralValue).asIntegerValue!!) - } - VarDeclType.CONST -> { - // consts should have been const-folded away - } - VarDeclType.STRUCT -> TODO("struct decltype") } return super.visit(decl) } diff --git a/examples/test.p8 b/examples/test.p8 index e9a4c7249..c7a214ad5 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -4,6 +4,7 @@ ~ main { sub start() { + uword derp Color foreground ; = [0,1,2] @todo init values Color background