prog8/compiler/src/prog8/functions/BuiltinFunctions.kt

462 lines
29 KiB
Kotlin
Raw Normal View History

2018-09-15 14:21:05 +00:00
package prog8.functions
2018-08-13 21:28:04 +00:00
2018-09-15 14:21:05 +00:00
import prog8.ast.*
2019-07-08 11:30:28 +00:00
import prog8.ast.base.*
import prog8.ast.expressions.DirectMemoryRead
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.LiteralValue
import prog8.ast.statements.VarDecl
import prog8.compiler.CompilerException
2019-06-20 19:06:51 +00:00
import kotlin.math.*
2018-08-13 21:28:04 +00:00
2018-08-16 14:22:51 +00:00
2018-10-12 12:01:29 +00:00
class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType>)
class FunctionSignature(val pure: Boolean, // does it have side effects?
val parameters: List<BuiltinFunctionParam>,
val returntype: DataType?,
2019-06-20 19:06:51 +00:00
val constExpressionFunc: ((args: List<IExpression>, position: Position, program: Program) -> LiteralValue)? = null)
2018-09-30 12:19:41 +00:00
2018-09-30 12:19:41 +00:00
val BuiltinFunctions = mapOf(
// this set of function have no return value and operate in-place:
2018-10-12 12:01:29 +00:00
"rol" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
"ror" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
"rol2" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
"ror2" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
"lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
"lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
2018-12-18 00:43:04 +00:00
// these few have a return value depending on the argument(s):
2019-06-20 19:06:51 +00:00
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.max()!! }}, // type depends on args
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.min()!! }}, // type depends on args
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArgOutputNumber(a, p, prg) { it.sum() }}, // type depends on args
2018-12-18 00:43:04 +00:00
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
2019-02-24 14:25:46 +00:00
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
// normal functions follow:
2019-06-20 19:06:51 +00:00
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
"sin8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
"sin8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
"sin16" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
"sin16u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
2019-06-20 19:06:51 +00:00
"cos" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
"cos8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
"cos8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
"cos16" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
"cos16u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
2019-06-20 19:06:51 +00:00
"tan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
"atan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
"ln" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
"log2" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
"sqrt16" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
2018-10-12 12:01:29 +00:00
"avg" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.FLOAT, ::builtinAvg),
2019-06-20 19:06:51 +00:00
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArgOutputBoolean(a, p, prg) { it.any { v -> v != 0.0} }},
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArgOutputBoolean(a, p, prg) { it.all { v -> v != 0.0} }},
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
2019-01-09 00:03:33 +00:00
"mkword" to FunctionSignature(true, listOf(
BuiltinFunctionParam("lsb", setOf(DataType.UBYTE)),
BuiltinFunctionParam("msb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
2018-10-10 07:21:20 +00:00
"rnd" to FunctionSignature(true, emptyList(), DataType.UBYTE),
"rndw" to FunctionSignature(true, emptyList(), DataType.UWORD),
"rndf" to FunctionSignature(true, emptyList(), DataType.FLOAT),
2018-10-14 20:28:16 +00:00
"rsave" to FunctionSignature(false, emptyList(), null),
"rrestore" to FunctionSignature(false, emptyList(), null),
"set_carry" to FunctionSignature(false, emptyList(), null),
"clear_carry" to FunctionSignature(false, emptyList(), null),
"set_irqd" to FunctionSignature(false, emptyList(), null),
"clear_irqd" to FunctionSignature(false, emptyList(), null),
"read_flags" to FunctionSignature(false, emptyList(), DataType.UBYTE),
2019-01-18 00:33:54 +00:00
"swap" to FunctionSignature(false, listOf(BuiltinFunctionParam("first", NumericDatatypes), BuiltinFunctionParam("second", NumericDatatypes)), null),
2019-01-02 01:47:52 +00:00
"memcopy" to FunctionSignature(false, listOf(
BuiltinFunctionParam("from", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("to", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("numbytes", setOf(DataType.UBYTE))), null),
"memset" to FunctionSignature(false, listOf(
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("numbytes", setOf(DataType.UWORD)),
2019-02-25 00:08:10 +00:00
BuiltinFunctionParam("bytevalue", ByteDatatypes)), null),
"memsetw" to FunctionSignature(false, listOf(
BuiltinFunctionParam("address", IterableDatatypes + setOf(DataType.UWORD)),
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
BuiltinFunctionParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
2019-03-15 22:10:26 +00:00
"strlen" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", StringDatatypes)), DataType.UBYTE, ::builtinStrlen),
"vm_write_memchr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_memstr" to FunctionSignature(false, listOf(BuiltinFunctionParam("address", setOf(DataType.UWORD))), null),
"vm_write_num" to FunctionSignature(false, listOf(BuiltinFunctionParam("number", NumericDatatypes)), null),
"vm_write_char" to FunctionSignature(false, listOf(BuiltinFunctionParam("char", setOf(DataType.UBYTE))), null),
"vm_write_str" to FunctionSignature(false, listOf(BuiltinFunctionParam("string", StringDatatypes)), null),
"vm_input_str" to FunctionSignature(false, listOf(BuiltinFunctionParam("intovar", StringDatatypes)), null),
"vm_gfx_clearscr" to FunctionSignature(false, listOf(BuiltinFunctionParam("color", setOf(DataType.UBYTE))), null),
"vm_gfx_pixel" to FunctionSignature(false, listOf(
2018-10-12 12:01:29 +00:00
BuiltinFunctionParam("x", IntegerDatatypes),
BuiltinFunctionParam("y", IntegerDatatypes),
BuiltinFunctionParam("color", IntegerDatatypes)), null),
"vm_gfx_line" to FunctionSignature(false, listOf(
2018-10-12 12:01:29 +00:00
BuiltinFunctionParam("x1", IntegerDatatypes),
BuiltinFunctionParam("y1", IntegerDatatypes),
BuiltinFunctionParam("x2", IntegerDatatypes),
BuiltinFunctionParam("y2", IntegerDatatypes),
BuiltinFunctionParam("color", IntegerDatatypes)), null),
"vm_gfx_text" to FunctionSignature(false, listOf(
2018-10-12 12:01:29 +00:00
BuiltinFunctionParam("x", IntegerDatatypes),
BuiltinFunctionParam("y", IntegerDatatypes),
BuiltinFunctionParam("color", IntegerDatatypes),
BuiltinFunctionParam("text", StringDatatypes)),
null)
2018-09-30 12:19:41 +00:00
)
2018-09-13 23:24:12 +00:00
2019-06-20 19:06:51 +00:00
fun builtinFunctionReturnType(function: String, args: List<IExpression>, program: Program): DataType? {
fun datatypeFromIterableArg(arglist: IExpression): DataType {
if(arglist is LiteralValue) {
2019-07-08 11:30:28 +00:00
if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
val dt = arglist.arrayvalue!!.map {it.inferType(program)}
2019-07-08 11:30:28 +00:00
if(dt.any { it!= DataType.UBYTE && it!= DataType.UWORD && it!= DataType.FLOAT}) {
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
}
2019-07-08 11:30:28 +00:00
if(dt.any { it== DataType.FLOAT }) return DataType.FLOAT
if(dt.any { it== DataType.UWORD }) return DataType.UWORD
2018-10-10 07:21:20 +00:00
return DataType.UBYTE
}
}
if(arglist is IdentifierReference) {
val dt = arglist.inferType(program)
return when(dt) {
2019-02-25 00:08:10 +00:00
in NumericDatatypes -> dt!!
in StringDatatypes -> dt!!
2019-06-28 20:10:01 +00:00
in ArrayDatatypes -> ArrayElementTypes.getValue(dt!!)
2019-02-25 00:08:10 +00:00
else -> throw FatalAstException("function '$function' requires one argument which is an iterable")
}
}
throw FatalAstException("function '$function' requires one argument which is an iterable")
}
2019-01-27 21:59:40 +00:00
val func = BuiltinFunctions.getValue(function)
if(func.returntype!=null)
return func.returntype
2018-09-30 12:19:41 +00:00
// function has return values, but the return type depends on the arguments
return when (function) {
"abs" -> {
val dt = args.single().inferType(program)
when(dt) {
2019-02-25 00:08:10 +00:00
in ByteDatatypes -> DataType.UBYTE
in WordDatatypes -> DataType.UWORD
DataType.FLOAT -> DataType.FLOAT
else -> throw FatalAstException("weird datatype passed to abs $dt")
}
}
"max", "min" -> {
val dt = datatypeFromIterableArg(args.single())
when(dt) {
2019-02-25 00:08:10 +00:00
in NumericDatatypes -> dt
in StringDatatypes -> DataType.UBYTE
2019-06-28 20:10:01 +00:00
in ArrayDatatypes -> ArrayElementTypes.getValue(dt)
2019-02-25 00:08:10 +00:00
else -> null
}
}
2018-09-17 22:31:11 +00:00
"sum" -> {
2019-06-20 19:06:51 +00:00
when(datatypeFromIterableArg(args.single())) {
2018-10-10 07:21:20 +00:00
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
2018-09-17 22:31:11 +00:00
DataType.BYTE, DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT
2018-10-10 07:21:20 +00:00
DataType.ARRAY_UB, DataType.ARRAY_UW -> DataType.UWORD
DataType.ARRAY_B, DataType.ARRAY_W -> DataType.WORD
2018-10-01 23:52:08 +00:00
DataType.ARRAY_F -> DataType.FLOAT
2019-02-25 00:08:10 +00:00
in StringDatatypes -> DataType.UWORD
else -> null
2018-09-17 22:31:11 +00:00
}
}
2019-02-24 14:25:46 +00:00
"len" -> {
// a length can be >255 so in that case, the result is an UWORD instead of an UBYTE
// but to avoid a lot of code duplication we simply assume UWORD in all cases for now
return DataType.UWORD
}
else -> return null
}
}
2018-09-02 09:54:42 +00:00
class NotConstArgumentException: AstException("not a const argument to a built-in function")
2019-06-20 19:06:51 +00:00
private fun oneDoubleArg(args: List<IExpression>, position: Position, program: Program, 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)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
2019-07-08 11:30:28 +00:00
if(constval.type!= DataType.FLOAT)
throw SyntaxError("built-in function requires one floating point argument", position)
2018-08-13 21:28:04 +00:00
val float = constval.asNumericValue?.toDouble()!!
return numericLiteral(function(float), args[0].position)
2018-08-13 21:28:04 +00:00
}
2019-06-20 19:06:51 +00:00
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, program: Program, 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)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
2019-07-08 11:30:28 +00:00
if(constval.type!= DataType.FLOAT)
2018-10-11 07:54:26 +00:00
throw SyntaxError("built-in function requires one floating point argument", position)
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.WORD, wordvalue = function(constval.asNumericValue!!.toDouble()).toInt(), position = args[0].position)
2018-08-13 21:28:04 +00:00
}
2019-06-20 19:06:51 +00:00
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, program: Program, 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)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
2019-07-08 11:30:28 +00:00
if(constval.type!= DataType.UBYTE && constval.type!= DataType.UWORD)
throw SyntaxError("built-in function requires one integer argument", position)
2018-08-13 21:28:04 +00:00
val integer = constval.asNumericValue?.toInt()!!
return numericLiteral(function(integer).toInt(), args[0].position)
2018-09-02 09:54:42 +00:00
}
private fun collectionArgOutputNumber(args: List<IExpression>, position: Position,
2019-06-20 19:06:51 +00:00
program: Program,
function: (arg: Collection<Double>)->Number): LiteralValue {
if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position)
2019-06-20 19:06:51 +00:00
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue != null) {
2019-06-20 19:06:51 +00:00
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
2018-09-30 12:19:41 +00:00
if(null in constants)
throw NotConstArgumentException()
function(constants.map { it!!.toDouble() }).toDouble()
} else {
when(iterable.type) {
2018-10-10 07:21:20 +00:00
DataType.UBYTE, DataType.UWORD, DataType.FLOAT -> throw SyntaxError("function expects an iterable type", position)
else -> {
2019-07-01 12:10:52 +00:00
val heapId = iterable.heapId ?: throw FatalAstException("iterable value should be on the heap")
val array = program.heap.get(heapId).array ?: throw SyntaxError("function expects an iterable type", position)
2019-04-04 19:02:24 +00:00
function(array.map {
if(it.integer!=null)
it.integer.toDouble()
else
throw FatalAstException("cannot perform function over array that contains other values besides constant integers")
})
}
}
}
return numericLiteral(result, args[0].position)
}
private fun collectionArgOutputBoolean(args: List<IExpression>, position: Position,
2019-06-20 19:06:51 +00:00
program: Program,
function: (arg: Collection<Double>)->Boolean): LiteralValue {
if(args.size!=1)
throw SyntaxError("builtin function requires one non-scalar argument", position)
2019-06-20 19:06:51 +00:00
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue != null) {
2019-06-20 19:06:51 +00:00
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
2018-09-30 12:19:41 +00:00
if(null in constants)
throw NotConstArgumentException()
function(constants.map { it!!.toDouble() })
} else {
2019-06-20 19:06:51 +00:00
val array = program.heap.get(iterable.heapId!!).array ?: throw SyntaxError("function requires array argument", position)
2019-04-04 19:02:24 +00:00
function(array.map {
if(it.integer!=null)
it.integer.toDouble()
else
throw FatalAstException("cannot perform function over array that contains other values besides constant integers")
})
}
return LiteralValue.fromBoolean(result, position)
}
2019-06-20 19:06:51 +00:00
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): LiteralValue {
2019-06-23 11:49:35 +00:00
// 1 arg, type = float or int, result type= isSameAs as argument type
if(args.size!=1)
throw SyntaxError("abs requires one numeric argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val number = constval.asNumericValue
return when (number) {
2019-06-20 19:06:51 +00:00
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-08-13 23:15:11 +00:00
2019-06-20 19:06:51 +00:00
private fun builtinAvg(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if(args.size!=1)
2018-10-30 19:29:03 +00:00
throw SyntaxError("avg requires array argument", position)
2019-06-20 19:06:51 +00:00
val iterable = args[0].constValue(program) ?: throw NotConstArgumentException()
val result = if(iterable.arrayvalue!=null) {
2019-06-20 19:06:51 +00:00
val constants = iterable.arrayvalue.map { it.constValue(program)?.asNumericValue }
2018-09-30 12:19:41 +00:00
if (null in constants)
throw NotConstArgumentException()
(constants.map { it!!.toDouble() }).average()
}
else {
2019-07-01 12:10:52 +00:00
val heapId = iterable.heapId!!
val integerarray = program.heap.get(heapId).array
2019-04-21 01:04:36 +00:00
if(integerarray!=null) {
if (integerarray.all { it.integer != null }) {
integerarray.map { it.integer!! }.average()
} else {
throw ExpressionError("cannot avg() over array that does not only contain constant numerical values", position)
}
2019-04-04 19:02:24 +00:00
} else {
2019-07-01 12:10:52 +00:00
val doublearray = program.heap.get(heapId).doubleArray
2019-04-21 01:04:36 +00:00
doublearray?.average() ?: throw SyntaxError("avg requires array argument", position)
2019-04-04 19:02:24 +00:00
}
}
return numericLiteral(result, args[0].position)
}
2019-06-20 19:06:51 +00:00
private fun builtinStrlen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
2019-03-15 22:10:26 +00:00
if (args.size != 1)
throw SyntaxError("strlen requires one argument", position)
2019-06-20 19:06:51 +00:00
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
2019-03-15 22:10:26 +00:00
if(argument.type !in StringDatatypes)
throw SyntaxError("strlen must have string argument", position)
val string = argument.strvalue!!
2019-03-15 22:10:26 +00:00
val zeroIdx = string.indexOf('\u0000')
return if(zeroIdx>=0)
LiteralValue.optimalInteger(zeroIdx, position=position)
else
LiteralValue.optimalInteger(string.length, position=position)
}
2019-06-20 19:06:51 +00:00
private fun builtinLen(args: List<IExpression>, position: Position, program: Program): LiteralValue {
2019-02-24 14:25:46 +00:00
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
if(args.size!=1)
throw SyntaxError("len requires one argument", position)
2019-06-20 19:06:51 +00:00
var argument = args[0].constValue(program)
if(argument==null) {
2019-06-20 19:06:51 +00:00
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
2019-04-21 01:04:36 +00:00
val arraySize = directMemVar?.arraysize?.size()
if(arraySize != null)
return LiteralValue.optimalInteger(arraySize, position)
if(args[0] !is IdentifierReference)
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
2019-06-20 19:06:51 +00:00
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
val argValue = (target as? VarDecl)?.value
2019-06-20 19:06:51 +00:00
argument = argValue?.constValue(program)
?: throw NotConstArgumentException()
}
return when(argument.type) {
2018-10-30 19:29:03 +00:00
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
2019-06-20 19:06:51 +00:00
val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
2019-01-27 00:02:45 +00:00
if(arraySize>256)
throw CompilerException("array length exceeds byte limit ${argument.position}")
2019-02-24 14:25:46 +00:00
LiteralValue.optimalInteger(arraySize, args[0].position)
}
2018-10-01 23:52:08 +00:00
DataType.ARRAY_F -> {
2019-06-20 19:06:51 +00:00
val arraySize = argument.arrayvalue?.size ?: program.heap.get(argument.heapId!!).arraysize
2019-01-27 00:02:45 +00:00
if(arraySize>256)
throw CompilerException("array length exceeds byte limit ${argument.position}")
2019-02-24 14:25:46 +00:00
LiteralValue.optimalInteger(arraySize, args[0].position)
2018-10-01 23:52:08 +00:00
}
2019-02-25 00:08:10 +00:00
in StringDatatypes -> {
val str = argument.strvalue!!
if(str.length>255)
throw CompilerException("string length exceeds byte limit ${argument.position}")
2019-02-24 14:25:46 +00:00
LiteralValue.optimalInteger(str.length, args[0].position)
}
2019-02-25 00:08:10 +00:00
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
else -> throw CompilerException("weird datatype")
}
}
2018-08-13 23:15:11 +00:00
2019-01-09 00:03:33 +00:00
2019-06-20 19:06:51 +00:00
private fun builtinMkword(args: List<IExpression>, position: Position, program: Program): LiteralValue {
2019-01-09 00:03:33 +00:00
if (args.size != 2)
throw SyntaxError("mkword requires lsb and msb arguments", position)
2019-06-20 19:06:51 +00:00
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
val constMsb = args[1].constValue(program) ?: throw NotConstArgumentException()
2019-01-09 00:03:33 +00:00
val result = (constMsb.asIntegerValue!! shl 8) or constLsb.asIntegerValue!!
return LiteralValue(DataType.UWORD, wordvalue = result, position = position)
}
2019-06-20 19:06:51 +00:00
private fun builtinSin8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("sin8 requires one argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.BYTE, bytevalue = (127.0 * sin(rad)).toShort(), position = position)
}
2019-06-20 19:06:51 +00:00
private fun builtinSin8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("sin8u requires one argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * sin(rad)).toShort(), position = position)
}
2019-06-20 19:06:51 +00:00
private fun builtinCos8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("cos8 requires one argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.BYTE, bytevalue = (127.0 * cos(rad)).toShort(), position = position)
}
2019-06-20 19:06:51 +00:00
private fun builtinCos8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("cos8u requires one argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * cos(rad)).toShort(), position = position)
}
2019-06-20 19:06:51 +00:00
private fun builtinSin16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("sin16 requires one argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.WORD, wordvalue = (32767.0 * sin(rad)).toInt(), position = position)
}
2019-06-20 19:06:51 +00:00
private fun builtinSin16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("sin16u requires one argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * sin(rad)).toInt(), position = position)
}
2019-06-20 19:06:51 +00:00
private fun builtinCos16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("cos16 requires one argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.WORD, wordvalue = (32767.0 * cos(rad)).toInt(), position = position)
}
2019-06-20 19:06:51 +00:00
private fun builtinCos16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
if (args.size != 1)
throw SyntaxError("cos16u requires one argument", position)
2019-06-20 19:06:51 +00:00
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
2019-07-08 11:30:28 +00:00
return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * cos(rad)).toInt(), position = position)
}
private fun numericLiteral(value: Number, position: Position): LiteralValue {
val floatNum=value.toDouble()
val tweakedValue: Number =
2019-06-20 19:06:51 +00:00
if(floatNum== floor(floatNum) && (floatNum>=-32768 && floatNum<=65535))
floatNum.toInt() // we have an integer disguised as a float.
else
floatNum
return when(tweakedValue) {
is Int -> LiteralValue.optimalNumeric(value.toInt(), position)
is Short -> LiteralValue.optimalNumeric(value.toInt(), position)
2018-10-10 07:21:20 +00:00
is Byte -> LiteralValue(DataType.UBYTE, 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)
else -> throw FatalAstException("invalid number type ${value::class}")
}
}