mirror of
https://github.com/irmen/prog8.git
synced 2024-12-01 15:52:54 +00:00
started a c++ language compiler code target
(meant to be an intermediate step before direct Wasm/binaryen, via clang compilation to wasm)
This commit is contained in:
parent
8c2e602cc7
commit
165eec4054
@ -7,6 +7,8 @@ import prog8.compiler.target.CompilationTarget
|
|||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
|
import prog8.compiler.target.clang.ClangGen
|
||||||
|
import prog8.compiler.target.clang.ClangMachineDefinition
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
import prog8.vm.astvm.AstVm
|
import prog8.vm.astvm.AstVm
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
@ -60,7 +62,12 @@ private fun compileMain(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"clang" -> {
|
"clang" -> {
|
||||||
TODO("clang target")
|
with(CompilationTarget) {
|
||||||
|
name = "clang"
|
||||||
|
machine = ClangMachineDefinition
|
||||||
|
encodeString = { str -> str.toByteArray().map { it.toShort()} }
|
||||||
|
asmGenerator = ::ClangGen
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
System.err.println("invalid compilation target")
|
System.err.println("invalid compilation target")
|
||||||
|
331
compiler/src/prog8/compiler/target/clang/ClangGen.kt
Normal file
331
compiler/src/prog8/compiler/target/clang/ClangGen.kt
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
package prog8.compiler.target.clang
|
||||||
|
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
|
import prog8.compiler.CompilationOptions
|
||||||
|
import prog8.compiler.Zeropage
|
||||||
|
import prog8.compiler.target.IAssemblyGenerator
|
||||||
|
import prog8.compiler.target.IAssemblyProgram
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
|
internal class ClangGen(private val program: Program,
|
||||||
|
private val zeropage: Zeropage,
|
||||||
|
private val options: CompilationOptions,
|
||||||
|
private val outputDir: Path) : IAssemblyGenerator {
|
||||||
|
override fun compileToAssembly(optimize: Boolean): IAssemblyProgram {
|
||||||
|
|
||||||
|
println("Generating C++ language code... [UNFINISHED, EXPERIMENTAL]")
|
||||||
|
|
||||||
|
// TODO manually flatten all scopes. Needed because in C++ even with forward declarations it's not possible to freely access members across all classes
|
||||||
|
|
||||||
|
val outputFile = outputDir.resolve("${program.name}.cpp").toFile()
|
||||||
|
outputFile.printWriter().use { out ->
|
||||||
|
header(out)
|
||||||
|
program.allBlocks().forEach { block(out, it) }
|
||||||
|
footer(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClangAssemblyProgram(program.name, outputDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun header(out: PrintWriter) {
|
||||||
|
out.println("// c++ transpiled version of Prog8 program ${program.name}\n")
|
||||||
|
out.println("#include <cstdint>")
|
||||||
|
out.println("""
|
||||||
|
template <typename T,T iBegin,T iEnd,T iStep=1>
|
||||||
|
class _prog8_range {
|
||||||
|
public:
|
||||||
|
struct iterator {
|
||||||
|
T value;
|
||||||
|
iterator (T v) : value(v) {}
|
||||||
|
operator T () const { return value; }
|
||||||
|
operator T& () { return value; }
|
||||||
|
T operator* () const { return value; }
|
||||||
|
iterator& operator++ () { value += iStep; return *this; }
|
||||||
|
};
|
||||||
|
iterator begin() { return iBegin; }
|
||||||
|
iterator end() { return iEnd; }
|
||||||
|
};
|
||||||
|
|
||||||
|
void _prog8_inlineasm(const char* assembly) { /* nothing */ }
|
||||||
|
void _prog8_goto_addr(uint16_t addr) { /* nothing */ }
|
||||||
|
void _prog8_directive_asminclude(const char* module, const char* scopename) { /* nothing */ }
|
||||||
|
|
||||||
|
uint8_t _prog8_register_A = 0;
|
||||||
|
uint8_t _prog8_register_X = 0;
|
||||||
|
uint8_t _prog8_register_Y = 0;
|
||||||
|
|
||||||
|
uint8_t _prog8_memory[65536];
|
||||||
|
|
||||||
|
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun footer(out: PrintWriter) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun block(out: PrintWriter, block: Block) {
|
||||||
|
out.println("// block: $block")
|
||||||
|
out.println("class ${cname(block.name)} {")
|
||||||
|
out.println("\tconst char* _prog8_linepos = \"${block.position}\";")
|
||||||
|
out.print("\tvoid* _prog8_block_address = ")
|
||||||
|
if (block.address == null)
|
||||||
|
out.println("nullptr;")
|
||||||
|
else {
|
||||||
|
out.println("0x${block.address.toString(16)};")
|
||||||
|
}
|
||||||
|
out.println("public:")
|
||||||
|
block.statements.forEach { statement(out, it) }
|
||||||
|
out.println("};\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun statement(out: PrintWriter, stmt: Statement) {
|
||||||
|
when (stmt) {
|
||||||
|
is BuiltinFunctionStatementPlaceholder -> TODO("$stmt")
|
||||||
|
is Directive -> directive(out, stmt)
|
||||||
|
is Label -> out.println("${cname(stmt.name)}:")
|
||||||
|
is Return -> {
|
||||||
|
if (stmt.parent is Subroutine) {
|
||||||
|
if (stmt.value == null)
|
||||||
|
out.println("return;")
|
||||||
|
else {
|
||||||
|
out.println("return ${expression(stmt.value!!)};")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is Continue -> out.println("continue;")
|
||||||
|
is Break -> out.println("break;")
|
||||||
|
is VarDecl -> {
|
||||||
|
val sub = stmt.definingSubroutine()
|
||||||
|
if (sub == null || sub.parameters.all { it.name != stmt.name })
|
||||||
|
vardecl(out, stmt)
|
||||||
|
}
|
||||||
|
is Assignment -> assignment(out, stmt)
|
||||||
|
is PostIncrDecr -> out.println("${assigntarget(stmt.target)}${stmt.operator};")
|
||||||
|
is Jump -> jump(out, stmt)
|
||||||
|
is FunctionCallStatement -> out.println("${stmt.target.nameInSource.joinToString("::")}(${arguments(stmt.arglist)});")
|
||||||
|
is InlineAssembly -> {
|
||||||
|
val asmlines = stmt.assembly.lines().map { "\"$it\"" }.joinToString("\n")
|
||||||
|
out.println("_prog8_inlineasm($asmlines);")
|
||||||
|
}
|
||||||
|
is AnonymousScope -> {
|
||||||
|
out.println("{")
|
||||||
|
stmt.statements.forEach { statement(out, it) }
|
||||||
|
out.println("}")
|
||||||
|
}
|
||||||
|
is NopStatement -> out.println("// NOP")
|
||||||
|
is Subroutine -> {
|
||||||
|
if (stmt.isAsmSubroutine) {
|
||||||
|
out.println("// TODO ASM-SUBROUTINE: ${stmt.name} @ ${stmt.position}") // TODO
|
||||||
|
} else {
|
||||||
|
val returnType = datatype(stmt.returntypes.singleOrNull(), null)
|
||||||
|
val params = stmt.parameters.map {
|
||||||
|
val dt = datatype(it.type, null)
|
||||||
|
"${dt.first} ${cname(it.name)}${dt.second}"
|
||||||
|
}
|
||||||
|
val paramstr = params.joinToString(", ")
|
||||||
|
out.println("\nstatic ${returnType.first} ${cname(stmt.name)}${returnType.second}($paramstr) {")
|
||||||
|
out.println("\tconst char* _prog8_linepos = \"${stmt.position}\";")
|
||||||
|
stmt.statements.forEach { statement(out, it) }
|
||||||
|
out.println("}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IfStatement -> ifstatement(out, stmt)
|
||||||
|
is BranchStatement -> branchstatement(out, stmt)
|
||||||
|
is ForLoop -> forloop(out, stmt)
|
||||||
|
is WhileLoop -> whileloop(out, stmt)
|
||||||
|
is RepeatLoop -> repeatloop(out, stmt)
|
||||||
|
is WhenStatement -> out.println("// stmt: $stmt") // TODO
|
||||||
|
is StructDecl -> out.println("// stmt: $stmt") // TODO
|
||||||
|
else -> throw AssemblyError("c++ translation error: unexpected statement: $stmt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assignment(out: PrintWriter, assign: Assignment) {
|
||||||
|
val target = assigntarget(assign.target)
|
||||||
|
val operator = assign.aug_op ?: "="
|
||||||
|
out.println("$target $operator ${expression(assign.value)};")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assigntarget(target: AssignTarget): String {
|
||||||
|
return when {
|
||||||
|
target.register!=null -> "_prog8_register_${target.register}"
|
||||||
|
target.identifier!=null -> expression(target.identifier!!)
|
||||||
|
target.memoryAddress!=null -> "(*(uint8_t*)${expression(target.memoryAddress.addressExpression)}) "
|
||||||
|
target.arrayindexed!=null -> expression(target.arrayindexed!!)
|
||||||
|
else -> "?????"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun jump(out: PrintWriter, jmp: Jump) {
|
||||||
|
when {
|
||||||
|
jmp.generatedLabel!=null -> out.println("goto ${jmp.generatedLabel};")
|
||||||
|
jmp.address!=null -> out.println("_prog8_goto_addr(${jmp.address.toString(16)});")
|
||||||
|
jmp.identifier!=null -> out.println("goto ${expression(jmp.identifier)};")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forloop(out: PrintWriter, forlp: ForLoop) {
|
||||||
|
out.println("// forloop $forlp") // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun repeatloop(out: PrintWriter, repeatLoop: RepeatLoop) {
|
||||||
|
out.println("do {")
|
||||||
|
statement(out, repeatLoop.body)
|
||||||
|
out.println("} while(!(${expression(repeatLoop.untilCondition)}));")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun whileloop(out: PrintWriter, whileLoop: WhileLoop) {
|
||||||
|
out.println("while(${expression(whileLoop.condition)}) {")
|
||||||
|
statement(out, whileLoop.body)
|
||||||
|
out.println("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ifstatement(out: PrintWriter, ifs: IfStatement) {
|
||||||
|
out.println("if(${expression(ifs.condition)}) {")
|
||||||
|
statement(out, ifs.truepart)
|
||||||
|
if(ifs.elsepart.containsCodeOrVars()) {
|
||||||
|
out.println("} else {")
|
||||||
|
statement(out, ifs.elsepart)
|
||||||
|
}
|
||||||
|
out.println("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun branchstatement(out: PrintWriter, branch: BranchStatement) {
|
||||||
|
out.println("if(BranchFlag.${branch.condition}) {")
|
||||||
|
statement(out, branch.truepart)
|
||||||
|
if(branch.elsepart.containsCodeOrVars()) {
|
||||||
|
out.println("} else {")
|
||||||
|
statement(out, branch.elsepart)
|
||||||
|
}
|
||||||
|
out.println("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun directive(out: PrintWriter, dr: Directive) {
|
||||||
|
val args = dr.args.map {
|
||||||
|
when {
|
||||||
|
it.int!=null -> it.int.toString()
|
||||||
|
it.name!=null -> "\"${it.name}\""
|
||||||
|
it.str!=null -> "\"${it.str}\""
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.println("_prog8_directive_${dr.directive.substring(1)}(${args.joinToString(", ")});")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun arguments(args: List<Expression>): String = args.map{expression(it)}.joinToString(", ")
|
||||||
|
|
||||||
|
private fun vardecl(out: PrintWriter, decl: VarDecl) {
|
||||||
|
fun cdecl(memory: Boolean) {
|
||||||
|
val dtype = datatype(decl.datatype, decl.arraysize)
|
||||||
|
out.print(dtype.first)
|
||||||
|
if (memory) {
|
||||||
|
out.print("* ${cname(decl.name)}")
|
||||||
|
} else {
|
||||||
|
out.print(" ${cname(decl.name)}${dtype.second}")
|
||||||
|
}
|
||||||
|
if (decl.value != null) {
|
||||||
|
// TODO c++ requires the value initialization to be done outside the class in a static assignment...
|
||||||
|
if (memory)
|
||||||
|
out.print(" = static_cast<${dtype.first}*>(${expression(decl.value!!)})")
|
||||||
|
else
|
||||||
|
out.print(" = ${expression(decl.value!!)}")
|
||||||
|
}
|
||||||
|
// TODO zeropage flag
|
||||||
|
out.println(";")
|
||||||
|
}
|
||||||
|
|
||||||
|
out.print("static ")
|
||||||
|
when (decl.type) {
|
||||||
|
VarDeclType.VAR -> {
|
||||||
|
cdecl(false)
|
||||||
|
}
|
||||||
|
VarDeclType.CONST -> {
|
||||||
|
out.print("constexpr const ")
|
||||||
|
cdecl(false)
|
||||||
|
}
|
||||||
|
VarDeclType.MEMORY -> {
|
||||||
|
out.print("constexpr const ")
|
||||||
|
cdecl(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun expression(expr: Expression): String {
|
||||||
|
return when (expr) {
|
||||||
|
is PrefixExpression -> "${expr.operator}${expression(expr.expression)}"
|
||||||
|
is BinaryExpression -> "${expression(expr.left)} ${expr.operator} ${expression(expr.right)}"
|
||||||
|
is ArrayIndexedExpression -> "${expression(expr.identifier)}[${expression(expr.arrayspec.index)}]"
|
||||||
|
is TypecastExpression -> "static_cast<${datatype(expr.type, null)}>(${expression(expr.expression)}) "
|
||||||
|
is AddressOf -> "&${expr.identifier.nameInSource.joinToString("::")}"
|
||||||
|
is DirectMemoryRead -> "_prog8_memory[${expression(expr.addressExpression)}]"
|
||||||
|
is NumericLiteralValue -> "${expr.number}"
|
||||||
|
is StructLiteralValue -> TODO("struct literal")
|
||||||
|
is StringLiteralValue -> "\"${expr.value}\""
|
||||||
|
is ArrayLiteralValue -> arrayliteral(expr)
|
||||||
|
is RangeExpr -> rangeexpr(expr)
|
||||||
|
is RegisterExpr -> "_prog8_register_${expr.register}"
|
||||||
|
is IdentifierReference -> expr.nameInSource.joinToString("::")
|
||||||
|
is FunctionCall -> functioncall(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rangeexpr(range: RangeExpr): String {
|
||||||
|
val constrange = range.toConstantIntegerRange()
|
||||||
|
return if(constrange!=null) {
|
||||||
|
if(constrange.first>=0 && constrange.last>=0)
|
||||||
|
"_prog8_range<uint16_t, ${constrange.first}, ${constrange.last}, ${constrange.step}>()"
|
||||||
|
else
|
||||||
|
"_prog8_range<int16_t, ${constrange.first}, ${constrange.last}, ${constrange.step}>()"
|
||||||
|
} else {
|
||||||
|
TODO("rangeexpr $range")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun functioncall(fc: FunctionCall): String {
|
||||||
|
val args = fc.arglist.map { expression(it) }.joinToString(", ")
|
||||||
|
return "${expression(fc.target)}($args)"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun arrayliteral(arraylv: ArrayLiteralValue): String {
|
||||||
|
val values = arraylv.value.map { expression(it) }.joinToString(", ")
|
||||||
|
return "{ $values }"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cname(name: String): String {
|
||||||
|
return if (name in listOf("char", "int", "short", "void")) name + "__c_" else name
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun datatype(dt: DataType?, arraysize: ArrayIndex?): Pair<String, String> {
|
||||||
|
return when (dt) {
|
||||||
|
DataType.UBYTE -> Pair("uint8_t", "")
|
||||||
|
DataType.BYTE -> Pair("int8_t", "")
|
||||||
|
DataType.UWORD -> Pair("uint16_t", "")
|
||||||
|
DataType.WORD -> Pair("int16_t", "")
|
||||||
|
DataType.FLOAT -> Pair("float", "")
|
||||||
|
DataType.STR -> Pair("const char*", "")
|
||||||
|
DataType.STR_S -> Pair("const char*", "")
|
||||||
|
DataType.ARRAY_UB -> if (arraysize == null) Pair("uint8_t", "[]") else Pair("uint8_t", "[${expression(arraysize.index)}]")
|
||||||
|
DataType.ARRAY_B -> if (arraysize == null) Pair("int8_t", "[]") else Pair("int8_t", "[${expression(arraysize.index)}]")
|
||||||
|
DataType.ARRAY_UW -> if (arraysize == null) Pair("uint16_t", "[]") else Pair("uint16_t", "[${expression(arraysize.index)}]")
|
||||||
|
DataType.ARRAY_W -> if (arraysize == null) Pair("int16_t", "[]") else Pair("int16_t", "[${expression(arraysize.index)}]")
|
||||||
|
DataType.ARRAY_F -> if (arraysize == null) Pair("float", "[]") else Pair("float", "[${expression(arraysize.index)}]")
|
||||||
|
DataType.STRUCT -> TODO("struct")
|
||||||
|
null -> Pair("void", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClangAssemblyProgram(override val name: String, outputDir: Path) : IAssemblyProgram {
|
||||||
|
override fun assemble(options: CompilationOptions) {
|
||||||
|
println("TODO C++ COMPILE?")
|
||||||
|
TODO("not implemented - use g++ or clang to compile the code")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package prog8.compiler.target.clang
|
||||||
|
|
||||||
|
import prog8.compiler.CompilationOptions
|
||||||
|
import prog8.compiler.Zeropage
|
||||||
|
import prog8.compiler.target.IMachineDefinition
|
||||||
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
|
|
||||||
|
object ClangMachineDefinition: IMachineDefinition {
|
||||||
|
override val FLOAT_MAX_NEGATIVE: Double = C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
|
override val FLOAT_MAX_POSITIVE: Double = C64MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
|
override val FLOAT_MEM_SIZE: Int = C64MachineDefinition.FLOAT_MEM_SIZE
|
||||||
|
override val opcodeNames: Set<String> = C64MachineDefinition.opcodeNames
|
||||||
|
|
||||||
|
override fun getZeropage(compilerOptions: CompilationOptions): Zeropage = ClangZeropage(compilerOptions)
|
||||||
|
|
||||||
|
class ClangZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
override val exitProgramStrategy: ExitProgramStrategy = ExitProgramStrategy.SYSTEM_RESET
|
||||||
|
init {
|
||||||
|
for (reserved in options.zpReserved)
|
||||||
|
reserve(reserved)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user