mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
some infix functions
This commit is contained in:
parent
f473be8951
commit
6dd3371781
@ -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"
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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) {
|
||||
|
@ -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--
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user