diff --git a/codeCore/src/prog8/code/core/CompilationOptions.kt b/codeCore/src/prog8/code/core/CompilationOptions.kt index 16adf6eed..06c0ab0aa 100644 --- a/codeCore/src/prog8/code/core/CompilationOptions.kt +++ b/codeCore/src/prog8/code/core/CompilationOptions.kt @@ -29,6 +29,7 @@ class CompilationOptions(val output: OutputType, var slabsGolden: Boolean = false, var splitWordArrays: Boolean = false, var breakpointCpuInstruction: String? = null, + var ignoreFootguns: Boolean = false, var outputDir: Path = Path(""), var symbolDefs: Map = emptyMap() ) { diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index 554fb5be4..269724a34 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -7,6 +7,7 @@ import prog8.code.target.* import prog8.code.target.virtual.VirtualMachineDefinition import prog8.compiler.CompilationResult import prog8.compiler.CompilerArguments +import prog8.compiler.ErrorReporter import prog8.compiler.compileProgram import java.io.File import java.nio.file.FileSystems @@ -52,10 +53,12 @@ private fun compileMain(args: Array): Boolean { val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation") val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen") val float2bytes by cli.option(ArgType.String, fullName = "float2bytes", description = "convert floating point number to a list of bytes for the target system. NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!") + val ignoreFootguns by cli.option(ArgType.Boolean, fullName = "ignorefootguns", description = "don't print warnings for 'footgun' issues: 'Yes I know I'm treading on mighty thin ice here'.") val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code") val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations") val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".") val printAst1 by cli.option(ArgType.Boolean, fullName = "printast1", description = "print out the compiler AST") + val plainText by cli.option(ArgType.Boolean, fullName = "plaintext", description = "output only plain text, no colors or fancy symbols") val printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the intermediate AST that is used for code generation") val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results") val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.") @@ -162,6 +165,7 @@ private fun compileMain(args: Array): Boolean { results.clear() for(filepathRaw in moduleFiles) { val filepath = pathFrom(filepathRaw).normalize() + val txtcolors = if(plainText==true) ErrorReporter.PlainText else ErrorReporter.AnsiColors val compilerArgs = CompilerArguments( filepath, if(checkSource==true) false else dontOptimize != true, @@ -182,9 +186,11 @@ private fun compileMain(args: Array): Boolean { breakpointCpuInstruction, printAst1 == true, printAst2 == true, + ignoreFootguns == true, processedSymbols, srcdirs, - outputPath + outputPath, + errors = ErrorReporter(txtcolors) ) val compilationResult = compileProgram(compilerArgs) @@ -242,6 +248,7 @@ private fun compileMain(args: Array): Boolean { val filepath = pathFrom(filepathRaw).normalize() val compilationResult: CompilationResult try { + val txtcolors = if(plainText==true) ErrorReporter.PlainText else ErrorReporter.AnsiColors val compilerArgs = CompilerArguments( filepath, if(checkSource==true) false else dontOptimize != true, @@ -262,9 +269,11 @@ private fun compileMain(args: Array): Boolean { breakpointCpuInstruction, printAst1 == true, printAst2 == true, + ignoreFootguns == true, processedSymbols, srcdirs, - outputPath + outputPath, + errors = ErrorReporter(txtcolors) ) val result = compileProgram(compilerArgs) diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 19fb36b49..1aab71ee5 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -52,10 +52,11 @@ class CompilerArguments(val filepath: Path, val breakpointCpuInstruction: String?, val printAst1: Boolean, val printAst2: Boolean, + val ignoreFootguns: Boolean, val symbolDefs: Map, val sourceDirs: List = emptyList(), val outputDir: Path = Path(""), - val errors: IErrorReporter = ErrorReporter()) + val errors: IErrorReporter = ErrorReporter(ErrorReporter.AnsiColors)) fun compileProgram(args: CompilerArguments): CompilationResult? { @@ -81,6 +82,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? { dumpVariables = args.dumpVariables dumpSymbols = args.dumpSymbols breakpointCpuInstruction = args.breakpointCpuInstruction + ignoreFootguns = args.ignoreFootguns varsHighBank = args.varsHighBank varsGolden = args.varsGolden slabsHighBank = args.slabsHighBank @@ -337,7 +339,7 @@ fun parseMainModule(filepath: Path, return Triple(program, compilerOptions, importedFiles) } -fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions { +internal fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions { val toplevelModule = program.toplevelModule val outputDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive) val launcherDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive) diff --git a/compiler/src/prog8/compiler/ErrorReporter.kt b/compiler/src/prog8/compiler/ErrorReporter.kt index b0649c04e..6439d61c6 100644 --- a/compiler/src/prog8/compiler/ErrorReporter.kt +++ b/compiler/src/prog8/compiler/ErrorReporter.kt @@ -2,9 +2,9 @@ package prog8.compiler import prog8.code.core.IErrorReporter import prog8.code.core.Position +import java.io.PrintStream - -internal class ErrorReporter: IErrorReporter { +internal class ErrorReporter(val colors: IConsoleColors): IErrorReporter { private enum class MessageSeverity { INFO, WARNING, @@ -44,20 +44,27 @@ internal class ErrorReporter: IErrorReporter { when(it.severity) { MessageSeverity.ERROR -> { System.out.flush() - printer.print("\u001b[91mERROR\u001B[0m ") // bright red + colors.error(printer) + printer.print("ERROR ") + colors.normal(printer) numErrors++ } MessageSeverity.WARNING -> { - printer.print("\u001b[93mWARN\u001B[0m ") // bright yellow + colors.warning(printer) + printer.print("WARN ") + colors.normal(printer) numWarnings++ } MessageSeverity.INFO -> { - printer.print("\u001b[92mINFO\u001B[0m ") // bright green + colors.info(printer) + printer.print("INFO ") + colors.normal(printer) numInfos++ } } - printer.println(msg) - alreadyReportedMessages.add(msg) + val filtered = colors.filtered(msg) + printer.println(filtered) + alreadyReportedMessages.add(filtered) } } System.out.flush() @@ -69,4 +76,28 @@ internal class ErrorReporter: IErrorReporter { override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR } override fun noErrorForLine(position: Position) = !messages.any { it.position.line==position.line && it.severity!=MessageSeverity.INFO } + + interface IConsoleColors { + fun error(printer: PrintStream) + fun warning(printer: PrintStream) + fun info(printer: PrintStream) + fun normal(printer: PrintStream) + fun filtered(msg: String): String + } + + object AnsiColors: IConsoleColors { + override fun error(printer: PrintStream) = printer.print("\u001b[91m") // red + override fun warning(printer: PrintStream) = printer.print("\u001b[93m") // yellow + override fun info(printer: PrintStream) = printer.print("\u001b[92m") // green + override fun normal(printer: PrintStream) = printer.print("\u001B[0m") + override fun filtered(msg: String): String = msg + } + + object PlainText: IConsoleColors { + override fun error(printer: PrintStream) {} + override fun warning(printer: PrintStream) {} + override fun info(printer: PrintStream) {} + override fun normal(printer: PrintStream) {} + override fun filtered(msg: String): String = msg.filter { !it.isSurrogate() } + } } diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 32a8f6775..8aecc05ad 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -516,7 +516,8 @@ internal class AstChecker(private val program: Program, if (!subroutine.isAsmSubroutine && p.registerOrPair!=null) { if (p.registerOrPair !in Cx16VirtualRegisters) errors.err("can only use R0-R15 as register param for normal subroutines", p.position) else { - errors.warn("\uD83D\uDCA3 footgun: reusing R0-R15 as parameters risks overwriting due to clobbering or no callstack", subroutine.position) + if(!compilerOptions.ignoreFootguns) + errors.warn("\uD83D\uDCA3 footgun: reusing R0-R15 as parameters risks overwriting due to clobbering or no callstack", subroutine.position) if(p.type !in WordDatatypes && p.type !in ByteDatatypesWithBoolean) { errors.err("can only use register param when type is boolean, byte or word", p.position) } @@ -929,8 +930,10 @@ internal class AstChecker(private val program: Program, if(decl.datatype==DataType.STR) errors.err("string variables cannot be @dirty", decl.position) else { - if(decl.value==null) - errors.warn("\uD83D\uDCA3 footgun: dirty variable, initial value will be undefined", decl.position) + if(decl.value==null) { + if(!compilerOptions.ignoreFootguns) + errors.warn("\uD83D\uDCA3 footgun: dirty variable, initial value will be undefined", decl.position) + } else errors.err("dirty variable can't have initialization value", decl.position) } diff --git a/compiler/test/TestCompilerOnExamples.kt b/compiler/test/TestCompilerOnExamples.kt index ba33343c3..521791e1a 100644 --- a/compiler/test/TestCompilerOnExamples.kt +++ b/compiler/test/TestCompilerOnExamples.kt @@ -43,6 +43,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat breakpointCpuInstruction = null, printAst1 = false, printAst2 = false, + ignoreFootguns = false, symbolDefs = emptyMap(), outputDir = outputDir ) diff --git a/compiler/test/TestCompilerOptionLibdirs.kt b/compiler/test/TestCompilerOptionLibdirs.kt index f0961cf7c..f17748f46 100644 --- a/compiler/test/TestCompilerOptionLibdirs.kt +++ b/compiler/test/TestCompilerOptionLibdirs.kt @@ -41,6 +41,7 @@ class TestCompilerOptionSourcedirs: FunSpec({ breakpointCpuInstruction = null, printAst1 = false, printAst2 = false, + ignoreFootguns = false, symbolDefs = emptyMap(), sourceDirs, outputDir diff --git a/compiler/test/helpers/compileXyz.kt b/compiler/test/helpers/compileXyz.kt index 28ae0e11d..9a3025d3c 100644 --- a/compiler/test/helpers/compileXyz.kt +++ b/compiler/test/helpers/compileXyz.kt @@ -43,6 +43,7 @@ internal fun compileFile( breakpointCpuInstruction = null, printAst1 = false, printAst2 = false, + ignoreFootguns = false, ) return compileProgram(args) } diff --git a/docs/source/compiling.rst b/docs/source/compiling.rst index 94b1a4571..a1fbc129a 100644 --- a/docs/source/compiling.rst +++ b/docs/source/compiling.rst @@ -176,6 +176,12 @@ One or more .p8 module files NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well! Also see -bytes2float +``-ignorefootguns`` + Don't print warnings for 'footgun' issues. + Footgun issues are certain things you can do in Prog8 that may make your program blow up unexpectedly, + for instance uncareful use of dirty variables, or reusing the R0-R15 registers for subroutine parameters. + With this option you're basically saying: "Yes, I know I am treading on mighty thin ice here, but I don't want to be reminded about that". + ``-noasm`` Do not create assembly code and output program. Useful for debugging or doing quick syntax checks. @@ -187,6 +193,9 @@ One or more .p8 module files ``-out `` sets directory location for output files instead of current directory +``-plaintext`` + Prints output messages in plain text: no colors or fancy symbols. + ``-printast1`` Prints the "compiler AST" (the internal representation of the program) after all processing steps. diff --git a/docs/source/todo.rst b/docs/source/todo.rst index d709fbbcb..df3a13e26 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,7 +1,6 @@ TODO ==== -add a -plain option to turn off text output coloring and unicode symbols in error messages (footguns) make a compiler switch to disable footgun warnings -ignorefootguns update zsmkit to newest version that includes the on_deck routines when stabilized