mirror of
https://github.com/irmen/prog8.git
synced 2025-02-16 07:31:48 +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>) {
|
||||
printSoftwareHeader("compiler")
|
||||
|
||||
compileMain(args)
|
||||
}
|
||||
|
||||
internal fun printSoftwareHeader(what: String) {
|
||||
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")
|
||||
|
||||
val succes = compileMain(args)
|
||||
if(!succes)
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
|
||||
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 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(".")
|
||||
@ -47,19 +45,19 @@ private fun compileMain(args: Array<String>) {
|
||||
cli.parse(args)
|
||||
} catch (e: IllegalStateException) {
|
||||
System.err.println(e.message)
|
||||
exitProcess(1)
|
||||
return false
|
||||
}
|
||||
|
||||
val outputPath = pathFrom(outputDir)
|
||||
if(!outputPath.toFile().isDirectory) {
|
||||
System.err.println("Output path doesn't exist")
|
||||
exitProcess(1)
|
||||
return false
|
||||
}
|
||||
|
||||
val faultyOption = moduleFiles.firstOrNull { it.startsWith('-') }
|
||||
if(faultyOption!=null) {
|
||||
System.err.println("Unknown command line option given: $faultyOption")
|
||||
exitProcess(1)
|
||||
return false
|
||||
}
|
||||
|
||||
val libdirs = libDirs.toMutableList()
|
||||
@ -114,11 +112,11 @@ private fun compileMain(args: Array<String>) {
|
||||
try {
|
||||
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
|
||||
if(!compilationResult.success)
|
||||
exitProcess(1)
|
||||
return false
|
||||
} catch (x: ParsingFailedError) {
|
||||
exitProcess(1)
|
||||
return false
|
||||
} catch (x: AstException) {
|
||||
exitProcess(1)
|
||||
return false
|
||||
}
|
||||
|
||||
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.InputStream
|
||||
import java.nio.file.Path
|
||||
import kotlin.system.exitProcess
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
|
||||
@ -81,10 +80,7 @@ fun compileProgram(filepath: Path,
|
||||
when(compilationTarget) {
|
||||
C64Target.name -> C64Target
|
||||
Cx16Target.name -> Cx16Target
|
||||
else -> {
|
||||
System.err.println("invalid compilation target")
|
||||
exitProcess(1)
|
||||
}
|
||||
else -> throw IllegalArgumentException("invalid compilation target")
|
||||
}
|
||||
|
||||
try {
|
||||
@ -102,8 +98,15 @@ fun compileProgram(filepath: Path,
|
||||
|
||||
// printAst(programAst)
|
||||
|
||||
if(writeAssembly)
|
||||
programName = writeAssembly(programAst, errors, outputDir, compilationOptions)
|
||||
if(writeAssembly) {
|
||||
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.err.flush()
|
||||
@ -198,10 +201,10 @@ private fun parseImports(filepath: Path,
|
||||
|
||||
private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
||||
val mainModule = program.mainModule
|
||||
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val outputDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive)
|
||||
val launcherDirective = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive)
|
||||
val outputTypeStr = outputDirective?.args?.single()?.name?.uppercase()
|
||||
val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase()
|
||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
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!! }
|
||||
.toList()
|
||||
|
||||
if (outputType != null && !OutputType.values().any { it.name == outputType }) {
|
||||
System.err.println("invalid output type $outputType")
|
||||
exitProcess(1)
|
||||
val outputType = if (outputTypeStr == null) OutputType.PRG else {
|
||||
try {
|
||||
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
|
||||
}
|
||||
}
|
||||
if (launcherType != null && !LauncherType.values().any { it.name == launcherType }) {
|
||||
System.err.println("invalid launcher type $launcherType")
|
||||
exitProcess(1)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return CompilationOptions(
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
outputType,
|
||||
launcherType,
|
||||
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||
compTarget
|
||||
)
|
||||
@ -316,7 +327,7 @@ private fun postprocessAst(programAst: Program, errors: IErrorReporter, compiler
|
||||
private fun writeAssembly(programAst: Program,
|
||||
errors: IErrorReporter,
|
||||
outputDir: Path,
|
||||
compilerOptions: CompilationOptions): String {
|
||||
compilerOptions: CompilationOptions): Pair<Boolean, String> {
|
||||
// asm generation directly from the Ast
|
||||
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
@ -330,9 +341,13 @@ private fun writeAssembly(programAst: Program,
|
||||
compilerOptions.compTarget.machine.zeropage,
|
||||
compilerOptions,
|
||||
outputDir).compileToAssembly()
|
||||
assembly.assemble(compilerOptions)
|
||||
errors.report()
|
||||
return assembly.name
|
||||
val assemblerReturnStatus = assembly.assemble(compilerOptions)
|
||||
return if(assemblerReturnStatus!=0)
|
||||
Pair(false, "assembler step failed with return code $assemblerReturnStatus")
|
||||
else {
|
||||
errors.report()
|
||||
Pair(true, assembly.name)
|
||||
}
|
||||
}
|
||||
|
||||
fun printAst(programAst: Program) {
|
||||
|
@ -12,5 +12,5 @@ internal const val subroutineFloatEvalResultVar2 = "_prog8_float_eval_result2"
|
||||
|
||||
internal interface IAssemblyProgram {
|
||||
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.generatedLabelPrefix
|
||||
import java.nio.file.Path
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class AssemblyProgram(override val name: String, outputDir: Path, private val compTarget: String) : IAssemblyProgram {
|
||||
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 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)
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||
@ -35,13 +34,11 @@ class AssemblyProgram(override val name: String, outputDir: Path, private val co
|
||||
|
||||
val proc = ProcessBuilder(command).inheritIO().start()
|
||||
val result = proc.waitFor()
|
||||
if (result != 0) {
|
||||
System.err.println("assembler failed with returncode $result")
|
||||
exitProcess(result)
|
||||
if (result == 0) {
|
||||
removeGeneratedLabelsFromMonlist()
|
||||
generateBreakpointList()
|
||||
}
|
||||
|
||||
removeGeneratedLabelsFromMonlist()
|
||||
generateBreakpointList()
|
||||
return result
|
||||
}
|
||||
|
||||
private fun removeGeneratedLabelsFromMonlist() {
|
||||
|
@ -2,7 +2,6 @@ package prog8tests
|
||||
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.IBuiltinFunctions
|
||||
|
@ -2,17 +2,19 @@ package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import kotlin.test.*
|
||||
import kotlin.io.path.*
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.compiler.target.Cx16Target
|
||||
|
||||
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
|
||||
|
||||
|
||||
/**
|
||||
@ -135,4 +137,4 @@ class TestCompilerOnCharLit {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
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.target.Cx16Target
|
||||
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
|
||||
|
||||
|
||||
/**
|
||||
@ -75,4 +77,4 @@ class TestCompilerOnExamples {
|
||||
testExample("animals", Cx16Target, true)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ main {
|
||||
|
||||
label:
|
||||
sub start() {
|
||||
|
||||
sub2(&label)
|
||||
sub2(&label_local)
|
||||
sub2(&main.sub2.label_in_sub2)
|
||||
|
Loading…
x
Reference in New Issue
Block a user