mirror of
https://github.com/irmen/prog8.git
synced 2024-12-22 18:30:01 +00:00
added -dumpsymbols option to print a dump of all the variables and subroutine signatures
This commit is contained in:
parent
19ebc6d6b3
commit
2aae46d632
@ -52,7 +52,7 @@ What does Prog8 provide?
|
||||
------------------------
|
||||
|
||||
- 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
|
||||
- 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)
|
||||
@ -71,7 +71,7 @@ What does Prog8 provide?
|
||||
- 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
|
||||
- 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:*
|
||||
|
||||
|
@ -20,6 +20,7 @@ class CompilationOptions(val output: OutputType,
|
||||
var asmListfile: Boolean = false,
|
||||
var includeSourcelines: Boolean = false,
|
||||
var dumpVariables: Boolean = false,
|
||||
var dumpSymbols: Boolean = false,
|
||||
var experimentalCodegen: Boolean = false,
|
||||
var varsHighBank: Int? = null,
|
||||
var varsGolden: Boolean = false,
|
||||
|
@ -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 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 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 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")
|
||||
@ -155,6 +156,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
includeSourcelines == true,
|
||||
experimentalCodegen == true,
|
||||
dumpVariables == true,
|
||||
dumpSymbols == true,
|
||||
varsHighBank,
|
||||
varsGolden == true,
|
||||
slabsHighBank,
|
||||
@ -235,6 +237,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
includeSourcelines == true,
|
||||
experimentalCodegen == true,
|
||||
dumpVariables == true,
|
||||
dumpSymbols==true,
|
||||
varsHighBank,
|
||||
varsGolden == true,
|
||||
slabsHighBank,
|
||||
|
@ -7,6 +7,7 @@ import prog8.ast.base.AstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.printProgram
|
||||
import prog8.ast.printSymbols
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.code.SymbolTableMaker
|
||||
import prog8.code.ast.PtProgram
|
||||
@ -22,6 +23,7 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.nameWithoutExtension
|
||||
import kotlin.math.round
|
||||
import kotlin.system.exitProcess
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
|
||||
@ -39,6 +41,7 @@ class CompilerArguments(val filepath: Path,
|
||||
val includeSourcelines: Boolean,
|
||||
val experimentalCodegen: Boolean,
|
||||
val dumpVariables: Boolean,
|
||||
val dumpSymbols: Boolean,
|
||||
val varsHighBank: Int?,
|
||||
val varsGolden: Boolean,
|
||||
val slabsHighBank: Int?,
|
||||
@ -71,7 +74,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
var compilationOptions: CompilationOptions
|
||||
var ast: PtProgram? = null
|
||||
var resultingProgram: Program? = null
|
||||
var importedFiles: List<Path> = emptyList()
|
||||
var importedFiles: List<Path>
|
||||
|
||||
try {
|
||||
val totalTime = measureTimeMillis {
|
||||
@ -86,6 +89,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
includeSourcelines = args.includeSourcelines
|
||||
experimentalCodegen = args.experimentalCodegen
|
||||
dumpVariables = args.dumpVariables
|
||||
dumpSymbols = args.dumpSymbols
|
||||
breakpointCpuInstruction = args.breakpointCpuInstruction
|
||||
varsHighBank = args.varsHighBank
|
||||
varsGolden = args.varsGolden
|
||||
@ -402,6 +406,12 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
|
||||
|
||||
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
program.preprocessAst(errors, compilerOptions)
|
||||
|
||||
if(compilerOptions.dumpSymbols) {
|
||||
printSymbols(program)
|
||||
exitProcess(0)
|
||||
}
|
||||
|
||||
program.checkIdentifiers(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.charLiteralsToUByteLiterals(compilerOptions.compTarget, errors)
|
||||
|
@ -33,6 +33,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
dumpVariables = false,
|
||||
dumpSymbols = false,
|
||||
varsHighBank = null,
|
||||
varsGolden = false,
|
||||
slabsHighBank = null,
|
||||
|
@ -31,6 +31,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
dumpVariables = false,
|
||||
dumpSymbols = false,
|
||||
varsHighBank = null,
|
||||
varsGolden = false,
|
||||
slabsHighBank = null,
|
||||
|
@ -22,7 +22,7 @@ class TestAstToSourceText: AnnotationSpec() {
|
||||
.addModule(module)
|
||||
|
||||
var generatedText = ""
|
||||
val it = AstToSourceTextConverter({ str -> generatedText += str }, program)
|
||||
val it = AstToSourceTextConverter({ str -> generatedText += str }, program, true)
|
||||
it.visit(program)
|
||||
|
||||
return generatedText
|
||||
|
@ -30,6 +30,7 @@ internal fun compileFile(
|
||||
includeSourcelines = false,
|
||||
experimentalCodegen = false,
|
||||
dumpVariables = false,
|
||||
dumpSymbols = false,
|
||||
varsHighBank = null,
|
||||
varsGolden = false,
|
||||
slabsHighBank = null,
|
||||
|
@ -8,7 +8,7 @@ import prog8.code.core.*
|
||||
|
||||
fun printProgram(program: Program) {
|
||||
println()
|
||||
val printer = AstToSourceTextConverter(::print, program)
|
||||
val printer = AstToSourceTextConverter(::print, program, true)
|
||||
printer.visit(program)
|
||||
println()
|
||||
}
|
||||
@ -18,7 +18,7 @@ fun printProgram(program: Program) {
|
||||
* Produces Prog8 source text from a [Program] (AST node),
|
||||
* 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 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) {
|
||||
if(!module.isLibrary) {
|
||||
if(!module.isLibrary || !skipLibraries) {
|
||||
outputln("; ----------- module: ${module.name} -----------")
|
||||
super.visit(module)
|
||||
}
|
||||
|
153
compilerAst/src/prog8/ast/SymbolPrinter.kt
Normal file
153
compilerAst/src/prog8/ast/SymbolPrinter.kt
Normal 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")
|
||||
}
|
||||
}
|
@ -26,12 +26,13 @@ You can compile programs for various machines with this CPU:
|
||||
* Commodore PET (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
|
||||
^^^^^^^^^^^^^^^^
|
||||
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)
|
||||
*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*.
|
||||
@ -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
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Language features
|
||||
-----------------
|
||||
|
||||
- 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
|
||||
- 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
|
||||
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)
|
||||
- Modular programming and scoping via modules, code blocks, and subroutines.
|
||||
- No need for forward declarations.
|
||||
- the language looks like a mix of Python and C so should be quite easy to learn
|
||||
- Modular programming, scoping via modules, code blocks, and subroutines. No need for forward declarations.
|
||||
- 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,
|
||||
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
|
||||
- Variables are all allocated statically
|
||||
- Conditional branches for status flags that map 1:1 to processor branch instructions
|
||||
- Variables are all allocated statically, no memory allocator overhead
|
||||
- Conditional branches for status flags that map 1:1 to processor branch instructions for optimal efficiency
|
||||
- ``when`` statement to avoid if-else chains
|
||||
- ``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``
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- Advanced code optimizations, such as const-folding (zero-allocation constants that are optimized away in expressions), expression and statement simplifications/rewriting.
|
||||
- Programs can be run multiple times without reloading because of 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.
|
||||
- Support for low level system features such as Vera Fx hardware word multiplication on the Commander X16
|
||||
- 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)
|
||||
- Advanced code optimizations to make the resulting program smaller and faster
|
||||
- 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. These are also available on the other compilation targets!
|
||||
- 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.
|
||||
- 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
|
||||
|
@ -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 verafx
|
||||
%import conv
|
||||
%import cx16logo
|
||||
%import math
|
||||
%import prog8_lib
|
||||
%import string
|
||||
%import test_stack
|
||||
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared x,y = multi()
|
||||
}
|
||||
|
||||
asmsub multi() -> ubyte @A, ubyte @Y {
|
||||
%asm {{
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user