diff --git a/docs/source/programming.rst b/docs/source/programming.rst index 8ee22501c..2029158b5 100644 --- a/docs/source/programming.rst +++ b/docs/source/programming.rst @@ -360,3 +360,54 @@ by nothing, which is the same as AXY) to tell the compiler you want to preserve value of the given registers after the subroutine call. Otherwise, the subroutine may just as well clobber all three registers. Preserving the original values does result in some stack manipulation code to be inserted for every call like this, which can be quite slow. + + +Built-in Functions +------------------ + +The compiler has the following built-in functions that you can use in expressions: + +sin(value) + Sine. + +cos(value) + Cosine. + +abs(value) + Absolute value. + +acos(value) + Arccosine. + +asin(value) + Arcsine. + +tan(value) + Tangent. + +atan(value) + Arctangent. + +log(value) + Natural logarithm. + +log10(value) + Base-10 logarithm. + +sqrt(value) + Square root. + +max(value [, value, ...]) + Maximum of the values. + +min(value [, value, ...]) + Minumum of the values. + +round(value) + Rounds the floating point to an integer. + +rad(value) + Degrees to radians. + +deg(value) + Radians to degrees. diff --git a/il65/examples/test.ill b/il65/examples/test.ill index 9abe0aad5..306a37204 100644 --- a/il65/examples/test.ill +++ b/il65/examples/test.ill @@ -1,7 +1,12 @@ ~ main $c003 { - ;memory byte derp = $ffdd - ;memory byte derp2 = 2+$ffdd+sin(3) - memory byte derp3 = round(sin(3)) + memory byte derp = max($ffdd) + memory byte derpA = abs(-2.5-0.5) + memory byte derpB = max(1, 2.2, 4.4, 100) + memory byte cderp = min($ffdd) + memory byte cderpA = min($ffdd, 10, 20, 30) + memory byte cderpB = min(1, 2.2, 4.4, 100) + memory byte derp2 = 2+$ffdd+round(10*sin(3)) + memory byte derp3 = round(100*sin(3)) const byte hopla=55-33 const byte hopla2=100+(-main.hopla) const byte hopla3=100+(-hopla) diff --git a/il65/src/il65/ast/AST.kt b/il65/src/il65/ast/AST.kt index ab9e096f1..b24c87bd5 100644 --- a/il65/src/il65/ast/AST.kt +++ b/il65/src/il65/ast/AST.kt @@ -525,7 +525,6 @@ data class FunctionCall(var location: Identifier, var arglist: List override fun constValue(namespace: INameScope): LiteralValue? { // if the function is a built-in function and the args are consts, should evaluate! - println("CONSTVALUE of Function call $location") // todo if(location.scopedName.size>1) return null return when(location.scopedName[0]){ "sin" -> builtin_sin(arglist, namespace) @@ -541,6 +540,8 @@ data class FunctionCall(var location: Identifier, var arglist: List "max" -> builtin_max(arglist, namespace) "min" -> builtin_min(arglist, namespace) "round" -> builtin_round(arglist, namespace) + "rad" -> builtin_rad(arglist, namespace) + "deg" -> builtin_deg(arglist, namespace) else -> null } } diff --git a/il65/src/il65/ast/AstOptimizer.kt b/il65/src/il65/ast/AstOptimizer.kt index 81d3474e9..d2e6d03d0 100644 --- a/il65/src/il65/ast/AstOptimizer.kt +++ b/il65/src/il65/ast/AstOptimizer.kt @@ -30,9 +30,12 @@ class AstOptimizer(private val globalNamespace: INameScope) : IAstProcessor { * some identifiers can be replaced with the constant value they refer to */ override fun process(identifier: Identifier): IExpression { - println("PROCESS ID $identifier") // todo - val const = identifier.constValue(globalNamespace) - return const ?: identifier + return identifier.constValue(globalNamespace) ?: identifier + } + + override fun process(functionCall: FunctionCall): IExpression { + super.process(functionCall) + return functionCall.constValue(globalNamespace) ?: functionCall } /** diff --git a/il65/src/il65/functions/BuiltinFunctions.kt b/il65/src/il65/functions/BuiltinFunctions.kt index 988f77c89..360442533 100644 --- a/il65/src/il65/functions/BuiltinFunctions.kt +++ b/il65/src/il65/functions/BuiltinFunctions.kt @@ -3,6 +3,7 @@ package il65.functions import il65.ast.IExpression import il65.ast.INameScope import il65.ast.LiteralValue +import il65.ast.Position private fun oneDoubleArg(args: List, namespace: INameScope, function: (arg: Double)->Double): LiteralValue { if(args.size!=1) @@ -50,7 +51,6 @@ private fun twoDoubleArg(args: List, namespace: INameScope, functio fun builtin_round(args: List, namespace: INameScope): LiteralValue = oneDoubleArgOutputInt(args, namespace) { it -> Math.round(it).toInt() } fun builtin_sin(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::sin) fun builtin_cos(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::cos) -fun builtin_abs(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::abs) fun builtin_acos(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::acos) fun builtin_asin(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::asin) fun builtin_tan(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::tan) @@ -58,5 +58,46 @@ fun builtin_atan(args: List, namespace: INameScope): LiteralValue = fun builtin_log(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::log) fun builtin_log10(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::log10) fun builtin_sqrt(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::sqrt) -fun builtin_max(args: List, namespace: INameScope): LiteralValue = twoDoubleArg(args, namespace, Math::max) -fun builtin_min(args: List, namespace: INameScope): LiteralValue = twoDoubleArg(args, namespace, Math::min) +fun builtin_rad(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::toRadians) +fun builtin_deg(args: List, namespace: INameScope): LiteralValue = oneDoubleArg(args, namespace, Math::toDegrees) + +fun builtin_abs(args: List, namespace: INameScope): LiteralValue { + if(args.size!=1) + throw UnsupportedOperationException("built-in function abs requires one numeric argument") + val float = args[0].constValue(namespace)?.asFloat() + if(float!=null) + return IntOrFloatLiteral(Math.abs(float), args[0].position) + else + throw UnsupportedOperationException("built-in function abs requires floating point value as argument") +} + +fun builtin_max(args: List, namespace: INameScope): LiteralValue { + if(args.isEmpty()) + throw UnsupportedOperationException("max requires at least one argument") + val constants = args.map { it.constValue(namespace) } + if(constants.contains(null)) + throw UnsupportedOperationException("not all arguments to max are a constant value") + val result = constants.map { it?.asFloat()!! }.max() + return IntOrFloatLiteral(result!!, args[0].position) +} + +fun builtin_min(args: List, namespace: INameScope): LiteralValue { + if(args.isEmpty()) + throw UnsupportedOperationException("min requires at least one argument") + val constants = args.map { it.constValue(namespace) } + if(constants.contains(null)) + throw UnsupportedOperationException("not all arguments to min are a constant value") + val result = constants.map { it?.asFloat()!! }.min() + return IntOrFloatLiteral(result!!, args[0].position) +} + + +private fun IntOrFloatLiteral(value: Double, position: Position?): LiteralValue { + val intresult = value.toInt() + val result = if(value-intresult==0.0) + LiteralValue(intvalue = intresult) + else + LiteralValue(floatvalue = value) + result.position = position + return result +} \ No newline at end of file