Separate simple Ast and Symboltable from codeCore into new simpleAst module. VirtualMachine and Intermediate do not need them, just codeCore.

This commit is contained in:
Irmen de Jong 2025-02-24 22:06:52 +01:00
parent ae04f5aee8
commit 3e2b2a698d
43 changed files with 295 additions and 216 deletions

1
.idea/modules.xml generated
View File

@ -17,6 +17,7 @@
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
<module fileurl="file://$PROJECT_DIR$/virtualmachine/virtualmachine.iml" filepath="$PROJECT_DIR$/virtualmachine/virtualmachine.iml" />
</modules>
</component>

View File

@ -0,0 +1,8 @@
package prog8.code
// the automatically generated module where all string literals are interned to:
const val INTERNED_STRINGS_MODULENAME = "prog8_interned_strings"
// all automatically generated labels everywhere need to have the same label name prefix:
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"

View File

@ -6,6 +6,7 @@ plugins {
dependencies {
implementation(project(":codeCore"))
implementation(project(":simpleAst"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")

View File

@ -11,6 +11,7 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />

View File

@ -1,6 +1,9 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import prog8.code.GENERATED_LABEL_PREFIX
import prog8.code.IAssemblyProgram
import prog8.code.ICodeGeneratorBackend
import prog8.code.StNode
import prog8.code.StNodeType
import prog8.code.SymbolTable
@ -39,7 +42,7 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
when(node) {
is PtAsmSub, is PtSub -> node.name = "p8s_${node.name}"
is PtBlock -> node.name = "p8b_${node.name}"
is PtLabel -> if(!node.name.startsWith(PtLabel.GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
is PtLabel -> if(!node.name.startsWith(GENERATED_LABEL_PREFIX)) node.name = "p8l_${node.name}" // don't prefix autogenerated labels
is PtConstant -> node.name = "p8c_${node.name}"
is PtVariable, is PtMemMapped, is PtSubroutineParameter -> node.name = "p8v_${node.name}"
}
@ -121,14 +124,14 @@ class AsmGen6502(val prefixSymbols: Boolean, private val lastGeneratedLabelSeque
private fun prefixScopedName(name: String, type: Char): String {
if('.' !in name) {
if(name.startsWith(PtLabel.GENERATED_LABEL_PREFIX))
if(name.startsWith(GENERATED_LABEL_PREFIX))
return name
return "p8${type}_$name"
}
val parts = name.split('.')
val firstPrefixed = "p8b_${parts[0]}"
val lastPart = parts.last()
val lastPrefixed = if(lastPart.startsWith(PtLabel.GENERATED_LABEL_PREFIX)) lastPart else "p8${type}_$lastPart"
val lastPrefixed = if(lastPart.startsWith(GENERATED_LABEL_PREFIX)) lastPart else "p8${type}_$lastPart"
// the parts in between are assumed to be subroutine scopes.
val inbetweenPrefixed = parts.drop(1).dropLast(1).map{ "p8s_$it" }
val prefixed = listOf(firstPrefixed) + inbetweenPrefixed + listOf(lastPrefixed)
@ -1502,7 +1505,7 @@ $repeatLabel""")
internal fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++
return "${PtLabel.GENERATED_LABEL_PREFIX}${generatedLabelSequenceNumber}_$postfix"
return "$GENERATED_LABEL_PREFIX${generatedLabelSequenceNumber}_$postfix"
}
internal fun assignConstFloatToPointerAY(number: PtNumber) {

View File

@ -1,9 +1,9 @@
package prog8.codegen.cpu6502
import prog8.code.GENERATED_LABEL_PREFIX
import prog8.code.StConstant
import prog8.code.StMemVar
import prog8.code.SymbolTable
import prog8.code.ast.PtLabel
import prog8.code.core.ICompilationTarget
@ -362,7 +362,7 @@ or *_afterif labels.
This gets generated after certain if conditions, and only the branch instruction is needed in these cases.
*/
val autoLabelPrefix = PtLabel.GENERATED_LABEL_PREFIX
val autoLabelPrefix = GENERATED_LABEL_PREFIX
if(first=="beq +" && second=="lda #1" && third=="+") {
if((fourth.startsWith("beq $autoLabelPrefix") || fourth.startsWith("bne $autoLabelPrefix")) &&
(fourth.endsWith("_shortcut") || fourth.endsWith("_afterif") || fourth.endsWith("_shortcut:") || fourth.endsWith("_afterif:"))) {

View File

@ -1,6 +1,7 @@
package prog8.codegen.cpu6502
import prog8.code.ast.PtLabel
import prog8.code.GENERATED_LABEL_PREFIX
import prog8.code.IAssemblyProgram
import prog8.code.core.*
import prog8.code.target.C128Target
import prog8.code.target.C64Target
@ -131,7 +132,7 @@ internal class AssemblyProgram(
}
private fun removeGeneratedLabelsFromMonlist() {
val pattern = Regex("""al (\w+) \S+${PtLabel.GENERATED_LABEL_PREFIX}.+?""")
val pattern = Regex("""al (\w+) \S+$GENERATED_LABEL_PREFIX.+?""")
val lines = viceMonListFile.toFile().readLines()
viceMonListFile.toFile().outputStream().bufferedWriter().use {
for (line in lines) {

View File

@ -6,6 +6,7 @@ plugins {
dependencies {
implementation(project(":codeCore"))
implementation(project(":simpleAst"))
implementation(project(":intermediate"))
implementation(project(":codeGenIntermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

View File

@ -13,5 +13,6 @@
<orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" />
</component>
</module>

View File

@ -3,8 +3,8 @@ package prog8.codegen.experimental
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.IAssemblyProgram
import prog8.code.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter

View File

@ -7,6 +7,7 @@ plugins {
dependencies {
implementation(project(":codeCore"))
implementation(project(":simpleAst"))
implementation(project(":intermediate"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// implementation "org.jetbrains.kotlin:kotlin-reflect"

View File

@ -11,6 +11,7 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />

View File

@ -25,7 +25,7 @@ class IRCodeGen(
verifyNameScoping(program, symbolTable)
changeGlobalVarInits(symbolTable)
val irSymbolTable = IRSymbolTable.fromAstSymboltable(symbolTable)
val irSymbolTable = convertStToIRSt(symbolTable)
val irProg = IRProgram(program.name, irSymbolTable, options, program.encoding)
// collect global variables initializers
@ -1899,7 +1899,7 @@ class IRCodeGen(
private var labelSequenceNumber = 0
internal fun createLabelName(): String {
labelSequenceNumber++
return "${PtLabel.GENERATED_LABEL_PREFIX}$labelSequenceNumber"
return "${GENERATED_LABEL_PREFIX}$labelSequenceNumber"
}
internal fun translateBuiltinFunc(call: PtBuiltinFunctionCall): ExpressionCodeResult

View File

@ -0,0 +1,146 @@
package prog8.codegen.intermediate
import prog8.code.StArrayElement
import prog8.code.StConstant
import prog8.code.StMemVar
import prog8.code.StMemorySlab
import prog8.code.StNodeType
import prog8.code.StStaticVariable
import prog8.code.SymbolTable
import prog8.code.core.DataType
import prog8.intermediate.IRStArrayElement
import prog8.intermediate.IRStConstant
import prog8.intermediate.IRStMemVar
import prog8.intermediate.IRStMemorySlab
import prog8.intermediate.IRStStaticVariable
import prog8.intermediate.IRSymbolTable
fun convertStToIRSt(sourceSt: SymbolTable?): IRSymbolTable {
val st = IRSymbolTable()
if (sourceSt != null) {
sourceSt.flat.forEach {
when(it.value.type) {
StNodeType.STATICVAR -> st.add(convert(it.value as StStaticVariable))
StNodeType.MEMVAR -> st.add(convert(it.value as StMemVar))
StNodeType.CONSTANT -> st.add(convert(it.value as StConstant))
StNodeType.MEMORYSLAB -> st.add(convert(it.value as StMemorySlab))
else -> { }
}
}
st.validate()
st.allVariables().forEach { variable ->
variable.onetimeInitializationArrayValue?.let {
it.forEach { arrayElt ->
val addrOfSymbol = arrayElt.addressOfSymbol
if (addrOfSymbol != null) {
require(addrOfSymbol.contains('.')) {
"pointer var in array should be properly scoped: ${addrOfSymbol} in ${variable.name}"
}
}
}
}
}
}
return st
}
private fun convert(variable: StStaticVariable): IRStStaticVariable {
fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
else
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
val scopedName: String
if('.' in variable.name) {
scopedName = variable.name
return IRStStaticVariable(variable.name,
variable.dt,
variable.initializationNumericValue,
variable.initializationStringValue,
variable.initializationArrayValue?.map { convertArrayElt(it) },
variable.length,
variable.zpwish,
variable.align)
} else {
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
if(array==null)
return null
val newArray = mutableListOf<IRStArrayElement>()
array.forEach {
if(it.addressOfSymbol!=null) {
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
newArray.add(IRStArrayElement(null, null, target.scopedName))
} else {
newArray.add(convertArrayElt(it))
}
}
return newArray
}
scopedName = variable.scopedName
return IRStStaticVariable(scopedName,
variable.dt,
variable.initializationNumericValue,
variable.initializationStringValue,
fixupAddressOfInArray(variable.initializationArrayValue),
variable.length,
variable.zpwish,
variable.align
)
}
}
private fun convert(variable: StMemVar): IRStMemVar {
val scopedName: String
if('.' in variable.name) {
scopedName = variable.name
return IRStMemVar(
variable.name,
variable.dt,
variable.address,
variable.length
)
} else {
scopedName = try {
variable.scopedName
} catch (_: UninitializedPropertyAccessException) {
variable.name
}
return IRStMemVar(scopedName, variable.dt, variable.address, variable.length)
}
}
private fun convert(constant: StConstant): IRStConstant {
val dt = DataType.forDt(constant.dt)
val scopedName = if('.' in constant.name) {
constant.name
} else {
try {
constant.scopedName
} catch (_: UninitializedPropertyAccessException) {
constant.name
}
}
return IRStConstant(scopedName, dt, constant.value)
}
private fun convert(variable: StMemorySlab): IRStMemorySlab {
return if('.' in variable.name)
IRStMemorySlab(variable.name, variable.size, variable.align)
else
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
}
/*
*/

View File

@ -3,8 +3,8 @@ package prog8.codegen.vm
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IAssemblyProgram
import prog8.code.core.ICodeGeneratorBackend
import prog8.code.IAssemblyProgram
import prog8.code.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter

View File

@ -10,7 +10,7 @@ import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.internedStringsModuleName
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.compiler.CallGraph
@ -93,7 +93,7 @@ class UnusedCodeRemover(private val program: Program,
override fun after(block: Block, parent: Node): Iterable<IAstModification> {
if("force_output" !in block.options()) {
if (block.containsNoCodeNorVars) {
if (block.name != internedStringsModuleName && "ignore_unused" !in block.options()) {
if (block.name != INTERNED_STRINGS_MODULENAME && "ignore_unused" !in block.options()) {
if (!block.statements.any { it is Subroutine && it.hasBeenInlined })
errors.info("removing unused block '${block.name}'", block.position)
}

View File

@ -10,6 +10,7 @@ plugins {
dependencies {
implementation(project(":codeCore"))
implementation(project(":simpleAst"))
implementation(project(":codeOptimizers"))
implementation(project(":compilerAst"))
implementation(project(":codeGenCpu6502"))

View File

@ -17,13 +17,14 @@
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="module" module-name="simpleAst" />
<orderEntry type="module" module-name="compilerAst" />
<orderEntry type="module" module-name="codeOptimizers" />
<orderEntry type="module" module-name="codeGenCpu6502" />
<orderEntry type="module" module-name="codeGenExperimental" />
<orderEntry type="module" module-name="codeGenIntermediate" />
<orderEntry type="module" module-name="virtualmachine" />
<orderEntry type="module" module-name="intermediate" scope="TEST" />
<orderEntry type="module" module-name="intermediate" />
<orderEntry type="library" name="antlr.antlr4" level="project" />
</component>
</module>

View File

@ -6,7 +6,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.core.*
import prog8.code.internedStringsModuleName
import prog8.code.INTERNED_STRINGS_MODULENAME
internal class VerifyFunctionArgTypes(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : IAstVisitor {
@ -22,7 +22,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
}
// remove unused strings from interned strings block
val internedBlock = program.allBlocks.singleOrNull { it.name == internedStringsModuleName }
val internedBlock = program.allBlocks.singleOrNull { it.name == INTERNED_STRINGS_MODULENAME }
internedBlock?.statements?.withIndex()?.reversed()?.forEach { (index, st) ->
if(st is VarDecl && st.scopedName !in allStringRefs) {
internedBlock.statements.removeAt(index)

View File

@ -12,7 +12,7 @@ import io.kotest.matchers.string.shouldContain
import prog8.ast.Program
import prog8.code.core.IErrorReporter
import prog8.code.source.SourceCode
import prog8.code.internedStringsModuleName
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.compiler.ModuleImporter
import prog8.parser.ParseError
import prog8tests.helpers.*
@ -176,7 +176,7 @@ class TestModuleImporter: FunSpec({
}
}
withClue("imported module with error in it should not be present") { program.modules.size shouldBe 1 }
program.modules[0].name shouldBe internedStringsModuleName
program.modules[0].name shouldBe INTERNED_STRINGS_MODULENAME
}
}
@ -258,7 +258,7 @@ class TestModuleImporter: FunSpec({
}
}
withClue("imported module with error in it should not be present") { program.modules.size shouldBe 1 }
program.modules[0].name shouldBe internedStringsModuleName
program.modules[0].name shouldBe INTERNED_STRINGS_MODULENAME
importer.errors.report()
}
}

View File

@ -7,7 +7,7 @@ import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldStartWith
import prog8.code.core.ZeropageType
import prog8.code.internedStringsModuleName
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
@ -40,7 +40,7 @@ main {
}
withClue("module order in parse tree") {
moduleNames.drop(1) shouldBe listOf(
internedStringsModuleName,
INTERNED_STRINGS_MODULENAME,
"textio",
"syslib",
"conv",
@ -101,7 +101,7 @@ main {
withClue("module order in parse tree") {
program.modules.map { it.name } shouldBe
listOf(
internedStringsModuleName,
INTERNED_STRINGS_MODULENAME,
filenameBase,
"textio", "syslib", "conv", "shared_cbm_textio_functions", "floats", "shared_floats_functions", "prog8_math", "prog8_lib"
)

View File

@ -7,7 +7,7 @@ import prog8.ast.AstToSourceTextConverter
import prog8.ast.Module
import prog8.ast.Program
import prog8.code.source.SourceCode
import prog8.code.internedStringsModuleName
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.parser.ParseError
import prog8.parser.Prog8Parser.parseModule
import prog8tests.helpers.DummyFunctions
@ -42,7 +42,7 @@ class TestAstToSourceText: AnnotationSpec() {
fun testMentionsInternedStringsModule() {
val orig = SourceCode.Text("\n")
val (txt, _) = roundTrip(parseModule(orig))
txt shouldContain Regex(";.*$internedStringsModuleName")
txt shouldContain Regex(";.*$INTERNED_STRINGS_MODULENAME")
}
@Test

View File

@ -15,7 +15,7 @@ import prog8.ast.statements.Block
import prog8.code.ast.PtBlock
import prog8.code.core.Position
import prog8.code.source.SourceCode
import prog8.code.internedStringsModuleName
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.code.target.C64Target
import prog8tests.helpers.DummyFunctions
import prog8tests.helpers.DummyMemsizer
@ -28,7 +28,7 @@ class TestProgram: FunSpec({
test("withNameBuiltinsAndMemsizer") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
program.modules.size shouldBe 1
program.modules[0].name shouldBe internedStringsModuleName
program.modules[0].name shouldBe INTERNED_STRINGS_MODULENAME
program.modules[0].program shouldBeSameInstanceAs program
program.modules[0].parent shouldBeSameInstanceAs program.namespace
}
@ -64,7 +64,7 @@ class TestProgram: FunSpec({
test("withInternedStringsModule") {
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val m = program.modules[0]
m.name shouldBe internedStringsModuleName
m.name shouldBe INTERNED_STRINGS_MODULENAME
val retVal = program.moveModuleToFront(m)
retVal shouldBeSameInstanceAs program

View File

@ -4,9 +4,9 @@ import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.ast.PtLabel
import prog8.code.GENERATED_LABEL_PREFIX
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.code.core.*
import prog8.code.internedStringsModuleName
import prog8.code.source.SourceCode
/*********** Everything starts from here, the Program; zero or more modules *************/
@ -23,8 +23,8 @@ class Program(val name: String,
init {
// insert a container module for all interned strings later
val internedStringsModule = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(internedStringsModuleName))
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
val internedStringsModule = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(INTERNED_STRINGS_MODULENAME))
val block = Block(INTERNED_STRINGS_MODULENAME, null, mutableListOf(), true, Position.DUMMY)
val directive = Directive("%option", listOf(DirectiveArg("no_symbol_prefixing", null, Position.DUMMY)), Position.DUMMY)
block.statements.add(directive)
directive.linkParents(block)
@ -67,7 +67,7 @@ class Program(val name: String,
}
val toplevelModule: Module
get() = modules.first { it.name!= internedStringsModuleName }
get() = modules.first { it.name!= INTERNED_STRINGS_MODULENAME }
private val internedStringsReferenceCounts = mutableMapOf<VarDecl, Int>()
@ -81,8 +81,8 @@ class Program(val name: String,
}
val internedStringsBlock = modules
.first { it.name == internedStringsModuleName }.statements
.first { it is Block && it.name == internedStringsModuleName } as Block
.first { it.name == INTERNED_STRINGS_MODULENAME }.statements
.first { it is Block && it.name == INTERNED_STRINGS_MODULENAME } as Block
fun addNewInternedStringvar(string: StringLiteral): Pair<List<String>, VarDecl> {
val varName = "string_${internedStringsBlock.statements.size}"
@ -93,7 +93,7 @@ class Program(val name: String,
)
internedStringsBlock.statements.add(decl)
decl.linkParents(internedStringsBlock)
return Pair(listOf(internedStringsModuleName, decl.name), decl)
return Pair(listOf(INTERNED_STRINGS_MODULENAME, decl.name), decl)
}
val existingDecl = internedStringsBlock.statements.filterIsInstance<VarDecl>().singleOrNull {
@ -133,8 +133,8 @@ class Program(val name: String,
fun removeStrings(modules: List<Module>) {
if(removals.isNotEmpty()) {
val internedStringsBlock = modules
.first { it.name == internedStringsModuleName }.statements
.first { it is Block && it.name == internedStringsModuleName } as Block
.first { it.name == INTERNED_STRINGS_MODULENAME }.statements
.first { it is Block && it.name == INTERNED_STRINGS_MODULENAME } as Block
removals.forEach { scopedname ->
val decl = internedStringsBlock.statements.filterIsInstance<VarDecl>().single { decl -> decl.scopedName == scopedname }
val numRefs = program.internedStringsReferenceCounts.getValue(decl) - 1
@ -151,7 +151,7 @@ class Program(val name: String,
fun makeLabel(postfix: String): String {
generatedLabelSequenceNumber++
return "${PtLabel.GENERATED_LABEL_PREFIX}${generatedLabelSequenceNumber}_$postfix"
return "$GENERATED_LABEL_PREFIX${generatedLabelSequenceNumber}_$postfix"
}
fun makeLabel(postfix: String, position: Position): Label {

View File

@ -9,7 +9,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor
import prog8.code.core.*
import prog8.code.internedStringsModuleName
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.code.target.encodings.JapaneseCharacterConverter
import java.io.CharConversionException
import java.util.*
@ -1250,7 +1250,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
return false
val scope=decl.definingModule
return scope.name==internedStringsModuleName
return scope.name==INTERNED_STRINGS_MODULENAME
}
}

View File

@ -37,7 +37,7 @@ Future Things and Ideas
IR/VM
-----
- Split the simplified AST and Symboltable from codeCore. VirtualMachine and Intermediate should not need those. (maybe others too?)
- can onetimeInitializationNumericValue be removed from IRStStaticVariable?
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
- fix call() return value handling
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg

View File

@ -1,4 +1,4 @@
package prog8.code
package prog8
/**
* By convention, the right side of an `Either` is used to hold successful values.

View File

@ -1,6 +1,5 @@
package prog8.intermediate
import prog8.code.*
import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.code.target.getCompilationTargetByName
@ -154,7 +153,7 @@ class IRFileReader {
}
}
private fun parseVarsWithoutInit(reader: XMLEventReader): List<StStaticVariable> {
private fun parseVarsWithoutInit(reader: XMLEventReader): List<IRStStaticVariable> {
skipText(reader)
val start = reader.nextEvent().asStartElement()
require(start.name.localPart=="VARIABLESNOINIT") { "missing VARIABLESNOINIT" }
@ -165,7 +164,7 @@ class IRFileReader {
emptyList()
else {
val varPattern = Regex("(?<type>.+?)(?<arrayspec>\\[.+?\\])? (?<name>.+) zp=(?<zp>.+?)\\s?(split=(?<split>.+?))?\\s?(align=(?<align>.+?))?")
val variables = mutableListOf<StStaticVariable>()
val variables = mutableListOf<IRStStaticVariable>()
text.lineSequence().forEach { line ->
// example: uword main.start.qq2 zp=DONTCARE
val match = varPattern.matchEntire(line) ?: throw IRParseException("invalid VARIABLESNOINIT $line")
@ -182,14 +181,14 @@ class IRFileReader {
val zp = if(zpwish.isBlank()) ZeropageWish.DONTCARE else ZeropageWish.valueOf(zpwish)
// val isSplit = if(split.isBlank()) false else split.toBoolean()
val align = if(alignment.isBlank()) 0u else alignment.toUInt()
val newVar = StStaticVariable(name, dt, null, null, arraysize, zp, align.toInt(), null)
val newVar = IRStStaticVariable(name, dt, null, null, null, arraysize, zp, align.toInt())
variables.add(newVar)
}
return variables
}
}
private fun parseConstants(reader: XMLEventReader): List<StConstant> {
private fun parseConstants(reader: XMLEventReader): List<IRStConstant> {
skipText(reader)
val start = reader.nextEvent().asStartElement()
require(start.name.localPart=="CONSTANTS") { "missing CONSTANTS" }
@ -200,7 +199,7 @@ class IRFileReader {
emptyList()
else {
val constantPattern = Regex("(.+?) (.+)=(.*?)")
val constants = mutableListOf<StConstant>()
val constants = mutableListOf<IRStConstant>()
text.lineSequence().forEach { line ->
// examples:
// uword main.start.qq2=0
@ -210,13 +209,13 @@ class IRFileReader {
throw IRParseException("unscoped name: $name")
val dt = parseDatatype(type, false)
val value = parseIRValue(valueStr)
constants.add(StConstant(name, dt.base, value, null))
constants.add(IRStConstant(name, dt, value))
}
return constants
}
}
private fun parseVariables(reader: XMLEventReader): List<StStaticVariable> {
private fun parseVariables(reader: XMLEventReader): List<IRStStaticVariable> {
skipText(reader)
val start = reader.nextEvent().asStartElement()
require(start.name.localPart=="VARIABLESWITHINIT") { "missing VARIABLESWITHINIT" }
@ -227,7 +226,7 @@ class IRFileReader {
emptyList()
else {
val varPattern = Regex("(?<type>.+?)(?<arrayspec>\\[.+?\\])? (?<name>.+)=(?<value>.*?) zp=(?<zp>.+?)\\s?(split=(?<split>.+?))?\\s?(align=(?<align>.+?))?")
val variables = mutableListOf<StStaticVariable>()
val variables = mutableListOf<IRStStaticVariable>()
text.lineSequence().forEach { line ->
// examples:
// uword main.start.qq2=0 zp=REQUIRE_ZP
@ -248,21 +247,21 @@ class IRFileReader {
if(split.isBlank()) false else split.toBoolean()
val align = if(alignment.isBlank()) 0u else alignment.toUInt()
var initNumeric: Double? = null
var initArray: StArray? = null
var initArray: IRStArray? = null
when {
dt.isNumericOrBool -> initNumeric = parseIRValue(value)
dt.isBoolArray -> {
initArray = value.split(',').map {
val boolean = parseIRValue(it) != 0.0
StArrayElement(null, null, boolean)
IRStArrayElement(boolean, null, null)
}
}
dt.isArray -> {
initArray = value.split(',').map {
if (it.startsWith('@'))
StArrayElement(null, it.drop(1), null)
IRStArrayElement(null, null, it.drop(1))
else
StArrayElement(parseIRValue(it), null, null)
IRStArrayElement(null, parseIRValue(it), null)
}
}
dt.isString -> throw IRParseException("STR should have been converted to byte array")
@ -271,16 +270,14 @@ class IRFileReader {
if(arraysize!=null && initArray!=null && initArray.all { it.number==0.0 }) {
initArray=null // arrays with just zeros can be left uninitialized
}
val stVar = StStaticVariable(name, dt, null, initArray, arraysize, zp, align.toInt(), null)
if(initNumeric!=null)
stVar.setOnetimeInitNumeric(initNumeric)
val stVar = IRStStaticVariable(name, dt, initNumeric, null, initArray, arraysize, zp, align.toInt())
variables.add(stVar)
}
return variables
}
}
private fun parseMemMapped(reader: XMLEventReader): List<StMemVar> {
private fun parseMemMapped(reader: XMLEventReader): List<IRStMemVar> {
skipText(reader)
val start = reader.nextEvent().asStartElement()
require(start.name.localPart=="MEMORYMAPPEDVARIABLES") { "missing MEMORYMAPPEDVARIABLES" }
@ -290,7 +287,7 @@ class IRFileReader {
return if(text.isBlank())
emptyList()
else {
val memvars = mutableListOf<StMemVar>()
val memvars = mutableListOf<IRStMemVar>()
val mappedPattern = Regex("@(.+?)(\\[.+?\\])? (.+)=(.+)")
text.lineSequence().forEach { line ->
// examples:
@ -300,13 +297,13 @@ class IRFileReader {
val (type, arrayspec, name, address) = match.destructured
val arraysize = if(arrayspec.isNotBlank()) arrayspec.substring(1, arrayspec.length-1).toInt() else null
val dt = parseDatatype(type, arraysize!=null)
memvars.add(StMemVar(name, dt, parseIRValue(address).toUInt(), arraysize, null))
memvars.add(IRStMemVar(name, dt, parseIRValue(address).toUInt(), arraysize))
}
memvars
}
}
private fun parseSlabs(reader: XMLEventReader): List<StMemorySlab> {
private fun parseSlabs(reader: XMLEventReader): List<IRStMemorySlab> {
skipText(reader)
val start = reader.nextEvent().asStartElement()
require(start.name.localPart=="MEMORYSLABS") { "missing MEMORYSLABS" }
@ -316,13 +313,13 @@ class IRFileReader {
return if(text.isBlank())
emptyList()
else {
val slabs = mutableListOf<StMemorySlab>()
val slabs = mutableListOf<IRStMemorySlab>()
val slabPattern = Regex("(.+) (.+) (.+)")
text.lineSequence().forEach { line ->
// example: "slabname 4096 0"
val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid slab $line")
val (name, size, align) = match.destructured
slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), null))
slabs.add(IRStMemorySlab(name, size.toUInt(), align.toUInt()))
}
slabs
}

View File

@ -12,38 +12,6 @@ class IRSymbolTable {
private val table = mutableMapOf<String, IRStNode>()
private val asmSymbols = mutableMapOf<String, String>()
companion object {
fun fromAstSymboltable(sourceSt: SymbolTable?): IRSymbolTable {
val st = IRSymbolTable()
if (sourceSt != null) {
sourceSt.flat.forEach {
when(it.value.type) {
StNodeType.STATICVAR -> st.add(it.value as StStaticVariable)
StNodeType.MEMVAR -> st.add(it.value as StMemVar)
StNodeType.CONSTANT -> st.add(it.value as StConstant)
StNodeType.MEMORYSLAB -> st.add(it.value as StMemorySlab)
else -> { }
}
}
require(st.table.all { it.key == it.value.name })
st.allVariables().forEach { variable ->
variable.onetimeInitializationArrayValue?.let {
it.forEach { arrayElt ->
if (arrayElt.addressOfSymbol != null) {
require(arrayElt.addressOfSymbol.contains('.')) {
"pointer var in array should be properly scoped: ${arrayElt.addressOfSymbol} in ${variable.name}"
}
}
}
}
}
}
return st
}
}
fun allConstants(): Sequence<IRStConstant> =
table.asSequence().map { it.value }.filterIsInstance<IRStConstant>()
@ -58,92 +26,8 @@ class IRSymbolTable {
fun lookup(name: String) = table[name]
fun add(variable: StStaticVariable) {
val scopedName: String
val varToadd: IRStStaticVariable
if('.' in variable.name) {
scopedName = variable.name
varToadd = IRStStaticVariable(variable.name,
variable.dt,
variable.initializationNumericValue,
variable.initializationStringValue,
variable.initializationArrayValue?.map { convertArrayElt(it) },
variable.length,
variable.zpwish,
variable.align)
} else {
fun fixupAddressOfInArray(array: List<StArrayElement>?): List<IRStArrayElement>? {
if(array==null)
return null
val newArray = mutableListOf<IRStArrayElement>()
array.forEach {
if(it.addressOfSymbol!=null) {
val target = variable.lookup(it.addressOfSymbol!!) ?: throw NoSuchElementException("can't find variable ${it.addressOfSymbol}")
newArray.add(IRStArrayElement(null, null, target.scopedName))
} else {
newArray.add(convertArrayElt(it))
}
}
return newArray
}
scopedName = variable.scopedName
varToadd = IRStStaticVariable(scopedName,
variable.dt,
variable.initializationNumericValue,
variable.initializationStringValue,
fixupAddressOfInArray(variable.initializationArrayValue),
variable.length,
variable.zpwish,
variable.align
)
}
table[scopedName] = varToadd
}
fun add(variable: StMemVar) {
val scopedName: String
val varToadd: IRStMemVar
if('.' in variable.name) {
scopedName = variable.name
varToadd = IRStMemVar(
variable.name,
variable.dt,
variable.address,
variable.length
)
} else {
scopedName = try {
variable.scopedName
} catch (_: UninitializedPropertyAccessException) {
variable.name
}
varToadd = IRStMemVar(scopedName, variable.dt, variable.address, variable.length)
}
table[scopedName] = varToadd
}
fun add(variable: StMemorySlab) {
val varToadd = if('.' in variable.name)
IRStMemorySlab(variable.name, variable.size, variable.align)
else {
IRStMemorySlab("prog8_slabs.${variable.name}", variable.size, variable.align)
}
table[varToadd.name] = varToadd
}
fun add(constant: StConstant) {
val scopedName: String
val dt = DataType.forDt(constant.dt)
if('.' in constant.name) {
scopedName = constant.name
} else {
scopedName = try {
constant.scopedName
} catch (_: UninitializedPropertyAccessException) {
constant.name
}
}
table[scopedName] = IRStConstant(scopedName, dt, constant.value)
fun add(node: IRStNode) {
table[node.name] = node
}
fun addAsmSymbol(name: String, value: String) {
@ -157,17 +41,15 @@ class IRSymbolTable {
val vars = table.filter { it.key.startsWith(prefix) }
vars.forEach {
// check if attempt is made to delete interned strings, if so, refuse that.
if(!it.key.startsWith(internedStringsModuleName)) {
if(!it.key.startsWith(INTERNED_STRINGS_MODULENAME)) {
table.remove(it.key)
}
}
}
private fun convertArrayElt(elt: StArrayElement): IRStArrayElement = if(elt.boolean!=null)
IRStArrayElement(elt.boolean, null, elt.addressOfSymbol)
else
IRStArrayElement(null, elt.number, elt.addressOfSymbol)
fun validate() {
require(table.all { it.key == it.value.name })
}
}
@ -178,10 +60,7 @@ enum class IRStNodeType {
CONST
}
open class IRStNode(val name: String,
val type: IRStNodeType,
val children: MutableMap<String, StNode> = mutableMapOf()
)
open class IRStNode(val name: String, val type: IRStNodeType)
class IRStMemVar(name: String,
val dt: DataType,
@ -209,7 +88,7 @@ class IRStConstant(name: String, val dt: DataType, val value: Double) : IRStNode
class IRStStaticVariable(name: String,
val dt: DataType,
val onetimeInitializationNumericValue: Double?, // regular (every-run-time) initialization is done via regular assignments
val onetimeInitializationNumericValue: Double?, // TODO still needed? Or can go? regular (every-run-time) initialization is done via regular assignments
val onetimeInitializationStringValue: IRStString?,
val onetimeInitializationArrayValue: IRStArray?,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte

View File

@ -1,9 +1,9 @@
package prog8.intermediate
import prog8.code.Either
import prog8.code.core.*
import prog8.code.left
import prog8.code.right
import prog8.Either
import prog8.left
import prog8.right
fun DataType.irTypeString(length: Int?): String {

View File

@ -1,6 +1,7 @@
include(
':parser',
':codeCore',
':simpleAst',
':intermediate',
':compilerAst',
':codeOptimizers',

View File

@ -0,0 +1,24 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("jvm")
}
dependencies {
implementation(project(":codeCore"))
// implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.1")
}
sourceSets {
main {
java {
srcDir("${project.projectDir}/src")
}
resources {
srcDir("${project.projectDir}/res")
}
}
}
// note: there are no unit tests yet in this module!

17
simpleAst/simpleAst.iml Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/testResources" type="java-test-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="codeCore" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component>
</module>

View File

@ -1,13 +1,15 @@
package prog8.code.core
package prog8.code
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.core.IErrorReporter
interface ICodeGeneratorBackend {
fun generate(program: PtProgram,
symbolTable: SymbolTable,
options: CompilationOptions,
errors: IErrorReporter): IAssemblyProgram?
errors: IErrorReporter
): IAssemblyProgram?
}

View File

@ -6,9 +6,6 @@ import prog8.code.ast.PtProgram
import prog8.code.core.*
const val internedStringsModuleName = "prog8_interned_strings"
/**
* Tree structure containing all symbol definitions in the program
* (blocks, subroutines, variables (all types), memoryslabs, and labels).
@ -260,7 +257,7 @@ class StMemorySlab(
class StSub(name: String, val parameters: List<StSubroutineParameter>, val returns: List<DataType>, astNode: PtNode) :
StNode(name, StNodeType.SUBROUTINE, astNode)
StNode(name, StNodeType.SUBROUTINE, astNode)
class StExtSub(name: String,

View File

@ -96,12 +96,7 @@ class PtInlineAssembly(val assembly: String, val isIR: Boolean, position: Positi
}
class PtLabel(name: String, position: Position) : PtNamedNode(name, position) {
companion object {
// all automatically generated labels everywhere need to have the same label name prefix:
const val GENERATED_LABEL_PREFIX = "p8_label_gen_"
}
}
class PtLabel(name: String, position: Position) : PtNamedNode(name, position)
class PtBreakpoint(position: Position): PtNode(position)

View File

@ -1,10 +1,10 @@
package prog8.vm
import prog8.code.Either
import prog8.code.core.DataType
import prog8.code.left
import prog8.code.right
import prog8.Either
import prog8.left
import prog8.right
import prog8.intermediate.*
import prog8.code.core.DataType
class VmProgramLoader {
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname