diff --git a/compiler/res/version.txt b/compiler/res/version.txt index ea3769f29..41ee4bdb2 100644 --- a/compiler/res/version.txt +++ b/compiler/res/version.txt @@ -1 +1 @@ -1.81 +1.82-SNAPSHOT diff --git a/compiler/src/prog8/ast/AstToplevel.kt b/compiler/src/prog8/ast/AstToplevel.kt index eeefb6809..958dcb862 100644 --- a/compiler/src/prog8/ast/AstToplevel.kt +++ b/compiler/src/prog8/ast/AstToplevel.kt @@ -3,6 +3,8 @@ package prog8.ast import prog8.ast.base.* import prog8.ast.expressions.Expression import prog8.ast.expressions.IdentifierReference +import prog8.ast.processing.IAstModifyingVisitor +import prog8.ast.processing.IAstVisitor import prog8.ast.statements.* import prog8.functions.BuiltinFunctions import java.nio.file.Path @@ -175,6 +177,7 @@ interface INameScope { is ForeverLoop -> find(it.body) is WhileLoop -> find(it.body) is WhenStatement -> it.choices.forEach { choice->find(choice.statements) } + else -> { /* do nothing */ } } } } @@ -191,7 +194,7 @@ interface IAssignable { /*********** Everything starts from here, the Program; zero or more modules *************/ -class Program(val name: String, val modules: MutableList) { +class Program(val name: String, val modules: MutableList): Node { val namespace = GlobalNamespace(modules) val definedLoadAddress: Int @@ -211,6 +214,17 @@ class Program(val name: String, val modules: MutableList) { } fun allBlocks(): List = modules.flatMap { it.statements.filterIsInstance() } + + override val position: Position = Position.DUMMY + override var parent: Node + get() = throw FatalAstException("program has no parent") + set(value) = throw FatalAstException("can't set parent of program") + + override fun linkParents(parent: Node) { + modules.forEach { + it.linkParents(this) + } + } } class Module(override val name: String, @@ -218,6 +232,7 @@ class Module(override val name: String, override val position: Position, val isLibraryModule: Boolean, val source: Path) : Node, INameScope { + override lateinit var parent: Node lateinit var program: Program val importedBy = mutableListOf() @@ -233,8 +248,12 @@ class Module(override val name: String, override fun definingScope(): INameScope = program.namespace override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)" + + fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this) + fun accept(visitor: IAstVisitor) = visitor.visit(this) } + class GlobalNamespace(val modules: List): Node, INameScope { override val name = "<<>>" override val position = Position("<<>>", 0, 0, 0) diff --git a/compiler/src/prog8/ast/base/Base.kt b/compiler/src/prog8/ast/base/Base.kt index 164c31479..c398d8d15 100644 --- a/compiler/src/prog8/ast/base/Base.kt +++ b/compiler/src/prog8/ast/base/Base.kt @@ -154,4 +154,8 @@ object ParentSentinel : Node { data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) { override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]" + + companion object { + val DUMMY = Position("", 0, 0, 0) + } } diff --git a/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt b/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt index 44f888e5d..0bd1ea5cd 100644 --- a/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt @@ -9,7 +9,7 @@ import prog8.ast.statements.* interface IAstModifyingVisitor { fun visit(program: Program) { - program.modules.forEach { visit(it) } + program.modules.forEach { it.accept(this) } } fun visit(module: Module) { diff --git a/compiler/src/prog8/ast/processing/IAstVisitor.kt b/compiler/src/prog8/ast/processing/IAstVisitor.kt index a95e96c1f..7d7f4e400 100644 --- a/compiler/src/prog8/ast/processing/IAstVisitor.kt +++ b/compiler/src/prog8/ast/processing/IAstVisitor.kt @@ -7,7 +7,7 @@ import prog8.ast.statements.* interface IAstVisitor { fun visit(program: Program) { - program.modules.forEach { visit(it) } + program.modules.forEach { it.accept(this) } } fun visit(module: Module) { diff --git a/compiler/src/prog8/ast/processing/ReflectionAstWalker.kt b/compiler/src/prog8/ast/processing/ReflectionAstWalker.kt new file mode 100644 index 000000000..c8eab449b --- /dev/null +++ b/compiler/src/prog8/ast/processing/ReflectionAstWalker.kt @@ -0,0 +1,71 @@ +package prog8.ast.processing + + +/* +This is here for reference only, reflection based ast walking is very slow +when compared to the more verbose visitor pattern interfaces. +Too bad, because the code is very small +*/ + + +//import prog8.ast.NoAstWalk +//import prog8.ast.Node +//import prog8.ast.Program +//import prog8.ast.base.Position +//import prog8.ast.expressions.BinaryExpression +//import prog8.ast.expressions.NumericLiteralValue +//import kotlin.reflect.KClass +//import kotlin.reflect.KVisibility +//import kotlin.reflect.full.declaredMemberProperties +//import kotlin.reflect.full.isSubtypeOf +//import kotlin.reflect.full.starProjectedType +// +// +//class ReflectionAstWalker { +// private val nodeType = Node::class.starProjectedType +// private val collectionType = Collection::class.starProjectedType +// +// +// fun walk(node: Node, nesting: Int) { +// val nodetype: KClass = node::class +// val indent = " ".repeat(nesting) +// //println("$indent VISITING ${nodetype.simpleName}") +// val visibleAstMembers = nodetype.declaredMemberProperties.filter { +// it.visibility!=KVisibility.PRIVATE && !it.isLateinit && +// !(it.annotations.any{a->a is NoAstWalk}) +// } +// for(prop in visibleAstMembers) { +// if(prop.returnType.isSubtypeOf(nodeType)) { +// // println("$indent +PROP: ${prop.name}") +// walk(prop.call(node) as Node, nesting + 1) +// } +// else if(prop.returnType.isSubtypeOf(collectionType)) { +// val elementType = prop.returnType.arguments.single().type +// if(elementType!=null && elementType.isSubtypeOf(nodeType)) { +// val nodes = prop.call(node) as Collection +// nodes.forEach { walk(it, nesting+1) } +// } +// } +// } +// } +// fun walk(program: Program) { +// for(module in program.modules) { +// println("---MODULE $module---") +// walk(module, 0) +// } +// } +//} +// +// +//fun main() { +// val ast = BinaryExpression( +// NumericLiteralValue.optimalInteger(100, Position.DUMMY), +// "+", +// NumericLiteralValue.optimalInteger(200, Position.DUMMY), +// Position.DUMMY +// ) +// +// val walker = ReflectionAstWalker() +// walker.walk(ast,0) +// +//} diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index da04af66c..2d69fe7a6 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -44,7 +44,7 @@ fun compileProgram(filepath: Path, importer.importModule(programAst, filepath) errors.handle() - importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map{ it.source } + importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source } val compilerOptions = determineCompilationOptions(programAst) if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG) diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index 7b84f54fe..13ff3d1e4 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -356,8 +356,8 @@ class TestPetscii { @Test fun testLiteralValueComparisons() { - val ten = NumericLiteralValue(DataType.UWORD, 10, Position("", 0, 0, 0)) - val nine = NumericLiteralValue(DataType.UBYTE, 9, Position("", 0, 0, 0)) + val ten = NumericLiteralValue(DataType.UWORD, 10, Position.DUMMY) + val nine = NumericLiteralValue(DataType.UBYTE, 9, Position.DUMMY) assertEquals(ten, ten) assertNotEquals(ten, nine) assertFalse(ten != ten) @@ -373,8 +373,8 @@ class TestPetscii { assertTrue(ten <= ten) assertFalse(ten < ten) - val abc = StringLiteralValue("abc", false, Position("", 0, 0, 0)) - val abd = StringLiteralValue("abd", false, Position("", 0, 0, 0)) + val abc = StringLiteralValue("abc", false, Position.DUMMY) + val abd = StringLiteralValue("abd", false, Position.DUMMY) assertEquals(abc, abc) assertTrue(abc!=abd) assertFalse(abc!=abc)