2018-08-13 21:28:04 +00:00
|
|
|
package il65.functions
|
|
|
|
|
2018-08-16 13:09:24 +00:00
|
|
|
import il65.ast.*
|
2018-09-04 23:28:06 +00:00
|
|
|
import kotlin.math.abs
|
|
|
|
import kotlin.math.floor
|
2018-08-13 21:28:04 +00:00
|
|
|
|
2018-08-16 14:22:51 +00:00
|
|
|
|
2018-09-06 19:13:49 +00:00
|
|
|
val BuiltinFunctionNames = setOf(
|
|
|
|
"P_carry", "P_irqd", "rol", "ror", "rol2", "ror2", "lsl", "lsr",
|
|
|
|
"sin", "cos", "abs", "acos", "asin", "tan", "atan",
|
|
|
|
"log", "log10", "sqrt", "rad", "deg", "round", "floor", "ceil",
|
|
|
|
"max", "min", "avg", "sum", "len", "any", "all", "lsb", "msb")
|
2018-09-03 21:19:25 +00:00
|
|
|
|
2018-09-13 23:24:12 +00:00
|
|
|
val BuiltinFunctionsWithoutSideEffects = BuiltinFunctionNames - setOf("P_carry", "P_irqd")
|
|
|
|
|
2018-09-03 21:19:25 +00:00
|
|
|
|
2018-09-02 09:54:42 +00:00
|
|
|
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
|
|
|
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
private fun oneDoubleArg(args: List<IExpression>, position: Position, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
|
2018-08-13 21:28:04 +00:00
|
|
|
if(args.size!=1)
|
2018-08-16 13:09:24 +00:00
|
|
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
2018-09-06 19:13:49 +00:00
|
|
|
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
|
2018-09-14 23:14:48 +00:00
|
|
|
if(constval.type!=DataType.FLOAT)
|
2018-09-06 19:13:49 +00:00
|
|
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
2018-08-13 21:28:04 +00:00
|
|
|
|
2018-09-06 19:13:49 +00:00
|
|
|
val float = constval.asNumericValue?.toDouble()!!
|
2018-09-13 20:31:59 +00:00
|
|
|
return numericLiteral(function(float), args[0].position)
|
2018-08-13 21:28:04 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
private fun oneDoubleArgOutputInt(args: List<IExpression>, position: Position, namespace:INameScope, function: (arg: Double)->Number): LiteralValue {
|
2018-08-13 21:28:04 +00:00
|
|
|
if(args.size!=1)
|
2018-08-16 13:09:24 +00:00
|
|
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
2018-09-06 19:13:49 +00:00
|
|
|
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
|
2018-09-14 23:14:48 +00:00
|
|
|
if(constval.type!=DataType.FLOAT)
|
2018-09-06 19:13:49 +00:00
|
|
|
throw SyntaxError("built-in function requires one floating point argument", position)
|
2018-08-13 21:28:04 +00:00
|
|
|
|
2018-09-06 19:13:49 +00:00
|
|
|
val float = constval.asNumericValue?.toDouble()!!
|
2018-09-13 20:31:59 +00:00
|
|
|
return numericLiteral(function(float).toInt(), args[0].position)
|
2018-08-13 21:28:04 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, namespace:INameScope, function: (arg: Int)->Number): LiteralValue {
|
2018-09-02 09:54:42 +00:00
|
|
|
if(args.size!=1)
|
|
|
|
throw SyntaxError("built-in function requires one integer argument", position)
|
2018-09-06 19:13:49 +00:00
|
|
|
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
|
2018-09-14 23:14:48 +00:00
|
|
|
if(constval.type!=DataType.BYTE && constval.type!=DataType.WORD)
|
2018-09-06 19:13:49 +00:00
|
|
|
throw SyntaxError("built-in function requires one integer argument", position)
|
2018-08-13 21:28:04 +00:00
|
|
|
|
2018-09-06 19:13:49 +00:00
|
|
|
val integer = constval.asNumericValue?.toInt()!!
|
2018-09-13 20:31:59 +00:00
|
|
|
return numericLiteral(function(integer).toInt(), args[0].position)
|
2018-09-02 09:54:42 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position, namespace:INameScope,
|
2018-09-04 23:28:06 +00:00
|
|
|
function: (arg: Collection<Double>)->Number): LiteralValue {
|
2018-09-04 21:37:21 +00:00
|
|
|
if(args.size!=1)
|
|
|
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
|
|
|
val iterable = args[0].constValue(namespace)
|
|
|
|
if(iterable?.arrayvalue == null)
|
|
|
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
2018-09-04 23:28:06 +00:00
|
|
|
val constants = iterable.arrayvalue.map { it.constValue(namespace)?.asNumericValue }
|
2018-09-04 21:37:21 +00:00
|
|
|
if(constants.contains(null))
|
|
|
|
throw NotConstArgumentException()
|
2018-09-04 23:28:06 +00:00
|
|
|
val result = function(constants.map { it!!.toDouble() }).toDouble()
|
2018-09-13 20:31:59 +00:00
|
|
|
return numericLiteral(result, args[0].position)
|
2018-09-04 21:37:21 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
private fun collectionArgOutputBoolean(args: List<IExpression>, position: Position, namespace:INameScope,
|
2018-09-04 23:28:06 +00:00
|
|
|
function: (arg: Collection<Double>)->Boolean): LiteralValue {
|
2018-09-04 21:37:21 +00:00
|
|
|
if(args.size!=1)
|
|
|
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
|
|
|
val iterable = args[0].constValue(namespace)
|
|
|
|
if(iterable?.arrayvalue == null)
|
|
|
|
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
2018-09-04 23:28:06 +00:00
|
|
|
val constants = iterable.arrayvalue.map { it.constValue(namespace)?.asNumericValue }
|
2018-09-04 21:37:21 +00:00
|
|
|
if(constants.contains(null))
|
|
|
|
throw NotConstArgumentException()
|
2018-09-04 23:28:06 +00:00
|
|
|
val result = function(constants.map { it?.toDouble()!! })
|
2018-09-14 23:14:48 +00:00
|
|
|
return LiteralValue.fromBoolean(result, position)
|
2018-09-04 21:37:21 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinRound(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 21:37:21 +00:00
|
|
|
= oneDoubleArgOutputInt(args, position, namespace, Math::round)
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinFloor(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 21:37:21 +00:00
|
|
|
= oneDoubleArgOutputInt(args, position, namespace, Math::floor)
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinCeil(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 21:37:21 +00:00
|
|
|
= oneDoubleArgOutputInt(args, position, namespace, Math::ceil)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinSin(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::sin)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinCos(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::cos)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinAcos(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::acos)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinAsin(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::asin)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinTan(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::tan)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinAtan(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::atan)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinLog(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::log)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinLog10(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::log10)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinSqrt(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::sqrt)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinRad(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::toRadians)
|
2018-08-14 10:00:45 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinDeg(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-08-16 13:09:24 +00:00
|
|
|
= oneDoubleArg(args, position, namespace, Math::toDegrees)
|
2018-08-13 23:15:11 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinAbs(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue {
|
2018-09-13 20:31:59 +00:00
|
|
|
// 1 arg, type = float or int, result type= same as argument type
|
2018-09-04 23:28:06 +00:00
|
|
|
if(args.size!=1)
|
|
|
|
throw SyntaxError("abs requires one numeric argument", position)
|
|
|
|
|
|
|
|
val constval = args[0].constValue(namespace) ?: throw NotConstArgumentException()
|
2018-09-13 20:31:59 +00:00
|
|
|
val number = constval.asNumericValue
|
|
|
|
return when (number) {
|
|
|
|
is Int, is Byte, is Short -> numericLiteral(abs(number.toInt()), args[0].position)
|
|
|
|
is Double -> numericLiteral(abs(number.toDouble()), args[0].position)
|
|
|
|
else -> throw SyntaxError("abs requires one numeric argument", position)
|
|
|
|
}
|
2018-09-04 23:28:06 +00:00
|
|
|
}
|
2018-08-13 23:15:11 +00:00
|
|
|
|
2018-09-06 19:13:49 +00:00
|
|
|
|
2018-09-13 20:31:59 +00:00
|
|
|
// todo different functions for byte/word params/results?
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinLsb(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-06 19:13:49 +00:00
|
|
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x and 255 }
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinMsb(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-06 19:13:49 +00:00
|
|
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 8 and 255}
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinLsl(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 21:37:21 +00:00
|
|
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x shl 1 }
|
2018-08-13 23:15:11 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinLsr(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 21:37:21 +00:00
|
|
|
= oneIntArgOutputInt(args, position, namespace) { x: Int -> x ushr 1 }
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinMin(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 23:28:06 +00:00
|
|
|
= collectionArgOutputNumber(args, position, namespace) { it.min()!! }
|
2018-09-04 21:37:21 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinMax(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 23:28:06 +00:00
|
|
|
= collectionArgOutputNumber(args, position, namespace) { it.max()!! }
|
2018-09-04 21:37:21 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinSum(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 23:28:06 +00:00
|
|
|
= collectionArgOutputNumber(args, position, namespace) { it.sum() }
|
2018-09-04 21:37:21 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinAvg(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue {
|
2018-09-04 23:28:06 +00:00
|
|
|
if(args.size!=1)
|
|
|
|
throw SyntaxError("avg requires one non-scalar argument", position)
|
|
|
|
val iterable = args[0].constValue(namespace)
|
|
|
|
if(iterable?.arrayvalue == null)
|
|
|
|
throw SyntaxError("avg requires one non-scalar argument", position)
|
|
|
|
val constants = iterable.arrayvalue.map { it.constValue(namespace)?.asNumericValue }
|
|
|
|
if(constants.contains(null))
|
|
|
|
throw NotConstArgumentException()
|
|
|
|
val result = (constants.map { it!!.toDouble() }).average()
|
2018-09-13 20:31:59 +00:00
|
|
|
return numericLiteral(result, args[0].position)
|
2018-09-04 23:28:06 +00:00
|
|
|
}
|
2018-09-04 21:37:21 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinLen(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue {
|
2018-09-04 23:28:06 +00:00
|
|
|
if(args.size!=1)
|
2018-09-14 17:38:22 +00:00
|
|
|
throw SyntaxError("len requires one argument", position)
|
|
|
|
val argument = args[0].constValue(namespace) ?: throw NotConstArgumentException()
|
2018-09-14 23:14:48 +00:00
|
|
|
return when(argument.type) {
|
|
|
|
DataType.ARRAY, DataType.ARRAY_W -> numericLiteral(argument.arrayvalue!!.size, args[0].position)
|
|
|
|
DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS -> numericLiteral(argument.strvalue!!.length, args[0].position)
|
2018-09-14 17:38:22 +00:00
|
|
|
else -> throw FatalAstException("len of weird argument ${args[0]}")
|
|
|
|
}
|
2018-09-04 23:28:06 +00:00
|
|
|
}
|
2018-08-13 23:15:11 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinAny(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 23:28:06 +00:00
|
|
|
= collectionArgOutputBoolean(args, position, namespace) { it.any { v -> v != 0.0} }
|
2018-09-02 09:54:42 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
fun builtinAll(args: List<IExpression>, position: Position, namespace:INameScope): LiteralValue
|
2018-09-04 23:28:06 +00:00
|
|
|
= collectionArgOutputBoolean(args, position, namespace) { it.all { v -> v != 0.0} }
|
2018-09-02 09:54:42 +00:00
|
|
|
|
2018-08-13 23:15:11 +00:00
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
private fun numericLiteral(value: Number, position: Position): LiteralValue {
|
2018-09-13 20:31:59 +00:00
|
|
|
val floatNum=value.toDouble()
|
|
|
|
val tweakedValue: Number =
|
|
|
|
if(floatNum==floor(floatNum) && floatNum in -32768..65535)
|
|
|
|
floatNum.toInt() // we have an integer disguised as a float.
|
|
|
|
else
|
|
|
|
floatNum
|
|
|
|
|
2018-09-14 23:14:48 +00:00
|
|
|
return when(tweakedValue) {
|
|
|
|
is Int -> LiteralValue.optimalNumeric(value.toInt(), position)
|
|
|
|
is Short -> LiteralValue.optimalNumeric(value.toInt(), position)
|
|
|
|
is Byte -> LiteralValue(DataType.BYTE, bytevalue = value.toShort(), position = position)
|
|
|
|
is Double -> LiteralValue(DataType.FLOAT, floatvalue = value.toDouble(), position = position)
|
|
|
|
is Float -> LiteralValue(DataType.FLOAT, floatvalue = value.toDouble(), position = position)
|
2018-09-04 23:28:06 +00:00
|
|
|
else -> throw FatalAstException("invalid number type ${value::class}")
|
|
|
|
}
|
|
|
|
}
|