mirror of
https://github.com/irmen/prog8.git
synced 2025-01-23 00:31:14 +00:00
improve undefined symbol error reporting
This commit is contained in:
parent
831d41dbc6
commit
f23808eaae
@ -30,7 +30,7 @@
|
||||
const word max1 = max([-1,-2,3,99+22])
|
||||
|
||||
const word min1 = min([1,2,3,99+22])
|
||||
|
||||
word dinges = round(not_main.len1)
|
||||
|
||||
A = X>2
|
||||
X = Y>Y
|
||||
|
@ -60,7 +60,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
// compile the syntax tree into intermediate form, and optimize that
|
||||
|
||||
val compiler = Compiler(compilerOptions, globalNamespaceAfterOptimize)
|
||||
val compiler = Compiler(compilerOptions)
|
||||
val intermediate = compiler.compile(moduleAst)
|
||||
intermediate.optimize()
|
||||
|
||||
|
@ -70,13 +70,16 @@ class NameError(override var message: String, val position: Position?) : AstExce
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionException(message: String, val position: Position?) : AstException(message) {
|
||||
open class ExpressionException(message: String, val position: Position?) : AstException(message) {
|
||||
override fun toString(): String {
|
||||
val location = position?.toString() ?: ""
|
||||
return "$location Error: $message"
|
||||
}
|
||||
}
|
||||
|
||||
class UndefinedSymbolException(val symbol: IdentifierReference)
|
||||
: ExpressionException("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||
|
||||
|
||||
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}]"
|
||||
@ -273,8 +276,8 @@ interface INameScope {
|
||||
namespace.labelsAndVariables().forEach {
|
||||
println(" ".repeat(4 * (1 + indent)) + "${it.key} -> ${it.value::class.simpleName} at ${it.value.position}")
|
||||
}
|
||||
namespace.subScopes().forEach {
|
||||
printNames(indent+1, it.value)
|
||||
namespace.statements.filter { it is INameScope }.forEach {
|
||||
printNames(indent+1, it as INameScope)
|
||||
}
|
||||
}
|
||||
printNames(0, this)
|
||||
@ -347,47 +350,49 @@ class Module(override val name: String,
|
||||
processor.process(this)
|
||||
}
|
||||
|
||||
override fun definingScope(): INameScope = GlobalNamespace("<<<global>>>", statements, position)
|
||||
override fun definingScope(): INameScope = GlobalNamespace("<<<global>>>", statements, position)
|
||||
override fun usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
|
||||
override fun registerUsedName(name: String) = throw NotImplementedError("not implemented on sub-scopes")
|
||||
|
||||
|
||||
private class GlobalNamespace(override val name: String,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override val position: Position?) : INameScope {
|
||||
}
|
||||
|
||||
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main") // main is always used
|
||||
|
||||
override fun usedNames(): Set<String> = scopedNamesUsed
|
||||
private class GlobalNamespace(override val name: String,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override val position: Position?) : INameScope {
|
||||
|
||||
override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
|
||||
if(BuiltinFunctionNames.contains(scopedName.last())) {
|
||||
// builtin functions always exist, return a dummy statement for them
|
||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}")
|
||||
builtinPlaceholder.position = statement.position
|
||||
builtinPlaceholder.parent = ParentSentinel
|
||||
return builtinPlaceholder
|
||||
}
|
||||
val stmt = super.lookup(scopedName, statement)
|
||||
if(stmt!=null) {
|
||||
val targetScopedName = when(stmt) {
|
||||
is Label -> stmt.scopedname
|
||||
is VarDecl -> stmt.scopedname
|
||||
is Block -> stmt.scopedname
|
||||
is Subroutine -> stmt.scopedname
|
||||
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
||||
}
|
||||
registerUsedName(targetScopedName.joinToString("."))
|
||||
}
|
||||
return stmt
|
||||
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main") // main is always used
|
||||
|
||||
override fun usedNames(): Set<String> = scopedNamesUsed
|
||||
|
||||
override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
|
||||
if(BuiltinFunctionNames.contains(scopedName.last())) {
|
||||
// builtin functions always exist, return a dummy statement for them
|
||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}")
|
||||
builtinPlaceholder.position = statement.position
|
||||
builtinPlaceholder.parent = ParentSentinel
|
||||
return builtinPlaceholder
|
||||
}
|
||||
|
||||
override fun registerUsedName(name: String) {
|
||||
// make sure to also register each scope separately
|
||||
scopedNamesUsed.add(name)
|
||||
if(name.contains('.'))
|
||||
registerUsedName(name.substringBeforeLast('.'))
|
||||
val stmt = super.lookup(scopedName, statement)
|
||||
if(stmt!=null) {
|
||||
val targetScopedName = when(stmt) {
|
||||
is Label -> stmt.scopedname
|
||||
is VarDecl -> stmt.scopedname
|
||||
is Block -> stmt.scopedname
|
||||
is Subroutine -> stmt.scopedname
|
||||
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
||||
}
|
||||
registerUsedName(targetScopedName.joinToString("."))
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
|
||||
override fun registerUsedName(name: String) {
|
||||
// make sure to also register each scope separately
|
||||
scopedNamesUsed.add(name)
|
||||
if(name.contains('.'))
|
||||
registerUsedName(name.substringBeforeLast('.'))
|
||||
}
|
||||
}
|
||||
|
||||
@ -678,10 +683,10 @@ data class IdentifierReference(val nameInSource: List<String>) : IExpression {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
|
||||
override fun constValue(namespace: INameScope): LiteralValue? {
|
||||
val node = namespace.lookup(nameInSource, this)
|
||||
?:
|
||||
throw ExpressionException("undefined symbol: ${nameInSource.joinToString(".")}", position)
|
||||
?: throw UndefinedSymbolException(this)
|
||||
val vardecl = node as? VarDecl
|
||||
if(vardecl==null) {
|
||||
throw ExpressionException("name should be a constant, instead of: ${node::class.simpleName}", position)
|
||||
|
@ -88,9 +88,16 @@ data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, va
|
||||
}
|
||||
|
||||
|
||||
class Compiler(private val options: CompilationOptions, val namespace: INameScope) {
|
||||
class Compiler(private val options: CompilationOptions) {
|
||||
fun compile(module: Module) : IntermediateForm {
|
||||
println("\nCompiling parsed source code to intermediate code...")
|
||||
|
||||
// make sure the 'main' block is the first block. Statement even.
|
||||
val mainBlock = module.statements.single { it is Block && it.name=="main" }
|
||||
module.statements.remove(mainBlock)
|
||||
module.statements.add(0, mainBlock)
|
||||
val namespace = module.definingScope()
|
||||
|
||||
// todo
|
||||
|
||||
namespace.debugPrint()
|
||||
|
@ -11,7 +11,7 @@ fun Module.optimizeExpressions(globalNamespace: INameScope) {
|
||||
try {
|
||||
this.process(optimizer)
|
||||
} catch (ax: AstException) {
|
||||
optimizer.errors.add(ax)
|
||||
optimizer.addError(ax)
|
||||
}
|
||||
|
||||
if(optimizer.optimizationsDone==0)
|
||||
@ -56,6 +56,15 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
var optimizationsDone: Int = 0
|
||||
var errors : MutableList<AstException> = mutableListOf()
|
||||
|
||||
private val reportedErrorMessages = mutableSetOf<String>()
|
||||
|
||||
fun addError(x: AstException) {
|
||||
// check that we don't add the same error more than once
|
||||
if(!reportedErrorMessages.contains(x.message)) {
|
||||
reportedErrorMessages.add(x.message)
|
||||
errors.add(x)
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
@ -101,7 +110,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
return try {
|
||||
identifier.constValue(globalNamespace) ?: identifier
|
||||
} catch (ax: AstException) {
|
||||
errors.add(ax)
|
||||
addError(ax)
|
||||
identifier
|
||||
}
|
||||
}
|
||||
@ -111,7 +120,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
super.process(functionCall)
|
||||
functionCall.constValue(globalNamespace) ?: functionCall
|
||||
} catch (ax: AstException) {
|
||||
errors.add(ax)
|
||||
addError(ax)
|
||||
functionCall
|
||||
}
|
||||
}
|
||||
@ -166,7 +175,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
}
|
||||
return expr
|
||||
} catch (ax: AstException) {
|
||||
errors.add(ax)
|
||||
addError(ax)
|
||||
expr
|
||||
}
|
||||
}
|
||||
@ -191,7 +200,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
else -> expr
|
||||
}
|
||||
} catch (ax: AstException) {
|
||||
errors.add(ax)
|
||||
addError(ax)
|
||||
expr
|
||||
}
|
||||
}
|
||||
@ -231,7 +240,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
||||
}
|
||||
return range
|
||||
} catch (ax: AstException) {
|
||||
errors.add(ax)
|
||||
addError(ax)
|
||||
range
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user