mirror of
https://github.com/irmen/prog8.git
synced 2025-02-24 13:29:10 +00:00
fix some scoping related symbol lookup issues, clarified scoping rules in docs
This commit is contained in:
parent
b00db4f8a2
commit
e52d05c7db
@ -92,9 +92,9 @@ internal class AstChecker(private val program: Program,
|
||||
fun checkUnsignedLoopDownto0(range: RangeExpr?) {
|
||||
if(range==null)
|
||||
return
|
||||
val step = range.step.constValue(program)?.number?.toDouble() ?: 1.0
|
||||
val step = range.step.constValue(program)?.number ?: 1.0
|
||||
if(step < -1.0) {
|
||||
val limit = range.to.constValue(program)?.number?.toDouble()
|
||||
val limit = range.to.constValue(program)?.number
|
||||
if(limit==0.0 && range.from.constValue(program)==null)
|
||||
errors.err("for unsigned loop variable it's not possible to count down with step != -1 from a non-const value to exactly zero due to value wrapping", forLoop.position)
|
||||
}
|
||||
@ -496,7 +496,7 @@ internal class AstChecker(private val program: Program,
|
||||
if (assignment.value !is FunctionCall)
|
||||
errors.err("assignment value is invalid or has no proper datatype", assignment.value.position)
|
||||
} else {
|
||||
checkAssignmentCompatible(targetDatatype.getOr(DataType.BYTE), assignTarget,
|
||||
checkAssignmentCompatible(targetDatatype.getOr(DataType.BYTE),
|
||||
sourceDatatype.getOr(DataType.BYTE), assignment.value, assignment.position)
|
||||
}
|
||||
}
|
||||
@ -833,7 +833,7 @@ internal class AstChecker(private val program: Program,
|
||||
when(expr.operator){
|
||||
"/", "%" -> {
|
||||
val constvalRight = expr.right.constValue(program)
|
||||
val divisor = constvalRight?.number?.toDouble()
|
||||
val divisor = constvalRight?.number
|
||||
if(divisor==0.0)
|
||||
errors.err("division by zero", expr.right.position)
|
||||
if(expr.operator=="%") {
|
||||
@ -1280,7 +1280,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
when (targetDt) {
|
||||
DataType.FLOAT -> {
|
||||
val number=value.number.toDouble()
|
||||
val number=value.number
|
||||
if (number > 1.7014118345e+38 || number < -1.7014118345e+38)
|
||||
return err("value '$number' out of range for MFLPT format")
|
||||
}
|
||||
@ -1356,7 +1356,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
private fun checkAssignmentCompatible(targetDatatype: DataType,
|
||||
target: AssignTarget,
|
||||
sourceDatatype: DataType,
|
||||
sourceValue: Expression,
|
||||
position: Position) : Boolean {
|
||||
|
@ -85,11 +85,9 @@ class TestScoping: FunSpec({
|
||||
addr = &labelinside
|
||||
addr = &labeloutside
|
||||
addr = &main.start.nested.nestedlabel
|
||||
addr = &nested.nestedlabel
|
||||
goto labeloutside
|
||||
goto iflabel
|
||||
goto main.start.nested.nestedlabel
|
||||
goto nested.nestedlabel
|
||||
}
|
||||
iflabel:
|
||||
}
|
||||
@ -99,11 +97,9 @@ class TestScoping: FunSpec({
|
||||
addr = &labelinside
|
||||
addr = &labeloutside
|
||||
addr = &main.start.nested.nestedlabel
|
||||
addr = &nested.nestedlabel
|
||||
goto iflabel
|
||||
goto labelinside
|
||||
goto main.start.nested.nestedlabel
|
||||
goto nested.nestedlabel
|
||||
labelinside:
|
||||
}
|
||||
|
||||
@ -119,9 +115,7 @@ class TestScoping: FunSpec({
|
||||
addr = &labelinside
|
||||
addr = &labeloutside
|
||||
addr = &main.start.nested.nestedlabel
|
||||
addr = &nested.nestedlabel
|
||||
goto main.start.nested.nestedlabel
|
||||
goto nested.nestedlabel
|
||||
}
|
||||
}
|
||||
"""
|
||||
@ -306,7 +300,7 @@ class TestScoping: FunSpec({
|
||||
compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||
}
|
||||
|
||||
test("wrong variable refs with qualified names (not from root)") {
|
||||
test("wrong variable refs with qualified names 1 (not from root)") {
|
||||
val text="""
|
||||
main {
|
||||
sub start() {
|
||||
@ -315,12 +309,9 @@ class TestScoping: FunSpec({
|
||||
routine(5)
|
||||
routine.value = 5
|
||||
routine.arg = 5
|
||||
xx = &routine.nested
|
||||
routine.nested(5)
|
||||
routine.nested.nestedvalue = 5
|
||||
routine.nested.arg2 = 5
|
||||
xx = &wrong.routine
|
||||
wrong.routine.value = 5
|
||||
routine.nested.nestedvalue = 5
|
||||
nested.nestedvalue = 5
|
||||
}
|
||||
|
||||
sub routine(ubyte arg) {
|
||||
@ -334,16 +325,11 @@ class TestScoping: FunSpec({
|
||||
"""
|
||||
val errors= ErrorReporterForTests()
|
||||
compileText(C64Target, false, text, writeAssembly = false, errors=errors).assertFailure()
|
||||
errors.errors.size shouldBe 10
|
||||
errors.errors[0] shouldContain "undefined symbol: routine"
|
||||
errors.errors[1] shouldContain "undefined symbol: routine"
|
||||
errors.errors[2] shouldContain "undefined symbol: routine.value"
|
||||
errors.errors[3] shouldContain "undefined symbol: routine.arg"
|
||||
errors.errors[4] shouldContain "undefined symbol: routine.nested"
|
||||
errors.errors[5] shouldContain "undefined symbol: routine.nested"
|
||||
errors.errors[6] shouldContain "undefined symbol: routine.nested.nestedvalue"
|
||||
errors.errors[7] shouldContain "undefined symbol: routine.nested.arg2"
|
||||
errors.errors[8] shouldContain "undefined symbol: wrong.routine"
|
||||
errors.errors[9] shouldContain "undefined symbol: wrong.routine.value"
|
||||
errors.errors.size shouldBe 5
|
||||
errors.errors[0] shouldContain "undefined symbol: routine.value"
|
||||
errors.errors[1] shouldContain "undefined symbol: routine.arg"
|
||||
errors.errors[2] shouldContain "undefined symbol: routine.nested.arg2"
|
||||
errors.errors[3] shouldContain "undefined symbol: routine.nested.nestedvalue"
|
||||
errors.errors[4] shouldContain "undefined symbol: nested.nestedvalue"
|
||||
}
|
||||
})
|
||||
|
@ -7,7 +7,6 @@ import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.parser.SourceCode
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
const val internedStringsModuleName = "prog8_interned_strings"
|
||||
|
||||
@ -76,16 +75,10 @@ interface IStatementContainer {
|
||||
// is INamedStatement -> {
|
||||
// if(stmt.name==name) return stmt
|
||||
// }
|
||||
is VarDecl -> {
|
||||
if(stmt.name==name) return stmt
|
||||
}
|
||||
is Label -> {
|
||||
if(stmt.name==name) return stmt
|
||||
}
|
||||
is Subroutine -> {
|
||||
if(stmt.name==name)
|
||||
return stmt
|
||||
}
|
||||
is VarDecl -> if(stmt.name==name) return stmt
|
||||
is Label -> if(stmt.name==name) return stmt
|
||||
is Subroutine -> if(stmt.name==name) return stmt
|
||||
is Block -> if(stmt.name==name) return stmt
|
||||
is AnonymousScope -> {
|
||||
val found = stmt.searchSymbol(name)
|
||||
if(found!=null)
|
||||
@ -155,41 +148,22 @@ interface INameScope: IStatementContainer, INamedStatement {
|
||||
}
|
||||
|
||||
private fun lookupQualified(scopedName: List<String>): Statement? {
|
||||
// a scoped name refers to a name in another namespace.
|
||||
// look "up" from our current scope to search for the correct one.
|
||||
val localScope = this.subScope(scopedName[0])
|
||||
if(localScope!=null)
|
||||
return resolveLocally(localScope, scopedName.drop(1))
|
||||
|
||||
var statementScope = this
|
||||
while(statementScope !is GlobalNamespace) {
|
||||
if(statementScope !is Module && statementScope.name==scopedName[0]) {
|
||||
return resolveLocally(statementScope, scopedName.drop(1))
|
||||
} else {
|
||||
statementScope = (statementScope as Node).definingScope
|
||||
}
|
||||
}
|
||||
|
||||
// not found, try again but now assume it's a globally scoped name starting with the name of a block
|
||||
// a scoped name refers to a name in another namespace, and stars from the root.
|
||||
for(module in (this as Node).definingModule.program.modules) {
|
||||
module.statements.forEach {
|
||||
if(it is Block && it.name==scopedName[0])
|
||||
return it.lookup(scopedName)
|
||||
val block = module.searchSymbol(scopedName[0])
|
||||
if(block!=null) {
|
||||
var statement = block
|
||||
for(name in scopedName.drop(1)) {
|
||||
statement = (statement as? IStatementContainer)?.searchSymbol(name)
|
||||
if(statement==null)
|
||||
return null
|
||||
}
|
||||
return statement
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun resolveLocally(startScope: INameScope, name: List<String>): Statement? {
|
||||
var scope: INameScope? = startScope
|
||||
for(part in name.dropLast(1)) {
|
||||
scope = scope!!.subScope(part)
|
||||
if(scope==null)
|
||||
return null
|
||||
}
|
||||
return scope!!.searchSymbol(name.last())
|
||||
}
|
||||
|
||||
private fun lookupUnqualified(name: String): Statement? {
|
||||
val builtinFunctionsNames = (this as Node).definingModule.program.builtinFunctions.names
|
||||
if(name in builtinFunctionsNames) {
|
||||
@ -200,7 +174,6 @@ interface INameScope: IStatementContainer, INamedStatement {
|
||||
}
|
||||
|
||||
// search for the unqualified name in the current scope (and possibly in any anonymousscopes it may contain)
|
||||
// if it's not found there, jump up one higher in the namespaces and try again.
|
||||
var statementScope = this
|
||||
while(statementScope !is GlobalNamespace) {
|
||||
val symbol = statementScope.searchSymbol(name)
|
||||
|
@ -119,18 +119,20 @@ It must be >= ``$0200`` (because ``$00``--``$ff`` is the ZP and ``$100``--``$1ff
|
||||
|
||||
.. _scopes:
|
||||
|
||||
**Scopes**
|
||||
**Scoping rules**
|
||||
|
||||
.. sidebar::
|
||||
Scoped access to symbols / "dotted names"
|
||||
Use qualified names ("dotted names") to reference symbols defined elsewhere
|
||||
|
||||
Every symbol is 'public' and can be accessed from elsewhere given its full "dotted name".
|
||||
In prog8 every symbol is 'public' and can be accessed from anywhere else, given its *full* "dotted name".
|
||||
So, accessing a variable ``counter`` defined in subroutine ``worker`` in block ``main``,
|
||||
can be done from anywhere by using ``main.worker.counter``.
|
||||
|
||||
*Symbols* are names defined in a certain *scope*. Inside the same scope, you can refer
|
||||
to them by their 'short' name directly. If the symbol is not found in the same scope,
|
||||
the enclosing scope is searched for it, and so on, until the symbol is found.
|
||||
the enclosing scope is searched for it, and so on, up to the top level block, until the symbol is found.
|
||||
If the symbol was not found the compiler will issue an error message.
|
||||
|
||||
|
||||
Scopes are created using either of these two statements:
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user