mirror of
https://github.com/irmen/prog8.git
synced 2026-10-24 18:02:40 +00:00
296 lines
11 KiB
Kotlin
296 lines
11 KiB
Kotlin
package prog8tests.code
|
|
|
|
import io.kotest.core.spec.style.FunSpec
|
|
import io.kotest.matchers.shouldBe
|
|
import io.kotest.matchers.types.shouldBeSameInstanceAs
|
|
import prog8.code.*
|
|
import prog8.code.ast.PtBlock
|
|
import prog8.code.ast.PtLabel
|
|
import prog8.code.ast.PtProgram
|
|
import prog8.code.ast.PtStructDecl
|
|
import prog8.code.core.DataType
|
|
import prog8.code.core.Position
|
|
import prog8.code.core.ZeropageWish
|
|
import prog8.code.source.SourceCode
|
|
import prog8.code.target.VMTarget
|
|
|
|
/**
|
|
* Tests for SymbolTable construction and structural integrity.
|
|
*
|
|
* These tests verify that SymbolTables are built correctly from ASTs,
|
|
* that duplicate handling works as expected, and that parent linkage
|
|
* is maintained properly.
|
|
*
|
|
* These tests work directly with SymbolTable objects and do NOT require
|
|
* compiling Prog8 source code.
|
|
*/
|
|
class TestSymbolTableConstruction: FunSpec({
|
|
|
|
// ============================================================================
|
|
// Helper Functions
|
|
// ============================================================================
|
|
|
|
fun makeSimpleSymbolTable(): SymbolTable {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
return SymbolTable(astProgram)
|
|
}
|
|
|
|
fun makeSymbolTableWithDuplicates(): Pair<SymbolTable, StStaticVariable> {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val var1 = StStaticVariable("value", DataType.UWORD, null, null, null, ZeropageWish.DONTCARE, 0u, false, null)
|
|
val var2 = StStaticVariable("value", DataType.UWORD, null, null, null, ZeropageWish.DONTCARE, 0u, false, null)
|
|
|
|
blockNode.add(var1)
|
|
return st to var1
|
|
}
|
|
|
|
// ============================================================================
|
|
// Basic Construction Tests
|
|
// ============================================================================
|
|
|
|
test("empty symboltable creation") {
|
|
val astNode = PtProgram("test", VMTarget())
|
|
val st = SymbolTable(astNode)
|
|
st.name shouldBe "test"
|
|
st.type shouldBe StNodeType.GLOBAL
|
|
st.children shouldBe mutableMapOf()
|
|
st.astNode shouldBeSameInstanceAs astNode
|
|
st.astNode!!.position shouldBe Position.DUMMY
|
|
}
|
|
|
|
test("symboltable with single block") {
|
|
val st = makeSimpleSymbolTable()
|
|
st.name shouldBe "test"
|
|
st.children.size shouldBe 0
|
|
}
|
|
|
|
test("symboltable with multiple modules") {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock1 = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
val astBlock2 = PtBlock("block2", false, SourceCode.Generated("block2"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock1)
|
|
astProgram.add(astBlock2)
|
|
val st = SymbolTable(astProgram)
|
|
st.name shouldBe "test"
|
|
}
|
|
|
|
// ============================================================================
|
|
// Duplicate Handling Tests
|
|
// ============================================================================
|
|
|
|
test("adding duplicate symbol to same scope ignores second") {
|
|
val (st, var1) = makeSymbolTableWithDuplicates()
|
|
val blockNode = st.children["block1"]!!
|
|
|
|
val var2 = StStaticVariable("value", DataType.UWORD, null, null, null, ZeropageWish.DONTCARE, 0u, false, null)
|
|
blockNode.add(var2) // Should be ignored silently
|
|
|
|
blockNode.children["value"] shouldBeSameInstanceAs var1
|
|
blockNode.children.size shouldBe 1
|
|
}
|
|
|
|
test("duplicate struct instance insertion is handled") {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val structInstance1 = StStructInstance("instance1", "MyStruct", emptyList(), 10u, null)
|
|
val structInstance2 = StStructInstance("instance1", "MyStruct", emptyList(), 10u, null)
|
|
|
|
blockNode.add(structInstance1)
|
|
blockNode.add(structInstance2) // Should be ignored
|
|
|
|
blockNode.children["instance1"] shouldBeSameInstanceAs structInstance1
|
|
blockNode.children.size shouldBe 1
|
|
}
|
|
|
|
test("duplicate memory slab insertion is handled") {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val slab1 = StMemorySlab("slab1", 100u, 16u, null)
|
|
val slab2 = StMemorySlab("slab1", 200u, 32u, null)
|
|
|
|
blockNode.add(slab1)
|
|
blockNode.add(slab2) // Should be ignored
|
|
|
|
blockNode.children["slab1"] shouldBeSameInstanceAs slab1
|
|
blockNode.children.size shouldBe 1
|
|
}
|
|
|
|
test("duplicate label insertion is handled") {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val label1 = StNode("label1", StNodeType.LABEL, PtLabel("label1", Position.DUMMY))
|
|
val label2 = StNode("label1", StNodeType.LABEL, PtLabel("label1", Position.DUMMY)) // Same name!
|
|
|
|
blockNode.add(label1)
|
|
blockNode.add(label2) // Should be ignored
|
|
|
|
blockNode.children["label1"] shouldBeSameInstanceAs label1
|
|
blockNode.children.size shouldBe 1
|
|
}
|
|
|
|
// ============================================================================
|
|
// Parent Linkage Tests
|
|
// ============================================================================
|
|
|
|
test("parent pointer set correctly on add") {
|
|
val st = makeSimpleSymbolTable()
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, null)
|
|
st.add(blockNode)
|
|
|
|
blockNode.parent shouldBeSameInstanceAs st
|
|
}
|
|
|
|
test("parent pointer survives duplicate insertion") {
|
|
val (st, var1) = makeSymbolTableWithDuplicates()
|
|
val blockNode = st.children["block1"]!!
|
|
|
|
val var2 = StStaticVariable("value", DataType.UWORD, null, null, null, ZeropageWish.DONTCARE, 0u, false, null)
|
|
blockNode.add(var2) // Should be ignored
|
|
|
|
var1.parent shouldBeSameInstanceAs blockNode
|
|
}
|
|
|
|
// ============================================================================
|
|
// Collection Tests
|
|
// ============================================================================
|
|
|
|
test("allVariables collection") {
|
|
val st = makeStWithVariables()
|
|
st.allVariables.size shouldBe 2
|
|
st.allVariables.map { it.name } shouldBe listOf("var1", "var2")
|
|
}
|
|
|
|
test("allMemMappedVariables collection") {
|
|
val st = makeStWithMemVars()
|
|
st.allMemMappedVariables.size shouldBe 1
|
|
st.allMemMappedVariables.single().name shouldBe "memvar1"
|
|
}
|
|
|
|
test("allMemorySlabs collection") {
|
|
val st = makeStWithSlabs()
|
|
st.allMemorySlabs.size shouldBe 1
|
|
st.allMemorySlabs.single().name shouldBe "slab1"
|
|
}
|
|
|
|
test("allStructInstances collection") {
|
|
val st = makeStWithStructInstances()
|
|
st.allStructInstances().size shouldBe 1
|
|
st.allStructInstances().single().name shouldBe "instance1"
|
|
}
|
|
|
|
test("allStructTypes collection") {
|
|
val st = makeStWithStructs()
|
|
st.allStructTypes().size shouldBe 1
|
|
st.allStructTypes().single().name shouldBe "MyStruct"
|
|
}
|
|
})
|
|
|
|
// ================================================================================
|
|
// Helper Functions for Collection Tests
|
|
// ================================================================================
|
|
|
|
private fun makeStWithVariables(): SymbolTable {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val var1 = StStaticVariable("var1", DataType.UWORD, null, null, null, ZeropageWish.DONTCARE, 0u, false, null)
|
|
val var2 = StStaticVariable("var2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, null)
|
|
blockNode.add(var1)
|
|
blockNode.add(var2)
|
|
|
|
return st
|
|
}
|
|
|
|
private fun makeStWithMemVars(): SymbolTable {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val memvar = StMemVar("memvar1", DataType.UWORD, 0xD000u, null, null)
|
|
blockNode.add(memvar)
|
|
|
|
return st
|
|
}
|
|
|
|
private fun makeStWithSlabs(): SymbolTable {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val slab = StMemorySlab("slab1", 100u, 16u, null)
|
|
blockNode.add(slab)
|
|
|
|
return st
|
|
}
|
|
|
|
private fun makeStWithStructInstances(): SymbolTable {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val instance = StStructInstance("instance1", "MyStruct", emptyList(), 10u, null)
|
|
blockNode.add(instance)
|
|
|
|
return st
|
|
}
|
|
|
|
private fun makeStWithStructs(): SymbolTable {
|
|
val astProgram = PtProgram("test", VMTarget())
|
|
val astBlock = PtBlock("block1", false, SourceCode.Generated("block1"), PtBlock.Options(), Position.DUMMY)
|
|
astProgram.add(astBlock)
|
|
val st = SymbolTable(astProgram)
|
|
|
|
val blockNode = StNode("block1", StNodeType.BLOCK, astBlock)
|
|
st.add(blockNode)
|
|
|
|
val structDecl = PtStructDecl("MyStruct", emptyList(), Position.DUMMY)
|
|
val struct = StStruct("MyStruct", emptyList(), 10u, structDecl)
|
|
blockNode.add(struct)
|
|
|
|
return st
|
|
}
|