added -dumpsymbols option to print a dump of all the variables and subroutine signatures

This commit is contained in:
Irmen de Jong 2024-04-09 18:30:56 +02:00
parent 19ebc6d6b3
commit 2aae46d632
12 changed files with 219 additions and 33 deletions

View File

@ -52,7 +52,7 @@ What does Prog8 provide?
------------------------ ------------------------
- all advantages of a higher level language over having to write assembly code manually - all advantages of a higher level language over having to write assembly code manually
- programs run very fast because compilation to native machine code. It's possible to write games purely in Prog8, and even certain raster interrupt 'demoscene' effects. - programs run very fast because compilation to native machine code
- modularity, symbol scoping, subroutines - modularity, symbol scoping, subroutines
- various data types other than just bytes (16-bit words, floats, strings) - various data types other than just bytes (16-bit words, floats, strings)
- floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do) - floating point math is supported if the target system provides floating point library routines (C64 and Cx16 both do)
@ -71,7 +71,7 @@ What does Prog8 provide?
- convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses - convenience abstractions for low level aspects such as ZeroPage handling, program startup, explicit memory addresses
- inline assembly allows you to have full control when every cycle or byte matters - inline assembly allows you to have full control when every cycle or byte matters
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64. - supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16, and provides them also on the C64.
- encode strings and characters into petscii or screencodes as desired (C64/Cx16) - encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
*Rapid edit-compile-run-debug cycle:* *Rapid edit-compile-run-debug cycle:*

View File

@ -20,6 +20,7 @@ class CompilationOptions(val output: OutputType,
var asmListfile: Boolean = false, var asmListfile: Boolean = false,
var includeSourcelines: Boolean = false, var includeSourcelines: Boolean = false,
var dumpVariables: Boolean = false, var dumpVariables: Boolean = false,
var dumpSymbols: Boolean = false,
var experimentalCodegen: Boolean = false, var experimentalCodegen: Boolean = false,
var varsHighBank: Int? = null, var varsHighBank: Int? = null,
var varsGolden: Boolean = false, var varsGolden: Boolean = false,

View File

@ -43,6 +43,7 @@ private fun compileMain(args: Array<String>): Boolean {
val startEmulator2 by cli.option(ArgType.Boolean, fullName = "emu2", description = "auto-start alternative emulator after successful compilation") 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 experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental/alternative codegen")
val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program") val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program")
val dumpSymbols by cli.option(ArgType.Boolean, fullName = "dumpsymbols", description = "print a dump of the variables + subroutine definitions")
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code") val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
val noStrictBool by cli.option(ArgType.Boolean, fullName = "nostrictbool", description = "allow implicit conversions between bool and bytes") val noStrictBool by cli.option(ArgType.Boolean, fullName = "nostrictbool", description = "allow implicit conversions between bool and bytes")
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations") val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations")
@ -155,6 +156,7 @@ private fun compileMain(args: Array<String>): Boolean {
includeSourcelines == true, includeSourcelines == true,
experimentalCodegen == true, experimentalCodegen == true,
dumpVariables == true, dumpVariables == true,
dumpSymbols == true,
varsHighBank, varsHighBank,
varsGolden == true, varsGolden == true,
slabsHighBank, slabsHighBank,
@ -235,6 +237,7 @@ private fun compileMain(args: Array<String>): Boolean {
includeSourcelines == true, includeSourcelines == true,
experimentalCodegen == true, experimentalCodegen == true,
dumpVariables == true, dumpVariables == true,
dumpSymbols==true,
varsHighBank, varsHighBank,
varsGolden == true, varsGolden == true,
slabsHighBank, slabsHighBank,

View File

@ -7,6 +7,7 @@ import prog8.ast.base.AstException
import prog8.ast.expressions.Expression import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.printProgram import prog8.ast.printProgram
import prog8.ast.printSymbols
import prog8.ast.statements.Directive import prog8.ast.statements.Directive
import prog8.code.SymbolTableMaker import prog8.code.SymbolTableMaker
import prog8.code.ast.PtProgram import prog8.code.ast.PtProgram
@ -22,6 +23,7 @@ import java.nio.file.Path
import kotlin.io.path.Path import kotlin.io.path.Path
import kotlin.io.path.nameWithoutExtension import kotlin.io.path.nameWithoutExtension
import kotlin.math.round import kotlin.math.round
import kotlin.system.exitProcess
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -39,6 +41,7 @@ class CompilerArguments(val filepath: Path,
val includeSourcelines: Boolean, val includeSourcelines: Boolean,
val experimentalCodegen: Boolean, val experimentalCodegen: Boolean,
val dumpVariables: Boolean, val dumpVariables: Boolean,
val dumpSymbols: Boolean,
val varsHighBank: Int?, val varsHighBank: Int?,
val varsGolden: Boolean, val varsGolden: Boolean,
val slabsHighBank: Int?, val slabsHighBank: Int?,
@ -71,7 +74,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
var compilationOptions: CompilationOptions var compilationOptions: CompilationOptions
var ast: PtProgram? = null var ast: PtProgram? = null
var resultingProgram: Program? = null var resultingProgram: Program? = null
var importedFiles: List<Path> = emptyList() var importedFiles: List<Path>
try { try {
val totalTime = measureTimeMillis { val totalTime = measureTimeMillis {
@ -86,6 +89,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
includeSourcelines = args.includeSourcelines includeSourcelines = args.includeSourcelines
experimentalCodegen = args.experimentalCodegen experimentalCodegen = args.experimentalCodegen
dumpVariables = args.dumpVariables dumpVariables = args.dumpVariables
dumpSymbols = args.dumpSymbols
breakpointCpuInstruction = args.breakpointCpuInstruction breakpointCpuInstruction = args.breakpointCpuInstruction
varsHighBank = args.varsHighBank varsHighBank = args.varsHighBank
varsGolden = args.varsGolden varsGolden = args.varsGolden
@ -402,6 +406,12 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) { private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
program.preprocessAst(errors, compilerOptions) program.preprocessAst(errors, compilerOptions)
if(compilerOptions.dumpSymbols) {
printSymbols(program)
exitProcess(0)
}
program.checkIdentifiers(errors, compilerOptions) program.checkIdentifiers(errors, compilerOptions)
errors.report() errors.report()
program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors) program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors)

View File

@ -33,6 +33,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
includeSourcelines = false, includeSourcelines = false,
experimentalCodegen = false, experimentalCodegen = false,
dumpVariables = false, dumpVariables = false,
dumpSymbols = false,
varsHighBank = null, varsHighBank = null,
varsGolden = false, varsGolden = false,
slabsHighBank = null, slabsHighBank = null,

View File

@ -31,6 +31,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
includeSourcelines = false, includeSourcelines = false,
experimentalCodegen = false, experimentalCodegen = false,
dumpVariables = false, dumpVariables = false,
dumpSymbols = false,
varsHighBank = null, varsHighBank = null,
varsGolden = false, varsGolden = false,
slabsHighBank = null, slabsHighBank = null,

View File

@ -22,7 +22,7 @@ class TestAstToSourceText: AnnotationSpec() {
.addModule(module) .addModule(module)
var generatedText = "" var generatedText = ""
val it = AstToSourceTextConverter({ str -> generatedText += str }, program) val it = AstToSourceTextConverter({ str -> generatedText += str }, program, true)
it.visit(program) it.visit(program)
return generatedText return generatedText

View File

@ -30,6 +30,7 @@ internal fun compileFile(
includeSourcelines = false, includeSourcelines = false,
experimentalCodegen = false, experimentalCodegen = false,
dumpVariables = false, dumpVariables = false,
dumpSymbols = false,
varsHighBank = null, varsHighBank = null,
varsGolden = false, varsGolden = false,
slabsHighBank = null, slabsHighBank = null,

View File

@ -8,7 +8,7 @@ import prog8.code.core.*
fun printProgram(program: Program) { fun printProgram(program: Program) {
println() println()
val printer = AstToSourceTextConverter(::print, program) val printer = AstToSourceTextConverter(::print, program, true)
printer.visit(program) printer.visit(program)
println() println()
} }
@ -18,7 +18,7 @@ fun printProgram(program: Program) {
* Produces Prog8 source text from a [Program] (AST node), * Produces Prog8 source text from a [Program] (AST node),
* passing it as a String to the specified receiver function. * passing it as a String to the specified receiver function.
*/ */
class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: Program): IAstVisitor { class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: Program, val skipLibraries: Boolean): IAstVisitor {
private var scopelevel = 0 private var scopelevel = 0
private fun indent(s: String) = " ".repeat(scopelevel) + s private fun indent(s: String) = " ".repeat(scopelevel) + s
@ -33,7 +33,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
} }
override fun visit(module: Module) { override fun visit(module: Module) {
if(!module.isLibrary) { if(!module.isLibrary || !skipLibraries) {
outputln("; ----------- module: ${module.name} -----------") outputln("; ----------- module: ${module.name} -----------")
super.visit(module) super.visit(module)
} }

View File

@ -0,0 +1,153 @@
package prog8.ast
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.core.DataType
import prog8.code.core.ZeropageWish
import prog8.code.core.toHex
fun printSymbols(program: Program) {
println()
val printer = SymbolPrinter(::print, program, false)
printer.visit(program)
println()
}
class SymbolPrinter(val output: (text: String) -> Unit, val program: Program, val skipLibraries: Boolean): IAstVisitor {
private fun outputln(text: String) = output(text + "\n")
override fun visit(module: Module) {
if(!module.isLibrary || !skipLibraries) {
if(module.source.isFromFilesystem || module.source.isFromResources) {
outputln("MODULE FILE: ${module.source.origin}")
super.visit(module)
output("\n")
}
}
}
override fun visit(block: Block) {
outputln("${block.name} {")
val (vars, subs) = block.statements.filter{ it is Subroutine || it is VarDecl }.partition { it is VarDecl }
for(variable in vars.sortedBy { (it as VarDecl).name }) {
output(" ")
variable.accept(this)
}
for(subroutine in subs.sortedBy { (it as Subroutine).name }) {
output(" ")
subroutine.accept(this)
}
outputln("}\n")
}
private fun datatypeString(dt: DataType): String {
return when (dt) {
DataType.BOOL -> "bool"
DataType.UBYTE -> "ubyte"
DataType.BYTE -> "byte"
DataType.UWORD -> "uword"
DataType.WORD -> "word"
DataType.LONG -> "long"
DataType.FLOAT -> "float"
DataType.STR -> "str"
DataType.ARRAY_UB -> "ubyte["
DataType.ARRAY_B -> "byte["
DataType.ARRAY_UW -> "uword["
DataType.ARRAY_W -> "word["
DataType.ARRAY_F -> "float["
DataType.ARRAY_BOOL -> "bool["
DataType.ARRAY_UW_SPLIT -> "@split uword["
DataType.ARRAY_W_SPLIT -> "@split word["
DataType.UNDEFINED -> throw IllegalArgumentException("wrong dt")
}
}
override fun visit(decl: VarDecl) {
if(decl.origin==VarDeclOrigin.SUBROUTINEPARAM)
return
when(decl.type) {
VarDeclType.VAR -> {}
VarDeclType.CONST -> output("const ")
VarDeclType.MEMORY -> output("&")
}
output(datatypeString(decl.datatype))
if(decl.arraysize!=null) {
decl.arraysize!!.indexExpr.accept(this)
}
if(decl.isArray)
output("]")
if(decl.zeropage == ZeropageWish.REQUIRE_ZEROPAGE)
output(" @requirezp")
else if(decl.zeropage == ZeropageWish.PREFER_ZEROPAGE)
output(" @zp")
if(decl.sharedWithAsm)
output(" @shared")
output(" ")
if(decl.names.size>1)
output(decl.names.joinToString(prefix=" "))
else
output(" ${decl.name} ")
output("\n")
}
override fun visit(subroutine: Subroutine) {
if(subroutine.isAsmSubroutine) {
output("${subroutine.name} (")
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
val reg =
when {
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
param.second.statusflag!=null -> param.second.statusflag.toString()
else -> "?????"
}
output("${datatypeString(param.first.type)} ${param.first.name} @$reg")
if(param.first!==subroutine.parameters.last())
output(", ")
}
}
else {
output("${subroutine.name} (")
for(param in subroutine.parameters) {
output("${datatypeString(param.type)} ${param.name}")
if(param!==subroutine.parameters.last())
output(", ")
}
}
output(") ")
if(subroutine.asmClobbers.isNotEmpty()) {
output("-> clobbers (")
val regs = subroutine.asmClobbers.toList().sorted()
for(r in regs) {
output(r.toString())
if(r!==regs.last())
output(",")
}
output(") ")
}
if(subroutine.returntypes.any()) {
if(subroutine.asmReturnvaluesRegisters.isNotEmpty()) {
val rts = subroutine.returntypes.zip(subroutine.asmReturnvaluesRegisters).joinToString(", ") {
val dtstr = datatypeString(it.first)
if(it.second.registerOrPair!=null)
"$dtstr @${it.second.registerOrPair}"
else
"$dtstr @${it.second.statusflag}"
}
output("-> $rts ")
} else {
val rts = subroutine.returntypes.joinToString(", ") { datatypeString(it) }
output("-> $rts ")
}
}
if(subroutine.asmAddress!=null)
output("= ${subroutine.asmAddress.toHex()}")
output("\n")
}
}

View File

@ -26,12 +26,13 @@ You can compile programs for various machines with this CPU:
* Commodore PET (limited support) * Commodore PET (limited support)
* Atari 800 XL (limited support) * Atari 800 XL (limited support)
The source code is on github: https://github.com/irmen/prog8.git
Prog8 is copyright © Irmen de Jong (irmen@razorvine.net | http://www.razorvine.net).
The project is on github: https://github.com/irmen/prog8.git
Software License Software License
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
Prog8 is copyright © Irmen de Jong (irmen@razorvine.net | http://www.razorvine.net).
This is free software, as defined in the GNU GPL 3.0 (https://www.gnu.org/licenses/gpl.html) This is free software, as defined in the GNU GPL 3.0 (https://www.gnu.org/licenses/gpl.html)
*Exception:* All output files generated by the compiler (intermediary files and compiled binary programs) *Exception:* All output files generated by the compiler (intermediary files and compiled binary programs)
are excluded from this particular license: you can do with those *whatever you want*. are excluded from this particular license: you can do with those *whatever you want*.
@ -65,36 +66,38 @@ you can help me out a little bit over at https://ko-fi.com/irmen .
:alt: Chess program for the X16 :alt: Chess program for the X16
Features
--------
Language features - it is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
----------------- - the compiled programs run very fast, because compilation to highly efficient native machine code.
- Provides a convenient and fast edit/compile/run cycle by being able to directly launch
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
- Programs run very fast because compilation to native machine code. It's possible to write games purely in Prog8, and even certain raster interrupt 'demoscene' effects.
- Provides a very convenient edit/compile/run cycle by being able to directly launch
the compiled program in an emulator and provide debugging information to this emulator. the compiled program in an emulator and provide debugging information to this emulator.
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python) - the language looks like a mix of Python and C so should be quite easy to learn
- Modular programming and scoping via modules, code blocks, and subroutines. - Modular programming, scoping via modules, code blocks, and subroutines. No need for forward declarations.
- No need for forward declarations.
- Provide high level programming constructs but at the same time stay close to the metal; - Provide high level programming constructs but at the same time stay close to the metal;
still able to directly use memory addresses and ROM subroutines, still able to directly use memory addresses and ROM subroutines,
and inline assembly to have full control when every register, cycle or byte matters and inline assembly to have full control when every register, cycle or byte matters
- Subroutines with parameters and return values - Subroutines with parameters and return values of various types
- Complex nested expressions are possible - Complex nested expressions are possible
- Variables are all allocated statically - Variables are all allocated statically, no memory allocator overhead
- Conditional branches for status flags that map 1:1 to processor branch instructions - Conditional branches for status flags that map 1:1 to processor branch instructions for optimal efficiency
- ``when`` statement to avoid if-else chains - ``when`` statement to avoid if-else chains
- ``in`` expression for concise and efficient multi-value/containment test - ``in`` expression for concise and efficient multi-value/containment test
- Several powerful built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``, ``sort`` and ``reverse`` - Several powerful built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``, ``sort`` and ``reverse``
- Variable data types include signed and unsigned bytes and words, arrays, strings. - Variable data types include signed and unsigned bytes and words, arrays, strings.
- Various powerful built-in libraries to do I/O, number conversions, graphics and more
- Floating point math is supported on select compiler targets. - Floating point math is supported on select compiler targets.
- Easy and highly efficient integration with external subroutines and ROM routines on the target systems.
- Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents. - Strings can contain escaped characters but also many symbols directly if they have a PETSCII equivalent, such as "♠♥♣♦π▚●○╳". Characters like ^, _, \\, {, } and | are also accepted and converted to the closest PETSCII equivalents.
- Encode strings and characters into petscii or screencodes or even other encodings, as desired (C64/Cx16)
- Identifiers can contain Unicode Letters, so ``knäckebröd``, ``приблизительно``, ``見せしめ`` and ``π`` are all valid identifiers. - Identifiers can contain Unicode Letters, so ``knäckebröd``, ``приблизительно``, ``見せしめ`` and ``π`` are all valid identifiers.
- Advanced code optimizations, such as const-folding (zero-allocation constants that are optimized away in expressions), expression and statement simplifications/rewriting. - Advanced code optimizations to make the resulting program smaller and faster
- Programs can be run multiple times without reloading because of automatic variable (re)initializations. - Programs can be restarted after exiting (i.e. run them multiple times without having to reload everything), due to automatic variable (re)initializations.
- Supports the sixteen 'virtual' 16-bit registers R0 to R15 as defined on the Commander X16, also on the other machines. - Supports the sixteen 'virtual' 16-bit registers R0 to R15 as defined on the Commander X16. These are also available on the other compilation targets!
- Support for low level system features such as Vera Fx hardware word multiplication on the Commander X16 - On the Commander X16: Support for low level system features such as Vera Fx, which includes 16x16 bits multiplication in hardware and fast memory copy and fill.
- If you only use standard Kernal and core prog8 library routines, it is sometimes possible to compile the *exact same program* for different machines (just change the compilation target flag) - Many library routines are available across compiler targets. This means that as long as you only use standard Kernal
and core prog8 library routines, it is sometimes possible to compile the *exact same program* for different machines (just change the compilation target flag).
Code example Code example

View File

@ -1,15 +1,28 @@
%import bmx
%import diskio
%import emudbg
%import floats
%import gfx2
%import graphics
%import monogfx
%import palette
%import psg
%import sprites
%import syslib
%import textio %import textio
%import verafx
%import conv
%import cx16logo
%import math
%import prog8_lib
%import string
%import test_stack
%zeropage basicsafe %zeropage basicsafe
%option no_sysinit %option no_sysinit
main { main {
sub start() { sub start() {
ubyte @shared x,y = multi()
}
asmsub multi() -> ubyte @A, ubyte @Y {
%asm {{
rts
}}
} }
} }