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

View File

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

View File

@ -832,7 +832,7 @@ private class AstChecker(private val program: Program,
else { else {
for (arg in args.withIndex().zip(func.parameters)) { for (arg in args.withIndex().zip(func.parameters)) {
val argDt=arg.first.value.resultingDatatype(program) 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)) 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)) checkResult.add(ExpressionError("swap requires 2 args of identical type", position))
else if (args[0].constValue(program) != null || args[1].constValue(program) != null) else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
checkResult.add(ExpressionError("swap requires 2 variables, not constant value(s)", position)) 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)) checkResult.add(ExpressionError("swap should have 2 different args", position))
else if(dt1 !in NumericDatatypes) else if(dt1 !in NumericDatatypes)
checkResult.add(ExpressionError("swap requires args of numerical type", position)) checkResult.add(ExpressionError("swap requires args of numerical type", position))
@ -856,7 +856,7 @@ private class AstChecker(private val program: Program,
else { else {
for (arg in args.withIndex().zip(target.parameters)) { for (arg in args.withIndex().zip(target.parameters)) {
val argDt = arg.first.value.resultingDatatype(program) 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) // 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)) 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)) 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) nameError(subroutine.name, subroutine.position, existing)
// check that there are no local variables that redefine the subroutine's parameters // 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 paramNames = subroutine.parameters.map { it.name }.toSet()
val paramsToCheck = paramNames.intersect(allDefinedNames) val paramsToCheck = paramNames.intersect(allDefinedNames)
for(name in paramsToCheck) { for(name in paramsToCheck) {

View File

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

View File

@ -972,7 +972,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
throw AstException("swap requires 2 args of identical type") throw AstException("swap requires 2 args of identical type")
if (args[0].constValue(program) != null || args[1].constValue(program) != null) if (args[0].constValue(program) != null || args[1].constValue(program) != null)
throw AstException("swap requires 2 variables, not constant value(s)") 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") throw AstException("swap should have 2 different args")
if(dt1 !in NumericDatatypes) if(dt1 !in NumericDatatypes)
throw AstException("swap requires args of numerical type") 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? { private fun determineY(x: IExpression, subBinExpr: BinaryExpression): IExpression? {
return when { return when {
subBinExpr.left.same(x) -> subBinExpr.right subBinExpr.left isSameAs x -> subBinExpr.right
subBinExpr.right.same(x) -> subBinExpr.left subBinExpr.right isSameAs x -> subBinExpr.left
else -> null else -> null
} }
} }
@ -362,7 +362,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
} }
if(leftConstVal==null && rightConstVal!=null) { if(leftConstVal==null && rightConstVal!=null) {
if(isBiggerType(leftDt, rightDt)) { if(leftDt biggerThan rightDt) {
val (adjusted, newValue) = adjust(rightConstVal, leftDt) val (adjusted, newValue) = adjust(rightConstVal, leftDt)
if (adjusted) { if (adjusted) {
expr.right = newValue expr.right = newValue
@ -372,7 +372,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
} }
return false return false
} else if(leftConstVal!=null && rightConstVal==null) { } else if(leftConstVal!=null && rightConstVal==null) {
if(isBiggerType(rightDt, leftDt)) { if(rightDt biggerThan leftDt) {
val (adjusted, newValue) = adjust(leftConstVal, rightDt) val (adjusted, newValue) = adjust(leftConstVal, rightDt)
if (adjusted) { if (adjusted) {
expr.left = newValue 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 data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: LiteralValue?, val rightVal: LiteralValue?)
private fun reorderAssociative(expr: BinaryExpression, leftVal: LiteralValue?): ReorderedAssociativeBinaryExpr { 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()) { if (removeSubroutines.isNotEmpty()) {
removeSubroutines.forEach { 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()) { 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 // 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) { if(assignment.targets.size==1) {
val target=assignment.targets[0] val target=assignment.targets[0]
if(target.isSameAs(assignment.value)) { if(target isSameAs assignment.value) {
optimizationsDone++ optimizationsDone++
return NopStatement(assignment.position) 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() val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
if(cv==null) { if(cv==null) {
if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) { 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.operator = "*"
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position) bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
optimizationsDone++ optimizationsDone++
@ -466,7 +466,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
} }
} }
} else { } 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 // 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 // A = A <operator> B
val vardeclDt = (target.identifier?.targetVarDecl(program.namespace))?.type val vardeclDt = (target.identifier?.targetVarDecl(program.namespace))?.type