diff --git a/codeCore/src/prog8/code/ast/AstStatements.kt b/codeCore/src/prog8/code/ast/AstStatements.kt index 085c9aef1..32aa8d24c 100644 --- a/codeCore/src/prog8/code/ast/AstStatements.kt +++ b/codeCore/src/prog8/code/ast/AstStatements.kt @@ -29,6 +29,14 @@ class PtSub( override fun printProperties() { print(name) } + + init { + // params and return value should not be str + if(parameters.any{ it.type !in NumericDatatypes }) + throw AssemblyError("non-numeric parameter") + if(returntype!=null && returntype !in NumericDatatypes) + throw AssemblyError("non-numeric returntype $returntype") + } } diff --git a/codeCore/src/prog8/code/core/SourceCode.kt b/codeCore/src/prog8/code/core/SourceCode.kt index 3c2e5a82d..2b7811088 100644 --- a/codeCore/src/prog8/code/core/SourceCode.kt +++ b/codeCore/src/prog8/code/core/SourceCode.kt @@ -106,7 +106,7 @@ sealed class SourceCode { * [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8" */ class Resource(pathString: String): SourceCode() { - private val normalized = "/" + Path.of(pathString).normalize().toMutableList().joinToString("/") + private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/") override val isFromResources = true override val isFromFilesystem = false @@ -125,7 +125,7 @@ sealed class SourceCode { } val stream = object {}.javaClass.getResourceAsStream(normalized) text = stream!!.reader().use { it.readText() } - name = Path.of(pathString).toFile().nameWithoutExtension + name = Path(pathString).toFile().nameWithoutExtension } } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/VmCodeGen.kt b/codeGenVirtual/src/prog8/codegen/virtual/VmCodeGen.kt index 4b5ddb5a7..910ea441a 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/VmCodeGen.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/VmCodeGen.kt @@ -9,7 +9,7 @@ import prog8.code.core.IErrorReporter import prog8.codegen.intermediate.IRCodeGen import prog8.intermediate.IRFileReader import prog8.intermediate.IRFileWriter -import java.nio.file.Path +import kotlin.io.path.Path class VmCodeGen(private val program: PtProgram, private val symbolTable: SymbolTable, @@ -33,7 +33,7 @@ class VmCodeGen(private val program: PtProgram, companion object { fun compileIR(listingFilename: String): IAssemblyProgram { - val irProgram = IRFileReader(Path.of(""), listingFilename).readFile() + val irProgram = IRFileReader(Path(""), listingFilename).readFile() return VmAssemblyProgram(irProgram.name, irProgram) } } diff --git a/codeGenVirtual/src/prog8/codegen/virtual/VmVariableAllocator.kt b/codeGenVirtual/src/prog8/codegen/virtual/VmVariableAllocator.kt index d3ff4a396..f59724655 100644 --- a/codeGenVirtual/src/prog8/codegen/virtual/VmVariableAllocator.kt +++ b/codeGenVirtual/src/prog8/codegen/virtual/VmVariableAllocator.kt @@ -58,7 +58,12 @@ internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEnc } in ArrayDatatypes -> { if(variable.onetimeInitializationArrayValue!==null) { - variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toHex() } + variable.onetimeInitializationArrayValue!!.joinToString(",") { + if(it.number!=null) + it.number!!.toHex() + else + "&${it.addressOf!!.joinToString(".")}" + } } else { (1..variable.length!!).joinToString(",") { "0" } } diff --git a/compiler/res/version.txt b/compiler/res/version.txt index 188c40921..75d2b585d 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -8.5 +8.6-dev diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index bbba06cf8..643beb78b 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -13,6 +13,7 @@ import java.io.File import java.nio.file.* import java.time.LocalDateTime import kotlin.system.exitProcess +import kotlin.io.path.Path fun main(args: Array) { @@ -142,7 +143,7 @@ private fun compileMain(args: Array): Boolean { for (importedFile in allImportedFiles) { print(" ") println(importedFile) - val watchDir = importedFile.parent ?: Path.of("") + val watchDir = importedFile.parent ?: Path("") watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY) } println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.") diff --git a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt index e30706119..cc17511f8 100644 --- a/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/IntermediateAstMaker.kt @@ -320,9 +320,12 @@ class IntermediateAstMaker(val program: Program) { private fun transformSub(srcSub: Subroutine): PtSub { val (vardecls, statements) = srcSub.statements.partition { it is VarDecl } + var returntype = srcSub.returntypes.singleOrNull() + if(returntype==DataType.STR) + returntype=DataType.UWORD // if a sub returns 'str', replace with uword. Intermediate AST and I.R. don't contain 'str' datatype anymore. val sub = PtSub(srcSub.name, srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) }, - srcSub.returntypes.singleOrNull(), + returntype, srcSub.inline, srcSub.position) sub.parameters.forEach { it.parent=sub } diff --git a/compiler/test/helpers/compileXyz.kt b/compiler/test/helpers/compileXyz.kt index 52b1624e0..54ef33756 100644 --- a/compiler/test/helpers/compileXyz.kt +++ b/compiler/test/helpers/compileXyz.kt @@ -22,6 +22,7 @@ internal fun compileFile( outputDir: Path = prog8tests.helpers.outputDir, errors: IErrorReporter? = null, writeAssembly: Boolean = true, + keepIR: Boolean = true, optFloatExpr: Boolean = true ) : CompilationResult? { val filepath = fileDir.resolve(fileName) @@ -36,7 +37,7 @@ internal fun compileFile( quietAssembler = true, asmListfile = false, experimentalCodegen = false, - keepIR = false, + keepIR = keepIR, platform.name, evalStackBaseAddress = null, symbolDefs = emptyMap(), @@ -57,12 +58,14 @@ internal fun compileText( sourceText: String, errors: IErrorReporter? = null, writeAssembly: Boolean = true, + keepIR: Boolean = true, optFloatExpr: Boolean = true ) : CompilationResult? { val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8") // we don't assumeNotExists(filePath) - should be ok to just overwrite it filePath.toFile().writeText(sourceText) - return compileFile(platform, optimize, filePath.parent, filePath.name, errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr) + return compileFile(platform, optimize, filePath.parent, filePath.name, + errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, keepIR=keepIR) } diff --git a/compiler/test/vm/TestCompilerVirtual.kt b/compiler/test/vm/TestCompilerVirtual.kt new file mode 100644 index 000000000..1b462e710 --- /dev/null +++ b/compiler/test/vm/TestCompilerVirtual.kt @@ -0,0 +1,104 @@ +package prog8tests.vm + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldNotBe +import prog8.code.target.Cx16Target +import prog8.code.target.VMTarget +import prog8.vm.VmRunner +import prog8tests.helpers.compileText +import kotlin.io.path.readText + +class TestCompilerVirtual: FunSpec({ + test("compile virtual: any all sort reverse builtin funcs") { + val src = """ +main { + + sub start() { + uword[] words = [1111,2222,0,4444,3333] + ubyte result = all(words) + result++ + result = any(words) + result++ + sort(words) + reverse(words) + } +}""" + val target = VMTarget() + val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!! + val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt") + VmRunner().runProgram(virtfile.readText(), false) + } + + test("compile virtual: array with pointers") { + val src = """ +main { + + sub start() { + ubyte variable + uword[] words = [1111,2222,"three",&variable] + variable = 2222 in words + } +}""" + val othertarget = Cx16Target() + compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null + val target = VMTarget() + val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!! + val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt") + VmRunner().runProgram(virtfile.readText(), false) + } + + test("compile virtual: str args and return type") { + val src = """ +main { + + sub start() { + sub testsub(str s1) -> str { + return "result" + } + + uword result = testsub("arg") + } +}""" + val target = VMTarget() + val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!! + val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt") + VmRunner().runProgram(virtfile.readText(), false) + } + + test("compile virtual: nested labels") { + val src = """ +main { + sub start() { + uword i + uword k + +mylabel_outside: + for i in 0 to 10 { +mylabel_inside: + if i==100 { + goto mylabel_outside + goto mylabel_inside + } + while k <= 10 { + k++ + } + do { + k-- + } until k==0 + for k in 0 to 5 { + i++ + } + repeat 10 { + k++ + } + } + } +}""" + + val target = VMTarget() + val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!! + val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt") + VmRunner().runProgram(virtfile.readText(), false) + } + +}) \ No newline at end of file diff --git a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt index be048be8f..2ec20b69a 100644 --- a/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt +++ b/compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt @@ -8,7 +8,7 @@ import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.code.core.* import prog8.parser.Prog8ANTLRParser -import java.nio.file.Path +import kotlin.io.path.Path import kotlin.io.path.isRegularFile @@ -20,7 +20,7 @@ private data class NumericLiteralNode(val number: Double, val datatype: DataType private fun ParserRuleContext.toPosition() : Position { val pathString = start.inputStream.sourceName val filename = if(SourceCode.isRegularFilesystemPath(pathString)) { - val path = Path.of(pathString) + val path = Path(pathString) if(path.isRegularFile()) { SourceCode.relative(path).toString() } else { diff --git a/docs/source/todo.rst b/docs/source/todo.rst index d185e27df..0a0f5d816 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,8 +3,10 @@ TODO For next release ^^^^^^^^^^^^^^^^ -- fix compiler crash in examples/vm/bsieve.p8 -- fix nullpointer in allocator in examples/vm/textelite.p8 +- fix vm crash in TestCompilerVirtual: while loop +- fix vm crash in TestCompilerVirtual: array with pointers +- fix vm crash (while loop) in examples/vm/bsieve.p8 (Assembler) +- fix vm crash (parseValue error) in examples/vm/textelite.p8 (Assembler) ... diff --git a/examples/vm/bsieve.p8 b/examples/vm/bsieve.p8 index d2d3e0199..6168d7b02 100644 --- a/examples/vm/bsieve.p8 +++ b/examples/vm/bsieve.p8 @@ -26,13 +26,14 @@ main { @(flags_ptr + k) = false k += prime } -; txt.print_uw(prime) -; txt.spc() + txt.print_uw(prime) + txt.spc() count++ } } } + txt.nl() txt.print_uw(count) txt.print(" primes\n") } diff --git a/httpCompilerService/src/prog8/http/TestHttp.kt b/httpCompilerService/src/prog8/http/TestHttp.kt index 03a52eb40..8cddf3db7 100644 --- a/httpCompilerService/src/prog8/http/TestHttp.kt +++ b/httpCompilerService/src/prog8/http/TestHttp.kt @@ -13,8 +13,8 @@ import org.takes.rs.RsJson import org.takes.tk.TkSlf4j import prog8.compiler.CompilerArguments import prog8.compiler.compileProgram -import java.nio.file.Path import javax.json.Json +import kotlin.io.path.Path class Jsonding: RsJson.Source { @@ -31,7 +31,7 @@ class RequestParser : Take { val names = form.names() val a = form.param("a").single() val args = CompilerArguments( - Path.of(a), + Path(a), optimize = true, optimizeFloatExpressions = false, dontReinitGlobals = false, diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 4db965f70..9b7734a30 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -4,6 +4,7 @@ import prog8.code.* import prog8.code.core.* import prog8.code.target.* import java.nio.file.Path +import kotlin.io.path.Path import kotlin.io.path.bufferedReader import kotlin.io.path.div @@ -54,6 +55,7 @@ class IRFileReader(outputDir: Path, programName: String) { var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS var dontReinitGlobals = false var evalStackBaseAddress: UInt? = null + var outputDir = Path("") if(line!="") throw IRParseException("invalid OPTIONS") while(true) { @@ -82,6 +84,7 @@ class IRFileReader(outputDir: Path, programName: String) { val (start, end) = value.split(',') zpReserved.add(UIntRange(start.toUInt(), end.toUInt())) } + "outputDir" -> outputDir = Path(value) else -> throw IRParseException("illegal OPTION $name") } } @@ -96,7 +99,8 @@ class IRFileReader(outputDir: Path, programName: String) { target, loadAddress, dontReinitGlobals = dontReinitGlobals, - evalStackBaseAddress = evalStackBaseAddress + evalStackBaseAddress = evalStackBaseAddress, + outputDir = outputDir ) } @@ -130,7 +134,12 @@ class IRFileReader(outputDir: Path, programName: String) { } } in ArrayDatatypes -> { - initArray = value.split(',').map { StArrayElement(it.toDouble(), null) } + initArray = value.split(',').map { + if(it.startsWith('&')) + StArrayElement(null, it.drop(1).split('.')) + else + StArrayElement(it.toDouble(), null) + } } DataType.STR -> throw IRParseException("STR should have been converted to byte array") else -> throw IRParseException("weird dt") @@ -173,7 +182,7 @@ class IRFileReader(outputDir: Path, programName: String) { "uword" -> DataType.ARRAY_UW "float" -> DataType.ARRAY_F "bool" -> DataType.ARRAY_B - else -> throw IRParseException("invalid dt") + else -> throw IRParseException("invalid dt $type") } } else { return when(type) { @@ -183,7 +192,8 @@ class IRFileReader(outputDir: Path, programName: String) { "uword" -> DataType.UWORD "float" -> DataType.FLOAT "bool" -> DataType.BOOL - else -> throw IRParseException("invalid dt") + // note: 'str' should not occur anymore in IR. Should be 'uword' + else -> throw IRParseException("invalid dt $type") } } } diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 50f1aa741..cb7e83af0 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -93,6 +93,7 @@ class IRFileWriter(private val irProgram: IRProgram) { out.write("loadAddress=${irProgram.options.loadAddress}\n") out.write("dontReinitGlobals=${irProgram.options.dontReinitGlobals}\n") out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress}\n") + out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n") // other options not yet useful here? out.write("\n") } @@ -117,7 +118,12 @@ class IRFileWriter(private val irProgram: IRProgram) { } in ArrayDatatypes -> { if(variable.onetimeInitializationArrayValue!==null) { - variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toInt().toString() } + variable.onetimeInitializationArrayValue!!.joinToString(",") { + if(it.number!=null) + it.number!!.toInt().toString() + else + "&${it.addressOf!!.joinToString(".")}" + } } else { (1..variable.length!!).joinToString(",") { "0" } } diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index 5c7024f54..23e9139a6 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -97,6 +97,12 @@ class IRSubroutine(val name: String, throw IllegalArgumentException("subroutine name is not scoped: $name") if(name.startsWith("main.main.")) throw IllegalArgumentException("subroutine name invalid main prefix: $name") + + // params and return value should not be str + if(parameters.any{ it.dt !in NumericDatatypes }) + throw IllegalArgumentException("non-numeric parameter") + if(returnType!=null && returnType !in NumericDatatypes) + throw IllegalArgumentException("non-numeric returntype $returnType") } operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk } diff --git a/intermediate/test/TestIRFileInOut.kt b/intermediate/test/TestIRFileInOut.kt index d5835c2da..b724fac9e 100644 --- a/intermediate/test/TestIRFileInOut.kt +++ b/intermediate/test/TestIRFileInOut.kt @@ -12,17 +12,13 @@ import prog8.code.target.Cx16Target import prog8.intermediate.IRFileReader import prog8.intermediate.IRFileWriter import prog8.intermediate.IRProgram -import java.nio.file.Path -import kotlin.io.path.deleteExisting -import kotlin.io.path.name -import kotlin.io.path.readLines -import kotlin.io.path.writeText +import kotlin.io.path.* class TestIRFileInOut: FunSpec({ test("test IR writer") { val st = SymbolTable() val target = Cx16Target() - val tempdir = Path.of(System.getProperty("java.io.tmpdir")) + val tempdir = Path(System.getProperty("java.io.tmpdir")) val options = CompilationOptions( OutputType.RAW, CbmPrgLauncherType.NONE, diff --git a/virtualmachine/src/prog8/vm/VirtualMachine.kt b/virtualmachine/src/prog8/vm/VirtualMachine.kt index e4ff53515..002a5b696 100644 --- a/virtualmachine/src/prog8/vm/VirtualMachine.kt +++ b/virtualmachine/src/prog8/vm/VirtualMachine.kt @@ -2112,6 +2112,6 @@ class VmRunner: IVirtualMachineRunner { assembler.initializeMemory(memsrc, memory) val program = assembler.assembleProgram(programsrc) val vm = VirtualMachine(memory, program, assembler.cx16virtualregBaseAdress) - vm.run(throttle = true) + vm.run(throttle = throttle) } } \ No newline at end of file