mirror of
https://github.com/irmen/prog8.git
synced 2024-12-27 20:33:39 +00:00
commit
637a8899c5
@ -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) {
|
||||
|
@ -341,7 +341,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)
|
||||
|
@ -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.")
|
||||
@ -365,7 +375,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 +383,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())
|
||||
|
@ -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() }
|
||||
@ -42,7 +41,7 @@ class ModuleImporter(private val program: Program,
|
||||
val logMsg = "importing '${filePath.nameWithoutExtension}' (from file $srcPath)"
|
||||
println(logMsg)
|
||||
|
||||
return Ok(importModule(SourceCode.fromPath(srcPath)))
|
||||
return Ok(importModule(SourceCode.File(srcPath)))
|
||||
}
|
||||
|
||||
fun importLibraryModule(name: String): Module? {
|
||||
@ -69,30 +68,41 @@ class ModuleImporter(private val program: Program,
|
||||
}
|
||||
|
||||
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)
|
||||
return importedModule
|
||||
@ -100,33 +110,29 @@ class ModuleImporter(private val program: Program,
|
||||
|
||||
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?
|
||||
val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0
|
||||
sourcePaths.drop(dropCurDir) +
|
||||
// FIXME: won't work until Prog8Parser is fixed s.t. it fully initialzes the modules it returns
|
||||
listOf(Path(importingModule.position.file).parent ?: Path("")) +
|
||||
listOf(Path(".", "prog8lib"))
|
||||
@ -134,12 +140,11 @@ class ModuleImporter(private val program: Program,
|
||||
|
||||
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")))
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import prog8.compiler.ZeropageType
|
||||
import prog8.compiler.functions.BuiltinFunctions
|
||||
import prog8.compiler.functions.builtinFunctionReturnType
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import prog8.parser.SourceCode
|
||||
import java.io.CharConversionException
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
@ -44,7 +45,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
if(compilerOptions.floats) {
|
||||
if (compilerOptions.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||
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.mainModule.position)
|
||||
}
|
||||
|
||||
@ -152,11 +153,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 +284,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 +299,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 +429,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 +519,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 +597,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 +715,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 +727,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 +916,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 +985,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 +1119,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 +1157,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 +1201,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 +1220,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 +1239,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
|
||||
|
@ -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.*
|
||||
@ -75,8 +76,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 +104,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.
|
||||
|
@ -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,
|
||||
|
@ -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,7 +68,7 @@ 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)
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import prog8.ast.Program
|
||||
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 +192,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))
|
||||
@ -221,7 +222,7 @@ 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))
|
||||
@ -250,14 +251,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") // TODO don't add a p8 extension in the import logic...
|
||||
errors.report()
|
||||
assertThat(program.modules.size, equalTo(1))
|
||||
}
|
||||
@ -277,7 +278,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 +298,13 @@ 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))
|
||||
importer.errors.report()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
@ -41,7 +42,7 @@ class TestCompilerOnExamples {
|
||||
writeAssembly = true,
|
||||
slowCodegenWarnings = false,
|
||||
compilationTarget = platform.name,
|
||||
libdirs = listOf(),
|
||||
sourceDirs = listOf(),
|
||||
outputDir
|
||||
).assertSuccess("; $displayName")
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.PrefixExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8.parser.SourceCode
|
||||
import prog8tests.helpers.DummyFunctions
|
||||
import prog8tests.helpers.DummyMemsizer
|
||||
import kotlin.test.assertFalse
|
||||
@ -91,7 +92,7 @@ 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)
|
||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||
module.linkParents(program.namespace)
|
||||
return target
|
||||
}
|
||||
@ -110,7 +111,7 @@ 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)
|
||||
@ -124,7 +125,7 @@ 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)
|
||||
@ -138,7 +139,7 @@ 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)
|
||||
@ -152,7 +153,7 @@ 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)
|
||||
@ -167,7 +168,7 @@ 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)
|
||||
@ -182,7 +183,7 @@ 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)
|
||||
|
@ -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,9 +0,0 @@
|
||||
%import textio
|
||||
%import "foo_bar.p8"
|
||||
main {
|
||||
str myBar = "main.bar"
|
||||
sub start() {
|
||||
txt.print(myBar)
|
||||
txt.print(foo.bar)
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ internal fun compileFile(
|
||||
writeAssembly = true,
|
||||
slowCodegenWarnings = false,
|
||||
platform.name,
|
||||
libdirs = listOf(),
|
||||
sourceDirs = listOf(),
|
||||
outputDir
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
@ -347,14 +347,18 @@ class Program(val name: String,
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
@ -380,7 +384,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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -42,17 +42,9 @@ object Prog8Parser {
|
||||
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 +54,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 +63,7 @@ object Prog8Parser {
|
||||
fun add(child: Block) {
|
||||
child.linkParents(this)
|
||||
statements.add(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object Prog8ErrorStrategy: BailErrorStrategy() {
|
||||
|
@ -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])
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -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'
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
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
|
||||
@ -18,7 +17,6 @@ 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.name
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
import kotlin.test.assertContains
|
||||
@ -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,9 +285,9 @@ 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)
|
||||
}
|
||||
@ -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,26 +311,24 @@ class TestProg8Parser {
|
||||
main {
|
||||
}
|
||||
""".trimIndent()
|
||||
val module = parseModule(SourceCode.of(srcText))
|
||||
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!
|
||||
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
|
||||
@ -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]
|
||||
@ -388,7 +383,7 @@ class TestProg8Parser {
|
||||
|
||||
@Test
|
||||
fun `in argument position, no altEnc`() {
|
||||
val src = SourceCode.of("""
|
||||
val src = SourceCode.Text("""
|
||||
main {
|
||||
sub start() {
|
||||
chrout('\n')
|
||||
@ -409,7 +404,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 +421,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 +438,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 +458,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 +482,7 @@ class TestProg8Parser {
|
||||
|
||||
@Test
|
||||
fun `in for-loops`() {
|
||||
val module = parseModule(SourceCode.of("""
|
||||
val module = parseModule(SourceCode.Text("""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte ub
|
||||
|
@ -1,13 +1,11 @@
|
||||
package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Disabled
|
||||
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
|
||||
@ -22,7 +20,7 @@ class TestSourceCode {
|
||||
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]+>$"))
|
||||
@ -33,28 +31,27 @@ class TestSourceCode {
|
||||
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())
|
||||
}
|
||||
@ -64,9 +61,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,7 +71,7 @@ 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())
|
||||
@ -85,7 +81,7 @@ class TestSourceCode {
|
||||
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 +91,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 +102,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 +112,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 +125,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) }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
@ -52,7 +53,7 @@ class ProgramTests {
|
||||
assertFailsWith<IllegalArgumentException> { program.addModule(m1) }
|
||||
.let { assertThat(it.message, containsString(m1.name)) }
|
||||
|
||||
val m2 = Module(m1.name, mutableListOf(), m1.position, m1.source)
|
||||
val m2 = Module(mutableListOf(), m1.position, m1.source)
|
||||
assertFailsWith<IllegalArgumentException> { program.addModule(m2) }
|
||||
.let { assertThat(it.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)
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
@ -18,11 +15,12 @@ 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
|
||||
- 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,14 +1,18 @@
|
||||
%import textio
|
||||
|
||||
main {
|
||||
str myBar = "main.bar"
|
||||
sub start() {
|
||||
ubyte xx
|
||||
|
||||
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
|
||||
}
|
||||
when xx {
|
||||
2 -> {
|
||||
}
|
||||
3 -> {
|
||||
}
|
||||
50 -> {
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user