mirror of
https://github.com/irmen/prog8.git
synced 2024-07-05 06:29:02 +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 max1 = max([-1,-2,3,99+22])
|
||||||
|
|
||||||
const word min1 = min([1,2,3,99+22])
|
const word min1 = min([1,2,3,99+22])
|
||||||
|
word dinges = round(not_main.len1)
|
||||||
|
|
||||||
A = X>2
|
A = X>2
|
||||||
X = Y>Y
|
X = Y>Y
|
||||||
|
@ -60,7 +60,7 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
// compile the syntax tree into intermediate form, and optimize that
|
// compile the syntax tree into intermediate form, and optimize that
|
||||||
|
|
||||||
val compiler = Compiler(compilerOptions, globalNamespaceAfterOptimize)
|
val compiler = Compiler(compilerOptions)
|
||||||
val intermediate = compiler.compile(moduleAst)
|
val intermediate = compiler.compile(moduleAst)
|
||||||
intermediate.optimize()
|
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 {
|
override fun toString(): String {
|
||||||
val location = position?.toString() ?: ""
|
val location = position?.toString() ?: ""
|
||||||
return "$location Error: $message"
|
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) {
|
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}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
@ -273,8 +276,8 @@ interface INameScope {
|
|||||||
namespace.labelsAndVariables().forEach {
|
namespace.labelsAndVariables().forEach {
|
||||||
println(" ".repeat(4 * (1 + indent)) + "${it.key} -> ${it.value::class.simpleName} at ${it.value.position}")
|
println(" ".repeat(4 * (1 + indent)) + "${it.key} -> ${it.value::class.simpleName} at ${it.value.position}")
|
||||||
}
|
}
|
||||||
namespace.subScopes().forEach {
|
namespace.statements.filter { it is INameScope }.forEach {
|
||||||
printNames(indent+1, it.value)
|
printNames(indent+1, it as INameScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printNames(0, this)
|
printNames(0, this)
|
||||||
@ -347,47 +350,49 @@ class Module(override val name: String,
|
|||||||
processor.process(this)
|
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 usedNames(): Set<String> = throw NotImplementedError("not implemented on sub-scopes")
|
||||||
override fun registerUsedName(name: 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? {
|
private val scopedNamesUsed: MutableSet<String> = mutableSetOf("main") // main is always used
|
||||||
if(BuiltinFunctionNames.contains(scopedName.last())) {
|
|
||||||
// builtin functions always exist, return a dummy statement for them
|
override fun usedNames(): Set<String> = scopedNamesUsed
|
||||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}")
|
|
||||||
builtinPlaceholder.position = statement.position
|
override fun lookup(scopedName: List<String>, statement: Node): IStatement? {
|
||||||
builtinPlaceholder.parent = ParentSentinel
|
if(BuiltinFunctionNames.contains(scopedName.last())) {
|
||||||
return builtinPlaceholder
|
// builtin functions always exist, return a dummy statement for them
|
||||||
}
|
val builtinPlaceholder = Label("builtin::${scopedName.last()}")
|
||||||
val stmt = super.lookup(scopedName, statement)
|
builtinPlaceholder.position = statement.position
|
||||||
if(stmt!=null) {
|
builtinPlaceholder.parent = ParentSentinel
|
||||||
val targetScopedName = when(stmt) {
|
return builtinPlaceholder
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
val stmt = super.lookup(scopedName, statement)
|
||||||
override fun registerUsedName(name: String) {
|
if(stmt!=null) {
|
||||||
// make sure to also register each scope separately
|
val targetScopedName = when(stmt) {
|
||||||
scopedNamesUsed.add(name)
|
is Label -> stmt.scopedname
|
||||||
if(name.contains('.'))
|
is VarDecl -> stmt.scopedname
|
||||||
registerUsedName(name.substringBeforeLast('.'))
|
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
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun constValue(namespace: INameScope): LiteralValue? {
|
override fun constValue(namespace: INameScope): LiteralValue? {
|
||||||
val node = namespace.lookup(nameInSource, this)
|
val node = namespace.lookup(nameInSource, this)
|
||||||
?:
|
?: throw UndefinedSymbolException(this)
|
||||||
throw ExpressionException("undefined symbol: ${nameInSource.joinToString(".")}", position)
|
|
||||||
val vardecl = node as? VarDecl
|
val vardecl = node as? VarDecl
|
||||||
if(vardecl==null) {
|
if(vardecl==null) {
|
||||||
throw ExpressionException("name should be a constant, instead of: ${node::class.simpleName}", position)
|
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 {
|
fun compile(module: Module) : IntermediateForm {
|
||||||
println("\nCompiling parsed source code to intermediate code...")
|
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
|
// todo
|
||||||
|
|
||||||
namespace.debugPrint()
|
namespace.debugPrint()
|
||||||
|
@ -11,7 +11,7 @@ fun Module.optimizeExpressions(globalNamespace: INameScope) {
|
|||||||
try {
|
try {
|
||||||
this.process(optimizer)
|
this.process(optimizer)
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
optimizer.errors.add(ax)
|
optimizer.addError(ax)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(optimizer.optimizationsDone==0)
|
if(optimizer.optimizationsDone==0)
|
||||||
@ -56,6 +56,15 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
|||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
var errors : MutableList<AstException> = mutableListOf()
|
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 {
|
override fun process(decl: VarDecl): IStatement {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// 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 {
|
return try {
|
||||||
identifier.constValue(globalNamespace) ?: identifier
|
identifier.constValue(globalNamespace) ?: identifier
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
errors.add(ax)
|
addError(ax)
|
||||||
identifier
|
identifier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +120,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
|||||||
super.process(functionCall)
|
super.process(functionCall)
|
||||||
functionCall.constValue(globalNamespace) ?: functionCall
|
functionCall.constValue(globalNamespace) ?: functionCall
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
errors.add(ax)
|
addError(ax)
|
||||||
functionCall
|
functionCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +175,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
|||||||
}
|
}
|
||||||
return expr
|
return expr
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
errors.add(ax)
|
addError(ax)
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,7 +200,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
|||||||
else -> expr
|
else -> expr
|
||||||
}
|
}
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
errors.add(ax)
|
addError(ax)
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,7 +240,7 @@ class ExpressionOptimizer(private val globalNamespace: INameScope) : IAstProcess
|
|||||||
}
|
}
|
||||||
return range
|
return range
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
errors.add(ax)
|
addError(ax)
|
||||||
range
|
range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user