some infix functions

This commit is contained in:
Irmen de Jong 2019-06-23 15:43:52 +02:00
parent f473be8951
commit 6dd3371781
8 changed files with 52 additions and 45 deletions

View File

@ -1,6 +1,7 @@
plugins {
id "org.jetbrains.kotlin.jvm" version "1.3.40"
id 'application'
id 'org.jetbrains.dokka' version "0.9.18"
}
repositories {
@ -95,3 +96,9 @@ test {
events "passed", "skipped", "failed"
}
}
dokka {
outputFormat = 'html'
outputDirectory = "$buildDir/kdoc"
}

View File

@ -34,7 +34,10 @@ enum class DataType {
ARRAY_W,
ARRAY_F;
fun assignableTo(targetType: DataType) =
/**
* is the type assignable to the given other type?
*/
infix fun isAssignableTo(targetType: DataType) =
// what types are assignable to others without loss of precision?
when(this) {
UBYTE -> targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
@ -49,8 +52,14 @@ enum class DataType {
}
fun assignableTo(targetTypes: Set<DataType>) = targetTypes.any { this.assignableTo(it) }
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
infix fun biggerThan(other: DataType) =
when(this) {
in ByteDatatypes -> false
in WordDatatypes -> other in ByteDatatypes
else -> true
}
}
enum class Register {
@ -412,7 +421,7 @@ interface INameScope {
return null
}
fun allLabelsAndVariables(): Set<String> =
fun allDefinedNames(): Set<String> =
statements.filterIsInstance<Label>().map { it.name }.toSet() + statements.filterIsInstance<VarDecl>().map { it.name }.toSet()
fun lookup(scopedName: List<String>, localContext: Node) : IStatement? {
@ -455,8 +464,7 @@ interface INameScope {
fun containsNoCodeNorVars() = !containsCodeOrVars()
fun remove(stmt: IStatement) {
val removed = statements.remove(stmt)
if(!removed)
if(!statements.remove(stmt))
throw FatalAstException("stmt to remove wasn't found in scope")
}
}
@ -849,16 +857,16 @@ data class AssignTarget(val register: Register?,
fun isMemoryMapped(namespace: INameScope): Boolean =
memoryAddress!=null || (identifier?.targetVarDecl(namespace)?.type==VarDeclType.MEMORY)
fun isSameAs(value: IExpression): Boolean {
infix fun isSameAs(value: IExpression): Boolean {
return when {
this.memoryAddress!=null -> false
this.register!=null -> value is RegisterExpr && value.register==this.register
this.identifier!=null -> value is IdentifierReference && value.nameInSource==this.identifier.nameInSource
this.register!=null -> value is RegisterExpr && value.register==register
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier.nameInSource
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
value.identifier.nameInSource==this.arrayindexed.identifier.nameInSource &&
value.identifier.nameInSource==arrayindexed.identifier.nameInSource &&
value.arrayspec.size()!=null &&
this.arrayindexed.arrayspec.size()!=null &&
value.arrayspec.size()==this.arrayindexed.arrayspec.size()
arrayindexed.arrayspec.size()!=null &&
value.arrayspec.size()==arrayindexed.arrayspec.size()
else -> false
}
}
@ -912,23 +920,23 @@ interface IExpression: Node {
fun referencesIdentifier(name: String): Boolean
fun resultingDatatype(program: Program): DataType?
fun same(other: IExpression): Boolean {
infix fun isSameAs(other: IExpression): Boolean {
if(this===other)
return true
when(this) {
is RegisterExpr ->
return (other is RegisterExpr && other.register==this.register)
return (other is RegisterExpr && other.register==register)
is IdentifierReference ->
return (other is IdentifierReference && other.nameInSource==this.nameInSource)
return (other is IdentifierReference && other.nameInSource==nameInSource)
is PrefixExpression ->
return (other is PrefixExpression && other.operator==this.operator && other.expression.same(this.expression))
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
is BinaryExpression ->
return (other is BinaryExpression && other.operator==this.operator
&& other.left.same(this.left)
&& other.right.same(this.right))
return (other is BinaryExpression && other.operator==operator
&& other.left isSameAs left
&& other.right isSameAs right)
is ArrayIndexedExpression -> {
return (other is ArrayIndexedExpression && other.identifier.nameInSource == this.identifier.nameInSource
&& other.arrayspec.index.same(this.arrayspec.index))
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
&& other.arrayspec.index isSameAs arrayspec.index)
}
is LiteralValue -> return (other is LiteralValue && other==this)
}

View File

@ -832,7 +832,7 @@ private class AstChecker(private val program: Program,
else {
for (arg in args.withIndex().zip(func.parameters)) {
val argDt=arg.first.value.resultingDatatype(program)
if(argDt!=null && !argDt.assignableTo(arg.second.possibleDatatypes)) {
if(argDt!=null && !(argDt isAssignableTo arg.second.possibleDatatypes)) {
checkResult.add(ExpressionError("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position))
}
}
@ -844,7 +844,7 @@ private class AstChecker(private val program: Program,
checkResult.add(ExpressionError("swap requires 2 args of identical type", position))
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
checkResult.add(ExpressionError("swap requires 2 variables, not constant value(s)", position))
else if(args[0].same(args[1]))
else if(args[0] isSameAs args[1])
checkResult.add(ExpressionError("swap should have 2 different args", position))
else if(dt1 !in NumericDatatypes)
checkResult.add(ExpressionError("swap requires args of numerical type", position))
@ -856,7 +856,7 @@ private class AstChecker(private val program: Program,
else {
for (arg in args.withIndex().zip(target.parameters)) {
val argDt = arg.first.value.resultingDatatype(program)
if(argDt!=null && !argDt.assignableTo(arg.second.type)) {
if(argDt!=null && !(argDt isAssignableTo arg.second.type)) {
// for asm subroutines having STR param it's okay to provide a UWORD too (pointer value)
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt==DataType.UWORD))
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position))

View File

@ -115,7 +115,7 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
nameError(subroutine.name, subroutine.position, existing)
// check that there are no local variables that redefine the subroutine's parameters
val allDefinedNames = subroutine.allLabelsAndVariables()
val allDefinedNames = subroutine.allDefinedNames()
val paramNames = subroutine.parameters.map { it.name }.toSet()
val paramsToCheck = paramNames.intersect(allDefinedNames)
for(name in paramsToCheck) {

View File

@ -42,7 +42,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
module.statements.add(0, nonLibBlock.second)
val mainBlock = module.statements.singleOrNull { it is Block && it.name=="main" }
if(mainBlock!=null && (mainBlock as Block).address==null) {
module.statements.remove(mainBlock)
module.remove(mainBlock)
module.statements.add(0, mainBlock)
}
@ -64,7 +64,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
// move all subroutines to the end of the block
for (subroutine in subroutines) {
if(subroutine.name!="start" || block.name!="main") {
block.statements.remove(subroutine)
block.remove(subroutine)
block.statements.add(subroutine)
}
numSubroutinesAtEnd++
@ -72,7 +72,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
// move the "start" subroutine to the top
if(block.name=="main") {
block.statements.singleOrNull { it is Subroutine && it.name == "start" } ?.let {
block.statements.remove(it)
block.remove(it)
block.statements.add(0, it)
numSubroutinesAtEnd--
}

View File

@ -972,7 +972,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
throw AstException("swap requires 2 args of identical type")
if (args[0].constValue(program) != null || args[1].constValue(program) != null)
throw AstException("swap requires 2 variables, not constant value(s)")
if(args[0].same(args[1]))
if(args[0] isSameAs args[1])
throw AstException("swap should have 2 different args")
if(dt1 !in NumericDatatypes)
throw AstException("swap requires args of numerical type")

View File

@ -295,8 +295,8 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
private fun determineY(x: IExpression, subBinExpr: BinaryExpression): IExpression? {
return when {
subBinExpr.left.same(x) -> subBinExpr.right
subBinExpr.right.same(x) -> subBinExpr.left
subBinExpr.left isSameAs x -> subBinExpr.right
subBinExpr.right isSameAs x -> subBinExpr.left
else -> null
}
}
@ -362,7 +362,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
}
if(leftConstVal==null && rightConstVal!=null) {
if(isBiggerType(leftDt, rightDt)) {
if(leftDt biggerThan rightDt) {
val (adjusted, newValue) = adjust(rightConstVal, leftDt)
if (adjusted) {
expr.right = newValue
@ -372,7 +372,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
}
return false
} else if(leftConstVal!=null && rightConstVal==null) {
if(isBiggerType(rightDt, leftDt)) {
if(rightDt biggerThan leftDt) {
val (adjusted, newValue) = adjust(leftConstVal, rightDt)
if (adjusted) {
expr.left = newValue
@ -386,14 +386,6 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
}
}
private fun isBiggerType(type: DataType, other: DataType) =
when(type) {
in ByteDatatypes -> false
in WordDatatypes -> other in ByteDatatypes
else -> true
}
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: LiteralValue?, val rightVal: LiteralValue?)
private fun reorderAssociative(expr: BinaryExpression, leftVal: LiteralValue?): ReorderedAssociativeBinaryExpr {

View File

@ -104,7 +104,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
if (removeSubroutines.isNotEmpty()) {
removeSubroutines.forEach {
it.definingScope().statements.remove(it)
it.definingScope().remove(it)
}
}
@ -116,7 +116,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
}
if (removeBlocks.isNotEmpty()) {
removeBlocks.forEach { it.definingScope().statements.remove(it) }
removeBlocks.forEach { it.definingScope().remove(it) }
}
// remove modules that are not imported, or are empty
@ -448,7 +448,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(assignment.targets.size==1) {
val target=assignment.targets[0]
if(target.isSameAs(assignment.value)) {
if(target isSameAs assignment.value) {
optimizationsDone++
return NopStatement(assignment.position)
}
@ -458,7 +458,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
if(cv==null) {
if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) {
if (bexpr.left.same(bexpr.right) && target.isSameAs(bexpr.left)) {
if (bexpr.left isSameAs bexpr.right && target isSameAs bexpr.left) {
bexpr.operator = "*"
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
optimizationsDone++
@ -466,7 +466,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
}
}
} else {
if (target.isSameAs(bexpr.left)) {
if (target isSameAs bexpr.left) {
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
// A = A <operator> B
val vardeclDt = (target.identifier?.targetVarDecl(program.namespace))?.type