1
0
mirror of https://github.com/KarolS/millfork.git synced 2025-01-12 03:30:09 +00:00

Make less compilation errors fatal, improve error reporting (relates to #16)

This commit is contained in:
Karol Stasiak 2019-10-31 12:14:52 +01:00
parent 7092f2a5de
commit 12df1ef6e4
8 changed files with 77 additions and 22 deletions

View File

@ -204,7 +204,7 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
} }
val sourceType = getExpressionType(ctx, params.head) val sourceType = getExpressionType(ctx, params.head)
if (typ.size != sourceType.size && !sourceType.isAssignableTo(typ)) { if (typ.size != sourceType.size && !sourceType.isAssignableTo(typ)) {
ctx.log.error("Cannot cast a type to an incompatible type of different size") ctx.log.error(s"Cannot cast a type ${sourceType.name} to an incompatible type ${typ.name} of different size", params.head.position)
failed = true failed = true
} }
sourceType sourceType

View File

@ -495,11 +495,11 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
case (minus, arg) => minus -> optimizeExpr(arg, currentVarValues) case (minus, arg) => minus -> optimizeExpr(arg, currentVarValues)
}.filterNot{ }.filterNot{
case (_, e) => env.eval(e).exists(_.isProvablyZero) case (_, e) => env.eval(e).exists(_.isProvablyZero)
}, decimal = false) }, decimal = false).pos(pos)
case SumExpression(expressions, decimal) => case SumExpression(expressions, decimal) =>
// don't collapse additions, let the later stages deal with it // don't collapse additions, let the later stages deal with it
// expecially important when inside a nonet operation // expecially important when inside a nonet operation
SumExpression(expressions.map{case (minus, arg) => minus -> optimizeExpr(arg, currentVarValues)}, decimal) SumExpression(expressions.map{case (minus, arg) => minus -> optimizeExpr(arg, currentVarValues)}, decimal).pos(pos)
case IndexedExpression(name, index) => case IndexedExpression(name, index) =>
val pointy = env.getPointy(name) val pointy = env.getPointy(name)
val targetType = pointy.elementType val targetType = pointy.elementType

View File

@ -4,7 +4,7 @@ import millfork.assembly.m6809.{DAccumulatorIndexed, Indexed, MLine, MOpcode, Tw
import millfork.compiler.{AbstractExpressionCompiler, BranchIfFalse, BranchIfTrue, BranchSpec, ComparisonType, CompilationContext, NoBranching} import millfork.compiler.{AbstractExpressionCompiler, BranchIfFalse, BranchIfTrue, BranchSpec, ComparisonType, CompilationContext, NoBranching}
import millfork.node.{DerefExpression, Expression, FunctionCallExpression, GeneratedConstantExpression, IndexedExpression, LhsExpression, LiteralExpression, M6809Register, SumExpression, VariableExpression} import millfork.node.{DerefExpression, Expression, FunctionCallExpression, GeneratedConstantExpression, IndexedExpression, LhsExpression, LiteralExpression, M6809Register, SumExpression, VariableExpression}
import millfork.assembly.m6809.MOpcode._ import millfork.assembly.m6809.MOpcode._
import millfork.env.{AssemblyParamSignature, Constant, ConstantBooleanType, ConstantPointy, ExternFunction, FatBooleanType, FunctionInMemory, FunctionPointerType, M6809RegisterVariable, MacroFunction, MathOperator, MemoryVariable, NormalFunction, NormalParamSignature, NumericConstant, StackVariablePointy, ThingInMemory, Type, Variable, VariableInMemory, VariablePointy} import millfork.env.{AssemblyParamSignature, Constant, ConstantBooleanType, ConstantPointy, ExternFunction, FatBooleanType, FunctionInMemory, FunctionPointerType, M6809RegisterVariable, MacroFunction, MathOperator, MemoryVariable, NonFatalCompilationException, NormalFunction, NormalParamSignature, NumericConstant, StackVariablePointy, ThingInMemory, Type, Variable, VariableInMemory, VariablePointy}
import scala.collection.GenTraversableOnce import scala.collection.GenTraversableOnce
@ -43,7 +43,7 @@ import MExpressionTarget.toLd
object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] { object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
def compile(ctx: CompilationContext, expr: Expression, target: MExpressionTarget.Value, branches: BranchSpec = BranchSpec.None): List[MLine] = { def compile(ctx: CompilationContext, expr: Expression, target: MExpressionTarget.Value, branches: BranchSpec = BranchSpec.None): List[MLine] = try {
val env = ctx.env val env = ctx.env
val exprType = getExpressionType(ctx, expr) val exprType = getExpressionType(ctx, expr)
val targetSize = MExpressionTarget.size(target) val targetSize = MExpressionTarget.size(target)
@ -466,6 +466,10 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
} }
case _ => ??? case _ => ???
} }
} catch {
case ex: NonFatalCompilationException =>
ctx.log.error(ex.getMessage, ex.position.orElse(expr.position))
Nil
} }
def compileToA(ctx: CompilationContext, expr: Expression): List[MLine] = compile(ctx, expr, MExpressionTarget.A) def compileToA(ctx: CompilationContext, expr: Expression): List[MLine] = compile(ctx, expr, MExpressionTarget.A)

View File

@ -1,14 +1,12 @@
package millfork.compiler.mos package millfork.compiler.mos
import millfork.{CompilationFlag, env} import millfork.CompilationFlag
import millfork.assembly.Elidability import millfork.assembly.Elidability
import millfork.assembly.mos.AddrMode._ import millfork.assembly.mos.AddrMode._
import millfork.assembly.mos.Opcode._ import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._ import millfork.assembly.mos._
import millfork.assembly.z80.ZLine
import millfork.compiler._ import millfork.compiler._
import millfork.env._ import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node.{MosRegister, _} import millfork.node.{MosRegister, _}
import millfork.output.NoAlignment import millfork.output.NoAlignment
/** /**
@ -630,7 +628,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
} }
} }
def compile(ctx: CompilationContext, expr: Expression, exprTypeAndVariable: Option[(Type, Variable)], branches: BranchSpec): List[AssemblyLine] = { def compile(ctx: CompilationContext, expr: Expression, exprTypeAndVariable: Option[(Type, Variable)], branches: BranchSpec): List[AssemblyLine] = try {
val env = ctx.env val env = ctx.env
val b = env.get[Type]("byte") val b = env.get[Type]("byte")
val exprType = AbstractExpressionCompiler.getExpressionType(ctx, expr) val exprType = AbstractExpressionCompiler.getExpressionType(ctx, expr)
@ -1781,6 +1779,10 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
calculate ++ compile(ctx, VariableExpression(resultVariable), exprTypeAndVariable, branches) calculate ++ compile(ctx, VariableExpression(resultVariable), exprTypeAndVariable, branches)
} }
} }
} catch {
case ex: ConstantOverflowException =>
ctx.log.error(ex.getMessage, ex.position.orElse(expr.position))
Nil
} }
private def compileTransitiveRelation(ctx: CompilationContext, private def compileTransitiveRelation(ctx: CompilationContext,

View File

@ -204,7 +204,7 @@ case class UnexpandedConstant(name: String, requiredSize: Int) extends Constant
case class NumericConstant(value: Long, requiredSize: Int) extends Constant { case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
if (requiredSize == 1) { if (requiredSize == 1) {
if (value < -128 || value > 255) { if (value < -128 || value > 255) {
throw new IllegalArgumentException(s"The constant $value is too big") throw ConstantOverflowException(value, requiredSize)
} }
} }
override def isQuiteNegative: Boolean = value < 0 override def isQuiteNegative: Boolean = value < 0

View File

@ -313,12 +313,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
log.warn(s"Alias `$name` is deprecated, use `$target` instead", position) log.warn(s"Alias `$name` is deprecated, use `$target` instead", position)
} }
root.get[T](target) root.get[T](target)
case _ => log.fatal(s"`$name` is not a ${clazz.getSimpleName}", position) case _ => throw IdentifierHasWrongTypeOfThingException(clazz, name, position)
} }
} }
} else parent.fold { } else parent.fold {
hintTypo(name) hintTypo(name)
log.fatal(s"${clazz.getSimpleName} `$name` is not defined", position) throw UndefinedIdentifierException(clazz, name, position)
} { } {
_.get[T](name, position) _.get[T](name, position)
} }
@ -604,7 +604,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
case VariableExpression(name) => case VariableExpression(name) =>
maybeGet[Thing](name) match { maybeGet[Thing](name) match {
case None => case None =>
log.error(s"`$name` is not defined") log.error(s"`$name` is not defined", expr.position)
hintTypo(name) hintTypo(name)
1 1
case Some(thing) => thing match { case Some(thing) => thing match {
@ -628,7 +628,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
def eval(e: Expression): Option[Constant] = evalImpl(e, None) def eval(e: Expression): Option[Constant] = evalImpl(e, None)
//noinspection ScalaUnnecessaryParentheses,ZeroIndexToHead //noinspection ScalaUnnecessaryParentheses,ZeroIndexToHead
private def evalImpl(e: Expression, vv: Option[Map[String, Constant]]): Option[Constant] = { private def evalImpl(e: Expression, vv: Option[Map[String, Constant]]): Option[Constant] = try{{
e match { e match {
case LiteralExpression(value, size) => Some(NumericConstant(value, size)) case LiteralExpression(value, size) => Some(NumericConstant(value, size))
case tl:TextLiteralExpression => Some(getPointy(getTextLiteralArrayName(tl)).asInstanceOf[ConstantPointy].value) case tl:TextLiteralExpression => Some(getPointy(getTextLiteralArrayName(tl)).asInstanceOf[ConstantPointy].value)
@ -796,7 +796,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
} }
} }
} }
}.map(_.quickSimplify) }.map(_.quickSimplify)} catch {
case ez:NonFatalCompilationException =>
log.error(ez.getMessage, e.position)
None
}
def debugConstness(item: Expression): Unit = { def debugConstness(item: Expression): Unit = {
if (!log.debugEnabled) return if (!log.debugEnabled) return
@ -1996,11 +2000,18 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
fixStructFields() fixStructFields()
val pointies = collectPointies(program.declarations) val pointies = collectPointies(program.declarations)
pointiesUsed("") = pointies pointiesUsed("") = pointies
program.declarations.foreach { program.declarations.foreach { decl =>
case f: FunctionDeclarationStatement => registerFunction(f, options) try {
case v: VariableDeclarationStatement => registerVariable(v, options, pointies(v.name)) decl match {
case a: ArrayDeclarationStatement => registerArray(a, options) case f: FunctionDeclarationStatement => registerFunction(f, options)
case _ => case v: VariableDeclarationStatement => registerVariable(v, options, pointies(v.name))
case a: ArrayDeclarationStatement => registerArray(a, options)
case _ =>
}
} catch {
case ex: NonFatalCompilationException =>
log.error(ex.getMessage, ex.position.orElse(decl.position))
}
} }
expandAliases() expandAliases()
if (options.zpRegisterSize > 0 && !things.contains("__reg")) { if (options.zpRegisterSize > 0 && !things.contains("__reg")) {

View File

@ -0,0 +1,28 @@
package millfork.env
import millfork.node.Position
/**
* @author Karol Stasiak
*/
abstract class NonFatalCompilationException(msg: String) extends RuntimeException(msg) {
def position: Option[Position]
}
case class ConstantOverflowException(value: Long, expectedSize: Int) extends NonFatalCompilationException (
if (expectedSize == 1) {
s"The constant $value is too big to fit in a byte"
} else {
s"The constant $value is too big to fit in $expectedSize bytes"
}
) {
override def position: Option[Position] = None
}
case class UndefinedIdentifierException(thingClass: Class[_], name: String, val position: Option[Position])
extends NonFatalCompilationException(s"${thingClass.getSimpleName} `$name` is not defined") {
}
case class IdentifierHasWrongTypeOfThingException(thingClass: Class[_], name: String, val position: Option[Position])
extends NonFatalCompilationException(s"`$name` is not a ${thingClass.getSimpleName}") {
}

View File

@ -2,7 +2,7 @@ package millfork.test
import millfork.Cpu import millfork.Cpu
import millfork.env.{BasicPlainType, DerivedPlainType, NumericConstant} import millfork.env.{BasicPlainType, DerivedPlainType, NumericConstant}
import millfork.test.emu.EmuUnoptimizedCrossPlatformRun import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, ShouldNotCompile}
import org.scalatest.{FunSuite, Matchers} import org.scalatest.{FunSuite, Matchers}
/** /**
@ -33,4 +33,14 @@ class ConstantSuite extends FunSuite with Matchers {
NumericConstant(0x7f, 2).isProvablyNegative(signed(2)) should be(false) NumericConstant(0x7f, 2).isProvablyNegative(signed(2)) should be(false)
NumericConstant(-1, 8).isProvablyNegative(signed(8)) should be(true) NumericConstant(-1, 8).isProvablyNegative(signed(8)) should be(true)
} }
}
test ("Overflow errors should be nice") {
ShouldNotCompile(
"""
|word sum
|void main() {
| sum = 246 + 18
|}
|""".stripMargin)
}
}