diff --git a/codeAst/src/prog8/code/ast/AstBase.kt b/codeAst/src/prog8/code/ast/AstBase.kt index c7b144a96..1ef0b4058 100644 --- a/codeAst/src/prog8/code/ast/AstBase.kt +++ b/codeAst/src/prog8/code/ast/AstBase.kt @@ -3,6 +3,7 @@ package prog8.code.ast import prog8.code.core.IMemSizer import prog8.code.core.IStringEncoding import prog8.code.core.Position +import java.util.* // New (work-in-progress) simplified AST for the code generator. @@ -62,6 +63,9 @@ class PtProgram( print("'$name'") } + fun allModuleDirectives(): Sequence = + children.asSequence().flatMap { it.children }.filterIsInstance().distinct() + fun allBlocks(): Sequence = children.asSequence().flatMap { it.children }.filterIsInstance() @@ -72,7 +76,7 @@ class PtProgram( class PtModule( name: String, - val loadAddress: UInt, + val loadAddress: UInt?, val library: Boolean, position: Position ) : PtNamedNode(name, position) { @@ -100,6 +104,18 @@ class PtDirective(var name: String, position: Position) : PtNode(position) { override fun printProperties() { print(name) } + + override fun hashCode(): Int { + return Objects.hash(name, args) + } + + override fun equals(other: Any?): Boolean { + if(other !is PtDirective) + return false + if(other===this) + return true + return(name==other.name && args.zip(other.args).all { it.first==it.second }) + } } @@ -111,6 +127,18 @@ class PtDirectiveArg(val str: String?, override fun printProperties() { print("str=$str name=$name int=$int") } + + override fun hashCode(): Int { + return Objects.hash(str, name, int) + } + + override fun equals(other: Any?): Boolean { + if(other !is PtDirectiveArg) + return false + if(other===this) + return true + return str==other.str || name==other.name || int==other.int + } } diff --git a/codeAst/src/prog8/code/ast/AstExpressions.kt b/codeAst/src/prog8/code/ast/AstExpressions.kt index 04dc93d38..2169e7c26 100644 --- a/codeAst/src/prog8/code/ast/AstExpressions.kt +++ b/codeAst/src/prog8/code/ast/AstExpressions.kt @@ -5,76 +5,115 @@ import prog8.code.core.Encoding import prog8.code.core.Position -class PtAddressOf(position: Position) : PtNode(position) { - val identifier: PtIdentifier - get() = children.single() as PtIdentifier - - override fun printProperties() {} -} - - -class PtArrayIndexer(position: Position): PtNode(position) { - val variable: PtIdentifier - get() = children[0] as PtIdentifier - val index: PtNode - get() = children[1] - - override fun printProperties() {} -} - - -class PtArrayLiteral(val type: DataType, position: Position): PtNode(position) { +sealed class PtExpression(val type: DataType, position: Position) : PtNode(position) { override fun printProperties() { print(type) } } -class PtBinaryExpression(val operator: String, position: Position): PtNode(position) { - val left: PtNode - get() = children[0] - val right: PtNode - get() = children[1] +class PtAddressOf(position: Position) : PtExpression(DataType.UWORD, position) { + val identifier: PtIdentifier + get() = children.single() as PtIdentifier +} + +class PtArrayIndexer(type: DataType, position: Position): PtExpression(type, position) { + val variable: PtIdentifier + get() = children[0] as PtIdentifier + val index: PtExpression + get() = children[1] as PtExpression +} + + +class PtArrayLiteral(type: DataType, position: Position): PtExpression(type, position) + + +class PtBuiltinFunctionCall(val name: String, val void: Boolean, type: DataType, position: Position) : PtExpression(type, position) { + init { + if(!void) + require(type!=DataType.UNDEFINED) + } + + val args: List + get() = children.map { it as PtExpression } override fun printProperties() { - print(operator) + print("$name void=$void") } } -class PtContainmentCheck(position: Position): PtNode(position) { - val element: PtNode - get() = children[0] - val iterable: PtNode - get() = children[0] - override fun printProperties() {} -} +class PtBinaryExpression(val operator: String, type: DataType, position: Position): PtExpression(type, position) { + val left: PtExpression + get() = children[0] as PtExpression + val right: PtExpression + get() = children[1] as PtExpression - -class PtIdentifier(val ref: List, val targetName: List, position: Position) : PtNode(position) { override fun printProperties() { - print("$ref --> $targetName") + print("$operator -> $type") } } -class PtMemoryByte(position: Position) : PtNode(position) { - val address: PtNode - get() = children.single() +class PtContainmentCheck(position: Position): PtExpression(DataType.UBYTE, position) { + val element: PtExpression + get() = children[0] as PtExpression + val iterable: PtExpression + get() = children[0] as PtExpression +} + + +class PtFunctionCall(val functionName: List, + val void: Boolean, + type: DataType, + position: Position) : PtExpression(type, position) { + init { + if(!void) + require(type!=DataType.UNDEFINED) + } + + val args: List + get() = children.map { it as PtExpression } + override fun printProperties() { + print("${functionName.joinToString(".")} void=$void") + } +} + + +class PtIdentifier(val ref: List, val targetName: List, type: DataType, position: Position) : PtExpression(type, position) { + override fun printProperties() { + print("$ref --> $targetName $type") + } +} + + +class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position) { + val address: PtExpression + get() = children.single() as PtExpression override fun printProperties() {} } -class PtNumber(val type: DataType, val number: Double, position: Position) : PtNode(position) { +class PtNumber(type: DataType, val number: Double, position: Position) : PtExpression(type, position) { override fun printProperties() { print("$number ($type)") } } -class PtPrefix(val operator: String, position: Position): PtNode(position) { - val value: PtNode - get() = children.single() +class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpression(type, position) { + init { + if(!void) + require(type!=DataType.UNDEFINED) + } + + override fun printProperties() {} +} + + +class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) { + val value: PtExpression + get() = children.single() as PtExpression override fun printProperties() { print(operator) @@ -82,27 +121,26 @@ class PtPrefix(val operator: String, position: Position): PtNode(position) { } -class PtRange(position: Position) : PtNode(position) { - val from: PtNode - get() = children[0] - val to: PtNode - get() = children[1] - val step: PtNode - get() = children[2] +class PtRange(type: DataType, position: Position) : PtExpression(type, position) { + val from: PtExpression + get() = children[0] as PtExpression + val to: PtExpression + get() = children[1] as PtExpression + val step: PtExpression + get() = children[2] as PtExpression override fun printProperties() {} } -class PtString(val value: String, val encoding: Encoding, position: Position) : PtNode(position) { +class PtString(val value: String, val encoding: Encoding, position: Position) : PtExpression(DataType.STR, position) { override fun printProperties() { print("$encoding:\"$value\"") } } -class PtTypeCast(val type: DataType, position: Position) : PtNode(position) { - override fun printProperties() { - print(type) - } -} \ No newline at end of file +class PtTypeCast(type: DataType, position: Position) : PtExpression(type, position) { + val value: PtExpression + get() = children.single() as PtExpression +} diff --git a/codeAst/src/prog8/code/ast/AstStatements.kt b/codeAst/src/prog8/code/ast/AstStatements.kt index 63083b07b..3058285a0 100644 --- a/codeAst/src/prog8/code/ast/AstStatements.kt +++ b/codeAst/src/prog8/code/ast/AstStatements.kt @@ -1,13 +1,14 @@ package prog8.code.ast import prog8.code.core.* +import javax.xml.crypto.Data class PtAsmSub( name: String, val address: UInt?, val clobbers: Set, - val paramRegisters: List, + val parameters: List>, val retvalRegisters: List, val inline: Boolean, position: Position @@ -41,8 +42,8 @@ class PtSubroutineParameter(val name: String, val type: DataType, position: Posi class PtAssignment(val augmentable: Boolean, position: Position) : PtNode(position) { val target: PtAssignTarget get() = children[0] as PtAssignTarget - val value: PtNode - get() = children[1] + val value: PtExpression + get() = children[1] as PtExpression override fun printProperties() { print("aug=$augmentable") @@ -62,13 +63,6 @@ class PtAssignTarget(position: Position) : PtNode(position) { } -class PtBuiltinFunctionCall(val name: String, position: Position) : PtNode(position) { - override fun printProperties() { - print(name) - } -} - - class PtConditionalBranch(val condition: BranchCondition, position: Position) : PtNode(position) { val trueScope: PtNodeGroup get() = children[0] as PtNodeGroup @@ -84,8 +78,8 @@ class PtConditionalBranch(val condition: BranchCondition, position: Position) : class PtForLoop(position: Position) : PtNode(position) { val variable: PtIdentifier get() = children[0] as PtIdentifier - val iterable: PtNode - get() = children[1] + val iterable: PtExpression + get() = children[1] as PtExpression val statements: PtNodeGroup get() = children[2] as PtNodeGroup @@ -93,18 +87,6 @@ class PtForLoop(position: Position) : PtNode(position) { } -class PtFunctionCall(val void: Boolean, position: Position) : PtNode(position) { - val target: PtIdentifier - get() = children[0] as PtIdentifier - val args: PtNodeGroup - get() = children[1] as PtNodeGroup - - override fun printProperties() { - print("void=$void") - } -} - - class PtGosub(val identifier: PtIdentifier?, val address: UInt?, val generatedLabel: String?, @@ -118,8 +100,8 @@ class PtGosub(val identifier: PtIdentifier?, class PtIfElse(position: Position) : PtNode(position) { - val condition: PtNode - get() = children[0] + val condition: PtExpression + get() = children[0] as PtExpression val ifScope: PtNodeGroup get() = children[1] as PtNodeGroup val elseScope: PtNodeGroup @@ -141,11 +123,6 @@ class PtJump(val identifier: PtIdentifier?, } -class PtPipe(position: Position) : PtNode(position) { - override fun printProperties() {} -} - - class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position) { val target: PtAssignTarget get() = children.single() as PtAssignTarget @@ -157,8 +134,10 @@ class PtPostIncrDecr(val operator: String, position: Position) : PtNode(position class PtRepeatLoop(position: Position) : PtNode(position) { - val count: PtNode - get() = children.single() + val count: PtExpression + get() = children[0] as PtExpression + val statements: PtNodeGroup + get() = children[1] as PtNodeGroup override fun printProperties() {} } @@ -166,10 +145,10 @@ class PtRepeatLoop(position: Position) : PtNode(position) { class PtReturn(position: Position) : PtNode(position) { val hasValue = children.any() - val value: PtNode? + val value: PtExpression? get() { return if(children.any()) - children.single() + children.single() as PtExpression else null } @@ -200,8 +179,8 @@ class PtMemMapped(name: String, val type: DataType, val address: UInt, position: class PtWhen(position: Position) : PtNode(position) { - val value: PtNode - get() = children[0] + val value: PtExpression + get() = children[0] as PtExpression val choices: PtNodeGroup get() = children[1] as PtNodeGroup @@ -210,5 +189,9 @@ class PtWhen(position: Position) : PtNode(position) { class PtWhenChoice(val isElse: Boolean, position: Position) : PtNode(position) { + val values: PtNodeGroup + get() = children[0] as PtNodeGroup + val statements: PtNodeGroup + get() = children[1] as PtNodeGroup override fun printProperties() {} } diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt index c1dc17342..b795bb9be 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/ProgramAndVarsGen.kt @@ -65,7 +65,7 @@ internal class ProgramAndVarsGen( asmgen.out("") asmgen.out(".cpu '$cpu'\n.enc 'none'\n") - program.actualLoadAddress = program.definedLoadAddress + program.actualLoadAddress = program.definedLoadAddress ?: 0u if (program.actualLoadAddress == 0u) { when(options.output) { OutputType.RAW -> { diff --git a/codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt b/codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt index b0f64d853..dfa5f0989 100644 --- a/codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt +++ b/codeGenExperimental/src/prog8/codegen/experimental/AsmGen.kt @@ -1,11 +1,11 @@ package prog8.codegen.experimental import prog8.code.SymbolTable -import prog8.code.ast.PtProgram -import prog8.code.core.CompilationOptions -import prog8.code.core.IAssemblyGenerator -import prog8.code.core.IAssemblyProgram -import prog8.code.core.IErrorReporter +import prog8.code.ast.* +import prog8.code.core.* +import javax.xml.stream.XMLOutputFactory +import kotlin.io.path.Path +import kotlin.io.path.div /* @@ -17,21 +17,578 @@ import prog8.code.core.IErrorReporter */ + class AsmGen(internal val program: PtProgram, internal val errors: IErrorReporter, internal val symbolTable: SymbolTable, internal val options: CompilationOptions ): IAssemblyGenerator { + private lateinit var xml: IndentingXmlWriter + override fun compileToAssembly(): IAssemblyProgram? { println("\n** experimental code generator **\n") - symbolTable.print() - symbolTable.flat.forEach { println(it) } - program.print() - + val writer = (options.outputDir / Path(program.name+"-ast.xml")).toFile().printWriter() + xml = IndentingXmlWriter(XMLOutputFactory.newFactory().createXMLStreamWriter(writer)) + xml.doc() + xml.elt("program") + xml.attr("name", program.name) + xml.startChildren() + program.allModuleDirectives().forEach { writeNode(it) } + program.children.forEach { writeNode(it) } + xml.endElt() + xml.endDoc() + xml.close() println("..todo: create assembly code into ${options.outputDir.toAbsolutePath()}..") return AssemblyProgram("dummy") } + + private fun writeNode(it: PtNode) { + when(it) { + is PtModule -> write(it) + is PtBlock -> write(it) + is PtDirective -> write(it) + is PtSub -> write(it) + is PtVariable -> write(it) + is PtAssignment -> write(it) + is PtConstant -> write(it) + is PtAsmSub -> write(it) + is PtAddressOf -> write(it) + is PtArrayIndexer -> write(it) + is PtArrayLiteral -> write(it) + is PtBinaryExpression -> write(it) + is PtBuiltinFunctionCall -> write(it) + is PtConditionalBranch -> write(it) + is PtContainmentCheck -> write(it) + is PtForLoop -> write(it) + is PtFunctionCall -> write(it) + is PtGosub -> write(it) + is PtIdentifier -> write(it) + is PtIfElse -> write(it) + is PtInlineAssembly -> write(it) + is PtJump -> write(it) + is PtMemoryByte -> write(it) + is PtMemMapped -> write(it) + is PtNumber -> write(it) + is PtPipe -> write(it) + is PtPostIncrDecr -> write(it) + is PtPrefix -> write(it) + is PtRange -> write(it) + is PtRepeatLoop -> write(it) + is PtReturn -> write(it) + is PtString -> write(it) + is PtTypeCast -> write(it) + is PtWhen -> write(it) + is PtWhenChoice -> write(it) + is PtLabel -> write(it) + is PtNodeGroup -> it.children.forEach { writeNode(it) } + else -> TODO("$it") + } + } + + private fun write(pipe: PtPipe) { + xml.elt("pipe") + xml.attr("type", pipe.type.name) + xml.startChildren() + pipe.children.forEach { writeNode(it) } + xml.endElt() + } + + private fun write(array: PtArrayLiteral) { + xml.elt("array") + xml.attr("type", array.type.name) + xml.startChildren() + array.children.forEach { writeNode(it) } + xml.endElt() + } + + private fun write(prefix: PtPrefix) { + xml.elt("prefix") + xml.attr("op", prefix.operator) + xml.attr("type", prefix.type.name) + xml.startChildren() + xml.elt("value") + xml.startChildren() + writeNode(prefix.value) + xml.endElt() + xml.endElt() + } + + private fun write(string: PtString) { + xml.elt("string") + xml.attr("encoding", string.encoding.name) + xml.startChildren() + xml.text(string.value) + xml.endElt() + } + + private fun write(rept: PtRepeatLoop) { + xml.elt("repeat") + xml.startChildren() + xml.pos(rept.position) + xml.elt("count") + xml.startChildren() + writeNode(rept.count) + xml.endElt() + xml.elt("statements") + writeNode(rept.statements) + xml.endElt() + xml.endElt() + } + + private fun write(branch: PtConditionalBranch) { + xml.elt("conditionalbranch") + xml.attr("condition", branch.condition.name) + xml.startChildren() + xml.pos(branch.position) + xml.elt("true") + xml.startChildren() + writeNode(branch.trueScope) + xml.endElt() + if(branch.falseScope.children.isNotEmpty()) { + xml.elt("false") + xml.startChildren() + writeNode(branch.falseScope) + xml.endElt() + } + xml.endElt() + } + + private fun write(check: PtContainmentCheck) { + xml.elt("containment") + xml.attr("type", check.type.name) + xml.startChildren() + xml.elt("element") + xml.startChildren() + writeNode(check.element) + xml.endElt() + xml.elt("iterable") + xml.startChildren() + writeNode(check.iterable) + xml.endElt() + xml.endElt() + } + + private fun write(range: PtRange) { + xml.elt("range") + xml.attr("type", range.type.name) + xml.startChildren() + xml.elt("from") + xml.startChildren() + writeNode(range.from) + xml.endElt() + xml.elt("to") + xml.startChildren() + writeNode(range.to) + xml.endElt() + xml.elt("step") + xml.startChildren() + writeNode(range.step) + xml.endElt() + xml.endElt() + } + + private fun write(forLoop: PtForLoop) { + xml.elt("for") + xml.attr("var", strTargetName(forLoop.variable)) + xml.startChildren() + xml.pos(forLoop.position) + xml.elt("iterable") + xml.startChildren() + writeNode(forLoop.iterable) + xml.endElt() + xml.elt("statements") + xml.startChildren() + writeNode(forLoop.statements) + xml.endElt() + xml.endElt() + } + + private fun write(membyte: PtMemoryByte) { + xml.elt("membyte") + xml.attr("type", membyte.type.name) + xml.startChildren() + xml.elt("address") + xml.startChildren() + writeNode(membyte.address) + xml.endElt() + xml.endElt() + } + + private fun write(whenStmt: PtWhen) { + xml.elt("when") + xml.startChildren() + xml.pos(whenStmt.position) + xml.elt("value") + xml.startChildren() + writeNode(whenStmt.value) + xml.endElt() + xml.elt("choices") + xml.startChildren() + writeNode(whenStmt.choices) + xml.endElt() + xml.endElt() + } + + private fun write(choice: PtWhenChoice) { + xml.elt("choice") + if(choice.isElse) { + xml.attr("else", "true") + xml.startChildren() + } else { + xml.startChildren() + xml.elt("values") + xml.startChildren() + writeNode(choice.values) + xml.endElt() + } + xml.elt("statements") + xml.startChildren() + writeNode(choice.statements) + xml.endElt() + xml.endElt() + } + + private fun write(inlineAsm: PtInlineAssembly) { + xml.elt("assembly") + xml.startChildren() + xml.pos(inlineAsm.position) + xml.elt("code") + xml.text(inlineAsm.assembly) + xml.endElt() + xml.endElt() + } + + private fun write(fcall: PtBuiltinFunctionCall) { + xml.elt("builtinfcall") + xml.attr("name", fcall.name) + if(fcall.void) + xml.attr("type", "VOID") + else + xml.attr("type", fcall.type.name) + xml.startChildren() + fcall.children.forEach { writeNode(it) } + xml.endElt() + } + + private fun write(cast: PtTypeCast) { + xml.elt("cast") + xml.attr("type", cast.type.name) + xml.startChildren() + writeNode(cast.value) + xml.endElt() + } + + private fun write(aix: PtArrayIndexer) { + xml.elt("arrayindexed") + xml.attr("type", aix.type.name) + xml.startChildren() + write(aix.variable) + writeNode(aix.index) + xml.endElt() + } + + private fun write(binexpr: PtBinaryExpression) { + xml.elt("binexpr") + xml.attr("op", binexpr.operator) + xml.attr("type", binexpr.type.name) + xml.startChildren() + writeNode(binexpr.left) + writeNode(binexpr.right) + xml.endElt() + } + + private fun write(addrof: PtAddressOf) { + xml.elt("addrof") + xml.startChildren() + write(addrof.identifier) + xml.endElt() + } + + private fun write(fcall: PtFunctionCall) { + xml.elt("fcall") + xml.attr("name", strTargetName(fcall)) + if(fcall.void) + xml.attr("type", "VOID") + else + xml.attr("type", fcall.type.name) + xml.startChildren() + xml.pos(fcall.position) + fcall.children.forEach { writeNode(it) } + xml.endElt() + } + + private fun write(number: PtNumber) { + xml.elt("number") + xml.attr("type", number.type.name) + xml.attr("value", intOrDouble(number.type, number.number).toString()) + xml.endElt() + } + + private fun write(symbol: PtIdentifier) { + xml.elt("symbol") + xml.attr("name", strTargetName(symbol)) + xml.attr("type", symbol.type.name) + xml.endElt() + } + + private fun write(assign: PtAssignment) { + xml.elt("assign") + xml.attr("aug", assign.augmentable.toString()) + xml.startChildren() + xml.pos(assign.position) + write(assign.target) + writeNode(assign.value) + xml.endElt() + } + + private fun write(ifElse: PtIfElse) { + xml.elt("ifelse") + xml.startChildren() + xml.pos(ifElse.position) + xml.elt("condition") + xml.startChildren() + writeNode(ifElse.condition) + xml.endElt() + xml.elt("true") + xml.startChildren() + xml.pos(ifElse.ifScope.position) + writeNode(ifElse.ifScope) + xml.endElt() + if(ifElse.elseScope.children.isNotEmpty()) { + xml.elt("false") + xml.startChildren() + xml.pos(ifElse.elseScope.position) + writeNode(ifElse.elseScope) + xml.endElt() + } + xml.endElt() + } + + private fun write(ret: PtReturn) { + xml.elt("return") + if(ret.hasValue) { + xml.startChildren() + writeNode(ret.value!!) + } + xml.endElt() + } + + private fun write(incdec: PtPostIncrDecr) { + if(incdec.operator=="++") xml.elt("inc") else xml.elt("dec") + xml.startChildren() + write(incdec.target) + xml.endElt() + } + + private fun write(label: PtLabel) { + xml.elt("label") + xml.attr("name", label.name) + xml.startChildren() + xml.pos(label.position) + xml.endElt() + } + + private fun write(module: PtModule) { + xml.elt("module") + xml.attr("name", module.name) + xml.attr("library", module.library.toString()) + if(module.loadAddress!=null) + xml.attr("loadaddress", module.loadAddress.toString()) + xml.startChildren() + xml.pos(module.position) + module.children.asSequence().filter { it !is PtDirective }.forEach { write(it as PtBlock) } + xml.endElt() + } + + private fun write(block: PtBlock) { + xml.elt("block") + xml.attr("name", block.name) + if(block.address!=null) + xml.attr("address", block.address!!.toString()) + xml.attr("library", block.library.toString()) + xml.startChildren() + xml.pos(block.position) + block.children.forEach { writeNode(it) } + xml.endElt() + } + + private fun write(memMapped: PtMemMapped) { + xml.elt("memvar") + xml.attr("type", memMapped.type.name) + xml.attr("name", memMapped.name) + xml.attr("address", memMapped.address.toString()) + xml.endElt() + } + + private fun write(target: PtAssignTarget) { + xml.elt("target") + xml.startChildren() + if(target.identifier!=null) { + writeNode(target.identifier!!) + } else if(target.memory!=null) { + writeNode(target.memory!!) + } else if(target.array!=null) { + writeNode(target.array!!) + } else + throw InternalCompilerException("weird assign target") + xml.endElt() + } + + private fun write(jump: PtJump) { + xml.elt("jump") + if(jump.identifier!=null) xml.attr("symbol", strTargetName(jump.identifier!!)) + else if(jump.address!=null) xml.attr("address", jump.address!!.toString()) + else if(jump.generatedLabel!=null) xml.attr("label", jump.generatedLabel!!) + else + throw InternalCompilerException("weird jump target") + xml.endElt() + } + + private fun write(gosub: PtGosub) { + xml.elt("gosub") + if(gosub.identifier!=null) xml.attr("symbol", strTargetName(gosub.identifier!!)) + else if(gosub.address!=null) xml.attr("address", gosub.address!!.toString()) + else if(gosub.generatedLabel!=null) xml.attr("label", gosub.generatedLabel!!) + else + throw InternalCompilerException("weird jump target") + xml.endElt() + } + + private fun write(sub: PtSub) { + xml.elt("sub") + xml.attr("name", sub.name) + if(sub.inline) + xml.attr("inline", "true") + xml.attr("returntype", sub.returntype?.toString() ?: "VOID") + xml.startChildren() + xml.pos(sub.position) + if(sub.parameters.isNotEmpty()) { + xml.elt("parameters") + xml.startChildren() + sub.parameters.forEach { write(it) } + xml.endElt() + } + xml.elt("statements") + xml.startChildren() + sub.children.forEach { writeNode(it) } + xml.endElt() + xml.endElt() + } + + private fun write(parameter: PtSubroutineParameter, registerOrStatusflag: RegisterOrStatusflag? = null) { + xml.elt("param") + xml.attr("name", parameter.name) + xml.attr("type", parameter.type.name) + if(registerOrStatusflag?.statusflag!=null) { + xml.attr("statusflag", registerOrStatusflag.statusflag!!.toString()) + } + if(registerOrStatusflag?.registerOrPair!=null){ + xml.attr("registers", registerOrStatusflag.registerOrPair!!.name) + } + xml.endElt() + } + + private fun write(asmSub: PtAsmSub) { + if(asmSub.address!=null) { + xml.elt("romsub") + xml.attr("name", asmSub.name) + xml.attr("address", asmSub.address!!.toString()) + if(asmSub.inline) + xml.attr("inline", "true") + xml.startChildren() + xml.pos(asmSub.position) + paramsEtcetera(asmSub) + xml.endElt() + } + else { + xml.elt("asmsub") + xml.attr("name", asmSub.name) + if(asmSub.inline) + xml.attr("inline", "true") + xml.startChildren() + xml.pos(asmSub.position) + paramsEtcetera(asmSub) + xml.elt("code") + xml.startChildren() + asmSub.children.forEach { writeNode(it) } + xml.endElt() + xml.endElt() + } + } + + private fun paramsEtcetera(asmSub: PtAsmSub) { + if(asmSub.parameters.isNotEmpty()) { + xml.elt("parameters") + xml.startChildren() + asmSub.parameters.forEach { (param, reg) -> write(param, reg) } + xml.endElt() + } + if(asmSub.clobbers.isNotEmpty()) { + xml.elt("clobbers") + xml.attr("registers", asmSub.clobbers.map {it.name}.joinToString(",")) + xml.endElt() + } + if(asmSub.retvalRegisters.isNotEmpty()) { + xml.elt("returns") + xml.startChildren() + asmSub.retvalRegisters.forEach { + xml.elt("register") + if(it.statusflag!=null) + xml.attr("statusflag", it.statusflag!!.toString()) + if(it.registerOrPair!=null) + xml.attr("registers", it.registerOrPair!!.toString()) + xml.endElt() + } + xml.endElt() + } + } + + private fun write(constant: PtConstant) { + xml.elt("const") + xml.attr("name", constant.name) + xml.attr("type", constant.type.name) + xml.attr("value", intOrDouble(constant.type, constant.value).toString()) + xml.endElt() + } + + private fun write(variable: PtVariable) { + // TODO get this from the AST only? + xml.elt("var") + xml.attr("name", variable.name) + xml.attr("type", variable.type.name) + xml.endElt() + } + + private fun write(directive: PtDirective) { + if(directive.name=="%import") + return + xml.elt("directive") + xml.attr("name", directive.name.substring(1)) + if(directive.args.isNotEmpty()) { + xml.startChildren() + directive.args.forEach { + xml.elt("arg") + if(it.name!=null) + xml.attr("name", it.name!!) + if(it.str!=null) + xml.attr("string", it.str!!) + if(it.int!=null) + xml.attr("number", it.int!!.toString()) + xml.endElt() + } + } + xml.endElt() + } + + + + private fun strTargetName(ident: PtIdentifier): String = ident.targetName.joinToString(".") + + private fun strTargetName(call: PtFunctionCall): String = call.functionName.joinToString(".") + + private fun intOrDouble(type: DataType, value: Double): Number = + if(type in IntegerDatatypes) value.toInt() else value } \ No newline at end of file diff --git a/codeGenExperimental/src/prog8/codegen/experimental/IndentingXmlWriter.kt b/codeGenExperimental/src/prog8/codegen/experimental/IndentingXmlWriter.kt new file mode 100644 index 000000000..9125939b2 --- /dev/null +++ b/codeGenExperimental/src/prog8/codegen/experimental/IndentingXmlWriter.kt @@ -0,0 +1,80 @@ +package prog8.codegen.experimental + +import prog8.code.core.Position +import java.util.* +import javax.xml.stream.XMLStreamWriter + +class IndentingXmlWriter(val xml: XMLStreamWriter): XMLStreamWriter by xml { + private var indent = 0 + private var content = Stack() + + fun doc(version: String? = null) = if(version==null) writeStartDocument() else writeStartDocument(version) + fun endDoc() = writeEndDocument() + fun elt(name: String) = writeStartElement(name) + fun attr(name: String, value: String) = writeAttribute(name, value) + fun attrs(attributes: List>) = attributes.forEach { writeAttribute(it.first, it.second) } + fun text(text: String) = writeCData(text) + fun startChildren() { + xml.writeCharacters("\n") + content.pop() + content.push(true) + } + fun endElt() = this.writeEndElement() + fun pos(pos: Position) { + elt("src") + attr("pos", pos.toString()) + endElt() + } + fun comment(text: String) { + writeComment(text) + writeCharacters("\n") + } + + override fun writeStartDocument() { + xml.writeStartDocument() + xml.writeCharacters("\n") + content.push(true) + } + + override fun writeStartDocument(version: String) { + xml.writeStartDocument(version) + xml.writeCharacters("\n") + content.push(true) + } + + override fun writeEndDocument() { + xml.writeEndDocument() + xml.writeCharacters("\n") + require(indent==0) + require(content.size==1) + } + + override fun writeStartElement(name: String) { + xml.writeCharacters(" ".repeat(indent)) + xml.writeStartElement(name) + indent++ + content.push(false) + } + + override fun writeStartElement(name: String, ns: String) { + xml.writeCharacters(" ".repeat(indent)) + xml.writeStartElement(name, ns) + indent++ + content.push(false) + } + + override fun writeEndElement() { + indent-- + if(content.pop()) + xml.writeCharacters(" ".repeat(indent)) + xml.writeEndElement() + xml.writeCharacters("\n") + } + + override fun writeStartElement(name: String, ns: String, p2: String) { + xml.writeCharacters(" ".repeat(indent)) + xml.writeStartElement(name, ns, p2) + indent++ + content.push(false) + } +} diff --git a/compiler/src/prog8/compiler/IntermediateAstMaker.kt b/compiler/src/prog8/compiler/IntermediateAstMaker.kt index d7fc6d3bd..871d74e11 100644 --- a/compiler/src/prog8/compiler/IntermediateAstMaker.kt +++ b/compiler/src/prog8/compiler/IntermediateAstMaker.kt @@ -6,21 +6,22 @@ import prog8.ast.base.FatalAstException import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.code.ast.* +import prog8.code.core.DataType -class IntermediateAstMaker(val srcProgram: Program) { +class IntermediateAstMaker(val program: Program) { fun transform(): PtProgram { - val program = PtProgram( - srcProgram.name, - srcProgram.memsizer, - srcProgram.encoding + val ptProgram = PtProgram( + program.name, + program.memsizer, + program.encoding ) - for (module in srcProgram.modules) { - program.add(transform(module)) + for (module in program.modules) { + ptProgram.add(transform(module)) } - return program + return ptProgram } private fun transform(srcModule: Module): PtModule { @@ -71,7 +72,7 @@ class IntermediateAstMaker(val srcProgram: Program) { } } - private fun transformExpression(expr: Expression): PtNode { + private fun transformExpression(expr: Expression): PtExpression { return when(expr) { is AddressOf -> transform(expr) is ArrayIndexedExpression -> transform(expr) @@ -112,13 +113,19 @@ class IntermediateAstMaker(val srcProgram: Program) { return target } - private fun transform(identifier: IdentifierReference): PtIdentifier { - val target=identifier.targetStatement(srcProgram)!! as INamedStatement - val targetname = if(target.name in srcProgram.builtinFunctions.names) + private fun targetOf(identifier: IdentifierReference): Pair, DataType> { + val target=identifier.targetStatement(program)!! as INamedStatement + val targetname = if(target.name in program.builtinFunctions.names) listOf("", target.name) else target.scopedName - return PtIdentifier(identifier.nameInSource, targetname, identifier.position) + val type = identifier.inferType(program).getOr(DataType.UNDEFINED) + return Pair(targetname, type) + } + + private fun transform(identifier: IdentifierReference): PtIdentifier { + val (target, type) = targetOf(identifier) + return PtIdentifier(identifier.nameInSource, target, type, identifier.position) } private fun transform(srcBlock: Block): PtBlock { @@ -131,7 +138,8 @@ class IntermediateAstMaker(val srcProgram: Program) { } private fun transform(srcNode: BuiltinFunctionCallStatement): PtBuiltinFunctionCall { - val call = PtBuiltinFunctionCall(srcNode.name, srcNode.position) + val type = builtinFunctionReturnType(srcNode.name, srcNode.args, program).getOr(DataType.UNDEFINED) + val call = PtBuiltinFunctionCall(srcNode.name, true, type, srcNode.position) for (arg in srcNode.args) call.add(transformExpression(arg)) return call @@ -173,22 +181,19 @@ class IntermediateAstMaker(val srcProgram: Program) { } private fun transform(srcCall: FunctionCallStatement): PtFunctionCall { - val call = PtFunctionCall(true, srcCall.position) - call.add(transform(srcCall.target)) - val args = PtNodeGroup() + val (target, type) = targetOf(srcCall.target) + val call = PtFunctionCall(target,true, type, srcCall.position) for (arg in srcCall.args) - args.add(transformExpression(arg)) - call.add(args) + call.add(transformExpression(arg)) return call } private fun transform(srcCall: FunctionCallExpression): PtFunctionCall { - val call = PtFunctionCall(false, srcCall.position) - call.add(transform(srcCall.target)) - val args = PtNodeGroup() + val (target, _) = targetOf(srcCall.target) + val type = srcCall.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val call = PtFunctionCall(target, false, type, srcCall.position) for (arg in srcCall.args) - args.add(transformExpression(arg)) - call.add(args) + call.add(transformExpression(arg)) return call } @@ -229,7 +234,8 @@ class IntermediateAstMaker(val srcProgram: Program) { PtLabel(label.name, label.position) private fun transform(srcPipe: Pipe): PtPipe { - val pipe = PtPipe(srcPipe.position) + val type = srcPipe.segments.last().inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val pipe = PtPipe(type, true, srcPipe.position) pipe.add(transformExpression(srcPipe.source)) for (segment in srcPipe.segments) pipe.add(transformExpression(segment)) @@ -247,9 +253,11 @@ class IntermediateAstMaker(val srcProgram: Program) { throw FatalAstException("repeat-forever loop should have been replaced with label+jump") val repeat = PtRepeatLoop(srcRepeat.position) repeat.add(transformExpression(srcRepeat.iterations!!)) + val scope = PtNodeGroup() for (statement in srcRepeat.body.statements) { - repeat.add(transformStatement(statement)) + scope.add(transformStatement(statement)) } + repeat.add(scope) return repeat } @@ -261,10 +269,13 @@ class IntermediateAstMaker(val srcProgram: Program) { } private fun transformAsmSub(srcSub: Subroutine): PtAsmSub { + val params = srcSub.parameters + .map { PtSubroutineParameter(it.name, it.type, it.position) } + .zip(srcSub.asmParameterRegisters) val sub = PtAsmSub(srcSub.name, srcSub.asmAddress, srcSub.asmClobbers, - srcSub.asmParameterRegisters, + params, srcSub.asmReturnvaluesRegisters, srcSub.inline, srcSub.position) @@ -334,7 +345,8 @@ class IntermediateAstMaker(val srcProgram: Program) { } private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer { - val array = PtArrayIndexer(srcArr.position) + val type = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val array = PtArrayIndexer(type, srcArr.position) array.add(transform(srcArr.arrayvar)) array.add(transformExpression(srcArr.indexer.indexExpr)) return array @@ -348,14 +360,16 @@ class IntermediateAstMaker(val srcProgram: Program) { } private fun transform(srcExpr: BinaryExpression): PtBinaryExpression { - val expr = PtBinaryExpression(srcExpr.operator, srcExpr.position) + val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position) expr.add(transformExpression(srcExpr.left)) expr.add(transformExpression(srcExpr.right)) return expr } private fun transform(srcCall: BuiltinFunctionCall): PtBuiltinFunctionCall { - val call = PtBuiltinFunctionCall(srcCall.name, srcCall.position) + val type = srcCall.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val call = PtBuiltinFunctionCall(srcCall.name, false, type, srcCall.position) for (arg in srcCall.args) call.add(transformExpression(arg)) return call @@ -384,7 +398,8 @@ class IntermediateAstMaker(val srcProgram: Program) { PtNumber(number.type, number.number, number.position) private fun transform(srcPipe: PipeExpression): PtPipe { - val pipe = PtPipe(srcPipe.position) + val type = srcPipe.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val pipe = PtPipe(type, false, srcPipe.position) pipe.add(transformExpression(srcPipe.source)) for (segment in srcPipe.segments) pipe.add(transformExpression(segment)) @@ -392,13 +407,15 @@ class IntermediateAstMaker(val srcProgram: Program) { } private fun transform(srcPrefix: PrefixExpression): PtPrefix { - val prefix = PtPrefix(srcPrefix.operator, srcPrefix.position) + val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val prefix = PtPrefix(srcPrefix.operator, type, srcPrefix.position) prefix.add(transformExpression(srcPrefix.expression)) return prefix } private fun transform(srcRange: RangeExpression): PtRange { - val range=PtRange(srcRange.position) + val type = srcRange.inferType(program).getOrElse { throw FatalAstException("unknown dt") } + val range=PtRange(type, srcRange.position) range.add(transformExpression(srcRange.from)) range.add(transformExpression(srcRange.to)) range.add(transformExpression(srcRange.step)) diff --git a/compilerAst/src/prog8/ast/AstToplevel.kt b/compilerAst/src/prog8/ast/AstToplevel.kt index 536951fbc..5312ebf5f 100644 --- a/compilerAst/src/prog8/ast/AstToplevel.kt +++ b/compilerAst/src/prog8/ast/AstToplevel.kt @@ -285,9 +285,8 @@ open class Module(final override var statements: MutableList, .substringAfterLast("/") .substringAfterLast("\\") - val loadAddress: UInt by lazy { - val address = (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int ?: 0u - address + val loadAddress: UInt? by lazy { + (statements.singleOrNull { it is Directive && it.directive == "%address" } as? Directive)?.args?.single()?.int } override fun linkParents(parent: Node) { diff --git a/compilerAst/src/prog8/ast/Program.kt b/compilerAst/src/prog8/ast/Program.kt index f40ef65b6..29370c188 100644 --- a/compilerAst/src/prog8/ast/Program.kt +++ b/compilerAst/src/prog8/ast/Program.kt @@ -67,7 +67,7 @@ class Program(val name: String, val toplevelModule: Module get() = modules.first { it.name!= internedStringsModuleName } - val definedLoadAddress: UInt + val definedLoadAddress: UInt? get() = toplevelModule.loadAddress var actualLoadAddress = 0u diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4315231e2..c29ef3634 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -3,6 +3,8 @@ TODO For next release ^^^^^^^^^^^^^^^^ +- unit test for PtProgram AST: should also test new things such as the Datatype in nodes. + ... diff --git a/examples/test.p8 b/examples/test.p8 index ee3022edd..6a515e4e0 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -28,11 +28,11 @@ main { txt.print_ub(amount) txt.nl() - ; test_stack.test() - } + uword[] @shared array = [111,222,333,444,555] - sub derp() -> ubyte, ubyte { - return 0, 1 + amount = amount |> sin8u() |> cos8u() |> sin8u() + + ; test_stack.test() } sub find_next_prime() -> ubyte {