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.*
2018-12-16 12:58:18 +00:00
import prog8.compiler.CompilerException
2018-09-29 15:33:59 +00:00
import prog8.compiler.HeapValues
2018-09-17 22:31:11 +00:00
import kotlin.math.log2
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 > )
2018-09-30 18:19:25 +00:00
class FunctionSignature ( val pure : Boolean , // does it have side effects?
val parameters : List < BuiltinFunctionParam > ,
val returntype : DataType ? ,
val constExpressionFunc : ( ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) -> LiteralValue ) ? = null )
2018-09-30 12:19:41 +00:00
2018-09-03 21:19:25 +00:00
2018-09-30 12:19:41 +00:00
val BuiltinFunctions = mapOf (
2018-10-20 21:45:03 +00:00
// 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 " , setOf ( DataType . UBYTE , DataType . UWORD ) ) ) , null ) ,
" lsr " to FunctionSignature ( false , listOf ( BuiltinFunctionParam ( " item " , setOf ( DataType . UBYTE , DataType . UWORD ) ) ) , null ) ,
2018-12-18 00:43:04 +00:00
// these few have a return value depending on the argument(s):
2018-10-20 21:45:03 +00:00
" max " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " values " , ArrayDatatypes ) ) , null ) { a , p , n , h -> collectionArgOutputNumber ( a , p , n , h ) { it . max ( ) !! } } , // type depends on args
" min " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " values " , ArrayDatatypes ) ) , null ) { a , p , n , h -> collectionArgOutputNumber ( a , p , n , h ) { it . min ( ) !! } } , // type depends on args
" sum " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " values " , ArrayDatatypes ) ) , null ) { a , p , n , h -> collectionArgOutputNumber ( a , p , n , h ) { 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
2018-10-20 21:45:03 +00:00
// normal functions follow:
2018-10-12 12:01:29 +00:00
" sin " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " rads " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , Math :: sin ) } ,
" cos " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " rads " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , Math :: cos ) } ,
" tan " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " rads " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , Math :: tan ) } ,
" atan " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " rads " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , Math :: atan ) } ,
" ln " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , Math :: log ) } ,
" log2 " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , :: log2 ) } ,
" sqrt " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , Math :: sqrt ) } ,
" rad " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , Math :: toRadians ) } ,
" deg " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . FLOAT ) ) ) , DataType . FLOAT ) { a , p , n , h -> oneDoubleArg ( a , p , n , h , Math :: toDegrees ) } ,
" avg " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " values " , ArrayDatatypes ) ) , DataType . FLOAT , :: builtinAvg ) ,
" round " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . FLOAT ) ) ) , DataType . WORD ) { a , p , n , h -> oneDoubleArgOutputWord ( a , p , n , h , Math :: round ) } ,
" floor " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . FLOAT ) ) ) , DataType . WORD ) { a , p , n , h -> oneDoubleArgOutputWord ( a , p , n , h , Math :: floor ) } ,
" ceil " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . FLOAT ) ) ) , DataType . WORD ) { a , p , n , h -> oneDoubleArgOutputWord ( a , p , n , h , Math :: ceil ) } ,
2018-10-22 22:52:51 +00:00
" len " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " values " , IterableDatatypes ) ) , DataType . UBYTE , :: builtinLen ) ,
2018-10-12 12:01:29 +00:00
" any " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " values " , ArrayDatatypes ) ) , DataType . UBYTE ) { a , p , n , h -> collectionArgOutputBoolean ( a , p , n , h ) { it . any { v -> v != 0.0 } } } ,
" all " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " values " , ArrayDatatypes ) ) , DataType . UBYTE ) { a , p , n , h -> collectionArgOutputBoolean ( a , p , n , h ) { it . all { v -> v != 0.0 } } } ,
" lsb " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . UWORD , DataType . WORD ) ) ) , DataType . UBYTE ) { a , p , n , h -> oneIntArgOutputInt ( a , p , n , h ) { x : Int -> x and 255 } } ,
" msb " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . UWORD , DataType . WORD ) ) ) , DataType . UBYTE ) { a , p , n , h -> oneIntArgOutputInt ( a , p , n , h ) { x : Int -> x ushr 8 and 255 } } ,
" flt " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , NumericDatatypes ) ) , DataType . FLOAT , :: builtinFlt ) ,
" uwrd " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . UBYTE , DataType . BYTE , DataType . WORD ) ) ) , DataType . UWORD , :: builtinUwrd ) ,
" wrd " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . UBYTE , DataType . BYTE , DataType . UWORD ) ) ) , DataType . WORD , :: builtinWrd ) ,
" b2ub " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . BYTE ) ) ) , DataType . UBYTE , :: builtinB2ub ) ,
" ub2b " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " value " , setOf ( DataType . UBYTE ) ) ) , DataType . BYTE , :: builtinUb2b ) ,
2018-10-10 07:21:20 +00:00
" rnd " to FunctionSignature ( true , emptyList ( ) , DataType . UBYTE ) ,
" rndw " to FunctionSignature ( true , emptyList ( ) , DataType . UWORD ) ,
2018-09-30 18:19:25 +00:00
" 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 ) ,
2018-09-30 18:19:25 +00:00
" 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 ) ,
2018-10-12 12:01:29 +00:00
" str2byte " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " string " , StringDatatypes ) ) , DataType . BYTE ) ,
" str2ubyte " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " string " , StringDatatypes ) ) , DataType . UBYTE ) ,
" str2word " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " string " , StringDatatypes ) ) , DataType . WORD ) ,
" str2uword " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " string " , StringDatatypes ) ) , DataType . UWORD ) ,
" str2float " to FunctionSignature ( true , listOf ( BuiltinFunctionParam ( " string " , StringDatatypes ) ) , DataType . FLOAT ) ,
2018-10-20 21:45:03 +00:00
" 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 ) ,
2018-10-20 21:45:03 +00:00
" 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 ) ,
2018-10-20 21:45:03 +00:00
" 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 ) ) ,
2018-09-30 20:43:34 +00:00
null )
2018-09-30 12:19:41 +00:00
)
2018-09-13 23:24:12 +00:00
2018-09-24 21:38:33 +00:00
2018-09-29 15:33:59 +00:00
fun builtinFunctionReturnType ( function : String , args : List < IExpression > , namespace : INameScope , heap : HeapValues ) : DataType ? {
2018-09-15 00:12:37 +00:00
fun datatypeFromListArg ( arglist : IExpression ) : DataType {
if ( arglist is LiteralValue ) {
2018-10-30 19:29:03 +00:00
if ( arglist . type == DataType . ARRAY _UB || arglist . type == DataType . ARRAY _UW || arglist . type == DataType . ARRAY _F ) {
2018-09-29 15:33:59 +00:00
val dt = arglist . arrayvalue !! . map { it . resultingDatatype ( namespace , heap ) }
2018-10-10 07:21:20 +00:00
if ( dt . any { it != DataType . UBYTE && it != DataType . UWORD && it != DataType . FLOAT } ) {
2018-10-27 19:26:32 +00:00
throw FatalAstException ( " fuction $function only accepts arrayspec of numeric values " )
2018-09-15 00:12:37 +00:00
}
if ( dt . any { it == DataType . FLOAT } ) return DataType . FLOAT
2018-10-10 07:21:20 +00:00
if ( dt . any { it == DataType . UWORD } ) return DataType . UWORD
return DataType . UBYTE
2018-09-15 00:12:37 +00:00
}
}
2018-09-29 17:17:19 +00:00
if ( arglist is IdentifierReference ) {
val dt = arglist . resultingDatatype ( namespace , heap )
return when ( dt ) {
2018-10-10 07:21:20 +00:00
DataType . UBYTE , DataType . BYTE , DataType . UWORD , DataType . WORD , DataType . FLOAT ,
2018-09-29 17:17:19 +00:00
DataType . STR , DataType . STR _P , DataType . STR _S , DataType . STR _PS -> dt
2018-10-10 07:21:20 +00:00
DataType . ARRAY _UB -> DataType . UBYTE
DataType . ARRAY _B -> DataType . BYTE
DataType . ARRAY _UW -> DataType . UWORD
2018-09-29 17:17:19 +00:00
DataType . ARRAY _W -> DataType . WORD
2018-10-01 23:52:08 +00:00
DataType . ARRAY _F -> DataType . FLOAT
2018-10-27 19:26:32 +00:00
null -> throw FatalAstException ( " function requires one argument which is an arrayspec $function " )
2018-09-29 17:17:19 +00:00
}
}
2018-10-27 19:26:32 +00:00
throw FatalAstException ( " function requires one argument which is an arrayspec $function " )
2018-09-15 00:12:37 +00:00
}
2018-09-30 12:19:41 +00:00
val func = BuiltinFunctions [ function ] !!
2018-09-30 18:19:25 +00:00
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
2018-09-15 00:12:37 +00:00
return when ( function ) {
2018-12-18 00:43:04 +00:00
" max " , " min " , " abs " -> {
2018-09-29 17:17:19 +00:00
val dt = datatypeFromListArg ( args . single ( ) )
when ( dt ) {
2018-10-10 07:21:20 +00:00
DataType . UBYTE , DataType . BYTE , DataType . UWORD , DataType . WORD , DataType . FLOAT -> dt
DataType . STR , DataType . STR _P , DataType . STR _S , DataType . STR _PS -> DataType . UBYTE
DataType . ARRAY _UB -> DataType . UBYTE
DataType . ARRAY _B -> DataType . BYTE
DataType . ARRAY _UW -> DataType . UWORD
2018-09-29 17:17:19 +00:00
DataType . ARRAY _W -> DataType . WORD
2018-10-01 23:52:08 +00:00
DataType . ARRAY _F -> DataType . FLOAT
2018-09-29 17:17:19 +00:00
}
}
2018-09-17 22:31:11 +00:00
" sum " -> {
val dt = datatypeFromListArg ( args . single ( ) )
when ( dt ) {
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
2018-10-10 07:21:20 +00:00
DataType . STR , DataType . STR _P , DataType . STR _S , DataType . STR _PS -> DataType . UWORD
2018-09-17 22:31:11 +00:00
}
}
2018-10-20 21:45:03 +00:00
else -> return null
2018-09-15 00:12:37 +00:00
}
}
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-29 15:33:59 +00:00
private fun oneDoubleArg ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues , 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-29 15:33:59 +00:00
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: 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-10-11 07:54:26 +00:00
private fun oneDoubleArgOutputWord ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues , 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-29 15:33:59 +00:00
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-10-11 07:54:26 +00:00
if ( constval . type != DataType . FLOAT )
throw SyntaxError ( " built-in function requires one floating point argument " , position )
return LiteralValue ( DataType . WORD , wordvalue = function ( constval . asNumericValue !! . toDouble ( ) ) . toInt ( ) , position = args [ 0 ] . position )
2018-08-13 21:28:04 +00:00
}
2018-09-29 15:33:59 +00:00
private fun oneIntArgOutputInt ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues , 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-29 15:33:59 +00:00
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-10-10 07:21:20 +00:00
if ( constval . type != DataType . UBYTE && constval . type != DataType . UWORD )
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-29 15:33:59 +00:00
private fun collectionArgOutputNumber ( args : List < IExpression > , position : Position ,
namespace : INameScope , heap : HeapValues ,
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 )
2018-09-30 12:19:41 +00:00
val iterable = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-09-29 15:33:59 +00:00
val result = if ( iterable . arrayvalue != null ) {
2018-09-30 12:19:41 +00:00
val constants = iterable . arrayvalue . map { it . constValue ( namespace , heap ) ?. asNumericValue }
if ( null in constants )
2018-09-29 15:33:59 +00:00
throw NotConstArgumentException ( )
function ( constants . map { it !! . toDouble ( ) } ) . toDouble ( )
} else {
2018-09-30 18:58:47 +00:00
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 )
2018-09-30 18:58:47 +00:00
else -> {
if ( iterable . heapId == null )
throw FatalAstException ( " iterable value should be on the heap " )
val array = heap . get ( iterable . heapId ) . array ?: throw SyntaxError ( " function expects an iterable type " , position )
function ( array . map { it . toDouble ( ) } )
}
}
2018-09-29 15:33:59 +00:00
}
2018-09-13 20:31:59 +00:00
return numericLiteral ( result , args [ 0 ] . position )
2018-09-04 21:37:21 +00:00
}
2018-09-29 15:33:59 +00:00
private fun collectionArgOutputBoolean ( args : List < IExpression > , position : Position ,
namespace : INameScope , heap : HeapValues ,
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 )
2018-09-30 12:19:41 +00:00
val iterable = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-09-29 15:33:59 +00:00
val result = if ( iterable . arrayvalue != null ) {
2018-09-30 12:19:41 +00:00
val constants = iterable . arrayvalue . map { it . constValue ( namespace , heap ) ?. asNumericValue }
if ( null in constants )
2018-09-29 15:33:59 +00:00
throw NotConstArgumentException ( )
function ( constants . map { it !! . toDouble ( ) } )
} else {
2018-10-30 19:29:03 +00:00
val array = heap . get ( iterable . heapId !! ) . array ?: throw SyntaxError ( " function requires array argument " , position )
2018-09-29 15:33:59 +00:00
function ( array . 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-30 12:19:41 +00:00
private fun builtinFlt ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) : LiteralValue {
2018-09-18 21:14:32 +00:00
// 1 numeric arg, convert to float
if ( args . size != 1 )
throw SyntaxError ( " flt requires one numeric argument " , position )
2018-09-29 15:33:59 +00:00
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-09-18 21:14:32 +00:00
val number = constval . asNumericValue ?: throw SyntaxError ( " flt requires one numeric argument " , position )
return LiteralValue ( DataType . FLOAT , floatvalue = number . toDouble ( ) , position = position )
}
2018-10-05 22:10:30 +00:00
private fun builtinWrd ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) : LiteralValue {
// 1 byte arg, convert to word
if ( args . size != 1 )
throw SyntaxError ( " wrd requires one numeric argument " , position )
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-10-10 07:21:20 +00:00
if ( constval . type != DataType . UBYTE && constval . type != DataType . BYTE && constval . type != DataType . UWORD )
throw SyntaxError ( " wrd requires one argument of type ubyte, byte or uword " , position )
2018-12-14 22:15:44 +00:00
return LiteralValue ( DataType . WORD , wordvalue = constval . asIntegerValue , position = position )
2018-10-05 22:10:30 +00:00
}
2018-10-10 07:21:20 +00:00
private fun builtinUwrd ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) : LiteralValue {
// 1 arg, convert to uword
if ( args . size != 1 )
throw SyntaxError ( " uwrd requires one numeric argument " , position )
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
if ( constval . type != DataType . BYTE && constval . type != DataType . WORD && constval . type != DataType . UWORD )
throw SyntaxError ( " uwrd requires one argument of type byte, word or uword " , position )
2018-12-14 22:15:44 +00:00
return LiteralValue ( DataType . UWORD , wordvalue = constval . asIntegerValue !! and 65535 , position = position )
2018-10-10 07:21:20 +00:00
}
private fun builtinB2ub ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) : LiteralValue {
// 1 byte arg, convert to ubyte
if ( args . size != 1 )
throw SyntaxError ( " b2ub requires one byte argument " , position )
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-12-14 22:15:44 +00:00
if ( constval . type != DataType . BYTE && constval . type != DataType . UBYTE )
throw SyntaxError ( " b2ub requires one argument of type byte or ubyte " , position )
2018-10-12 12:01:29 +00:00
return LiteralValue ( DataType . UBYTE , bytevalue = ( constval . bytevalue !! . toInt ( ) and 255 ) . toShort ( ) , position = position )
2018-10-10 07:21:20 +00:00
}
private fun builtinUb2b ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) : LiteralValue {
// 1 ubyte arg, convert to byte
if ( args . size != 1 )
throw SyntaxError ( " ub2b requires one ubyte argument " , position )
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
if ( constval . type != DataType . UBYTE )
throw SyntaxError ( " ub2b requires one argument of type ubyte " , position )
2018-10-12 12:01:29 +00:00
return LiteralValue ( DataType . BYTE , bytevalue = ( constval . bytevalue !! . toInt ( ) and 255 ) . toShort ( ) , position = position )
2018-10-05 22:10:30 +00:00
}
2018-09-30 12:19:41 +00:00
private fun builtinAbs ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) : 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 )
2018-09-29 15:33:59 +00:00
val constval = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-09-13 20:31:59 +00:00
val number = constval . asNumericValue
return when ( number ) {
2018-09-17 22:31:11 +00:00
is Int , is Byte , is Short -> numericLiteral ( Math . abs ( number . toInt ( ) ) , args [ 0 ] . position )
is Double -> numericLiteral ( Math . abs ( number . toDouble ( ) ) , args [ 0 ] . position )
2018-09-13 20:31:59 +00:00
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-30 12:19:41 +00:00
private fun builtinAvg ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) : LiteralValue {
2018-09-04 23:28:06 +00:00
if ( args . size != 1 )
2018-10-30 19:29:03 +00:00
throw SyntaxError ( " avg requires array argument " , position )
2018-09-30 12:19:41 +00:00
val iterable = args [ 0 ] . constValue ( namespace , heap ) ?: throw NotConstArgumentException ( )
2018-09-29 15:33:59 +00:00
val result = if ( iterable . arrayvalue != null ) {
2018-09-30 12:19:41 +00:00
val constants = iterable . arrayvalue . map { it . constValue ( namespace , heap ) ?. asNumericValue }
if ( null in constants )
2018-09-29 15:33:59 +00:00
throw NotConstArgumentException ( )
( constants . map { it !! . toDouble ( ) } ) . average ( )
}
else {
2018-10-30 19:29:03 +00:00
val array = heap . get ( iterable . heapId !! ) . array ?: throw SyntaxError ( " avg requires array argument " , position )
2018-09-29 15:33:59 +00:00
array . 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-30 12:19:41 +00:00
private fun builtinLen ( args : List < IExpression > , position : Position , namespace : INameScope , heap : HeapValues ) : 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 )
2018-09-29 15:33:59 +00:00
var argument = args [ 0 ] . constValue ( namespace , heap )
if ( argument == null ) {
if ( args [ 0 ] !is IdentifierReference )
throw SyntaxError ( " len over weird argument ${args[0]} " , position )
2018-10-22 22:52:51 +00:00
val target = ( args [ 0 ] as IdentifierReference ) . targetStatement ( namespace )
val argValue = ( target as ? VarDecl ) ?. value
argument = argValue ?. constValue ( namespace , heap )
?: throw NotConstArgumentException ( )
2018-09-29 15:33:59 +00:00
}
2018-09-14 23:14:48 +00:00
return when ( argument . type ) {
2018-10-30 19:29:03 +00:00
DataType . ARRAY _UB , DataType . ARRAY _B , DataType . ARRAY _UW , DataType . ARRAY _W -> {
2018-10-22 22:52:51 +00:00
val arraySize = argument . arrayvalue ?. size ?: heap . get ( argument . heapId !! ) . arraysize
2018-10-11 07:54:26 +00:00
LiteralValue ( DataType . UWORD , wordvalue = arraySize , position = args [ 0 ] . position )
2018-09-29 15:33:59 +00:00
}
2018-10-01 23:52:08 +00:00
DataType . ARRAY _F -> {
2018-10-22 22:52:51 +00:00
val arraySize = argument . arrayvalue ?. size ?: heap . get ( argument . heapId !! ) . arraysize
2018-10-11 07:54:26 +00:00
LiteralValue ( DataType . UWORD , wordvalue = arraySize , position = args [ 0 ] . position )
2018-10-01 23:52:08 +00:00
}
2018-09-29 15:33:59 +00:00
DataType . STR , DataType . STR _P , DataType . STR _S , DataType . STR _PS -> {
2018-11-21 23:01:41 +00:00
val str = argument . strvalue ( heap )
2018-10-11 07:54:26 +00:00
LiteralValue ( DataType . UWORD , wordvalue = str . length , position = args [ 0 ] . position )
2018-09-29 15:33:59 +00:00
}
2018-10-10 07:21:20 +00:00
DataType . UBYTE , DataType . BYTE ,
DataType . UWORD , DataType . WORD ,
2018-10-01 23:52:08 +00:00
DataType . FLOAT -> throw SyntaxError ( " len of weird argument ${args[0]} " , position )
2018-09-14 17:38:22 +00:00
}
2018-09-04 23:28:06 +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 =
2018-10-30 08:22:32 +00:00
if ( floatNum == Math . floor ( floatNum ) && ( floatNum >= - 32768 && floatNum <= 65535 ) )
2018-09-13 20:31:59 +00:00
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 )
2018-10-10 07:21:20 +00:00
is Byte -> LiteralValue ( DataType . UBYTE , bytevalue = value . toShort ( ) , position = position )
2018-09-14 23:14:48 +00:00
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} " )
}
}