Compare commits

...
46 Commits
Author SHA1 Message Date
Irmen de Jong 7f8fe75ab2 version 7.1 2021-10-24 14:00:11 +02:00
Irmen de Jong 44143f481a Merge branch 'v7.1' 2021-10-20 22:50:35 +02:00
Irmen de Jong 440abf4998 fix test to recognise inserted return statements 2021-10-20 22:50:18 +02:00
Irmen de Jong 3c10427e04 Merge branch 'v7.1' 2021-10-20 22:38:23 +02:00
Irmen de Jong 85956b5828 code generator: add a return (RTS) to empty subroutines. Fixes #67 2021-10-20 22:36:13 +02:00
Irmen de Jong 41e40cad03 optimizer bug: don't remove empty subroutine if it's referenced. Fixes #67 2021-10-20 22:25:10 +02:00
Irmen de Jong df2d5c6585 tests for callgraph and unused subroutine removal in optimizer 2021-10-20 22:24:10 +02:00
Irmen de Jong f696fce187 Merge branch 'v7.1' 2021-10-19 23:59:07 +02:00
Irmen de Jong 82d3d81bb2 don't want to complicate things by introducing a boolean literal 2021-10-19 23:58:50 +02:00
Irmen de Jong f0cff661df Merge branch 'v7.1' 2021-10-19 23:21:44 +02:00
Irmen de Jong 82d20dea39 a few comment and TODO cleanups.
remove remark about chars UBYTE type, kotlin's closest native type that can contain 0-255 is a short.
2021-10-19 23:20:34 +02:00
Irmen de Jong 804bb06859 clarified isInRegularRAM() by making it an extension method on AssignTarget 2021-10-19 22:36:05 +02:00
Irmen de Jong 5afa7e53f8 got rid of program arg for isInRegularRAM 2021-10-19 22:30:30 +02:00
Irmen de Jong 7f15b7b716 remove unneeded check for duplicate module names as this is now caught by the logic in Program.addModule itself 2021-10-19 22:12:54 +02:00
Irmen de Jong 552e0c2248 rename mainModule to toplevelModule.
failed module no longer retains in the Ast.
improved some tests on that.
2021-10-19 21:49:05 +02:00
Irmen de Jong e5b9e1f5e7 string object identity hashcode can be negative sometimes, so allow a '-' character. 2021-10-19 21:08:15 +02:00
Irmen de Jong 502bf90007 comments 2021-10-19 01:12:28 +02:00
Irmen de Jong 40bf117497 avoid crash when parser doesn't report an offending token for a parse error 2021-10-19 00:44:33 +02:00
Irmen de Jong 4011dce31b added a few more tests for the file element of Position 2021-10-19 00:26:02 +02:00
Irmen de Jong 9e881e32f8 version 7.1-beta2 2021-10-16 18:47:16 +02:00
Irmen de Jong 637a8899c5 Merge pull request #65 from irmen/v7.1
more V7.1 updates
2021-10-16 17:51:56 +02:00
Irmen de Jong cf0e395921 got rid of SourceCode.pathString() and the 'need' to strip < and > 2021-10-16 17:15:22 +02:00
Irmen de Jong 6ef438ce50 todo 2021-10-16 15:08:36 +02:00
Irmen de Jong 46e4b977a4 another attempt to fix Windows path issues 2021-10-16 15:02:15 +02:00
Irmen de Jong 9626c5dead attempt to fix Windows path issue with "library:" prefixes in AsmGen 2021-10-16 14:50:08 +02:00
Irmen de Jong aea364e43d paths are now always relative to the current directory. Fixes #64 2021-10-16 14:26:33 +02:00
Irmen de Jong 06defd0cb0 paths are now always relative 2021-10-16 02:43:22 +02:00
Irmen de Jong 0f80897c50 todo 2021-10-15 01:02:32 +02:00
Irmen de Jong 57bb1c2c0d performance optimized checks against short ranges of values 2021-10-15 00:51:45 +02:00
Irmen de Jong 7b35b414e8 tweak check of DataType against multiple values 2021-10-15 00:39:42 +02:00
Irmen de Jong 761aac7a23 replace inferredType.istype() by infix form 2021-10-15 00:28:23 +02:00
Irmen de Jong 15a02d7664 making InferredType easier to use 2021-10-15 00:18:13 +02:00
Irmen de Jong 16ed68c1ec Module.name is now derived back from the source's origin string 2021-10-14 23:58:14 +02:00
Irmen de Jong e63cf660c6 petscii now use Result instead of Either 2021-10-13 23:22:46 +02:00
Irmen de Jong aaff484306 refactor executeImportDirective 2021-10-13 23:14:27 +02:00
Irmen de Jong 3281d9a215 fix error when sourcepaths is empty 2021-10-13 23:08:51 +02:00
Irmen de Jong 0fcd61e00f refactor tryGetModuleFromResource 2021-10-13 23:00:22 +02:00
Irmen de Jong c4523ea470 refactor tryGetModuleFromFile 2021-10-13 22:32:52 +02:00
Irmen de Jong 0447b3e4cc remove testcase that attempted to check invalid %import syntax.
we only allow unquoted names, without filename suffix, in %import.
2021-10-13 22:10:35 +02:00
Irmen de Jong 4d27c2901b fix weird error printing when doing %import textio.p8 2021-10-13 21:55:51 +02:00
Irmen de Jong 855e18b31c fix SourceCode to properly set the sourceName of a resource or string as well 2021-10-13 21:46:38 +02:00
Irmen de Jong d790878af6 enabled test 2021-10-13 20:28:42 +02:00
Irmen de Jong 85b244df2f remove remains of %target 2021-10-13 20:13:57 +02:00
Irmen de Jong 6070afa6b6 cleanup SourceCode class 2021-10-13 19:16:01 +02:00
Irmen de Jong 975594703d doc 2021-10-13 18:21:48 +02:00
Irmen de Jong 6b8c3ef614 renamed command line option -libdirs to -srcdirs
this more clearly separates this meaning from the internal library modules
2021-10-13 18:16:51 +02:00
58 changed files with 1911 additions and 1717 deletions

No files matched your search

+1 -1
View File
@@ -1 +1 @@
7.1-beta
7.1
+7 -7
View File
@@ -39,7 +39,7 @@ private fun compileMain(args: Array<String>): Boolean {
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watches for file changes), greatly increases compilation speed")
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
val libDirs by cli.option(ArgType.String, fullName="libdirs", description = "list of extra paths to search in for imported modules").multiple().delimiter(File.pathSeparator)
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths to search in for imported modules").multiple().delimiter(File.pathSeparator)
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
try {
@@ -61,9 +61,9 @@ private fun compileMain(args: Array<String>): Boolean {
return false
}
val libdirs = libDirs.toMutableList()
if(libdirs.firstOrNull()!=".")
libdirs.add(0, ".")
val srcdirs = sourceDirs.toMutableList()
if(srcdirs.firstOrNull()!=".")
srcdirs.add(0, ".")
if(watchMode==true) {
val watchservice = FileSystems.getDefault().newWatchService()
@@ -74,7 +74,7 @@ private fun compileMain(args: Array<String>): Boolean {
val results = mutableListOf<CompilationResult>()
for(filepathRaw in moduleFiles) {
val filepath = pathFrom(filepathRaw).normalize()
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, srcdirs, outputPath)
results.add(compilationResult)
}
@@ -85,7 +85,7 @@ private fun compileMain(args: Array<String>): Boolean {
for (importedFile in allImportedFiles) {
print(" ")
println(importedFile)
val watchDir = importedFile.parent ?: Path.of(".")
val watchDir = importedFile.parent ?: Path.of("")
watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
}
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")
@@ -111,7 +111,7 @@ private fun compileMain(args: Array<String>): Boolean {
val filepath = pathFrom(filepathRaw).normalize()
val compilationResult: CompilationResult
try {
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, srcdirs, outputPath)
if(!compilationResult.success)
return false
} catch (x: ParsingFailedError) {
@@ -9,6 +9,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor
import prog8.compiler.astprocessing.isInRegularRAMof
import prog8.compiler.target.ICompilationTarget
@@ -39,7 +40,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
// But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
if(!assignment.isAugmentable
&& assignment.target.identifier != null
&& compTarget.isInRegularRAM(assignment.target, program)) {
&& assignment.target.isInRegularRAMof(compTarget.machine)) {
val binExpr = assignment.value as? BinaryExpression
if (binExpr != null && binExpr.operator !in comparisonOperators) {
if (binExpr.left !is BinaryExpression) {
@@ -119,13 +120,13 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
// and if an assembly block doesn't contain a rts/rti, and some other situations.
val mods = mutableListOf<IAstModification>()
val returnStmt = Return(null, subroutine.position)
if (subroutine.asmAddress == null
&& !subroutine.inline
&& subroutine.statements.isNotEmpty()
&& subroutine.amountOfRtsInAsm() == 0
if (subroutine.asmAddress == null && !subroutine.inline) {
if(subroutine.statements.isEmpty() ||
(subroutine.amountOfRtsInAsm() == 0
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
&& subroutine.statements.last() !is Subroutine) {
mods += IAstModification.InsertLast(returnStmt, subroutine)
&& subroutine.statements.last() !is Subroutine)) {
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
}
// precede a subroutine with a return to avoid falling through into the subroutine from code above it
@@ -341,7 +342,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
val modifications = mutableListOf<IAstModification>()
val statement = expr.containingStatement
val dt = expr.indexer.indexExpr.inferType(program)
val register = if(dt.istype(DataType.UBYTE) || dt.istype(DataType.BYTE)) "r9L" else "r9"
val register = if(dt istype DataType.UBYTE || dt istype DataType.BYTE ) "r9L" else "r9"
// replace the indexer with just the variable (simply use a cx16 virtual register r9, that we HOPE is not used for other things in the expression...)
// assign the indexing expression to the helper variable, but only if that hasn't been done already
val target = AssignTarget(IdentifierReference(listOf("cx16", register), expr.indexer.position), null, null, expr.indexer.position)
+35 -23
View File
@@ -17,7 +17,9 @@ import prog8.compiler.target.Cx16Target
import prog8.compiler.target.ICompilationTarget
import prog8.compiler.target.asmGeneratorFor
import prog8.optimizer.*
import prog8.parser.ParseError
import prog8.parser.ParsingFailedError
import prog8.parser.SourceCode
import prog8.parser.SourceCode.Companion.libraryFilePrefix
import java.io.File
import java.nio.file.Path
@@ -70,7 +72,7 @@ fun compileProgram(filepath: Path,
writeAssembly: Boolean,
slowCodegenWarnings: Boolean,
compilationTarget: String,
libdirs: List<String>,
sourceDirs: List<String>,
outputDir: Path): CompilationResult {
var programName = ""
lateinit var programAst: Program
@@ -87,21 +89,27 @@ fun compileProgram(filepath: Path,
try {
val totalTime = measureTimeMillis {
// import main module and everything it needs
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget, libdirs)
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget, sourceDirs)
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
compilationOptions.optimize = optimize
programAst = ast
importedFiles = imported
processAst(programAst, errors, compilationOptions)
if (compilationOptions.optimize)
optimizeAst(programAst, errors, BuiltinFunctionsFacade(BuiltinFunctions), compTarget, compilationOptions)
optimizeAst(
programAst,
errors,
BuiltinFunctionsFacade(BuiltinFunctions),
compTarget,
compilationOptions
)
postprocessAst(programAst, errors, compilationOptions)
// printAst(programAst)
if(writeAssembly) {
if (writeAssembly) {
val result = writeAssembly(programAst, errors, outputDir, compilationOptions)
when(result) {
when (result) {
is WriteAssemblyResult.Ok -> programName = result.filename
is WriteAssemblyResult.Fail -> {
System.err.println(result.error)
@@ -114,10 +122,13 @@ fun compileProgram(filepath: Path,
System.err.flush()
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
return CompilationResult(true, programAst, programName, compTarget, importedFiles)
} catch (px: ParsingFailedError) {
} catch (px: ParseError) {
System.err.print("\u001b[91m") // bright red
System.err.println(px.message)
System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim())
System.err.print("\u001b[0m") // reset
} catch (pfx: ParsingFailedError) {
System.err.print("\u001b[91m") // bright red
System.err.println(pfx.message)
System.err.print("\u001b[0m") // reset
} catch (ax: AstException) {
System.err.print("\u001b[91m") // bright red
@@ -172,23 +183,22 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
}
fun parseImports(filepath: Path,
errors: IErrorReporter,
compTarget: ICompilationTarget,
libdirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
errors: IErrorReporter,
compTarget: ICompilationTarget,
sourceDirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
println("Compiler target: ${compTarget.name}. Parsing...")
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
val programAst = Program(filepath.nameWithoutExtension, bf, compTarget)
bf.program = programAst
val importer = ModuleImporter(programAst, compTarget.name, errors, libdirs)
val importer = ModuleImporter(programAst, compTarget.name, errors, sourceDirs)
val importedModuleResult = importer.importModule(filepath)
importedModuleResult.onFailure { throw it }
errors.report()
val importedFiles = programAst.modules
.mapNotNull { it.source }
.filter { !it.isFromResources } // TODO: parseImports/importedFiles - maybe rather `source.isFromFilesystem`?
.map { Path(it.pathString()) }
val importedFiles = programAst.modules.map { it.source }
.filter { it.isFromFilesystem }
.map { Path(it.origin) }
val compilerOptions = determineCompilationOptions(programAst, compTarget)
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
@@ -205,12 +215,12 @@ fun parseImports(filepath: Path,
}
fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
val mainModule = program.mainModule
val outputDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive)
val launcherDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive)
val toplevelModule = program.toplevelModule
val outputDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive)
val launcherDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive)
val outputTypeStr = outputDirective?.args?.single()?.name?.uppercase()
val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase()
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
as? Directive)?.args?.single()?.name?.uppercase()
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
.flatMap { (it as Directive).args }.toSet()
@@ -232,7 +242,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
zpType = ZeropageType.BASICSAFE
}
val zpReserved = mainModule.statements
val zpReserved = toplevelModule.statements
.asSequence()
.filter { it is Directive && it.directive == "%zpreserved" }
.map { (it as Directive).args }
@@ -270,6 +280,8 @@ private fun processAst(programAst: Program, errors: IErrorReporter, compilerOpti
programAst.checkIdentifiers(errors, compilerOptions)
errors.report()
// TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR
// NOTE: we will then lose the opportunity to do constant-folding on any expression containing a char literal, but how often will those occur?
// Also they might be optimized away eventually in codegen or by the assembler even
programAst.charLiteralsToUByteLiterals(errors, compilerOptions.compTarget)
errors.report()
programAst.constantFold(errors, compilerOptions.compTarget)
@@ -365,7 +377,7 @@ fun printAst(programAst: Program) {
println()
}
internal fun loadAsmIncludeFile(filename: String, sourcePath: Path): Result<String, NoSuchFileException> {
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
return if (filename.startsWith(libraryFilePrefix)) {
return runCatching {
val stream = object {}.javaClass.getResourceAsStream("/prog8lib/${filename.substring(libraryFilePrefix.length)}") // TODO handle via SourceCode
@@ -373,7 +385,7 @@ internal fun loadAsmIncludeFile(filename: String, sourcePath: Path): Result<Stri
}.mapError { NoSuchFileException(File(filename)) }
} else {
// first try in the isSameAs folder as where the containing file was imported from
val sib = sourcePath.resolveSibling(filename)
val sib = Path(source.origin).resolveSibling(filename)
if (sib.toFile().isFile)
Ok(sib.toFile().readText())
+63 -49
View File
@@ -1,8 +1,6 @@
package prog8.compiler
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.*
import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.Position
@@ -11,6 +9,7 @@ import prog8.ast.statements.Directive
import prog8.ast.statements.DirectiveArg
import prog8.parser.Prog8Parser
import prog8.parser.SourceCode
import java.io.File
import java.nio.file.Path
import kotlin.io.path.*
@@ -18,13 +17,13 @@ import kotlin.io.path.*
class ModuleImporter(private val program: Program,
private val compilationTargetName: String,
val errors: IErrorReporter,
libdirs: List<String>) {
sourceDirs: List<String>) {
private val libpaths: List<Path> = libdirs.map { Path(it) }
private val sourcePaths: List<Path> = sourceDirs.map { Path(it) }
fun importModule(filePath: Path): Result<Module, NoSuchFileException> {
val currentDir = Path("").absolute()
val searchIn = listOf(currentDir) + libpaths
val searchIn = listOf(currentDir) + sourcePaths
val candidates = searchIn
.map { it.absolute().div(filePath).normalize().absolute() }
.filter { it.exists() }
@@ -36,13 +35,17 @@ class ModuleImporter(private val program: Program,
file = filePath.normalize().toFile(),
reason = "searched in $searchIn"))
1 -> candidates.first()
else -> candidates.first() // TODO: report error if more than 1 candidate?
else -> candidates.first() // when more candiates, pick the one from the first location
}
val logMsg = "importing '${filePath.nameWithoutExtension}' (from file $srcPath)"
println(logMsg)
return Ok(importModule(SourceCode.fromPath(srcPath)))
val module = importModule(SourceCode.File(srcPath))
return if(module==null)
Err(NoSuchFileException(srcPath.toFile()))
else
Ok(module)
}
fun importLibraryModule(name: String): Module? {
@@ -52,94 +55,105 @@ class ModuleImporter(private val program: Program,
return executeImportDirective(import, null)
}
//private fun importModule(stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
private fun importModule(src: SourceCode) : Module {
private fun importModule(src: SourceCode) : Module? {
val moduleAst = Prog8Parser.parseModule(src)
program.addModule(moduleAst)
// accept additional imports
val lines = moduleAst.statements.toMutableList()
lines.asSequence()
try {
val lines = moduleAst.statements.toMutableList()
lines.asSequence()
.mapIndexed { i, it -> i to it }
.filter { (it.second as? Directive)?.directive == "%import" }
.forEach { executeImportDirective(it.second as Directive, moduleAst) }
moduleAst.statements = lines
return moduleAst
moduleAst.statements = lines
return moduleAst
} catch (x: Exception) {
// in case of error, make sure the module we're importing is no longer in the Ast
program.removeModule(moduleAst)
throw x
}
}
private fun executeImportDirective(import: Directive, importingModule: Module?): Module? {
if(import.directive!="%import" || import.args.size!=1 || import.args[0].name==null)
if(import.directive!="%import" || import.args.size!=1)
throw SyntaxError("invalid import directive", import.position)
if(!import.args[0].str.isNullOrEmpty() || import.args[0].name==null)
throw SyntaxError("%import requires unquoted module name", import.position)
val moduleName = import.args[0].name!!
if("$moduleName.p8" == import.position.file)
throw SyntaxError("cannot import self", import.position)
val existing = program.modules.singleOrNull { it.name == moduleName }
if (existing!=null)
return null // TODO: why return null instead of Module instance?
return existing
var srcCode = tryGetModuleFromResource("$moduleName.p8", compilationTargetName)
// try internal library first
val moduleResourceSrc = getModuleFromResource("$moduleName.p8", compilationTargetName)
val importedModule =
if (srcCode != null) {
println("importing '$moduleName' (from internal ${srcCode.origin})")
importModule(srcCode)
} else {
srcCode = tryGetModuleFromFile(moduleName, importingModule)
if (srcCode == null) {
errors.err("imported file not found: $moduleName.p8", import.position)
return null
//throw NoSuchFileException(File("$moduleName.p8"))
moduleResourceSrc.fold(
success = {
println("importing '$moduleName' (from internal ${it.origin})")
importModule(it)
},
failure = {
// try filesystem next
val moduleSrc = getModuleFromFile(moduleName, importingModule)
moduleSrc.fold(
success = {
println("importing '$moduleName' (from file ${it.origin})")
importModule(it)
},
failure = {
errors.err("no module found with name $moduleName", import.position)
return null
}
)
}
importModule(srcCode)
}
)
removeDirectivesFromImportedModule(importedModule)
if(importedModule!=null)
removeDirectivesFromImportedModule(importedModule)
return importedModule
}
private fun removeDirectivesFromImportedModule(importedModule: Module) {
// Most global directives don't apply for imported modules, so remove them
val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%target")
val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
var directives = importedModule.statements.filterIsInstance<Directive>()
importedModule.statements.removeAll(directives)
directives = directives.filter{ it.directive !in moduleLevelDirectives }
importedModule.statements.addAll(0, directives)
}
private fun tryGetModuleFromResource(name: String, compilationTargetName: String): SourceCode? {
// try target speficic first
try {
return SourceCode.fromResources("/prog8lib/$compilationTargetName/$name")
} catch (e: FileSystemException) {
}
try {
return SourceCode.fromResources("/prog8lib/$name")
} catch (e: FileSystemException) {
}
return null
private fun getModuleFromResource(name: String, compilationTargetName: String): Result<SourceCode, NoSuchFileException> {
val result =
runCatching { SourceCode.Resource("/prog8lib/$compilationTargetName/$name") }
.orElse { runCatching { SourceCode.Resource("/prog8lib/$name") } }
return result.mapError { NoSuchFileException(File(name)) }
}
private fun tryGetModuleFromFile(name: String, importingModule: Module?): SourceCode? {
private fun getModuleFromFile(name: String, importingModule: Module?): Result<SourceCode, NoSuchFileException> {
val fileName = "$name.p8"
val locations =
if (importingModule == null) { // <=> imported from library module
libpaths
sourcePaths
} else {
libpaths.drop(1) + // TODO: why drop the first?
// FIXME: won't work until Prog8Parser is fixed s.t. it fully initialzes the modules it returns
val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0
sourcePaths.drop(dropCurDir) +
// TODO: won't work until Prog8Parser is fixed s.t. it fully initializes the modules it returns. // hm, what won't work?)
listOf(Path(importingModule.position.file).parent ?: Path("")) +
listOf(Path(".", "prog8lib"))
}
locations.forEach {
try {
return SourceCode.fromPath(it.resolve(fileName))
return Ok(SourceCode.File(it.resolve(fileName)))
} catch (e: NoSuchFileException) {
}
}
//throw ParsingFailedError("$position Import: no module source file '$fileName' found (I've looked in: embedded libs and $locations)")
return null
return Err(NoSuchFileException(File("name")))
}
}
@@ -44,8 +44,8 @@ internal class AstChecker(private val program: Program,
}
if(compilerOptions.floats) {
if (compilerOptions.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
errors.err("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'", program.mainModule.position)
if (compilerOptions.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
errors.err("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'", program.toplevelModule.position)
}
super.visit(program)
@@ -152,11 +152,11 @@ internal class AstChecker(private val program: Program,
val to = range.to as? NumericLiteralValue
if(from != null)
checkValueTypeAndRange(loopvar.datatype, from)
else if(!range.from.inferType(program).istype(loopvar.datatype))
else if(range.from.inferType(program) isnot loopvar.datatype)
errors.err("range start value is incompatible with loop variable type", range.position)
if(to != null)
checkValueTypeAndRange(loopvar.datatype, to)
else if(!range.to.inferType(program).istype(loopvar.datatype))
else if(range.to.inferType(program) isnot loopvar.datatype)
errors.err("range end value is incompatible with loop variable type", range.position)
}
}
@@ -283,11 +283,11 @@ internal class AstChecker(private val program: Program,
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)
err("number of return registers is not the isSameAs as number of return values")
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
if(param.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if(param.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE)
err("parameter '${param.first.name}' should be (u)byte")
}
else if(param.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
&& param.first.type != DataType.STR && param.first.type !in ArrayDatatypes && param.first.type != DataType.FLOAT)
err("parameter '${param.first.name}' should be (u)word/address")
@@ -298,11 +298,11 @@ internal class AstChecker(private val program: Program,
}
}
subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).forEachIndexed { index, pair ->
if(pair.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if(pair.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
if (pair.first != DataType.UBYTE && pair.first != DataType.BYTE)
err("return value #${index + 1} should be (u)byte")
}
else if(pair.second.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
else if(pair.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
if (pair.first != DataType.UWORD && pair.first != DataType.WORD
&& pair.first != DataType.STR && pair.first !in ArrayDatatypes && pair.first != DataType.FLOAT)
err("return value #${index + 1} should be (u)word/address")
@@ -428,12 +428,12 @@ internal class AstChecker(private val program: Program,
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
if(targetDt.isIterable)
errors.err("cannot assign value to string or array", assignment.value.position)
else if(!(valueDt.istype(DataType.STR) && targetDt.istype(DataType.UWORD)))
else if(!(valueDt istype DataType.STR && targetDt istype DataType.UWORD))
errors.err("type of value doesn't match target", assignment.value.position)
}
if(assignment.value is TypecastExpression) {
if(assignment.isAugmentable && targetDt.istype(DataType.FLOAT))
if(assignment.isAugmentable && targetDt istype DataType.FLOAT)
errors.err("typecasting a float value in-place makes no sense", assignment.value.position)
}
@@ -518,7 +518,7 @@ internal class AstChecker(private val program: Program,
}
// FLOATS enabled?
if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!= VarDeclType.MEMORY)
if(!compilerOptions.floats && decl.datatype.oneOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!= VarDeclType.MEMORY)
err("floating point used, but that is not enabled via options")
if(decl.datatype == DataType.FLOAT && (decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE || decl.zeropage==ZeropageWish.PREFER_ZEROPAGE))
@@ -596,7 +596,7 @@ internal class AstChecker(private val program: Program,
val declValue = decl.value
if(declValue!=null && decl.type==VarDeclType.VAR) {
if (!declValue.inferType(program).istype(decl.datatype)) {
if (declValue.inferType(program) isnot decl.datatype) {
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
}
}
@@ -714,7 +714,7 @@ internal class AstChecker(private val program: Program,
err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty())
err("missing option directive argument(s)")
else if(directive.args.map{it.name in setOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page")}.any { !it })
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "align_word", "align_page")}.any { !it })
err("invalid option directive argument(s)")
}
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
@@ -726,25 +726,21 @@ internal class AstChecker(private val program: Program,
if (File(filename).isFile)
return
var definingModule = directive.parent // TODO: why not just use directive.definingModule() here?
while (definingModule !is Module)
definingModule = definingModule.parent
if (definingModule.isLibrary)
val definingModule = directive.definingModule
if (definingModule.isLibrary || !definingModule.source.isFromFilesystem)
return
val s = definingModule.source?.pathString()
if (s != null) {
val sourceFileCandidate = Path(s).resolveSibling(filename).toFile()
if (sourceFileCandidate.isFile)
return
}
errors.err("included file not found: $filename", directive.position)
val s = definingModule.source.origin
val sourceFileCandidate = Path(s).resolveSibling(filename).toFile()
if (sourceFileCandidate.isFile)
return
else
errors.err("included file not found: $filename", directive.position)
}
override fun visit(array: ArrayLiteralValue) {
if(array.type.isKnown) {
if (!compilerOptions.floats && array.type.getOr(DataType.UNDEFINED) in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
if (!compilerOptions.floats && array.type.oneOf(DataType.FLOAT, DataType.ARRAY_F)) {
errors.err("floating point used, but that is not enabled via options", array.position)
}
val arrayspec = ArrayIndex.forArray(array)
@@ -919,7 +915,7 @@ internal class AstChecker(private val program: Program,
// warn about sgn(unsigned) this is likely a mistake
if(functionCall.target.nameInSource.last()=="sgn") {
val sgnArgType = functionCall.args.first().inferType(program)
if(sgnArgType.istype(DataType.UBYTE) || sgnArgType.istype(DataType.UWORD))
if(sgnArgType istype DataType.UBYTE || sgnArgType istype DataType.UWORD)
errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCall.args.first().position)
}
@@ -988,12 +984,12 @@ internal class AstChecker(private val program: Program,
if(functionCallStatement.target.nameInSource.last() == "sort") {
// sort is not supported on float arrays
val idref = functionCallStatement.args.singleOrNull() as? IdentifierReference
if(idref!=null && idref.inferType(program).istype(DataType.ARRAY_F)) {
if(idref!=null && idref.inferType(program) istype DataType.ARRAY_F) {
errors.err("sorting a floating point array is not supported", functionCallStatement.args.first().position)
}
}
if(functionCallStatement.target.nameInSource.last() in setOf("rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
if(functionCallStatement.target.nameInSource.last() in arrayOf("rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
// in-place modification, can't be done on literals
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
@@ -1122,7 +1118,7 @@ internal class AstChecker(private val program: Program,
// check index value 0..255
val dtxNum = arrayIndexedExpression.indexer.indexExpr.inferType(program)
if(!dtxNum.istype(DataType.UBYTE) && !dtxNum.istype(DataType.BYTE))
if(dtxNum isnot DataType.UBYTE && dtxNum isnot DataType.BYTE)
errors.err("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)
super.visit(arrayIndexedExpression)
@@ -1160,7 +1156,7 @@ internal class AstChecker(private val program: Program,
when {
constvalue == null -> errors.err("choice value must be a constant", whenChoice.position)
constvalue.type !in IntegerDatatypes -> errors.err("choice value must be a byte or word", whenChoice.position)
constvalue.type != conditionType.getOr(DataType.UNDEFINED) -> errors.err("choice value datatype differs from condition value", whenChoice.position)
conditionType isnot constvalue.type -> errors.err("choice value datatype differs from condition value", whenChoice.position)
}
}
} else {
@@ -1204,7 +1200,7 @@ internal class AstChecker(private val program: Program,
DataType.STR -> return err("string value expected")
DataType.ARRAY_UB, DataType.ARRAY_B -> {
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
if(value.type.istype(targetDt)) {
if(value.type istype targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySpecSize = arrayspec.constIndex()
@@ -1223,7 +1219,7 @@ internal class AstChecker(private val program: Program,
}
DataType.ARRAY_UW, DataType.ARRAY_W -> {
// value may be either a single word, or a word arraysize, or a range
if(value.type.istype(targetDt)) {
if(value.type istype targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySpecSize = arrayspec.constIndex()
@@ -1242,7 +1238,7 @@ internal class AstChecker(private val program: Program,
}
DataType.ARRAY_F -> {
// value may be either a single float, or a float arraysize
if(value.type.istype(targetDt)) {
if(value.type istype targetDt) {
if(!checkArrayValues(value, targetDt))
return false
val arraySize = value.value.size
@@ -3,11 +3,9 @@ package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.CharLiteral
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.RangeExpr
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.*
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Directive
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
@@ -16,6 +14,7 @@ import prog8.compiler.CompilationOptions
import prog8.compiler.IErrorReporter
import prog8.compiler.IStringEncoding
import prog8.compiler.target.ICompilationTarget
import prog8.compiler.target.IMachineDefinition
import kotlin.math.abs
@@ -127,26 +126,6 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
lit2decl.visit(this)
lit2decl.applyModifications()
}
// Check if each module has a unique name.
// If not report those that haven't.
// TODO: move check for unique module names to earlier stage and/or to unit tests
val namesToModules = mapOf<String, MutableList<prog8.ast.Module>>().toMutableMap()
for (m in modules) {
val others = namesToModules[m.name]
if (others == null) {
namesToModules[m.name] = listOf(m).toMutableList()
} else {
others.add(m)
}
}
val nonUniqueNames = namesToModules.keys
.map { Pair(it, namesToModules[it]!!.size) }
.filter { it.second > 1 }
.map { "\"${it.first}\" (x${it.second})"}
if (nonUniqueNames.isNotEmpty()) {
throw FatalAstException("modules must have unique names; of the ttl ${modules.size} these have not: $nonUniqueNames")
}
}
internal fun Program.variousCleanups(program: Program, errors: IErrorReporter) {
@@ -186,3 +165,47 @@ internal fun Program.moveMainAndStartToFirst() {
modules[0].statements.add(0, directive)
}
}
internal fun AssignTarget.isInRegularRAMof(machine: IMachineDefinition): Boolean {
val memAddr = memoryAddress
val arrayIdx = arrayindexed
val ident = identifier
when {
memAddr != null -> {
return when (memAddr.addressExpression) {
is NumericLiteralValue -> {
machine.isRegularRAMaddress((memAddr.addressExpression as NumericLiteralValue).number.toInt())
}
is IdentifierReference -> {
val program = definingModule.program
val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(program)
if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue)
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
false
}
else -> false
}
}
arrayIdx != null -> {
val program = definingModule.program
val targetStmt = arrayIdx.arrayvar.targetVarDecl(program)
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteralValue
if (addr != null)
machine.isRegularRAMaddress(addr.number.toInt())
else
false
} else true
}
ident != null -> {
val program = definingModule.program
val decl = ident.targetVarDecl(program)!!
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue)
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
true
}
else -> return true
}
}
@@ -29,7 +29,7 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
if(vardecl!=null) {
// adjust the datatype of the array (to an educated guess)
val arrayDt = array.type
if(!arrayDt.istype(vardecl.datatype)) {
if(arrayDt isnot vardecl.datatype) {
val cast = array.cast(vardecl.datatype)
if (cast != null && cast !== array)
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
@@ -111,7 +111,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
}
}
is VarDecl -> {
if(!leftDt.istype(parent.datatype)) {
if(leftDt isnot parent.datatype) {
val cast = TypecastExpression(expr.left, parent.datatype, true, parent.position)
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
}
@@ -22,7 +22,7 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
val declValue = decl.value
if(decl.type==VarDeclType.VAR && declValue!=null) {
val valueDt = declValue.inferType(program)
if(!valueDt.istype(decl.datatype)) {
if(valueDt isnot decl.datatype) {
// don't add a typecast on an array initializer value
if(valueDt.isInteger && decl.datatype in ArrayDatatypes)
@@ -182,7 +182,7 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
// warn about any implicit type casts to Float, because that may not be intended
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
if(typecast.implicit && typecast.type.oneOf(DataType.FLOAT, DataType.ARRAY_F)) {
errors.warn("integer implicitly converted to float. Suggestion: use float literals, add an explicit cast, or revert to integer arithmetic", typecast.position)
}
return noModifications
@@ -217,7 +217,7 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
val subroutine = returnStmt.definingSubroutine!!
if(subroutine.returntypes.size==1) {
val subReturnType = subroutine.returntypes.first()
if (returnValue.inferType(program).istype(subReturnType))
if (returnValue.inferType(program) istype subReturnType)
return noModifications
if (returnValue is NumericLiteralValue) {
val cast = returnValue.cast(subroutine.returntypes.single())
@@ -71,7 +71,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter)
}
val sourceDt = typecast.expression.inferType(program)
if(sourceDt.istype(typecast.type))
if(sourceDt istype typecast.type)
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
return noModifications
@@ -9,7 +9,7 @@ import prog8.compiler.CompilerException
import kotlin.math.*
class FParam(val name: String, val possibleDatatypes: Set<DataType>)
class FParam(val name: String, val possibleDatatypes: Array<DataType>)
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program, memsizer: IMemSizer) -> NumericLiteralValue
@@ -90,10 +90,10 @@ class FSignature(val name: String,
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
private val functionSignatures: List<FSignature> = listOf(
// this set of function have no return value and operate in-place:
FSignature("rol" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("ror" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("rol2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("ror2" , false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("rol" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("ror" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("rol2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("ror2" , false, listOf(FParam("item", arrayOf(DataType.UBYTE, DataType.UWORD))), null),
FSignature("sort" , false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("reverse" , false, listOf(FParam("array", ArrayDatatypes)), null),
FSignature("cmp" , false, listOf(FParam("value1", IntegerDatatypes), FParam("value2", NumericDatatypes)), null),
@@ -103,46 +103,46 @@ private val functionSignatures: List<FSignature> = listOf(
FSignature("sum" , true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
FSignature("abs" , true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
FSignature("len" , true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
FSignature("sizeof" , true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
FSignature("sizeof" , true, listOf(FParam("object", DataType.values())), DataType.UBYTE, ::builtinSizeof),
// normal functions follow:
FSignature("sgn" , true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
FSignature("sin" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sin) },
FSignature("sin8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
FSignature("sin8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
FSignature("sin16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
FSignature("sin16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
FSignature("cos" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::cos) },
FSignature("cos8" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
FSignature("cos8u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
FSignature("cos16" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
FSignature("cos16u" , true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
FSignature("tan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::tan) },
FSignature("atan" , true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::atan) },
FSignature("ln" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::log) },
FSignature("log2" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, ::log2) },
FSignature("sqrt16" , true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
FSignature("sqrt" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sqrt) },
FSignature("rad" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toRadians) },
FSignature("deg" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toDegrees) },
FSignature("round" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
FSignature("floor" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
FSignature("ceil" , true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
FSignature("sin" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sin) },
FSignature("sin8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
FSignature("sin8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
FSignature("sin16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
FSignature("sin16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
FSignature("cos" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::cos) },
FSignature("cos8" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
FSignature("cos8u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
FSignature("cos16" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
FSignature("cos16u" , true, listOf(FParam("angle8", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
FSignature("tan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::tan) },
FSignature("atan" , true, listOf(FParam("rads", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::atan) },
FSignature("ln" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::log) },
FSignature("log2" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, ::log2) },
FSignature("sqrt16" , true, listOf(FParam("value", arrayOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
FSignature("sqrt" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::sqrt) },
FSignature("rad" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toRadians) },
FSignature("deg" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArg(a, p, prg, Math::toDegrees) },
FSignature("round" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
FSignature("floor" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
FSignature("ceil" , true, listOf(FParam("value", arrayOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg, ct -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
FSignature("any" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinAny) },
FSignature("all" , true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg, ct -> collectionArg(a, p, prg, ::builtinAll) },
FSignature("lsb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 } },
FSignature("msb" , true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255} },
FSignature("mkword" , true, listOf(FParam("msb", setOf(DataType.UBYTE)), FParam("lsb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
FSignature("peek" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UBYTE),
FSignature("peekw" , true, listOf(FParam("address", setOf(DataType.UWORD))), DataType.UWORD),
FSignature("poke" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UBYTE))), null),
FSignature("pokew" , false, listOf(FParam("address", setOf(DataType.UWORD)), FParam("value", setOf(DataType.UWORD))), null),
FSignature("lsb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 } },
FSignature("msb" , true, listOf(FParam("value", arrayOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg, ct -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255} },
FSignature("mkword" , true, listOf(FParam("msb", arrayOf(DataType.UBYTE)), FParam("lsb", arrayOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
FSignature("peek" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UBYTE),
FSignature("peekw" , true, listOf(FParam("address", arrayOf(DataType.UWORD))), DataType.UWORD),
FSignature("poke" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UBYTE))), null),
FSignature("pokew" , false, listOf(FParam("address", arrayOf(DataType.UWORD)), FParam("value", arrayOf(DataType.UWORD))), null),
FSignature("rnd" , false, emptyList(), DataType.UBYTE),
FSignature("rndw" , false, emptyList(), DataType.UWORD),
FSignature("rndf" , false, emptyList(), DataType.FLOAT),
FSignature("memory" , true, listOf(FParam("name", setOf(DataType.STR)), FParam("size", setOf(DataType.UWORD))), DataType.UWORD),
FSignature("memory" , true, listOf(FParam("name", arrayOf(DataType.STR)), FParam("size", arrayOf(DataType.UWORD))), DataType.UWORD),
FSignature("swap" , false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
FSignature("callfar" , false, listOf(FParam("bank", setOf(DataType.UBYTE)), FParam("address", setOf(DataType.UWORD)), FParam("arg", setOf(DataType.UWORD))), null),
FSignature("callrom" , false, listOf(FParam("bank", setOf(DataType.UBYTE)), FParam("address", setOf(DataType.UWORD)), FParam("arg", setOf(DataType.UWORD))), null),
FSignature("callfar" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
FSignature("callrom" , false, listOf(FParam("bank", arrayOf(DataType.UBYTE)), FParam("address", arrayOf(DataType.UWORD)), FParam("arg", arrayOf(DataType.UWORD))), null),
)
@@ -305,7 +305,7 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
val elementDt = ArrayToElementTypes.getValue(dt.getOr(DataType.UNDEFINED))
numericLiteral(memsizer.memorySize(elementDt) * length, position)
}
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
dt istype DataType.STR -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
else -> NumericLiteralValue(DataType.UBYTE, memsizer.memorySize(dt.getOr(DataType.UNDEFINED)), position)
}
} else {
@@ -1,5 +1,6 @@
package prog8.compiler.target
import com.github.michaelbull.result.fold
import prog8.ast.IMemSizer
import prog8.ast.Program
import prog8.ast.base.*
@@ -23,49 +24,6 @@ interface ICompilationTarget: IStringEncoding, IMemSizer {
val machine: IMachineDefinition
override fun encodeString(str: String, altEncoding: Boolean): List<Short>
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
// TODO: rename param target, and also AST node AssignTarget - *different meaning of "target"!*
// TODO: remove param program - can be obtained from AST node
fun isInRegularRAM(target: AssignTarget, program: Program): Boolean {
val memAddr = target.memoryAddress
val arrayIdx = target.arrayindexed
val ident = target.identifier
when {
memAddr != null -> {
return when (memAddr.addressExpression) {
is NumericLiteralValue -> {
machine.isRegularRAMaddress((memAddr.addressExpression as NumericLiteralValue).number.toInt())
}
is IdentifierReference -> {
val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(program)
if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue)
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
false
}
else -> false
}
}
arrayIdx != null -> {
val targetStmt = arrayIdx.arrayvar.targetVarDecl(program)
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteralValue
if (addr != null)
machine.isRegularRAMaddress(addr.number.toInt())
else
false
} else true
}
ident != null -> {
val decl = ident.targetVarDecl(program)!!
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue)
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
else
true
}
else -> return true
}
}
}
@@ -75,8 +33,8 @@ internal object C64Target: ICompilationTarget {
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
val coded = if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
return coded.fold(
{ throw it },
{ it }
failure = { throw it },
success = { it }
)
}
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
@@ -103,8 +61,8 @@ internal object Cx16Target: ICompilationTarget {
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
val coded= if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
return coded.fold(
{ throw it },
{ it}
failure = { throw it },
success = { it }
)
}
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
@@ -86,7 +86,7 @@ internal object C64MachineDefinition: IMachineDefinition {
init {
if (options.floats && options.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
if (options.floats && options.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
if (options.zeropage == ZeropageType.FULL) {
File diff suppressed because it is too large. Load diff
@@ -14,6 +14,7 @@ import prog8.compiler.target.cbm.AssemblyProgram
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
import prog8.optimizer.CallGraph
import prog8.parser.SourceCode
import java.nio.file.Path
import java.time.LocalDate
import java.time.LocalDateTime
@@ -154,7 +155,7 @@ internal class AsmGen(private val program: Program,
}
}
if(options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
if(options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
out("""
; zeropage is clobbered so we need to reset the machine at exit
lda #>sys.reset_system
@@ -1325,8 +1326,9 @@ $repeatLabel lda $counterVar
"%asminclude" -> {
// TODO: handle %asminclude with SourceCode
val includedName = stmt.args[0].str!!
val sourcePath = Path(stmt.definingModule.source!!.pathString()) // FIXME: %asminclude inside non-library, non-filesystem module
loadAsmIncludeFile(includedName, sourcePath).fold(
if(stmt.definingModule.source is SourceCode.Generated)
TODO("%asminclude inside non-library, non-filesystem module")
loadAsmIncludeFile(includedName, stmt.definingModule.source).fold(
success = { assemblyLines.add(it.trimEnd().trimStart('\n')) },
failure = { errors.err(it.toString(), stmt.position) }
)
@@ -1335,11 +1337,13 @@ $repeatLabel lda $counterVar
val includedName = stmt.args[0].str!!
val offset = if(stmt.args.size>1) ", ${stmt.args[1].int}" else ""
val length = if(stmt.args.size>2) ", ${stmt.args[2].int}" else ""
val sourcePath = Path(stmt.definingModule.source!!.pathString()) // FIXME: %asmbinary inside non-library, non-filesystem module
if(stmt.definingModule.source is SourceCode.Generated)
TODO("%asmbinary inside non-library, non-filesystem module")
val sourcePath = Path(stmt.definingModule.source.origin)
val includedPath = sourcePath.resolveSibling(includedName)
val pathForAssembler = outputDir // #54: 64tass needs the path *relative to the .asm file*
.absolute() // avoid IllegalArgumentExc due to non-absolute path .relativize(absolute path)
.relativize(includedPath)
.toAbsolutePath()
.relativize(includedPath.toAbsolutePath())
.normalize() // avoid assembler warnings (-Wportable; only some, not all)
.toString().replace('\\', '/')
out(" .binary \"$pathForAssembler\" $offset $length")
@@ -1459,11 +1463,11 @@ $label nop""")
if(pointerOffsetExpr is BinaryExpression && pointerOffsetExpr.operator=="+") {
val leftDt = pointerOffsetExpr.left.inferType(program)
val rightDt = pointerOffsetExpr.left.inferType(program)
if(leftDt.istype(DataType.UWORD) && rightDt.istype(DataType.UBYTE))
if(leftDt istype DataType.UWORD && rightDt istype DataType.UBYTE)
return Pair(pointerOffsetExpr.left, pointerOffsetExpr.right)
if(leftDt.istype(DataType.UBYTE) && rightDt.istype(DataType.UWORD))
if(leftDt istype DataType.UBYTE && rightDt istype DataType.UWORD)
return Pair(pointerOffsetExpr.right, pointerOffsetExpr.left)
if(leftDt.istype(DataType.UWORD) && rightDt.istype(DataType.UWORD)) {
if(leftDt istype DataType.UWORD && rightDt istype DataType.UWORD) {
// could be that the index was a constant numeric byte but converted to word, check that
val constIdx = pointerOffsetExpr.right.constValue(program)
if(constIdx!=null && constIdx.number.toInt()>=0 && constIdx.number.toInt()<=255) {
@@ -1471,10 +1475,10 @@ $label nop""")
}
// could be that the index was typecasted into uword, check that
val rightTc = pointerOffsetExpr.right as? TypecastExpression
if(rightTc!=null && rightTc.expression.inferType(program).istype(DataType.UBYTE))
if(rightTc!=null && rightTc.expression.inferType(program) istype DataType.UBYTE)
return Pair(pointerOffsetExpr.left, rightTc.expression)
val leftTc = pointerOffsetExpr.left as? TypecastExpression
if(leftTc!=null && leftTc.expression.inferType(program).istype(DataType.UBYTE))
if(leftTc!=null && leftTc.expression.inferType(program) istype DataType.UBYTE)
return Pair(pointerOffsetExpr.right, leftTc.expression)
}
@@ -1487,7 +1491,7 @@ $label nop""")
fun evalBytevalueWillClobberA(expr: Expression): Boolean {
val dt = expr.inferType(program)
if(!dt.istype(DataType.UBYTE) && !dt.istype(DataType.BYTE))
if(dt isnot DataType.UBYTE && dt isnot DataType.BYTE)
return true
return when(expr) {
is IdentifierReference -> false
@@ -715,11 +715,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val firstName = asmgen.asmVariableName(first)
val secondName = asmgen.asmVariableName(second)
val dt = first.inferType(program)
if(dt.istype(DataType.BYTE) || dt.istype(DataType.UBYTE)) {
if(dt istype DataType.BYTE || dt istype DataType.UBYTE) {
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | sty $secondName")
return
}
if(dt.istype(DataType.WORD) || dt.istype(DataType.UWORD)) {
if(dt istype DataType.WORD || dt istype DataType.UWORD) {
asmgen.out("""
ldy $firstName
lda $secondName
@@ -732,7 +732,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
""")
return
}
if(dt.istype(DataType.FLOAT)) {
if(dt istype DataType.FLOAT) {
asmgen.out("""
lda #<$firstName
sta P8ZP_SCRATCH_W1
@@ -157,11 +157,11 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
argForCarry = argi
}
argi.value.second.statusflag != null -> throw AssemblyError("can only use Carry as status flag parameter")
argi.value.second.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) -> {
argi.value.second.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) -> {
require(argForXregister==null)
argForXregister = argi
}
argi.value.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.AY) -> {
argi.value.second.registerOrPair in arrayOf(RegisterOrPair.A, RegisterOrPair.AY) -> {
require(argForAregister == null)
argForAregister = argi
}
@@ -205,7 +205,7 @@ internal class AsmAssignment(val source: AsmAssignSource,
val position: Position) {
init {
if(target.register !in setOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
if(target.register !in arrayOf(RegisterOrPair.XY, RegisterOrPair.AX, RegisterOrPair.AY))
require(source.datatype != DataType.UNDEFINED) { "must not be placeholder/undefined datatype" }
require(memsizer.memorySize(source.datatype) <= memsizer.memorySize(target.datatype)) {
"source storage size must be less or equal to target datatype storage size"
@@ -97,7 +97,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
init {
if (options.floats && options.zeropage !in setOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
if (options.floats && options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
throw CompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
@@ -9,6 +9,7 @@ import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.astprocessing.isInRegularRAMof
import prog8.compiler.target.ICompilationTarget
@@ -54,7 +55,7 @@ X = BinExpr X = LeftExpr
*/
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target, program)) {
if(binExpr.operator in augmentAssignmentOperators && isSimpleTarget(assignment.target)) {
if(assignment.target isSameAs binExpr.left || assignment.target isSameAs binExpr.right)
return noModifications
@@ -77,9 +78,9 @@ X = BinExpr X = LeftExpr
return noModifications
}
private fun isSimpleTarget(target: AssignTarget, program: Program) =
private fun isSimpleTarget(target: AssignTarget) =
if (target.identifier!=null || target.memoryAddress!=null)
compTarget.isInRegularRAM(target, program)
target.isInRegularRAMof(compTarget.machine)
else
false
@@ -137,7 +137,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
}
}
if(expr.inferType(program).istype(DataType.FLOAT)) {
if(expr.inferType(program) istype DataType.FLOAT) {
val subExpr: BinaryExpression? = when {
leftconst != null -> expr.right as? BinaryExpression
rightconst != null -> expr.left as? BinaryExpression
@@ -277,7 +277,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
val numval = decl.value as? NumericLiteralValue
if(decl.type== VarDeclType.CONST && numval!=null) {
val valueDt = numval.inferType(program)
if(!valueDt.istype(decl.datatype)) {
if(valueDt isnot decl.datatype) {
val cast = numval.cast(decl.datatype)
if(cast.isValid)
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
@@ -21,7 +21,7 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
try {
val declConstValue = decl.value?.constValue(program)
if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST)
&& !declConstValue.inferType(program).istype(decl.datatype)) {
&& declConstValue.inferType(program) isnot decl.datatype) {
// cast the numeric literal to the appropriate datatype of the variable
val cast = declConstValue.cast(decl.datatype)
if(cast.isValid)
@@ -45,7 +45,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
}
} else {
if (typecast.expression.inferType(program).istype(typecast.type)) {
if (typecast.expression.inferType(program) istype typecast.type) {
// remove duplicate cast
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
}
@@ -289,13 +289,13 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
val arg = functionCall.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
if (valueDt.istype(DataType.BYTE) || valueDt.istype(DataType.UBYTE)) {
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
// useless lsb() of byte value that was typecasted to word
return listOf(IAstModification.ReplaceNode(functionCall, arg.expression, parent))
}
} else {
val argDt = arg.inferType(program)
if (argDt.istype(DataType.BYTE) || argDt.istype(DataType.UBYTE)) {
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
// useless lsb() of byte value
return listOf(IAstModification.ReplaceNode(functionCall, arg, parent))
}
@@ -305,7 +305,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
val arg = functionCall.args[0]
if(arg is TypecastExpression) {
val valueDt = arg.expression.inferType(program)
if (valueDt.istype(DataType.BYTE) || valueDt.istype(DataType.UBYTE)) {
if (valueDt istype DataType.BYTE || valueDt istype DataType.UBYTE) {
// useless msb() of byte value that was typecasted to word, replace with 0
return listOf(IAstModification.ReplaceNode(
functionCall,
@@ -314,7 +314,7 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
}
} else {
val argDt = arg.inferType(program)
if (argDt.istype(DataType.BYTE) || argDt.istype(DataType.UBYTE)) {
if (argDt istype DataType.BYTE || argDt istype DataType.UBYTE) {
// useless msb() of byte value, replace with 0
return listOf(IAstModification.ReplaceNode(
functionCall,
@@ -10,6 +10,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.IErrorReporter
import prog8.compiler.astprocessing.isInRegularRAMof
import prog8.compiler.target.ICompilationTarget
@@ -80,20 +81,20 @@ internal class UnusedCodeRemover(private val program: Program,
val forceOutput = "force_output" in subroutine.definingBlock.options()
if (subroutine !== program.entrypoint && !forceOutput && !subroutine.inline && !subroutine.isAsmSubroutine) {
if(callgraph.unused(subroutine)) {
if(subroutine.containsNoCodeNorVars) {
if(!subroutine.definingModule.isLibrary)
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
val removals = mutableListOf(IAstModification.Remove(subroutine, subroutine.definingScope))
callgraph.calledBy[subroutine]?.let {
for(node in it)
removals.add(IAstModification.Remove(node, node.definingScope))
}
return removals
}
if(!subroutine.definingModule.isLibrary)
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope))
}
if(subroutine.containsNoCodeNorVars) {
if(!subroutine.definingModule.isLibrary)
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
val removals = mutableListOf(IAstModification.Remove(subroutine, subroutine.definingScope))
callgraph.calledBy[subroutine]?.let {
for(node in it)
removals.add(IAstModification.Remove(node, node.definingScope))
}
return removals
}
}
val removeDoubleAssignments = deduplicateAssignments(subroutine.statements)
@@ -122,7 +123,7 @@ internal class UnusedCodeRemover(private val program: Program,
val assign1 = stmtPairs[0] as? Assignment
val assign2 = stmtPairs[1] as? Assignment
if (assign1 != null && assign2 != null && !assign2.isAugmentable) {
if (assign1.target.isSameAs(assign2.target, program) && compTarget.isInRegularRAM(assign1.target, program)) {
if (assign1.target.isSameAs(assign2.target, program) && assign1.target.isInRegularRAMof(compTarget.machine)) {
if(assign2.target.identifier==null || !assign2.value.referencesIdentifier(*(assign2.target.identifier!!.nameInSource.toTypedArray())))
// only remove the second assignment if its value is a simple expression!
when(assign2.value) {
+4 -3
View File
@@ -15,6 +15,7 @@ import prog8.compiler.*
import prog8.compiler.target.C64Target
import prog8.compiler.target.c64.C64MachineDefinition
import prog8.compiler.target.cpu6502.codegen.AsmGen
import prog8.parser.SourceCode
import prog8tests.helpers.DummyFunctions
import prog8tests.helpers.DummyMemsizer
import java.nio.file.Path
@@ -67,10 +68,10 @@ locallabel:
val varInBlock = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, Position.DUMMY)
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
val module = Module("test", mutableListOf(block), Position.DUMMY, null)
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module)
module.linkParents(program.namespace)
module.linkIntoProgram(program)
return program
}
@@ -78,7 +79,7 @@ locallabel:
val errors = ErrorReporter()
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target)
val zp = C64MachineDefinition.C64Zeropage(options)
val asmgen = AsmGen(program, errors, zp, options, C64Target, Path.of("."))
val asmgen = AsmGen(program, errors, zp, options, C64Target, Path.of(""))
return asmgen
}
+13 -10
View File
@@ -12,9 +12,11 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.Disabled
import prog8.ast.Program
import prog8.ast.internedStringsModuleName
import prog8.compiler.IErrorReporter
import prog8.compiler.ModuleImporter
import prog8.parser.ParseError
import prog8.parser.SourceCode
import prog8tests.helpers.*
import kotlin.io.path.*
@@ -191,7 +193,7 @@ class TestModuleImporter {
repeat(2) { n ->
assertFailsWith<ParseError>(count[n] + " call") { act() }.let {
assertThat(it.position.file, equalTo(srcPath.absolutePathString()))
assertThat(it.position.file, equalTo(SourceCode.relative(srcPath).toString()))
assertThat("line; should be 1-based", it.position.line, equalTo(2))
assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6))
assertThat("endCol; should be 0-based", it.position.endCol, equalTo(6))
@@ -206,7 +208,6 @@ class TestModuleImporter {
}
@Test
@Disabled("TODO: module that imports faulty module should not be kept in Program.modules")
fun testImportingFileWithSyntaxError_twice() {
doTestImportingFileWithSyntaxError(2)
}
@@ -221,12 +222,13 @@ class TestModuleImporter {
repeat(repetitions) { n ->
assertFailsWith<ParseError>(count[n] + " call") { act() }.let {
assertThat(it.position.file, equalTo(imported.absolutePathString()))
assertThat(it.position.file, equalTo(SourceCode.relative(imported).toString()))
assertThat("line; should be 1-based", it.position.line, equalTo(2))
assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6))
assertThat("endCol; should be 0-based", it.position.endCol, equalTo(6))
}
assertThat(program.modules.size, equalTo(2))
assertThat("imported module with error in it should not be present", program.modules.size, equalTo(1))
assertThat(program.modules[0].name, equalTo(internedStringsModuleName))
}
}
}
@@ -250,14 +252,14 @@ class TestModuleImporter {
val result = importer.importLibraryModule(filenameNoExt)
assertThat(count[n] + " call / NO .p8 extension", result, Is(nullValue()))
assertFalse(errors.noErrors(), count[n] + " call / NO .p8 extension")
assertEquals(errors.errors.single(), "imported file not found: i_do_not_exist.p8")
assertEquals(errors.errors.single(), "no module found with name i_do_not_exist")
errors.report()
assertThat(program.modules.size, equalTo(1))
val result2 = importer.importLibraryModule(filenameWithExt)
assertThat(count[n] + " call / with .p8 extension", result2, Is(nullValue()))
assertFalse(importer.errors.noErrors(), count[n] + " call / with .p8 extension")
assertEquals(errors.errors.single(), "imported file not found: i_do_not_exist.p8.p8") // TODO don't duplicate the p8 extension in the import logic...
assertEquals(errors.errors.single(), "no module found with name i_do_not_exist.p8")
errors.report()
assertThat(program.modules.size, equalTo(1))
}
@@ -277,7 +279,7 @@ class TestModuleImporter {
repeat(2) { n ->
assertFailsWith<ParseError>(count[n] + " call")
{ importer.importLibraryModule(srcPath.nameWithoutExtension) }.let {
assertThat(it.position.file, equalTo(srcPath.absolutePathString()))
assertThat(it.position.file, equalTo(SourceCode.relative(srcPath).toString()))
assertThat("line; should be 1-based", it.position.line, equalTo(2))
assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6))
assertThat("endCol; should be 0-based", it.position.endCol, equalTo(6))
@@ -297,12 +299,14 @@ class TestModuleImporter {
repeat(repetitions) { n ->
assertFailsWith<ParseError>(count[n] + " call") { act() }.let {
assertThat(it.position.file, equalTo(imported.normalize().absolutePathString()))
assertThat(it.position.file, equalTo(SourceCode.relative(imported).toString()))
assertThat("line; should be 1-based", it.position.line, equalTo(2))
assertThat("startCol; should be 0-based", it.position.startCol, equalTo(6))
assertThat("endCol; should be 0-based", it.position.endCol, equalTo(6))
}
assertThat(program.modules.size, equalTo(2))
assertThat("imported module with error in it should not be present", program.modules.size, equalTo(1))
assertThat(program.modules[0].name, equalTo(internedStringsModuleName))
importer.errors.report()
}
}
@@ -312,7 +316,6 @@ class TestModuleImporter {
}
@Test
@Disabled("TODO: module that imports faulty module should not be kept in Program.modules")
fun testImportingFileWithSyntaxError_twice() {
doTestImportingFileWithSyntaxError(2)
}
+91
View File
@@ -0,0 +1,91 @@
package prog8tests
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine
import prog8.compiler.target.C64Target
import prog8.optimizer.CallGraph
import prog8tests.helpers.assertSuccess
import prog8tests.helpers.compileText
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCallgraph {
@Test
fun testGraphForEmptySubs() {
val sourcecode = """
%import string
main {
sub start() {
}
sub empty() {
}
}
"""
val result = compileText(C64Target, false, sourcecode).assertSuccess()
val graph = CallGraph(result.programAst)
assertEquals(1, graph.imports.size)
assertEquals(1, graph.importedBy.size)
val toplevelModule = result.programAst.toplevelModule
val importedModule = graph.imports.getValue(toplevelModule).single()
assertEquals("string", importedModule.name)
val importedBy = graph.importedBy.getValue(importedModule).single()
assertTrue(importedBy.name.startsWith("on_the_fly_test"))
assertFalse(graph.unused(toplevelModule))
assertFalse(graph.unused(importedModule))
val mainBlock = toplevelModule.statements.filterIsInstance<Block>().single()
for(stmt in mainBlock.statements) {
val sub = stmt as Subroutine
assertFalse(sub in graph.calls)
assertFalse(sub in graph.calledBy)
if(sub === result.programAst.entrypoint)
assertFalse(graph.unused(sub), "start() should always be marked as used to avoid having it removed")
else
assertTrue(graph.unused(sub))
}
}
@Test
fun testGraphForEmptyButReferencedSub() {
val sourcecode = """
%import string
main {
sub start() {
uword xx = &empty
xx++
}
sub empty() {
}
}
"""
val result = compileText(C64Target, false, sourcecode).assertSuccess()
val graph = CallGraph(result.programAst)
assertEquals(1, graph.imports.size)
assertEquals(1, graph.importedBy.size)
val toplevelModule = result.programAst.toplevelModule
val importedModule = graph.imports.getValue(toplevelModule).single()
assertEquals("string", importedModule.name)
val importedBy = graph.importedBy.getValue(importedModule).single()
assertTrue(importedBy.name.startsWith("on_the_fly_test"))
assertFalse(graph.unused(toplevelModule))
assertFalse(graph.unused(importedModule))
val mainBlock = toplevelModule.statements.filterIsInstance<Block>().single()
val startSub = mainBlock.statements.filterIsInstance<Subroutine>().single{it.name=="start"}
val emptySub = mainBlock.statements.filterIsInstance<Subroutine>().single{it.name=="empty"}
assertTrue(startSub in graph.calls, "start 'calls' (references) empty")
assertFalse(emptySub in graph.calls, "empty doesn't call anything")
assertTrue(emptySub in graph.calledBy, "empty gets 'called'")
assertFalse(startSub in graph.calledBy, "start doesn't get called (except as entrypoint ofc.)")
}
}
+4 -4
View File
@@ -42,7 +42,7 @@ class TestCompilerOnCharLit {
"char literal should have been replaced by ubyte literal")
val arg = funCall.args[0] as NumericLiteralValue
assertEquals(DataType.UBYTE, arg.type)
assertEquals(platform.encodeString("\n", false)[0], arg.number.toShort()) // TODO: short/int/UBYTE - which should it be?
assertEquals(platform.encodeString("\n", false)[0], arg.number.toShort())
}
@Test
@@ -77,7 +77,7 @@ class TestCompilerOnCharLit {
"char literal should have been replaced by ubyte literal")
val initializerValue = decl.value as NumericLiteralValue
assertEquals(DataType.UBYTE, initializerValue.type)
assertEquals(platform.encodeString("\n", false)[0], initializerValue.number.toShort()) // TODO: short/int/UBYTE - which should it be?
assertEquals(platform.encodeString("\n", false)[0], initializerValue.number.toShort())
}
@Test
@@ -105,12 +105,12 @@ class TestCompilerOnCharLit {
assertEquals(DataType.UBYTE, decl.datatype)
assertEquals(
platform.encodeString("\n", false)[0],
(decl.value as NumericLiteralValue).number.toShort()) // TODO: short/int/UBYTE - which should it be?
(decl.value as NumericLiteralValue).number.toShort())
}
is NumericLiteralValue -> {
assertEquals(
platform.encodeString("\n", false)[0],
arg.number.toShort()) // TODO: short/int/UBYTE - which should it be?
arg.number.toShort())
}
else -> assertIs<IdentifierReference>(funCall.args[0]) // make test fail
}
+6 -5
View File
@@ -1,5 +1,6 @@
package prog8tests
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DynamicTest
import org.junit.jupiter.api.DynamicTest.dynamicTest
import org.junit.jupiter.api.TestFactory
@@ -18,7 +19,7 @@ import kotlin.io.path.exists
* They are not really unit tests, but rather tests of the whole process,
* from source file loading all the way through to running 64tass.
*/
//@Disabled("to save some time")
// @Disabled("disable to save some time")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCompilerOnExamples {
private val examplesDir = assumeDirectory(workingDir, "../examples")
@@ -41,14 +42,14 @@ class TestCompilerOnExamples {
writeAssembly = true,
slowCodegenWarnings = false,
compilationTarget = platform.name,
libdirs = listOf(),
sourceDirs = listOf(),
outputDir
).assertSuccess("; $displayName")
}
}
@TestFactory
// @Disabled
// @Disabled("disable to save some time")
fun bothCx16AndC64() = mapCombinations(
dim1 = listOf(
"animals",
@@ -79,7 +80,7 @@ class TestCompilerOnExamples {
)
@TestFactory
// @Disabled
// @Disabled("disable to save some time")
fun onlyC64() = mapCombinations(
dim1 = listOf(
"balloonflight",
@@ -98,7 +99,7 @@ class TestCompilerOnExamples {
)
@TestFactory
// @Disabled
// @Disabled("disable to save some time")
fun onlyCx16() = mapCombinations(
dim1 = listOf(
"vtui/testvtui",
@@ -46,29 +46,6 @@ class TestCompilerOnImportsAndIncludes {
assertEquals("main", strLits[0].definingScope.name)
assertEquals("foo", strLits[1].definingScope.name)
}
@Test
@Disabled("TODO: why would we not accept string literals as argument to %import?")
fun testImportFromSameFolder_strLit() {
val filepath = assumeReadableFile(fixturesDir,"importFromSameFolder_strLit.p8")
val imported = assumeReadableFile(fixturesDir, "foo_bar.p8")
val platform = Cx16Target
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
.assertSuccess()
val program = result.programAst
val startSub = program.entrypoint
val strLits = startSub.statements
.filterIsInstance<FunctionCallStatement>()
.map { it.args[0] as IdentifierReference }
.map { it.targetVarDecl(program)!!.value as StringLiteralValue }
assertEquals("main.bar", strLits[0].value)
assertEquals("foo.bar", strLits[1].value)
assertEquals("main", strLits[0].definingScope.name)
assertEquals("foo", strLits[1].definingScope.name)
}
}
@Nested
+7 -7
View File
@@ -19,7 +19,7 @@ import kotlin.io.path.writeText
* from source file loading all the way through to running 64tass.
*/
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCompilerOptionLibdirs {
class TestCompilerOptionSourcedirs {
private lateinit var tempFileInWorkingDir: Path
@@ -39,14 +39,14 @@ class TestCompilerOptionLibdirs {
tempFileInWorkingDir.deleteExisting()
}
private fun compileFile(filePath: Path, libdirs: List<String>) =
private fun compileFile(filePath: Path, sourceDirs: List<String>) =
compileProgram(
filepath = filePath,
optimize = false,
writeAssembly = true,
slowCodegenWarnings = false,
compilationTarget = Cx16Target.name,
libdirs,
sourceDirs,
outputDir
)
@@ -65,7 +65,7 @@ class TestCompilerOptionLibdirs {
}
@Test
fun testFilePathInWorkingDirRelativeTo1stInLibdirs() {
fun testFilePathInWorkingDirRelativeTo1stInSourcedirs() {
val filepath = assumeReadableFile(tempFileInWorkingDir)
compileFile(filepath.fileName, listOf(workingDir.toString()))
.assertSuccess()
@@ -86,10 +86,10 @@ class TestCompilerOptionLibdirs {
}
@Test
fun testFilePathOutsideWorkingDirRelativeTo1stInLibdirs() {
fun testFilePathOutsideWorkingDirRelativeTo1stInSourcedirs() {
val filepath = assumeReadableFile(fixturesDir, "simple_main.p8")
val libdirs = listOf("$fixturesDir")
compileFile(filepath.fileName, libdirs)
val sourcedirs = listOf("$fixturesDir")
compileFile(filepath.fileName, sourcedirs)
.assertSuccess()
}
@@ -19,7 +19,7 @@ import kotlin.test.assertTrue
class TestImportedModulesOrderAndOptions {
@Test
fun testImportedModuleOrderCorrect() {
fun testImportedModuleOrderAndMainModuleCorrect() {
val result = compileText(C64Target, false, """
%import textio
%import floats
@@ -30,7 +30,7 @@ main {
}
}
""").assertSuccess()
assertTrue(result.programAst.mainModule.name.startsWith("on_the_fly_test"))
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
val moduleNames = result.programAst.modules.map { it.name }
assertTrue(moduleNames[0].startsWith("on_the_fly_test"), "main module must be first")
@@ -43,6 +43,8 @@ main {
"math",
"prog8_lib"
), moduleNames.drop(1), "module order in parse tree")
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
}
@Test
@@ -59,7 +61,7 @@ main {
}
}
""").assertSuccess()
assertTrue(result.programAst.mainModule.name.startsWith("on_the_fly_test"))
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
val options = determineCompilationOptions(result.programAst, C64Target)
assertTrue(options.floats)
assertEquals(ZeropageType.DONTUSE, options.zeropage)
@@ -86,7 +88,7 @@ main {
filepath.toFile().writeText(sourceText)
val (program, options, importedfiles) = parseImports(filepath, errors, C64Target, emptyList())
assertEquals(filenameBase, program.mainModule.name)
assertEquals(filenameBase, program.toplevelModule.name)
assertEquals(1, importedfiles.size, "all imports other than the test source must have been internal resources library files")
assertEquals(listOf(
internedStringsModuleName,
+37 -39
View File
@@ -5,7 +5,6 @@ import org.junit.jupiter.api.TestInstance
import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.ParentSentinel
import prog8.ast.base.Position
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.ArrayIndexedExpression
@@ -13,7 +12,9 @@ import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.*
import prog8.compiler.astprocessing.isInRegularRAMof
import prog8.compiler.target.C64Target
import prog8.parser.SourceCode
import prog8tests.helpers.DummyFunctions
import prog8tests.helpers.DummyMemsizer
import kotlin.test.assertFalse
@@ -28,24 +29,23 @@ class TestMemory {
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer)
assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAMof(C64Target.machine))
}
@Test
@@ -53,20 +53,19 @@ class TestMemory {
var memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer)
assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xafff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAMof(C64Target.machine))
memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAMof(C64Target.machine))
}
@Test
@@ -74,15 +73,15 @@ class TestMemory {
val program = Program("test", DummyFunctions, DummyMemsizer)
var target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.VAR)
assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAMof(C64Target.machine))
target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.VAR)
assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAMof(C64Target.machine))
target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.CONST)
assertTrue(C64Target.isInRegularRAM(target, program))
assertTrue(target.isInRegularRAMof(C64Target.machine))
target = createTestProgramForMemoryRefViaVar(program, 0xd020, VarDeclType.CONST)
assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAMof(C64Target.machine))
target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.MEMORY)
assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAMof(C64Target.machine))
}
private fun createTestProgramForMemoryRefViaVar(program: Program, address: Int, vartype: VarDeclType): AssignTarget {
@@ -91,8 +90,8 @@ class TestMemory {
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, null)
module.linkParents(program.namespace)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
module.linkIntoProgram(program)
return target
}
@@ -100,8 +99,7 @@ class TestMemory {
fun testInValidRamC64_memory_expression() {
val memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer)
assertFalse(C64Target.isInRegularRAM(target, program))
assertFalse(target.isInRegularRAMof(C64Target.machine))
}
@Test
@@ -110,11 +108,11 @@ class TestMemory {
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, null)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module)
module.linkParents(program.namespace)
assertTrue(C64Target.isInRegularRAM(target, program))
module.linkIntoProgram(program)
assertTrue(target.isInRegularRAMof(C64Target.machine))
}
@Test
@@ -124,11 +122,11 @@ class TestMemory {
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, null)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module)
module.linkParents(program.namespace)
assertTrue(C64Target.isInRegularRAM(target, program))
module.linkIntoProgram(program)
assertTrue(target.isInRegularRAMof(C64Target.machine))
}
@Test
@@ -138,11 +136,11 @@ class TestMemory {
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, null)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module)
module.linkParents(program.namespace)
assertFalse(C64Target.isInRegularRAM(target, program))
module.linkIntoProgram(program)
assertFalse(target.isInRegularRAMof(C64Target.machine))
}
@Test
@@ -152,11 +150,11 @@ class TestMemory {
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, null)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module)
module.linkParents(program.namespace)
assertTrue(C64Target.isInRegularRAM(target, program))
module.linkIntoProgram(program)
assertTrue(target.isInRegularRAMof(C64Target.machine))
}
@Test
@@ -167,11 +165,11 @@ class TestMemory {
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, null)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module)
module.linkParents(program.namespace)
assertTrue(C64Target.isInRegularRAM(target, program))
module.linkIntoProgram(program)
assertTrue(target.isInRegularRAMof(C64Target.machine))
}
@Test
@@ -182,10 +180,10 @@ class TestMemory {
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module("test", mutableListOf(subroutine), Position.DUMMY, null)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
val program = Program("test", DummyFunctions, DummyMemsizer)
.addModule(module)
module.linkParents(program.namespace)
assertFalse(C64Target.isInRegularRAM(target, program))
module.linkIntoProgram(program)
assertFalse(target.isInRegularRAMof(C64Target.machine))
}
}
+62
View File
@@ -0,0 +1,62 @@
package prog8tests
import org.hamcrest.CoreMatchers.instanceOf
import org.hamcrest.MatcherAssert.assertThat
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.statements.Block
import prog8.ast.statements.Return
import prog8.ast.statements.Subroutine
import prog8.compiler.target.C64Target
import prog8tests.helpers.assertSuccess
import prog8tests.helpers.compileText
import kotlin.test.assertEquals
import kotlin.test.assertSame
import kotlin.test.assertTrue
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestOptimization {
@Test
fun testRemoveEmptySubroutineExceptStart() {
val sourcecode = """
main {
sub start() {
}
sub empty() {
; going to be removed
}
}
"""
val result = compileText(C64Target, true, sourcecode).assertSuccess()
val toplevelModule = result.programAst.toplevelModule
val mainBlock = toplevelModule.statements.single() as Block
val startSub = mainBlock.statements.single() as Subroutine
assertSame(result.programAst.entrypoint, startSub)
assertEquals("start", startSub.name, "only start sub should remain")
assertTrue(startSub.statements.single() is Return, "compiler has inserted return in empty subroutines")
}
@Test
fun testDontRemoveEmptySubroutineIfItsReferenced() {
val sourcecode = """
main {
sub start() {
uword xx = &empty
xx++
}
sub empty() {
; should not be removed
}
}
"""
val result = compileText(C64Target, true, sourcecode).assertSuccess()
val toplevelModule = result.programAst.toplevelModule
val mainBlock = toplevelModule.statements.single() as Block
val startSub = mainBlock.statements[0] as Subroutine
val emptySub = mainBlock.statements[1] as Subroutine
assertSame(result.programAst.entrypoint, startSub)
assertEquals("start", startSub.name)
assertEquals("empty", emptySub.name)
assertTrue(emptySub.statements.single() is Return, "compiler has inserted return in empty subroutines")
}
}
+23 -24
View File
@@ -1,5 +1,6 @@
package prog8tests
import com.github.michaelbull.result.Ok
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test
@@ -9,8 +10,6 @@ import prog8.ast.base.Position
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.StringLiteralValue
import prog8.compiler.target.cbm.Petscii
import prog8.left
import prog8.right
import kotlin.test.*
@@ -19,8 +18,8 @@ class TestPetscii {
@Test
fun testZero() {
assertThat(Petscii.encodePetscii("\u0000", true), equalTo(right(listOf<Short>(0))))
assertThat(Petscii.encodePetscii("\u0000", false), equalTo(right(listOf<Short>(0))))
assertThat(Petscii.encodePetscii("\u0000", true), equalTo(Ok(listOf<Short>(0))))
assertThat(Petscii.encodePetscii("\u0000", false), equalTo(Ok(listOf<Short>(0))))
assertThat(Petscii.decodePetscii(listOf(0), true), equalTo("\u0000"))
assertThat(Petscii.decodePetscii(listOf(0), false), equalTo("\u0000"))
}
@@ -28,11 +27,11 @@ class TestPetscii {
@Test
fun testLowercase() {
assertThat(Petscii.encodePetscii("hello WORLD 123 @!£", true), equalTo(
right(listOf<Short>(72, 69, 76, 76, 79, 32, 0xd7, 0xcf, 0xd2, 0xcc, 0xc4, 32, 49, 50, 51, 32, 64, 33, 0x5c))))
assertThat(Petscii.encodePetscii("\uf11a", true), equalTo(right(listOf<Short>(0x12)))) // reverse vid
assertThat(Petscii.encodePetscii("✓", true), equalTo(right(listOf<Short>(0xfa))))
assertThat("expect lowercase error fallback", Petscii.encodePetscii("π", true), equalTo(right(listOf<Short>(255))))
assertThat("expect lowercase error fallback", Petscii.encodePetscii("♥", true), equalTo(right(listOf<Short>(0xd3))))
Ok(listOf<Short>(72, 69, 76, 76, 79, 32, 0xd7, 0xcf, 0xd2, 0xcc, 0xc4, 32, 49, 50, 51, 32, 64, 33, 0x5c))))
assertThat(Petscii.encodePetscii("\uf11a", true), equalTo(Ok(listOf<Short>(0x12)))) // reverse vid
assertThat(Petscii.encodePetscii("✓", true), equalTo(Ok(listOf<Short>(0xfa))))
assertThat("expect lowercase error fallback", Petscii.encodePetscii("π", true), equalTo(Ok(listOf<Short>(255))))
assertThat("expect lowercase error fallback", Petscii.encodePetscii("♥", true), equalTo(Ok(listOf<Short>(0xd3))))
assertThat(Petscii.decodePetscii(listOf(72, 0xd7, 0x5c, 0xfa, 0x12), true), equalTo("hW£✓\uF11A"))
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(-1), true) }
@@ -42,11 +41,11 @@ class TestPetscii {
@Test
fun testUppercase() {
assertThat(Petscii.encodePetscii("HELLO 123 @!£"), equalTo(
right(listOf<Short>(72, 69, 76, 76, 79, 32, 49, 50, 51, 32, 64, 33, 0x5c))))
assertThat(Petscii.encodePetscii("\uf11a"), equalTo(right(listOf<Short>(0x12)))) // reverse vid
assertThat(Petscii.encodePetscii("♥"), equalTo(right(listOf<Short>(0xd3))))
assertThat(Petscii.encodePetscii("π"), equalTo(right(listOf<Short>(0xff))))
assertThat("expecting fallback", Petscii.encodePetscii("✓"), equalTo(right(listOf<Short>(250))))
Ok(listOf<Short>(72, 69, 76, 76, 79, 32, 49, 50, 51, 32, 64, 33, 0x5c))))
assertThat(Petscii.encodePetscii("\uf11a"), equalTo(Ok(listOf<Short>(0x12)))) // reverse vid
assertThat(Petscii.encodePetscii("♥"), equalTo(Ok(listOf<Short>(0xd3))))
assertThat(Petscii.encodePetscii("π"), equalTo(Ok(listOf<Short>(0xff))))
assertThat("expecting fallback", Petscii.encodePetscii("✓"), equalTo(Ok(listOf<Short>(250))))
assertThat(Petscii.decodePetscii(listOf(72, 0x5c, 0xd3, 0xff)), equalTo("H£♥π"))
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(-1)) }
@@ -56,11 +55,11 @@ class TestPetscii {
@Test
fun testScreencodeLowercase() {
assertThat(Petscii.encodeScreencode("hello WORLD 123 @!£", true), equalTo(
right(listOf<Short>(0x08, 0x05, 0x0c, 0x0c, 0x0f, 0x20, 0x57, 0x4f, 0x52, 0x4c, 0x44, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c))
Ok(listOf<Short>(0x08, 0x05, 0x0c, 0x0c, 0x0f, 0x20, 0x57, 0x4f, 0x52, 0x4c, 0x44, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c))
))
assertThat(Petscii.encodeScreencode("✓", true), equalTo(right(listOf<Short>(0x7a))))
assertThat("expect fallback", Petscii.encodeScreencode("♥", true), equalTo(right(listOf<Short>(83))))
assertThat("expect fallback", Petscii.encodeScreencode("π", true), equalTo(right(listOf<Short>(94))))
assertThat(Petscii.encodeScreencode("✓", true), equalTo(Ok(listOf<Short>(0x7a))))
assertThat("expect fallback", Petscii.encodeScreencode("♥", true), equalTo(Ok(listOf<Short>(83))))
assertThat("expect fallback", Petscii.encodeScreencode("π", true), equalTo(Ok(listOf<Short>(94))))
assertThat(Petscii.decodeScreencode(listOf(0x08, 0x57, 0x1c, 0x7a), true), equalTo("hW£✓"))
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1), true) }
@@ -70,12 +69,12 @@ class TestPetscii {
@Test
fun testScreencodeUppercase() {
assertThat(Petscii.encodeScreencode("WORLD 123 @!£"), equalTo(
right(listOf<Short>(0x17, 0x0f, 0x12, 0x0c, 0x04, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c))))
assertThat(Petscii.encodeScreencode("♥"), equalTo(right(listOf<Short>(0x53))))
assertThat(Petscii.encodeScreencode("π"), equalTo(right(listOf<Short>(0x5e))))
assertThat(Petscii.encodeScreencode("HELLO"), equalTo(right(listOf<Short>(8, 5, 12, 12, 15))))
assertThat("expecting fallback", Petscii.encodeScreencode("hello"), equalTo(right(listOf<Short>(8, 5, 12, 12, 15))))
assertThat("expecting fallback", Petscii.encodeScreencode("✓"), equalTo(right(listOf<Short>(122))))
Ok(listOf<Short>(0x17, 0x0f, 0x12, 0x0c, 0x04, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c))))
assertThat(Petscii.encodeScreencode("♥"), equalTo(Ok(listOf<Short>(0x53))))
assertThat(Petscii.encodeScreencode("π"), equalTo(Ok(listOf<Short>(0x5e))))
assertThat(Petscii.encodeScreencode("HELLO"), equalTo(Ok(listOf<Short>(8, 5, 12, 12, 15))))
assertThat("expecting fallback", Petscii.encodeScreencode("hello"), equalTo(Ok(listOf<Short>(8, 5, 12, 12, 15))))
assertThat("expecting fallback", Petscii.encodeScreencode("✓"), equalTo(Ok(listOf<Short>(122))))
assertThat(Petscii.decodeScreencode(listOf(0x17, 0x1c, 0x53, 0x5e)), equalTo("W£♥π"))
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1)) }
+1 -1
View File
@@ -2,7 +2,7 @@
main {
str myBar = "main.bar"
;foo_bar:
; %asminclude "foo_bar.asm" ; FIXME: should be accessible from inside start() but give assembler error. See github issue #62
; %asminclude "foo_bar.asm" ; TODO: should be accessible from inside start() but give assembler error. See github issue #62
sub start() {
txt.print(myBar)
txt.print(&foo_bar)
-9
View File
@@ -1,9 +0,0 @@
%import textio
%import "foo_bar.p8"
main {
str myBar = "main.bar"
sub start() {
txt.print(myBar)
txt.print(foo.bar)
}
}
+1 -1
View File
@@ -38,7 +38,7 @@ internal fun compileFile(
writeAssembly = true,
slowCodegenWarnings = false,
platform.name,
libdirs = listOf(),
sourceDirs = listOf(),
outputDir
)
}
+1 -1
View File
@@ -304,7 +304,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
override fun visit(assignment: Assignment) {
val binExpr = assignment.value as? BinaryExpression
if(binExpr!=null && binExpr.left isSameAs assignment.target
&& binExpr.operator !in setOf("and", "or", "xor")
&& binExpr.operator !in arrayOf("and", "or", "xor")
&& binExpr.operator !in comparisonOperators) {
// we only support the inplace assignments of the form A = A <operator> <value>
assignment.target.accept(this)
+20 -11
View File
@@ -246,7 +246,7 @@ class Program(val name: String,
init {
// insert a container module for all interned strings later
val internedStringsModule = Module(internedStringsModuleName, mutableListOf(), Position.DUMMY, null)
val internedStringsModule = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(internedStringsModuleName))
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
internedStringsModule.statements.add(block)
@@ -259,11 +259,12 @@ class Program(val name: String,
require(null == _modules.firstOrNull { it.name == module.name })
{ "module '${module.name}' already present" }
_modules.add(module)
module.linkParents(namespace)
module.program = this
module.linkIntoProgram(this)
return this
}
fun removeModule(module: Module) = _modules.remove(module)
fun moveModuleToFront(module: Module): Program {
require(_modules.contains(module))
{ "Not a module of this program: '${module.name}'"}
@@ -285,11 +286,11 @@ class Program(val name: String,
}
}
val mainModule: Module // TODO: rename Program.mainModule - it's NOT necessarily the one containing the main *block*!
val toplevelModule: Module
get() = modules.first { it.name!=internedStringsModuleName }
val definedLoadAddress: Int
get() = mainModule.loadAddress
get() = toplevelModule.loadAddress
var actualLoadAddress: Int = 0
private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>()
@@ -342,19 +343,22 @@ class Program(val name: String,
require(node is Module && replacement is Module)
val idx = _modules.indexOfFirst { it===node }
_modules[idx] = replacement
replacement.parent = this // TODO: why not replacement.program = this; replacement.linkParents(namespace)?!
replacement.linkIntoProgram(this)
}
}
open class Module(override val name: String,
override var statements: MutableList<Statement>,
open class Module(final override var statements: MutableList<Statement>,
final override val position: Position,
val source: SourceCode?) : Node, INameScope {
val source: SourceCode) : Node, INameScope {
override lateinit var parent: Node
lateinit var program: Program
override val name = source.origin
.substringBeforeLast(".")
.substringAfterLast("/")
.substringAfterLast("\\")
val loadAddress: Int by lazy {
val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0
address
@@ -366,6 +370,11 @@ open class Module(override val name: String,
statements.forEach {it.linkParents(this)}
}
fun linkIntoProgram(program: Program) {
this.program = program
linkParents(program.namespace)
}
override val definingScope: INameScope
get() = program.namespace
override fun replaceChildNode(node: Node, replacement: Node) {
@@ -380,7 +389,7 @@ open class Module(override val name: String,
fun accept(visitor: IAstVisitor) = visitor.visit(this)
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
val isLibrary get() = (source == null) || source.isFromResources
val isLibrary get() = source.isFromResources
}
+14 -11
View File
@@ -6,6 +6,9 @@ import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.parser.Prog8ANTLRParser
import prog8.parser.SourceCode
import java.nio.file.Path
import kotlin.io.path.isRegularFile
/***************** Antlr Extension methods to create AST ****************/
@@ -14,17 +17,17 @@ private data class NumericLiteral(val number: Number, val datatype: DataType)
private fun ParserRuleContext.toPosition() : Position {
/*
val customTokensource = this.start.tokenSource as? CustomLexer
val filename =
when {
customTokensource!=null -> customTokensource.modulePath.toString()
start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
else -> File(start.inputStream.sourceName).name
}
*/
val filename = start.inputStream.sourceName
val pathString = start.inputStream.sourceName
val filename = if(SourceCode.isRegularFilesystemPath(pathString)) {
val path = Path.of(pathString)
if(path.isRegularFile()) {
SourceCode.relative(path).toString()
} else {
path.toString()
}
} else {
pathString
}
// note: beware of TAB characters in the source text, they count as 1 column...
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
}
+13 -12
View File
@@ -24,16 +24,17 @@ enum class DataType {
*/
infix fun isAssignableTo(targetType: DataType) =
when(this) {
UBYTE -> targetType in setOf(UBYTE, WORD, UWORD, FLOAT)
BYTE -> targetType in setOf(BYTE, WORD, FLOAT)
UWORD -> targetType in setOf(UWORD, FLOAT)
WORD -> targetType in setOf(WORD, FLOAT)
UBYTE -> targetType.oneOf(UBYTE, WORD, UWORD, FLOAT)
BYTE -> targetType.oneOf(BYTE, WORD, FLOAT)
UWORD -> targetType.oneOf(UWORD, FLOAT)
WORD -> targetType.oneOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT
STR -> targetType == STR
in ArrayDatatypes -> targetType == this
else -> false
}
fun oneOf(vararg types: DataType) = this in types
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
infix fun isNotAssignableTo(targetType: DataType) = !this.isAssignableTo(targetType)
infix fun isNotAssignableTo(targetTypes: Set<DataType>) = !this.isAssignableTo(targetTypes)
@@ -127,13 +128,13 @@ enum class VarDeclType {
MEMORY
}
val ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
val StringlyDatatypes = setOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val IterableDatatypes = setOf(
val ByteDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE)
val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
val IterableDatatypes = arrayOf(
DataType.STR,
DataType.ARRAY_UB, DataType.ARRAY_B,
DataType.ARRAY_UW, DataType.ARRAY_W,
@@ -156,7 +157,7 @@ val ElementToArrayTypes = mapOf(
DataType.UWORD to DataType.ARRAY_UW,
DataType.FLOAT to DataType.ARRAY_F
)
val Cx16VirtualRegisters = listOf(
val Cx16VirtualRegisters = arrayOf(
RegisterOrPair.R0, RegisterOrPair.R1, RegisterOrPair.R2, RegisterOrPair.R3,
RegisterOrPair.R4, RegisterOrPair.R5, RegisterOrPair.R6, RegisterOrPair.R7,
RegisterOrPair.R8, RegisterOrPair.R9, RegisterOrPair.R10, RegisterOrPair.R11,
@@ -301,7 +301,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifier(vararg scopedName: String) = expression.referencesIdentifier(*scopedName)
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
override fun inferType(program: Program) = InferredTypes.knownFor(type)
override fun constValue(program: Program): NumericLiteralValue? {
val cv = expression.constValue(program) ?: return null
val cast = cv.cast(type)
@@ -334,7 +334,7 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
override fun constValue(program: Program): NumericLiteralValue? = null
override fun referencesIdentifier(vararg scopedName: String) = false
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UWORD)
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
}
@@ -359,7 +359,7 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun referencesIdentifier(vararg scopedName: String) = false
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UBYTE)
override fun constValue(program: Program): NumericLiteralValue? = null
override fun toString(): String {
@@ -422,7 +422,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
override fun toString(): String = "NumericLiteral(${type.name}:$number)"
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
override fun inferType(program: Program) = InferredTypes.knownFor(type)
override fun hashCode(): Int = Objects.hash(type, number)
@@ -518,7 +518,7 @@ class CharLiteral(val value: Char,
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
override fun toString(): String = "'${escape(value.toString())}'"
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UNDEFINED) // FIXME: CharLiteral.inferType
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.UBYTE)
operator fun compareTo(other: CharLiteral): Int = value.compareTo(other.value)
override fun hashCode(): Int = Objects.hash(value, altEncoding)
override fun equals(other: Any?): Boolean {
@@ -550,7 +550,7 @@ class StringLiteralValue(val value: String,
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
override fun toString(): String = "'${escape(value)}'"
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STR)
override fun inferType(program: Program) = InferredTypes.knownFor(DataType.STR)
operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value)
override fun hashCode(): Int = Objects.hash(value, altEncoding)
override fun equals(other: Any?): Boolean {
@@ -638,7 +638,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
}
fun cast(targettype: DataType): ArrayLiteralValue? {
if(type.istype(targettype))
if(type istype targettype)
return this
if(targettype in ArrayDatatypes) {
val elementType = ArrayToElementTypes.getValue(targettype)
@@ -797,7 +797,7 @@ class FunctionCall(override var target: IdentifierReference,
args.forEach { it.linkParents(this) }
}
override val isSimple = target.nameInSource.size==1 && (target.nameInSource[0] in setOf("msb", "lsb", "peek", "peekw"))
override val isSimple = target.nameInSource.size==1 && (target.nameInSource[0] in arrayOf("msb", "lsb", "peek", "peekw"))
override fun replaceChildNode(node: Node, replacement: Node) {
if(node===target)
@@ -15,6 +15,8 @@ object InferredTypes {
fun getOrElse(transform: (InferredType) -> DataType): DataType =
if(isUnknown || isVoid) transform(this) else datatype!!
infix fun istype(type: DataType): Boolean = if(isUnknown || isVoid) false else this.datatype==type
infix fun isnot(type: DataType): Boolean = if(isUnknown || isVoid) true else this.datatype!=type
fun oneOf(vararg types: DataType) = if(isUnknown || isVoid) false else this.datatype in types
companion object {
fun unknown() = InferredType(isUnknown = true, isVoid = false, datatype = null)
@@ -648,8 +648,8 @@ class Subroutine(override val name: String,
return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
}
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in setOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasResult() = asmReturnvaluesRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun regXasParam() = asmParameterRegisters.any { it.registerOrPair in arrayOf(RegisterOrPair.X, RegisterOrPair.AX, RegisterOrPair.XY) }
fun shouldSaveX() = CpuRegister.X in asmClobbers || regXasResult() || regXasParam()
class KeepAresult(val saveOnEntry: Boolean, val saveOnReturn: Boolean)
+12 -20
View File
@@ -31,28 +31,17 @@ object Prog8Parser {
parser.addErrorListener(antlrErrorListener)
val parseTree = parser.module()
val module = ParsedModule(src)
// .linkParents called in ParsedModule.add
parseTree.directive().forEach { module.add(it.toAst()) }
// TODO: remove Encoding
parseTree.block().forEach { module.add(it.toAst(module.isLibrary)) }
return module
}
private class ParsedModule(source: SourceCode) : Module(
// FIXME: hacking together a name for the module:
name = source.pathString()
.substringBeforeLast(".") // must also work with an origin = "<String@123beef>"
.substringAfterLast("/")
.substringAfterLast("\\")
.replace("String@", "anonymous_"),
statements = mutableListOf(),
position = Position(source.origin, 1, 0, 0),
source
) {
private class ParsedModule(source: SourceCode) :
Module(mutableListOf(), Position(source.origin, 1, 0, 0), source)
{
/**
* Adds a [Directive] to [statements] and
@@ -62,7 +51,7 @@ object Prog8Parser {
fun add(child: Directive) {
child.linkParents(this)
statements.add(child)
}
}
/**
* Adds a [Block] to [statements] and
* sets this Module as its [parent].
@@ -71,7 +60,7 @@ object Prog8Parser {
fun add(child: Block) {
child.linkParents(this)
statements.add(child)
}
}
}
private object Prog8ErrorStrategy: BailErrorStrategy() {
@@ -103,10 +92,13 @@ object Prog8Parser {
private class AntlrErrorListener(val src: SourceCode): BaseErrorListener() {
override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) {
if (e == null) {
TODO("no RecognitionException - create your own ParseError")
//throw ParseError()
throw ParseError(msg, Position(src.origin, line, charPositionInLine, charPositionInLine), RuntimeException("parse error"))
} else {
throw ParseError(msg, e.getPosition(src.origin), e)
if(e.offendingToken==null) {
throw ParseError(msg, Position(src.origin, line, charPositionInLine, charPositionInLine), e)
} else {
throw ParseError(msg, e.getPosition(src.origin), e)
}
}
}
}
@@ -115,7 +107,7 @@ object Prog8Parser {
val offending = this.offendingToken
val line = offending.line
val beginCol = offending.charPositionInLine
val endCol = beginCol + offending.stopIndex - offending.startIndex // TODO: point to col *after* token?
val endCol = beginCol + offending.stopIndex - offending.startIndex // TODO: point to col *after* token? / why, what's wrong with endCol being inclusive
return Position(file, line, beginCol, endCol)
}
+84 -72
View File
@@ -3,8 +3,11 @@ package prog8.parser
import org.antlr.v4.runtime.CharStream
import org.antlr.v4.runtime.CharStreams
import java.io.File
import java.io.IOException
import java.nio.channels.Channels
import java.nio.charset.CodingErrorAction
import java.nio.charset.StandardCharsets
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.exists
import kotlin.io.path.isDirectory
import kotlin.io.path.isReadable
@@ -13,7 +16,7 @@ import kotlin.io.path.isReadable
* Encapsulates - and ties together - actual source code (=text)
* and its [origin].
*/
abstract class SourceCode {
sealed class SourceCode {
/**
* To be used *only* by the parser (as input to a TokenStream).
@@ -22,29 +25,23 @@ abstract class SourceCode {
internal abstract fun getCharStream(): CharStream
/**
* Whether this [SourceCode] instance was created by
* factory method [fromResources]
* Whether this [SourceCode] instance was created as a [Resource]
*/
abstract val isFromResources: Boolean
/**
* Where this [SourceCode] instance came from.
* This can be one of the following:
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [fromPath])
* * `<String@44c56085>` if was created via [of]
* * `library:/x/y/z.ext` if it is a library file that was loaded from resources (see [fromResources])
* Whether this [SourceCode] instance was created as a [File]
*/
abstract val origin: String
abstract val isFromFilesystem: Boolean
/**
* FIXME: hacking together a [SourceCode]'s "path string"
* This is really just [origin] with any stuff removed that would render it an invalid path name.
* (Note: a *valid* path name does NOT mean that the denoted file or folder *exists*)
* Where this [SourceCode] instance came from.
* This can be one of the following:
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [File])
* * `$stringSourcePrefix44c56085>` if was created via [String]
* * `library:/x/y/z.ext` if it is a library file that was loaded from resources (see [Resource])
*/
fun pathString() =
origin
.substringAfter("<").substringBeforeLast(">") // or from plain string?
abstract val origin: String
/**
* The source code as plain string.
@@ -58,42 +55,45 @@ abstract class SourceCode {
*/
final override fun toString() = "${this.javaClass.name}[${this.origin}]"
// "static" factory methods
companion object {
/**
* filename prefix to designate library files that will be retreived from internal resources rather than disk
*/
const val libraryFilePrefix = "library:"
const val stringSourcePrefix = "<String@"
val curdir: Path = Path.of(".").toAbsolutePath()
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
fun isRegularFilesystemPath(pathString: String) =
!(pathString.startsWith(libraryFilePrefix) || pathString.startsWith(stringSourcePrefix))
}
/**
* Turn a plain String into a [SourceCode] object.
* [origin] will be something like `<String@44c56085>`.
*/
fun of(text: String): SourceCode {
return object : SourceCode() {
override val isFromResources = false
override val origin = "<String@${System.identityHashCode(text).toString(16)}>"
override fun getCharStream(): CharStream {
return CharStreams.fromString(text)
}
}
}
/**
* Turn a plain String into a [SourceCode] object.
* [origin] will be something like `$stringSourcePrefix44c56085>`.
*/
class Text(val text: String): SourceCode() {
override val isFromResources = false
override val isFromFilesystem = false
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}>"
override fun getCharStream(): CharStream = CharStreams.fromString(text, origin)
}
/**
* Get [SourceCode] from the file represented by the specified Path.
* This does not actually *access* the file, but it does check
* whether it
* * exists
* * is a regular file (ie: not a directory)
* * and is actually readable
*
* [origin] will be the given path in absolute and normalized form.
* @throws NoSuchFileException if the file does not exist
* @throws AccessDeniedException if the given path points to a directory or the file is non-readable for some other reason
*/
fun fromPath(path: Path): SourceCode {
val normalized = path.normalize()
/**
* Get [SourceCode] from the file represented by the specified Path.
* This does not actually *access* the file, but it does check
* whether it
* * exists
* * is a regular file (ie: not a directory)
* * and is actually readable
*
* [origin] will be the given path in absolute and normalized form.
* @throws NoSuchFileException if the file does not exist
* @throws AccessDeniedException if the given path points to a directory or the file is non-readable for some other reason
*/
class File(path: Path): SourceCode() {
private val normalized = path.normalize()
init {
val file = normalized.toFile()
if (!path.exists())
throw NoSuchFileException(file)
@@ -101,40 +101,53 @@ abstract class SourceCode {
throw AccessDeniedException(file, reason = "Not a file but a directory")
if (!path.isReadable())
throw AccessDeniedException(file, reason = "Is not readable")
return object : SourceCode() {
override val isFromResources = false
override val origin = normalized.absolutePathString()
override fun getCharStream(): CharStream {
return CharStreams.fromPath(normalized)
}
}
}
/**
* [origin]: `<library:/x/y/z.p8>` for a given `pathString` of "x/y/z.p8"
*/
fun fromResources(pathString: String): SourceCode {
val path = Path.of(pathString).normalize()
val sep = "/"
val normalized = sep + path.toMutableList().joinToString(sep)
val rscURL = object{}.javaClass.getResource(normalized)
override val isFromResources = false
override val isFromFilesystem = true
override val origin = relative(normalized).toString()
override fun getCharStream(): CharStream = CharStreams.fromPath(normalized)
}
/**
* [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("/")
init {
val rscURL = object {}.javaClass.getResource(normalized)
if (rscURL == null) {
val rscRoot = object{}.javaClass.getResource("/")
val rscRoot = object {}.javaClass.getResource("/")
throw NoSuchFileException(
File(normalized),
reason = "looked in resources rooted at $rscRoot")
}
return object : SourceCode() {
override val isFromResources = true
override val origin = "$libraryFilePrefix$normalized"
override fun getCharStream(): CharStream {
val inpStr = object {}.javaClass.getResourceAsStream(normalized)
return CharStreams.fromStream(inpStr)
}
reason = "looked in resources rooted at $rscRoot"
)
}
}
// TODO: possibly more, like fromURL(..)
override val isFromResources = true
override val isFromFilesystem = false
override val origin = "$libraryFilePrefix$normalized"
override fun getCharStream(): CharStream {
val inpStr = object {}.javaClass.getResourceAsStream(normalized)!!
// CharStreams.fromStream() doesn't allow us to set the stream name properly, so we use a lower level api
val channel = Channels.newChannel(inpStr)
return CharStreams.fromChannel(channel, StandardCharsets.UTF_8, 4096, CodingErrorAction.REPLACE, origin, -1);
}
}
/**
* SourceCode for internally generated nodes (usually Modules)
*/
class Generated(name: String) : SourceCode() {
override fun getCharStream(): CharStream = throw IOException("generated code nodes doesn't have a stream to read")
override val isFromResources: Boolean = false
override val isFromFilesystem: Boolean = false
override val origin: String = name
}
// TODO: possibly more, like fromURL(..)
/* // For `jar:..` URLs
// see https://stackoverflow.com/questions/22605666/java-access-files-in-jar-causes-java-nio-file-filesystemnotfoundexception
var url = URL("jar:file:/E:/x16/prog8(meisl)/compiler/build/libs/prog8compiler-7.0-BETA3-all.jar!/prog8lib/c64/textio.p8")
@@ -143,5 +156,4 @@ abstract class SourceCode {
val fs = FileSystems.newFileSystem(URI.create(parts[0]), mutableMapOf(Pair("", "")) )
val path = fs.getPath(parts[1])
*/
}
}
+8 -8
View File
@@ -31,7 +31,7 @@ class TestAstToSourceCode {
private fun roundTrip(module: Module): Pair<String, Module> {
val generatedText = generateP8(module)
try {
val parsedAgain = parseModule(SourceCode.of(generatedText))
val parsedAgain = parseModule(SourceCode.Text(generatedText))
return Pair(generatedText, parsedAgain)
} catch (e: ParseError) {
assert(false) { "should produce valid Prog8 but threw $e" }
@@ -41,7 +41,7 @@ class TestAstToSourceCode {
@Test
fun testMentionsInternedStringsModule() {
val orig = SourceCode.of("\n")
val orig = SourceCode.Text("\n")
val (txt, _) = roundTrip(parseModule(orig))
// assertContains has *actual* first!
assertContains(txt, Regex(";.*$internedStringsModuleName"))
@@ -49,7 +49,7 @@ class TestAstToSourceCode {
@Test
fun testImportDirectiveWithLib() {
val orig = SourceCode.of("%import textio\n")
val orig = SourceCode.Text("%import textio\n")
val (txt, _) = roundTrip(parseModule(orig))
// assertContains has *actual* first!
assertContains(txt, Regex("%import +textio"))
@@ -57,7 +57,7 @@ class TestAstToSourceCode {
@Test
fun testImportDirectiveWithUserModule() {
val orig = SourceCode.of("%import my_own_stuff\n")
val orig = SourceCode.Text("%import my_own_stuff\n")
val (txt, _) = roundTrip(parseModule(orig))
// assertContains has *actual* first!
assertContains(txt, Regex("%import +my_own_stuff"))
@@ -66,7 +66,7 @@ class TestAstToSourceCode {
@Test
fun testStringLiteral_noAlt() {
val orig = SourceCode.of("""
val orig = SourceCode.Text("""
main {
str s = "fooBar\n"
}
@@ -78,7 +78,7 @@ class TestAstToSourceCode {
@Test
fun testStringLiteral_withAlt() {
val orig = SourceCode.of("""
val orig = SourceCode.Text("""
main {
str sAlt = @"fooBar\n"
}
@@ -90,7 +90,7 @@ class TestAstToSourceCode {
@Test
fun testCharLiteral_noAlt() {
val orig = SourceCode.of("""
val orig = SourceCode.Text("""
main {
ubyte c = 'x'
}
@@ -102,7 +102,7 @@ class TestAstToSourceCode {
@Test
fun testCharLiteral_withAlt() {
val orig = SourceCode.of("""
val orig = SourceCode.Text("""
main {
ubyte cAlt = @'x'
}
+97 -50
View File
@@ -1,10 +1,10 @@
package prog8tests
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.ast.IFunctionCall
import prog8.ast.Module
import prog8.ast.Node
import prog8.ast.base.Position
import prog8.ast.expressions.CharLiteral
@@ -18,13 +18,11 @@ import prog8.parser.SourceCode
import prog8tests.helpers.assumeNotExists
import prog8tests.helpers.assumeReadableFile
import prog8tests.helpers.fixturesDir
import kotlin.io.path.absolutePathString
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
import kotlin.io.path.name
import kotlin.io.path.nameWithoutExtension
import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertIs
import kotlin.test.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -39,7 +37,7 @@ class TestProg8Parser {
@Test
fun `is not required - #40, fixed by #45`() {
val nl = "\n" // say, Unix-style (different flavours tested elsewhere)
val src = SourceCode.of("foo {" + nl + "}") // source ends with '}' (= NO newline, issue #40)
val src = SourceCode.Text("foo {" + nl + "}") // source ends with '}' (= NO newline, issue #40)
// #40: Prog8ANTLRParser would report (throw) "missing <EOL> at '<EOF>'"
val module = parseModule(src)
@@ -50,7 +48,7 @@ class TestProg8Parser {
fun `is still accepted - #40, fixed by #45`() {
val nl = "\n" // say, Unix-style (different flavours tested elsewhere)
val srcText = "foo {" + nl + "}" + nl // source does end with a newline (issue #40)
val module = parseModule(SourceCode.of(srcText))
val module = parseModule(SourceCode.Text(srcText))
assertEquals(1, module.statements.size)
}
}
@@ -65,22 +63,22 @@ class TestProg8Parser {
// GOOD: 2nd block `bar` does start on a new line; however, a nl at the very end ain't needed
val srcGood = "foo {" + nl + "}" + nl + "bar {" + nl + "}"
assertFailsWith<ParseError> { parseModule(SourceCode.of(srcBad)) }
val module = parseModule(SourceCode.of(srcGood))
assertFailsWith<ParseError> { parseModule(SourceCode.Text(srcBad)) }
val module = parseModule(SourceCode.Text(srcGood))
assertEquals(2, module.statements.size)
}
@Test
fun `is required between two Blocks or Directives - #47`() {
// block and block
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
assertFailsWith<ParseError>{ parseModule(SourceCode.Text("""
blockA {
} blockB {
}
""")) }
// block and directive
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
assertFailsWith<ParseError>{ parseModule(SourceCode.Text("""
blockB {
} %import textio
""")) }
@@ -89,12 +87,12 @@ class TestProg8Parser {
// Leaving them in anyways.
// dir and block
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
assertFailsWith<ParseError>{ parseModule(SourceCode.Text("""
%import textio blockB {
}
""")) }
assertFailsWith<ParseError>{ parseModule(SourceCode.of("""
assertFailsWith<ParseError>{ parseModule(SourceCode.Text("""
%import textio %import syslib
""")) }
}
@@ -123,7 +121,7 @@ class TestProg8Parser {
"}" +
nlUnix // end with newline (see testModuleSourceNeedNotEndWithNewline)
val module = parseModule(SourceCode.of(srcText))
val module = parseModule(SourceCode.Text(srcText))
assertEquals(2, module.statements.size)
}
}
@@ -142,7 +140,7 @@ class TestProg8Parser {
blockA {
}
"""
val module = parseModule(SourceCode.of(srcText))
val module = parseModule(SourceCode.Text(srcText))
assertEquals(1, module.statements.size)
}
@@ -159,7 +157,7 @@ class TestProg8Parser {
blockB {
}
"""
val module = parseModule(SourceCode.of(srcText))
val module = parseModule(SourceCode.Text(srcText))
assertEquals(2, module.statements.size)
}
@@ -174,7 +172,7 @@ class TestProg8Parser {
; comment
"""
val module = parseModule(SourceCode.of(srcText))
val module = parseModule(SourceCode.Text(srcText))
assertEquals(1, module.statements.size)
}
}
@@ -187,7 +185,7 @@ class TestProg8Parser {
val importedNoExt = assumeNotExists(fixturesDir, "i_do_not_exist")
assumeNotExists(fixturesDir, "i_do_not_exist.p8")
val text = "%import ${importedNoExt.name}"
val module = parseModule(SourceCode.of(text))
val module = parseModule(SourceCode.Text(text))
assertEquals(1, module.statements.size)
}
@@ -198,14 +196,14 @@ class TestProg8Parser {
inner class EmptySourcecode {
@Test
fun `from an empty string should result in empty Module`() {
val module = parseModule(SourceCode.of(""))
val module = parseModule(SourceCode.Text(""))
assertEquals(0, module.statements.size)
}
@Test
fun `from an empty file should result in empty Module`() {
val path = assumeReadableFile(fixturesDir, "empty.p8")
val module = parseModule(SourceCode.fromPath(path))
val module = parseModule(SourceCode.File(path))
assertEquals(0, module.statements.size)
}
}
@@ -218,16 +216,16 @@ class TestProg8Parser {
main {
}
""".trimIndent()
val module = parseModule(SourceCode.of(srcText))
val module = parseModule(SourceCode.Text(srcText))
// Note: assertContains has *actual* as first param
assertContains(module.name, Regex("^anonymous_[0-9a-f]+$"))
assertContains(module.name, Regex("^<String@[0-9a-f\\-]+>$"))
}
@Test
fun `parsed from a file`() {
val path = assumeReadableFile(fixturesDir, "simple_main.p8")
val module = parseModule(SourceCode.fromPath(path))
val module = parseModule(SourceCode.File(path))
assertEquals(path.nameWithoutExtension, module.name)
}
}
@@ -287,11 +285,11 @@ class TestProg8Parser {
fun `in ParseError from bad string source code`() {
val srcText = "bad * { }\n"
assertFailsWith<ParseError> { parseModule(SourceCode.of(srcText)) }
assertFailsWith<ParseError> { parseModule(SourceCode.Text(srcText)) }
try {
parseModule(SourceCode.of(srcText))
parseModule(SourceCode.Text(srcText))
} catch (e: ParseError) {
assertPosition(e.position, Regex("^<String@[0-9a-f]+>$"), 1, 4, 4)
assertPosition(e.position, Regex("^<String@[0-9a-f\\-]+>$"), 1, 4, 4)
}
}
@@ -299,11 +297,11 @@ class TestProg8Parser {
fun `in ParseError from bad file source code`() {
val path = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
assertFailsWith<ParseError> { parseModule(SourceCode.fromPath(path)) }
assertFailsWith<ParseError> { parseModule(SourceCode.File(path)) }
try {
parseModule(SourceCode.fromPath(path))
parseModule(SourceCode.File(path))
} catch (e: ParseError) {
assertPosition(e.position, path.absolutePathString(), 2, 6) // TODO: endCol wrong
assertPosition(e.position, SourceCode.relative(path).toString(), 2, 6) // TODO: endCol wrong
}
}
@@ -313,30 +311,28 @@ class TestProg8Parser {
main {
}
""".trimIndent()
val module = parseModule(SourceCode.of(srcText))
assertPositionOf(module, Regex("^<String@[0-9a-f]+>$"), 1, 0) // TODO: endCol wrong
val module = parseModule(SourceCode.Text(srcText))
assertPositionOf(module, Regex("^<String@[0-9a-f\\-]+>$"), 1, 0) // TODO: endCol wrong
}
@Test
fun `of Module parsed from a file`() {
val path = assumeReadableFile(fixturesDir, "simple_main.p8")
val module = parseModule(SourceCode.fromPath(path))
assertPositionOf(module, path.absolutePathString(), 1, 0) // TODO: endCol wrong
val module = parseModule(SourceCode.File(path))
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) // TODO: endCol wrong
}
@Test
fun `of non-root Nodes parsed from file`() {
val path = assumeReadableFile(fixturesDir, "simple_main.p8")
val module = parseModule(SourceCode.fromPath(path))
val module = parseModule(SourceCode.File(path))
val mpf = module.position.file
assertPositionOf(module, path.absolutePathString(), 1, 0) // TODO: endCol wrong
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) // TODO: endCol wrong
val mainBlock = module.statements.filterIsInstance<Block>()[0]
assertPositionOf(mainBlock, mpf, 1, 0) // TODO: endCol wrong!
assertPositionOf(mainBlock, mpf, 2, 0) // TODO: endCol wrong!
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
assertPositionOf(startSub, mpf, 2, 4) // TODO: endCol wrong!
assertPositionOf(startSub, mpf, 3, 4) // TODO: endCol wrong!
}
@@ -344,10 +340,9 @@ class TestProg8Parser {
* TODO: this test is testing way too much at once
*/
@Test
@Disabled("TODO: fix .position of nodes below Module - step 8, 'refactor AST gen'")
fun `of non-root Nodes parsed from a string`() {
val srcText = """
%target 16, "abc" ; DirectiveArg directly inherits from Node - neither an Expression nor a Statement..?
%zeropage basicsafe ; DirectiveArg directly inherits from Node - neither an Expression nor a Statement..?
main {
sub start() {
ubyte foo = 42
@@ -360,7 +355,7 @@ class TestProg8Parser {
}
}
""".trimIndent()
val module = parseModule(SourceCode.of(srcText))
val module = parseModule(SourceCode.Text(srcText))
val mpf = module.position.file
val targetDirective = module.statements.filterIsInstance<Directive>()[0]
@@ -383,12 +378,65 @@ class TestProg8Parser {
}
}
@Nested
inner class PositionFile {
@Test
fun `isn't absolute for filesystem paths`() {
val path = assumeReadableFile(fixturesDir, "simple_main.p8")
val module = parseModule(SourceCode.File(path))
assertSomethingForAllNodes(module) {
assertFalse(Path(it.position.file).isAbsolute)
assertTrue(Path(it.position.file).isRegularFile())
}
}
@Test
fun `is mangled string id for string sources`()
{
val srcText="""
%zeropage basicsafe
main {
sub start() {
ubyte aa=99
aa++
}
}
""".trimIndent()
val module = parseModule(SourceCode.Text(srcText))
assertSomethingForAllNodes(module) {
assertTrue(it.position.file.startsWith(SourceCode.stringSourcePrefix))
}
}
@Test
fun `is library prefixed path for resources`()
{
val resource = SourceCode.Resource("prog8lib/math.p8")
val module = parseModule(resource)
assertSomethingForAllNodes(module) {
assertTrue(it.position.file.startsWith(SourceCode.libraryFilePrefix))
}
}
private fun assertSomethingForAllNodes(module: Module, asserter: (Node) -> Unit) {
asserter(module)
module.statements.forEach(asserter)
module.statements.filterIsInstance<Block>().forEach { b ->
asserter(b)
b.statements.forEach(asserter)
b.statements.filterIsInstance<Subroutine>().forEach { s ->
asserter(s)
s.statements.forEach(asserter)
}
}
} }
@Nested
inner class CharLiterals {
@Test
fun `in argument position, no altEnc`() {
val src = SourceCode.of("""
val src = SourceCode.Text("""
main {
sub start() {
chrout('\n')
@@ -409,7 +457,7 @@ class TestProg8Parser {
@Test
fun `on rhs of block-level var decl, no AltEnc`() {
val src = SourceCode.of("""
val src = SourceCode.Text("""
main {
ubyte c = 'x'
}
@@ -426,7 +474,7 @@ class TestProg8Parser {
@Test
fun `on rhs of block-level const decl, with AltEnc`() {
val src = SourceCode.of("""
val src = SourceCode.Text("""
main {
const ubyte c = @'x'
}
@@ -443,7 +491,7 @@ class TestProg8Parser {
@Test
fun `on rhs of subroutine-level var decl, no AltEnc`() {
val src = SourceCode.of("""
val src = SourceCode.Text("""
main {
sub start() {
ubyte c = 'x'
@@ -463,7 +511,7 @@ class TestProg8Parser {
@Test
fun `on rhs of subroutine-level const decl, with AltEnc`() {
val src = SourceCode.of("""
val src = SourceCode.Text("""
main {
sub start() {
const ubyte c = @'x'
@@ -487,7 +535,7 @@ class TestProg8Parser {
@Test
fun `in for-loops`() {
val module = parseModule(SourceCode.of("""
val module = parseModule(SourceCode.Text("""
main {
sub start() {
ubyte ub
@@ -524,7 +572,6 @@ class TestProg8Parser {
assertIs<CharLiteral>(it2.to, "parser should leave it as is")
val it3 = iterables[3] as RangeExpr
// TODO: intro BoolLiteral
assertIs<NumericLiteralValue>(it3.from, "parser should leave it as is")
assertIs<NumericLiteralValue>(it3.to, "parser should leave it as is")
+28 -35
View File
@@ -1,62 +1,63 @@
package prog8tests
import org.junit.jupiter.api.Disabled
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.core.StringStartsWith
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import prog8.parser.SourceCode
import prog8.parser.SourceCode.Companion.libraryFilePrefix
import prog8tests.helpers.*
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
import kotlin.test.*
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestSourceCode {
@Test
fun testFactoryMethod_Of() {
fun testFromString() {
val text = """
main { }
""".trimIndent()
val src = SourceCode.of(text)
"""
val src = SourceCode.Text(text)
val actualText = src.getCharStream().toString()
assertContains(src.origin, Regex("^<String@[0-9a-f]+>$"))
assertContains(src.origin, Regex("^<String@[0-9a-f\\-]+>$"))
assertEquals(text, actualText)
assertFalse(src.isFromResources)
assertFalse(src.isFromFilesystem)
assertThat(src.toString(), StringStartsWith("prog8.parser.SourceCode"))
}
@Test
fun testFromPathWithNonExistingPath() {
val filename = "i_do_not_exist.p8"
val path = assumeNotExists(fixturesDir, filename)
assertFailsWith<NoSuchFileException> { SourceCode.fromPath(path) }
assertFailsWith<NoSuchFileException> { SourceCode.File(path) }
}
@Test
fun testFromPathWithMissingExtension_p8() {
val pathWithoutExt = assumeNotExists(fixturesDir,"simple_main")
assumeReadableFile(fixturesDir,"simple_main.p8")
assertFailsWith<NoSuchFileException> { SourceCode.fromPath(pathWithoutExt) }
assertFailsWith<NoSuchFileException> { SourceCode.File(pathWithoutExt) }
}
@Test
fun testFromPathWithDirectory() {
assertFailsWith<AccessDeniedException> { SourceCode.fromPath(fixturesDir) }
assertFailsWith<AccessDeniedException> { SourceCode.File(fixturesDir) }
}
@Test
fun testFromPathWithExistingPath() {
val filename = "simple_main.p8"
val path = assumeReadableFile(fixturesDir, filename)
val src = SourceCode.fromPath(path)
val expectedOrigin = path.normalize().absolutePathString()
val src = SourceCode.File(path)
val expectedOrigin = SourceCode.relative(path).toString()
assertEquals(expectedOrigin, src.origin)
assertEquals(path.toFile().readText(), src.asString())
assertFalse(src.isFromResources)
assertTrue(src.isFromFilesystem)
}
@Test
@@ -64,9 +65,8 @@ class TestSourceCode {
val filename = "simple_main.p8"
val path = Path(".", "test", "..", "test", "fixtures", filename)
val srcFile = assumeReadableFile(path).toFile()
val src = SourceCode.fromPath(path)
val expectedOrigin = path.normalize().absolutePathString()
val src = SourceCode.File(path)
val expectedOrigin = SourceCode.relative(path).toString()
assertEquals(expectedOrigin, src.origin)
assertEquals(srcFile.readText(), src.asString())
}
@@ -75,17 +75,19 @@ class TestSourceCode {
fun testFromResourcesWithExistingP8File_withoutLeadingSlash() {
val pathString = "prog8lib/math.p8"
val srcFile = assumeReadableFile(resourcesDir, pathString).toFile()
val src = SourceCode.fromResources(pathString)
val src = SourceCode.Resource(pathString)
assertEquals("$libraryFilePrefix/$pathString", src.origin)
assertEquals(srcFile.readText(), src.asString())
assertTrue(src.isFromResources)
assertFalse(src.isFromFilesystem)
}
@Test
fun testFromResourcesWithExistingP8File_withLeadingSlash() {
val pathString = "/prog8lib/math.p8"
val srcFile = assumeReadableFile(resourcesDir, pathString.substring(1)).toFile()
val src = SourceCode.fromResources(pathString)
val src = SourceCode.Resource(pathString)
assertEquals("$libraryFilePrefix$pathString", src.origin)
assertEquals(srcFile.readText(), src.asString())
@@ -95,7 +97,7 @@ class TestSourceCode {
fun testFromResourcesWithExistingAsmFile_withoutLeadingSlash() {
val pathString = "prog8lib/math.asm"
val srcFile = assumeReadableFile(resourcesDir, pathString).toFile()
val src = SourceCode.fromResources(pathString)
val src = SourceCode.Resource(pathString)
assertEquals("$libraryFilePrefix/$pathString", src.origin)
assertEquals(srcFile.readText(), src.asString())
@@ -106,7 +108,7 @@ class TestSourceCode {
fun testFromResourcesWithExistingAsmFile_withLeadingSlash() {
val pathString = "/prog8lib/math.asm"
val srcFile = assumeReadableFile(resourcesDir, pathString.substring(1)).toFile()
val src = SourceCode.fromResources(pathString)
val src = SourceCode.Resource(pathString)
assertEquals("$libraryFilePrefix$pathString", src.origin)
assertEquals(srcFile.readText(), src.asString())
@@ -116,7 +118,7 @@ class TestSourceCode {
fun testFromResourcesWithNonNormalizedPath() {
val pathString = "/prog8lib/../prog8lib/math.p8"
val srcFile = assumeReadableFile(resourcesDir, pathString.substring(1)).toFile()
val src = SourceCode.fromResources(pathString)
val src = SourceCode.Resource(pathString)
assertEquals("$libraryFilePrefix/prog8lib/math.p8", src.origin)
assertEquals(srcFile.readText(), src.asString())
@@ -129,22 +131,13 @@ class TestSourceCode {
val pathString = "/prog8lib/i_do_not_exist"
assumeNotExists(resourcesDir, pathString.substring(1))
assertFailsWith<NoSuchFileException> { SourceCode.fromResources(pathString) }
assertFailsWith<NoSuchFileException> { SourceCode.Resource(pathString) }
}
@Test
fun testFromResourcesWithNonExistingFile_withoutLeadingSlash() {
val pathString = "prog8lib/i_do_not_exist"
assumeNotExists(resourcesDir, pathString)
assertFailsWith<NoSuchFileException> { SourceCode.fromResources(pathString) }
assertFailsWith<NoSuchFileException> { SourceCode.Resource(pathString) }
}
@Test
@Disabled("TODO: inside resources: cannot tell apart a folder from a file")
fun testFromResourcesWithDirectory() {
val pathString = "/prog8lib"
assumeDirectory(resourcesDir, pathString.substring(1))
assertFailsWith<AccessDeniedException> { SourceCode.fromResources(pathString) }
}
}
+12 -11
View File
@@ -11,6 +11,7 @@ import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.Position
import prog8.ast.internedStringsModuleName
import prog8.parser.SourceCode
import prog8tests.helpers.DummyFunctions
import prog8tests.helpers.DummyMemsizer
import kotlin.test.assertContains
@@ -39,7 +40,7 @@ class ProgramTests {
@Test
fun withEmptyModule() {
val program = Program("foo", DummyFunctions, DummyMemsizer)
val m1 = Module("bar", mutableListOf(), Position.DUMMY, null)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val retVal = program.addModule(m1)
@@ -49,12 +50,12 @@ class ProgramTests {
assertSame(program, m1.program)
assertSame(program.namespace, m1.parent)
assertFailsWith<IllegalArgumentException> { program.addModule(m1) }
.let { assertThat(it.message, containsString(m1.name)) }
assertThat("module may not occur multiple times",
assertFailsWith<IllegalArgumentException> { program.addModule(m1) }.message, containsString(m1.name))
val m2 = Module(m1.name, mutableListOf(), m1.position, m1.source)
assertFailsWith<IllegalArgumentException> { program.addModule(m2) }
.let { assertThat(it.message, containsString(m2.name)) }
val m2 = Module(mutableListOf(), m1.position, m1.source)
assertThat("other module but with same name may not occur multiple times",
assertFailsWith<IllegalArgumentException> { program.addModule(m2) }.message, containsString(m2.name))
}
}
@@ -73,15 +74,15 @@ class ProgramTests {
@Test
fun withForeignModule() {
val program = Program("foo", DummyFunctions, DummyMemsizer)
val m = Module("bar", mutableListOf(), Position.DUMMY, null)
val m = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
assertFailsWith<IllegalArgumentException> { program.moveModuleToFront(m) }
}
@Test
fun withFirstOfPreviouslyAddedModules() {
val program = Program("foo", DummyFunctions, DummyMemsizer)
val m1 = Module("bar", mutableListOf(), Position.DUMMY, null)
val m2 = Module("qmbl", mutableListOf(), Position.DUMMY, null)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
program.addModule(m1)
program.addModule(m2)
@@ -92,8 +93,8 @@ class ProgramTests {
@Test
fun withSecondOfPreviouslyAddedModules() {
val program = Program("foo", DummyFunctions, DummyMemsizer)
val m1 = Module("bar", mutableListOf(), Position.DUMMY, null)
val m2 = Module("qmbl", mutableListOf(), Position.DUMMY, null)
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
program.addModule(m1)
program.addModule(m2)
+3
View File
@@ -1,4 +1,7 @@
%zeropage basicsafe
main {
sub start() {
ubyte aa=99
aa++
}
}
+7 -5
View File
@@ -118,11 +118,13 @@ They are embedded into the packaged release version of the compiler so you don't
where they are, but their names are still reserved.
User defined library files and -location
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can create library files yourself too that can be shared among programs.
You can tell the compiler where it should look for these files, by using
the libdirs command line option.
Importing other source files and specifying search location(s)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can create multiple source files yourself to modularize your large programs into
multiple module files. You can also create "library" modules this way with handy routines,
that can be shared among programs. By importing those module files, you can use them in other modules.
It is possible to tell the compiler where it should look for these files, by using
the ``srcdirs`` command line option.
.. _debugging:
+5 -7
View File
@@ -3,10 +3,7 @@ TODO
For next compiler release
^^^^^^^^^^^^^^^^^^^^^^^^^
- rename libdirs option to srcdirs?
- can we derive module.name from module.source (taking just the filename base)?
- can Position.file be a Path- making the source variable for nodes unnecessary?
- address more questions/issues from the testability discussions.
...
Blocked by Commander-x16 v39 release
@@ -17,12 +14,13 @@ Blocked by Commander-x16 v39 release
Future
^^^^^^
- get rid of all TODO's and FIXME's in the code
- improve testability further, add more tests
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
- get rid of all TODO's in the code
- improve testability further, add more tests, address more questions/issues from the testability discussions.
- replace certain uses of inferredType.getOr(DataType.UNDEFINED) by i.getOrElse({ errorhandler })
- see if we can remove more "[InferredType].getOr(DataType.UNDEFINED)"
- use more of Result<> and Either<> to handle errors/ nulls better
- fix the asm-labels problem (github issue #62)
- can we get rid of pieces of asmgen.AssignmentAsmGen by just reusing the AugmentableAssignment ? generated code should not suffer
- c64: make the graphics.BITMAP_ADDRESS configurable (VIC banking)
- optimize several inner loops in gfx2 even further?
- add modes 2 and 3 to gfx2 (lowres 4 color and 16 color)?
+1 -1
View File
@@ -22,7 +22,7 @@ main {
; Not yet implemented in ROM: cx16.FB_set_palette(&colors, 0, len(colors)*3)
palette.set_rgb(&colors, len(colors))
cx16.screen_set_mode(128) ; low-res bitmap 256 colors
void cx16.screen_set_mode(128) ; low-res bitmap 256 colors
cx16.FB_init()
cx16.VERA_DC_VSCALE = 0 ; display trick spoiler.......: stretch display all the way to the bottom
cx16.set_rasterirq(&irq.irqhandler, 0)
+7 -11
View File
@@ -1,14 +1,10 @@
%import textio
main {
str myBar = "main.bar"
sub start() {
uword address = &irq
; cx16.set_irq(&irq, false)
address++
}
foo_bar:
%asminclude "compiler/test/fixtures/foo_bar.asm" ; FIXME: should be accessible from inside start() but give assembler error
sub start() {
txt.print(myBar)
txt.print(&foo_bar)
return
}
sub irq() {
}
}
@@ -34,8 +34,8 @@ class RequestParser : Take {
writeAssembly = true,
slowCodegenWarnings = true,
compilationTarget = "c64",
libdirs = emptyList(),
outputDir = Path.of(".")
sourceDirs = emptyList(),
outputDir = Path.of("")
)
return RsJson(Jsonding())
}