optimized name checking, no longer depends on scopedname

This commit is contained in:
Irmen de Jong 2019-03-28 21:30:30 +01:00
parent 62ceace941
commit 5502a3e3ee
4 changed files with 36 additions and 38 deletions

View File

@ -88,7 +88,7 @@ private fun compileMain(args: Array<String>) {
println("Syntax check...") println("Syntax check...")
val heap = HeapValues() val heap = HeapValues()
val time1= measureTimeMillis { val time1= measureTimeMillis {
moduleAst.checkIdentifiers(heap) moduleAst.checkIdentifiers(namespace)
} }
//println(" time1: $time1") //println(" time1: $time1")
val time2 = measureTimeMillis { val time2 = measureTimeMillis {
@ -104,9 +104,7 @@ private fun compileMain(args: Array<String>) {
} }
//println(" time4: $time4") //println(" time4: $time4")
val allScopedSymbolDefinitions = moduleAst.checkIdentifiers(heap) // useful for checking symbol usage later? moduleAst.checkIdentifiers(namespace)
// moduleAst.simplifyExpressions(namespace, heap)
// moduleAst.optimizeStatements(namespace, heap)
if(optimize) { if(optimize) {
// optimize the parse tree // optimize the parse tree
println("Optimizing...") println("Optimizing...")

View File

@ -326,6 +326,7 @@ inline fun <reified T> findParentNode(node: Node): T? {
interface IStatement : Node { interface IStatement : Node {
fun process(processor: IAstProcessor) : IStatement fun process(processor: IAstProcessor) : IStatement
fun makeScopedName(name: String): String { fun makeScopedName(name: String): String {
// TODO eventually get rid of this scopedName
// this is usually cached in a lazy property on the statement object itself (label, subroutine, vardecl) // this is usually cached in a lazy property on the statement object itself (label, subroutine, vardecl)
val scope = mutableListOf<String>() val scope = mutableListOf<String>()
var statementScope = this.parent var statementScope = this.parent

View File

@ -1,17 +1,15 @@
package prog8.ast package prog8.ast
import prog8.compiler.HeapValues
import prog8.functions.BuiltinFunctions import prog8.functions.BuiltinFunctions
/** /**
* Checks the validity of all identifiers (no conflicts) * Checks the validity of all identifiers (no conflicts)
* Also builds a list of all (scoped) symbol definitions
* Also makes sure that subroutine's parameters also become local variable decls in the subroutine's scope. * Also makes sure that subroutine's parameters also become local variable decls in the subroutine's scope.
* Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly. * Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly.
*/ */
fun Module.checkIdentifiers(heap: HeapValues): MutableMap<String, IStatement> { fun Module.checkIdentifiers(namespace: INameScope) {
val checker = AstIdentifiersChecker(heap) val checker = AstIdentifiersChecker(namespace)
this.process(checker) this.process(checker)
// add any anonymous variables for heap values that are used, // add any anonymous variables for heap values that are used,
@ -43,14 +41,13 @@ fun Module.checkIdentifiers(heap: HeapValues): MutableMap<String, IStatement> {
} }
printErrors(checker.result(), name) printErrors(checker.result(), name)
return checker.symbols
} }
private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor { private class AstIdentifiersChecker(private val namespace: INameScope) : IAstProcessor {
private val checkResult: MutableList<AstException> = mutableListOf() private val checkResult: MutableList<AstException> = mutableListOf()
var symbols: MutableMap<String, IStatement> = mutableMapOf() var blocks: MutableMap<String, Block> = mutableMapOf()
private set private set
fun result(): List<AstException> { fun result(): List<AstException> {
@ -58,16 +55,16 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
} }
private fun nameError(name: String, position: Position, existing: IStatement) { private fun nameError(name: String, position: Position, existing: IStatement) {
checkResult.add(NameError("name conflict '$name', first defined in ${existing.position.file} line ${existing.position.line}", position)) checkResult.add(NameError("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position))
} }
override fun process(block: Block): IStatement { override fun process(block: Block): IStatement {
val existing = symbols[block.name] val existing = blocks[block.name]
if(existing!=null) { if(existing!=null)
nameError(block.name, block.position, existing) nameError(block.name, block.position, existing)
} else { else
symbols[block.name] = block blocks[block.name] = block
}
return super.process(block) return super.process(block)
} }
@ -90,14 +87,10 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
// the builtin functions can't be redefined // the builtin functions can't be redefined
checkResult.add(NameError("builtin function cannot be redefined", decl.position)) checkResult.add(NameError("builtin function cannot be redefined", decl.position))
// TODO: check for name conflict only has to be done within the same scope, no need to get the full scoped name val existing = namespace.lookup(listOf(decl.name), decl)
val scopedName = decl.scopedname if (existing != null && existing !== decl)
val existing = symbols[scopedName]
if(existing!=null) {
nameError(decl.name, decl.position, existing) nameError(decl.name, decl.position, existing)
} else {
symbols[scopedName] = decl
}
return super.process(decl) return super.process(decl)
} }
@ -109,14 +102,9 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
if (subroutine.parameters.any { it.name in BuiltinFunctions }) if (subroutine.parameters.any { it.name in BuiltinFunctions })
checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position)) checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
// TODO: check for name conflict only has to be done within the same scope, no need to get the full scoped name val existing = namespace.lookup(listOf(subroutine.name), subroutine)
val scopedName = subroutine.scopedname if (existing != null && existing !== subroutine)
val existing = symbols[scopedName]
if (existing != null) {
nameError(subroutine.name, subroutine.position, existing) nameError(subroutine.name, subroutine.position, existing)
} else {
symbols[scopedName] = subroutine
}
// check that there are no local variables that redefine the subroutine's parameters // check that there are no local variables that redefine the subroutine's parameters
val allDefinedNames = subroutine.allLabelsAndVariables() val allDefinedNames = subroutine.allLabelsAndVariables()
@ -153,14 +141,9 @@ private class AstIdentifiersChecker(val heap: HeapValues) : IAstProcessor {
// the builtin functions can't be redefined // the builtin functions can't be redefined
checkResult.add(NameError("builtin function cannot be redefined", label.position)) checkResult.add(NameError("builtin function cannot be redefined", label.position))
} else { } else {
// TODO: check for name conflict only has to be done within the same scope, no need to get the full scoped name val existing = namespace.lookup(listOf(label.name), label)
val scopedName = label.scopedname if (existing != null && existing !== label)
val existing = symbols[scopedName]
if (existing != null) {
nameError(label.name, label.position, existing) nameError(label.name, label.position, existing)
} else {
symbols[scopedName] = label
}
} }
return super.process(label) return super.process(label)
} }

View File

@ -6,6 +6,22 @@
sub start() { sub start() {
byte var1
byte var1
sub subsub() {
byte var1
byte var1
}
sub subsub() {
byte var1
byte var2
byte var3
byte var2
}
if A>10 { if A>10 {
A=44 A=44
while true { while true {