mirror of
https://github.com/irmen/prog8.git
synced 2024-10-18 16:24:26 +00:00
improve testability: use error returnvalues instead of using exitProcess()
This commit is contained in:
parent
2cb1560bbd
commit
9bd3a6758a
@ -1 +1 @@
|
|||||||
7.0
|
7.1-dev
|
||||||
|
@ -16,22 +16,20 @@ import kotlin.system.exitProcess
|
|||||||
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
printSoftwareHeader("compiler")
|
|
||||||
|
|
||||||
compileMain(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun printSoftwareHeader(what: String) {
|
|
||||||
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
|
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
|
||||||
println("\nProg8 $what v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
println("\nProg8 compiler v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
||||||
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
||||||
|
|
||||||
|
val succes = compileMain(args)
|
||||||
|
if(!succes)
|
||||||
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
|
fun pathFrom(stringPath: String, vararg rest: String): Path = FileSystems.getDefault().getPath(stringPath, *rest)
|
||||||
|
|
||||||
|
|
||||||
private fun compileMain(args: Array<String>) {
|
private fun compileMain(args: Array<String>): Boolean {
|
||||||
val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
|
val cli = ArgParser("prog8compiler", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
|
||||||
val startEmulator by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
|
val startEmulator by cli.option(ArgType.Boolean, fullName = "emu", description = "auto-start emulator after successful compilation")
|
||||||
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||||
@ -47,19 +45,19 @@ private fun compileMain(args: Array<String>) {
|
|||||||
cli.parse(args)
|
cli.parse(args)
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
System.err.println(e.message)
|
System.err.println(e.message)
|
||||||
exitProcess(1)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val outputPath = pathFrom(outputDir)
|
val outputPath = pathFrom(outputDir)
|
||||||
if(!outputPath.toFile().isDirectory) {
|
if(!outputPath.toFile().isDirectory) {
|
||||||
System.err.println("Output path doesn't exist")
|
System.err.println("Output path doesn't exist")
|
||||||
exitProcess(1)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val faultyOption = moduleFiles.firstOrNull { it.startsWith('-') }
|
val faultyOption = moduleFiles.firstOrNull { it.startsWith('-') }
|
||||||
if(faultyOption!=null) {
|
if(faultyOption!=null) {
|
||||||
System.err.println("Unknown command line option given: $faultyOption")
|
System.err.println("Unknown command line option given: $faultyOption")
|
||||||
exitProcess(1)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
val libdirs = libDirs.toMutableList()
|
val libdirs = libDirs.toMutableList()
|
||||||
@ -114,11 +112,11 @@ private fun compileMain(args: Array<String>) {
|
|||||||
try {
|
try {
|
||||||
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
|
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
|
||||||
if(!compilationResult.success)
|
if(!compilationResult.success)
|
||||||
exitProcess(1)
|
return false
|
||||||
} catch (x: ParsingFailedError) {
|
} catch (x: ParsingFailedError) {
|
||||||
exitProcess(1)
|
return false
|
||||||
} catch (x: AstException) {
|
} catch (x: AstException) {
|
||||||
exitProcess(1)
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (startEmulator==true) {
|
if (startEmulator==true) {
|
||||||
@ -130,4 +128,6 @@ private fun compileMain(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import prog8.parser.moduleName
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.system.exitProcess
|
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
|
||||||
@ -81,10 +80,7 @@ fun compileProgram(filepath: Path,
|
|||||||
when(compilationTarget) {
|
when(compilationTarget) {
|
||||||
C64Target.name -> C64Target
|
C64Target.name -> C64Target
|
||||||
Cx16Target.name -> Cx16Target
|
Cx16Target.name -> Cx16Target
|
||||||
else -> {
|
else -> throw IllegalArgumentException("invalid compilation target")
|
||||||
System.err.println("invalid compilation target")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -102,8 +98,15 @@ fun compileProgram(filepath: Path,
|
|||||||
|
|
||||||
// printAst(programAst)
|
// printAst(programAst)
|
||||||
|
|
||||||
if(writeAssembly)
|
if(writeAssembly) {
|
||||||
programName = writeAssembly(programAst, errors, outputDir, compilationOptions)
|
val (success, message) = writeAssembly(programAst, errors, outputDir, compilationOptions)
|
||||||
|
if(success)
|
||||||
|
programName = message
|
||||||
|
else {
|
||||||
|
System.err.println(message)
|
||||||
|
return CompilationResult(false, programAst, programName, compTarget, importedFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
System.out.flush()
|
System.out.flush()
|
||||||
System.err.flush()
|
System.err.flush()
|
||||||
@ -198,10 +201,10 @@ private fun parseImports(filepath: Path,
|
|||||||
|
|
||||||
private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
||||||
val mainModule = program.mainModule
|
val mainModule = program.mainModule
|
||||||
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
val outputDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive)
|
||||||
as? Directive)?.args?.single()?.name?.uppercase()
|
val launcherDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive)
|
||||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
val outputTypeStr = outputDirective?.args?.single()?.name?.uppercase()
|
||||||
as? Directive)?.args?.single()?.name?.uppercase()
|
val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase()
|
||||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||||
as? Directive)?.args?.single()?.name?.uppercase()
|
as? Directive)?.args?.single()?.name?.uppercase()
|
||||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
|
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
|
||||||
@ -231,18 +234,26 @@ private fun determineCompilationOptions(program: Program, compTarget: ICompilati
|
|||||||
.map { it[0].int!!..it[1].int!! }
|
.map { it[0].int!!..it[1].int!! }
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
if (outputType != null && !OutputType.values().any { it.name == outputType }) {
|
val outputType = if (outputTypeStr == null) OutputType.PRG else {
|
||||||
System.err.println("invalid output type $outputType")
|
try {
|
||||||
exitProcess(1)
|
OutputType.valueOf(outputTypeStr)
|
||||||
|
} catch (x: IllegalArgumentException) {
|
||||||
|
// set default value; actual check and error handling of invalid option is handled in the AstChecker later
|
||||||
|
OutputType.PRG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val launcherType = if (launcherTypeStr == null) LauncherType.BASIC else {
|
||||||
|
try {
|
||||||
|
LauncherType.valueOf(launcherTypeStr)
|
||||||
|
} catch (x: IllegalArgumentException) {
|
||||||
|
// set default value; actual check and error handling of invalid option is handled in the AstChecker later
|
||||||
|
LauncherType.BASIC
|
||||||
}
|
}
|
||||||
if (launcherType != null && !LauncherType.values().any { it.name == launcherType }) {
|
|
||||||
System.err.println("invalid launcher type $launcherType")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return CompilationOptions(
|
return CompilationOptions(
|
||||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
outputType,
|
||||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
launcherType,
|
||||||
zpType, zpReserved, floatsEnabled, noSysInit,
|
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||||
compTarget
|
compTarget
|
||||||
)
|
)
|
||||||
@ -316,7 +327,7 @@ private fun postprocessAst(programAst: Program, errors: IErrorReporter, compiler
|
|||||||
private fun writeAssembly(programAst: Program,
|
private fun writeAssembly(programAst: Program,
|
||||||
errors: IErrorReporter,
|
errors: IErrorReporter,
|
||||||
outputDir: Path,
|
outputDir: Path,
|
||||||
compilerOptions: CompilationOptions): String {
|
compilerOptions: CompilationOptions): Pair<Boolean, String> {
|
||||||
// asm generation directly from the Ast
|
// asm generation directly from the Ast
|
||||||
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
||||||
errors.report()
|
errors.report()
|
||||||
@ -330,9 +341,13 @@ private fun writeAssembly(programAst: Program,
|
|||||||
compilerOptions.compTarget.machine.zeropage,
|
compilerOptions.compTarget.machine.zeropage,
|
||||||
compilerOptions,
|
compilerOptions,
|
||||||
outputDir).compileToAssembly()
|
outputDir).compileToAssembly()
|
||||||
assembly.assemble(compilerOptions)
|
val assemblerReturnStatus = assembly.assemble(compilerOptions)
|
||||||
|
return if(assemblerReturnStatus!=0)
|
||||||
|
Pair(false, "assembler step failed with return code $assemblerReturnStatus")
|
||||||
|
else {
|
||||||
errors.report()
|
errors.report()
|
||||||
return assembly.name
|
Pair(true, assembly.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printAst(programAst: Program) {
|
fun printAst(programAst: Program) {
|
||||||
|
@ -12,5 +12,5 @@ internal const val subroutineFloatEvalResultVar2 = "_prog8_float_eval_result2"
|
|||||||
|
|
||||||
internal interface IAssemblyProgram {
|
internal interface IAssemblyProgram {
|
||||||
val name: String
|
val name: String
|
||||||
fun assemble(options: CompilationOptions)
|
fun assemble(options: CompilationOptions): Int
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import prog8.compiler.OutputType
|
|||||||
import prog8.compiler.target.IAssemblyProgram
|
import prog8.compiler.target.IAssemblyProgram
|
||||||
import prog8.compiler.target.generatedLabelPrefix
|
import prog8.compiler.target.generatedLabelPrefix
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
class AssemblyProgram(override val name: String, outputDir: Path, private val compTarget: String) : IAssemblyProgram {
|
class AssemblyProgram(override val name: String, outputDir: Path, private val compTarget: String) : IAssemblyProgram {
|
||||||
private val assemblyFile = outputDir.resolve("$name.asm")
|
private val assemblyFile = outputDir.resolve("$name.asm")
|
||||||
@ -13,7 +12,7 @@ class AssemblyProgram(override val name: String, outputDir: Path, private val co
|
|||||||
private val binFile = outputDir.resolve("$name.bin")
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions) {
|
override fun assemble(options: CompilationOptions): Int {
|
||||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
@ -35,14 +34,12 @@ class AssemblyProgram(override val name: String, outputDir: Path, private val co
|
|||||||
|
|
||||||
val proc = ProcessBuilder(command).inheritIO().start()
|
val proc = ProcessBuilder(command).inheritIO().start()
|
||||||
val result = proc.waitFor()
|
val result = proc.waitFor()
|
||||||
if (result != 0) {
|
if (result == 0) {
|
||||||
System.err.println("assembler failed with returncode $result")
|
|
||||||
exitProcess(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
removeGeneratedLabelsFromMonlist()
|
removeGeneratedLabelsFromMonlist()
|
||||||
generateBreakpointList()
|
generateBreakpointList()
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private fun removeGeneratedLabelsFromMonlist() {
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""")
|
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""")
|
||||||
|
@ -2,7 +2,6 @@ package prog8tests
|
|||||||
|
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.Matchers.equalTo
|
import org.hamcrest.Matchers.equalTo
|
||||||
import org.junit.jupiter.api.Disabled
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
|
@ -2,17 +2,19 @@ package prog8tests
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import kotlin.test.*
|
|
||||||
import kotlin.io.path.*
|
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.compiler.target.Cx16Target
|
|
||||||
|
|
||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
|
import prog8.compiler.target.Cx16Target
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
import kotlin.io.path.isDirectory
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertIs
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,12 +2,14 @@ package prog8tests
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import kotlin.test.*
|
|
||||||
import kotlin.io.path.*
|
|
||||||
|
|
||||||
import prog8.compiler.target.Cx16Target
|
|
||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.ICompilationTarget
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.absolute
|
||||||
|
import kotlin.io.path.isDirectory
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,7 +5,6 @@ main {
|
|||||||
|
|
||||||
label:
|
label:
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
sub2(&label)
|
sub2(&label)
|
||||||
sub2(&label_local)
|
sub2(&label_local)
|
||||||
sub2(&main.sub2.label_in_sub2)
|
sub2(&main.sub2.label_in_sub2)
|
||||||
|
Loading…
Reference in New Issue
Block a user