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:
parent
7092f2a5de
commit
12df1ef6e4
@ -204,7 +204,7 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
|
||||
}
|
||||
val sourceType = getExpressionType(ctx, params.head)
|
||||
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
|
||||
}
|
||||
sourceType
|
||||
|
@ -495,11 +495,11 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
||||
case (minus, arg) => minus -> optimizeExpr(arg, currentVarValues)
|
||||
}.filterNot{
|
||||
case (_, e) => env.eval(e).exists(_.isProvablyZero)
|
||||
}, decimal = false)
|
||||
}, decimal = false).pos(pos)
|
||||
case SumExpression(expressions, decimal) =>
|
||||
// don't collapse additions, let the later stages deal with it
|
||||
// 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) =>
|
||||
val pointy = env.getPointy(name)
|
||||
val targetType = pointy.elementType
|
||||
|
@ -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.node.{DerefExpression, Expression, FunctionCallExpression, GeneratedConstantExpression, IndexedExpression, LhsExpression, LiteralExpression, M6809Register, SumExpression, VariableExpression}
|
||||
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
|
||||
|
||||
@ -43,7 +43,7 @@ import MExpressionTarget.toLd
|
||||
|
||||
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 exprType = getExpressionType(ctx, expr)
|
||||
val targetSize = MExpressionTarget.size(target)
|
||||
@ -466,6 +466,10 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
||||
}
|
||||
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)
|
||||
|
@ -1,14 +1,12 @@
|
||||
package millfork.compiler.mos
|
||||
|
||||
import millfork.{CompilationFlag, env}
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.Elidability
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.compiler._
|
||||
import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
import millfork.node.{MosRegister, _}
|
||||
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 b = env.get[Type]("byte")
|
||||
val exprType = AbstractExpressionCompiler.getExpressionType(ctx, expr)
|
||||
@ -1781,6 +1779,10 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
||||
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,
|
||||
|
2
src/main/scala/millfork/env/Constant.scala
vendored
2
src/main/scala/millfork/env/Constant.scala
vendored
@ -204,7 +204,7 @@ case class UnexpandedConstant(name: String, requiredSize: Int) extends Constant
|
||||
case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
||||
if (requiredSize == 1) {
|
||||
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
|
||||
|
31
src/main/scala/millfork/env/Environment.scala
vendored
31
src/main/scala/millfork/env/Environment.scala
vendored
@ -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)
|
||||
}
|
||||
root.get[T](target)
|
||||
case _ => log.fatal(s"`$name` is not a ${clazz.getSimpleName}", position)
|
||||
case _ => throw IdentifierHasWrongTypeOfThingException(clazz, name, position)
|
||||
}
|
||||
}
|
||||
} else parent.fold {
|
||||
hintTypo(name)
|
||||
log.fatal(s"${clazz.getSimpleName} `$name` is not defined", position)
|
||||
throw UndefinedIdentifierException(clazz, name, position)
|
||||
} {
|
||||
_.get[T](name, position)
|
||||
}
|
||||
@ -604,7 +604,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
case VariableExpression(name) =>
|
||||
maybeGet[Thing](name) match {
|
||||
case None =>
|
||||
log.error(s"`$name` is not defined")
|
||||
log.error(s"`$name` is not defined", expr.position)
|
||||
hintTypo(name)
|
||||
1
|
||||
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)
|
||||
|
||||
//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 {
|
||||
case LiteralExpression(value, size) => Some(NumericConstant(value, size))
|
||||
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 = {
|
||||
if (!log.debugEnabled) return
|
||||
@ -1996,11 +2000,18 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
||||
fixStructFields()
|
||||
val pointies = collectPointies(program.declarations)
|
||||
pointiesUsed("") = pointies
|
||||
program.declarations.foreach {
|
||||
case f: FunctionDeclarationStatement => registerFunction(f, options)
|
||||
case v: VariableDeclarationStatement => registerVariable(v, options, pointies(v.name))
|
||||
case a: ArrayDeclarationStatement => registerArray(a, options)
|
||||
case _ =>
|
||||
program.declarations.foreach { decl =>
|
||||
try {
|
||||
decl match {
|
||||
case f: FunctionDeclarationStatement => registerFunction(f, options)
|
||||
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()
|
||||
if (options.zpRegisterSize > 0 && !things.contains("__reg")) {
|
||||
|
28
src/main/scala/millfork/env/NonFatalCompilationException.scala
vendored
Normal file
28
src/main/scala/millfork/env/NonFatalCompilationException.scala
vendored
Normal 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}") {
|
||||
|
||||
}
|
@ -2,7 +2,7 @@ package millfork.test
|
||||
|
||||
import millfork.Cpu
|
||||
import millfork.env.{BasicPlainType, DerivedPlainType, NumericConstant}
|
||||
import millfork.test.emu.EmuUnoptimizedCrossPlatformRun
|
||||
import millfork.test.emu.{EmuUnoptimizedCrossPlatformRun, ShouldNotCompile}
|
||||
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(-1, 8).isProvablyNegative(signed(8)) should be(true)
|
||||
}
|
||||
}
|
||||
|
||||
test ("Overflow errors should be nice") {
|
||||
ShouldNotCompile(
|
||||
"""
|
||||
|word sum
|
||||
|void main() {
|
||||
| sum = 246 + 18
|
||||
|}
|
||||
|""".stripMargin)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user