simplified name conflict check for sub params

This commit is contained in:
Irmen de Jong 2021-10-29 00:20:33 +02:00
parent 6cb8b3b5cd
commit 7d22b9b9f9
7 changed files with 29 additions and 103 deletions

View File

@ -288,8 +288,7 @@ private fun processAst(programAst: Program, errors: IErrorReporter, compilerOpti
// TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR // TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR
// NOTE: we will then lose the opportunity to do constant-folding on any expression containing a char literal, but how often will those occur? // NOTE: we will then lose the opportunity to do constant-folding on any expression containing a char literal, but how often will those occur?
// Also they might be optimized away eventually in codegen or by the assembler even // Also they might be optimized away eventually in codegen or by the assembler even
programAst.charLiteralsToUByteLiterals(errors, compilerOptions.compTarget) programAst.charLiteralsToUByteLiterals(compilerOptions.compTarget)
errors.report()
programAst.constantFold(errors, compilerOptions.compTarget) programAst.constantFold(errors, compilerOptions.compTarget)
errors.report() errors.report()
programAst.reorderStatements(errors) programAst.reorderStatements(errors)

View File

@ -88,7 +88,7 @@ internal fun Program.reorderStatements(errors: IErrorReporter) {
} }
} }
internal fun Program.charLiteralsToUByteLiterals(errors: IErrorReporter, enc: IStringEncoding) { internal fun Program.charLiteralsToUByteLiterals(enc: IStringEncoding) {
val walker = object : AstWalker() { val walker = object : AstWalker() {
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> { override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
return listOf(IAstModification.ReplaceNode( return listOf(IAstModification.ReplaceNode(
@ -123,7 +123,7 @@ internal fun Program.preprocessAst() {
internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) { internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) {
val checker2 = AstIdentifiersChecker(this, errors, options.compTarget) val checker2 = AstIdentifiersChecker(errors, options.compTarget)
checker2.visit(this) checker2.visit(this)
if(errors.noErrors()) { if(errors.noErrors()) {

View File

@ -1,6 +1,5 @@
package prog8.compiler.astprocessing package prog8.compiler.astprocessing
import prog8.ast.Program
import prog8.ast.base.Position import prog8.ast.base.Position
import prog8.ast.expressions.StringLiteralValue import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.* import prog8.ast.statements.*
@ -9,7 +8,7 @@ import prog8.compiler.IErrorReporter
import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.BuiltinFunctions
import prog8.compiler.target.ICompilationTarget import prog8.compiler.target.ICompilationTarget
internal class AstIdentifiersChecker(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor { internal class AstIdentifiersChecker(private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor {
private var blocks = mutableMapOf<String, Block>() private var blocks = mutableMapOf<String, Block>()
private fun nameError(name: String, position: Position, existing: Statement) { private fun nameError(name: String, position: Position, existing: Statement) {
@ -69,19 +68,15 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
if (existing != null && existing !== subroutine) if (existing != null && existing !== subroutine)
nameError(subroutine.name, subroutine.position, existing) nameError(subroutine.name, subroutine.position, existing)
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters. Blocks are okay. // check that there are no local symbols (variables, labels, subs) that redefine the subroutine's parameters.
val symbolsInSub = subroutine.allDefinedSymbols val symbolsInSub = subroutine.allDefinedSymbols
val namesInSub = symbolsInSub.map{ it.first }.toSet() val namesInSub = symbolsInSub.map{ it.first }.toSet()
val paramNames = subroutine.parameters.map { it.name }.toSet() val paramNames = subroutine.parameters.map { it.name }.toSet()
val paramsToCheck = paramNames.intersect(namesInSub) val paramsToCheck = paramNames.intersect(namesInSub)
for(name in paramsToCheck) { for(name in paramsToCheck) {
// TODO clean this up? no two separate lookups? val symbol = subroutine.searchSymbol(name)
val labelOrVar = subroutine.searchLabelOrVariableNotSubscoped(name, false) if(symbol!=null && symbol.position != subroutine.position)
if(labelOrVar!=null && labelOrVar.position != subroutine.position) nameError(name, symbol.position, subroutine)
nameError(name, labelOrVar.position, subroutine)
val sub = subroutine.statements.firstOrNull { it is Subroutine && it.name==name}
if(sub!=null)
nameError(name, subroutine.position, sub)
} }
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) { if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {

View File

@ -28,6 +28,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
is FunctionCall -> translateFunctionCallResultOntoStack(expression) is FunctionCall -> translateFunctionCallResultOntoStack(expression)
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable") is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values") is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
} }
} }

View File

@ -105,8 +105,8 @@ class TestCompilerOnImportsAndIncludes {
val (where, p8Str, binStr) = it val (where, p8Str, binStr) = it
dynamicTest("%asmbinary from ${where}folder") { dynamicTest("%asmbinary from ${where}folder") {
val p8Path = assumeReadableFile(fixturesDir, p8Str) val p8Path = assumeReadableFile(fixturesDir, p8Str)
val binPath = assumeReadableFile(fixturesDir, binStr) // val binPath = assumeReadableFile(fixturesDir, binStr)
assertNotEquals( // the bug we're testing for (#54) was hidden if outputDir == workinDir assertNotEquals( // the bug we're testing for (#54) was hidden if outputDir == workingDir
workingDir.normalize().toAbsolutePath(), workingDir.normalize().toAbsolutePath(),
outputDir.normalize().toAbsolutePath(), outputDir.normalize().toAbsolutePath(),
"sanity check: workingDir and outputDir should not be the same folder" "sanity check: workingDir and outputDir should not be the same folder"

View File

@ -84,7 +84,7 @@ interface IStatementContainer {
fun isEmpty(): Boolean = statements.isEmpty() fun isEmpty(): Boolean = statements.isEmpty()
fun isNotEmpty(): Boolean = statements.isNotEmpty() fun isNotEmpty(): Boolean = statements.isNotEmpty()
fun searchLabelOrVariableNotSubscoped(name: String, alsoSubroutine: Boolean): Statement? { // TODO return INamedStatement instead? and rename to searchSymbol ? fun searchSymbol(name: String): Statement? {
// this is called quite a lot and could perhaps be optimized a bit more, // this is called quite a lot and could perhaps be optimized a bit more,
// but adding a memoization cache didn't make much of a practical runtime difference... // but adding a memoization cache didn't make much of a practical runtime difference...
for (stmt in statements) { for (stmt in statements) {
@ -99,47 +99,47 @@ interface IStatementContainer {
if(stmt.name==name) return stmt if(stmt.name==name) return stmt
} }
is Subroutine -> { is Subroutine -> {
if(alsoSubroutine && stmt.name==name) if(stmt.name==name)
return stmt return stmt
} }
is AnonymousScope -> { is AnonymousScope -> {
val found = stmt.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) val found = stmt.searchSymbol(name)
if(found!=null) if(found!=null)
return found return found
} }
is IfStatement -> { is IfStatement -> {
val found = stmt.truepart.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) ?: stmt.elsepart.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) val found = stmt.truepart.searchSymbol(name) ?: stmt.elsepart.searchSymbol(name)
if(found!=null) if(found!=null)
return found return found
} }
is BranchStatement -> { is BranchStatement -> {
val found = stmt.truepart.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) ?: stmt.elsepart.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) val found = stmt.truepart.searchSymbol(name) ?: stmt.elsepart.searchSymbol(name)
if(found!=null) if(found!=null)
return found return found
} }
is ForLoop -> { is ForLoop -> {
val found = stmt.body.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) val found = stmt.body.searchSymbol(name)
if(found!=null) if(found!=null)
return found return found
} }
is WhileLoop -> { is WhileLoop -> {
val found = stmt.body.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) val found = stmt.body.searchSymbol(name)
if(found!=null) if(found!=null)
return found return found
} }
is RepeatLoop -> { is RepeatLoop -> {
val found = stmt.body.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) val found = stmt.body.searchSymbol(name)
if(found!=null) if(found!=null)
return found return found
} }
is UntilLoop -> { is UntilLoop -> {
val found = stmt.body.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) val found = stmt.body.searchSymbol(name)
if(found!=null) if(found!=null)
return found return found
} }
is WhenStatement -> { is WhenStatement -> {
stmt.choices.forEach { stmt.choices.forEach {
val found = it.statements.searchLabelOrVariableNotSubscoped(name, alsoSubroutine) val found = it.statements.searchSymbol(name)
if(found!=null) if(found!=null)
return found return found
} }
@ -203,7 +203,7 @@ interface INameScope: IStatementContainer, INamedStatement {
if(scope==null) if(scope==null)
return null return null
} }
return scope!!.searchLabelOrVariableNotSubscoped(name.last(), true) return scope!!.searchSymbol(name.last())
} }
private fun lookupUnqualified(name: String): Statement? { private fun lookupUnqualified(name: String): Statement? {
@ -219,7 +219,7 @@ interface INameScope: IStatementContainer, INamedStatement {
// if it's not found there, jump up one higher in the namespaces and try again. // if it's not found there, jump up one higher in the namespaces and try again.
var statementScope = this var statementScope = this
while(statementScope !is GlobalNamespace) { while(statementScope !is GlobalNamespace) {
val symbol = statementScope.searchLabelOrVariableNotSubscoped(name, true) val symbol = statementScope.searchSymbol(name)
if(symbol!=null) if(symbol!=null)
return symbol return symbol
else else

View File

@ -1,85 +1,16 @@
%import syslib
%import textio
%zeropage basicsafe
%option no_sysinit
; Create a custom character set on the C64.
main { main {
sub start() { sub start() {
txt.color(1) fubar(1,2,3,4)
txt.print("creating charset...\n")
charset.make_custom_charset()
; activate the new charset in RAM
ubyte block = c64.CIA2PRA
const ubyte PAGE1 = ((c64.Screen >> 6) & $F0) | ((charset.CHARSET >> 10) & $0E)
c64.CIA2PRA = (block & $FC) | (lsb(c64.Screen >> 14) ^ $03)
c64.VMCSB = PAGE1
txt.print("\n @ @ @ @\n")
}
} }
charset { sub fubar(ubyte aa, ubyte bb, ubyte cc, ubyte dd) {
const uword CHARSET = $2000 ubyte aa
sub copy_rom_charset() { bb:
; copies the charset from ROM to RAM so we can modify it
sys.set_irqd()
ubyte bank = @($0001)
@($0001) = bank & %11111011 ; enable CHAREN, so the character rom accessible at $d000
sys.memcopy($d000, CHARSET, 256*8*2) ; copy the charset to RAM
@($0001) = bank ; reset previous memory banking
sys.clear_irqd()
}
sub make_custom_charset() {
copy_rom_charset()
; make all characters italic
ubyte c
for c in 0 to 255 {
uword ptr = CHARSET + c*$0008
@(ptr) >>= 2
@(ptr+1) >>= 2
@(ptr+2) >>= 1
@(ptr+3) >>= 1
;@(ptr+4) >>= 0
;@(ptr+5) >>= 0
@(ptr+6) <<= 1
@(ptr+7) <<= 1
ptr = CHARSET + 256*8 + c*$0008
@(ptr) >>= 2
@(ptr+1) >>= 2
@(ptr+2) >>= 1
@(ptr+3) >>= 1
;@(ptr+4) >>= 0
;@(ptr+5) >>= 0
@(ptr+6) <<= 1
@(ptr+7) <<= 1
}
; add a smiley over the '@'
ubyte[] smiley = [
%00111100,
%01000010,
%10100101,
%10000001,
%10100101,
%10011001,
%01000010,
%00111100
]
sys.memcopy(smiley, CHARSET, len(smiley))
sub cc() {
dd++
}
} }
} }