Added Decimal support for complex number

This commit is contained in:
Sergey Chernov 2026-03-30 08:20:55 +03:00
parent 8a560f5417
commit 286ec30422
4 changed files with 136 additions and 4 deletions

View File

@ -875,6 +875,7 @@ class Script(
}
addPackage("lyng.complex") { module ->
module.eval(Source("lyng.complex", complexLyng))
ObjComplexSupport.bindTo(module)
}
addPackage("lyng.buffer") {
it.addConstDoc(

View File

@ -0,0 +1,99 @@
/*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng.obj
import net.sergeych.lyng.*
import net.sergeych.lyng.miniast.addPropertyDoc
import net.sergeych.lyng.miniast.type
import net.sergeych.lyng.requiredArg
object ObjComplexSupport {
private object BoundMarker
private val complexTypeDecl = TypeDecl.Simple("lyng.complex.Complex", false)
suspend fun bindTo(module: ModuleScope) {
val complexClass = module.requireClass("Complex")
if (complexClass.kotlinClassData === BoundMarker) return
complexClass.kotlinClassData = BoundMarker
val decimalModule = module.currentImportProvider.createModuleScope(module.pos, "lyng.decimal")
val decimalClass = decimalModule.requireClass("Decimal")
decimalClass.addPropertyDoc(
name = "re",
doc = "Convert this Decimal to a Complex with zero imaginary part.",
type = type("lyng.complex.Complex"),
moduleName = "lyng.complex",
getter = {
newComplex(
complexClass,
decimalToReal(thisObj),
0.0
)
}
)
decimalClass.members["re"] = decimalClass.members.getValue("re").copy(typeDecl = complexTypeDecl)
decimalClass.addPropertyDoc(
name = "i",
doc = "Convert this Decimal to a pure imaginary Complex after rounding to Real.",
type = type("lyng.complex.Complex"),
moduleName = "lyng.complex",
getter = {
newComplex(
complexClass,
0.0,
decimalToReal(thisObj)
)
}
)
decimalClass.members["i"] = decimalClass.members.getValue("i").copy(typeDecl = complexTypeDecl)
OperatorInteropRegistry.register(
leftClass = decimalClass,
rightClass = complexClass,
commonClass = complexClass,
operatorNames = listOf(
InteropOperator.Plus.name,
InteropOperator.Minus.name,
InteropOperator.Mul.name,
InteropOperator.Div.name
),
leftToCommon = ObjExternCallable.fromBridge {
newComplex(
complexClass,
decimalToReal(requiredArg(0)),
0.0
)
},
rightToCommon = ObjExternCallable.fromBridge {
requiredArg<Obj>(0)
}
)
}
private suspend fun ScopeFacade.newComplex(complexClass: ObjClass, real: Double, imag: Double): ObjInstance =
call(
complexClass,
Arguments(ObjReal.of(real), ObjReal.of(imag))
) as? ObjInstance ?: raiseIllegalState("Complex() did not return an object instance")
private fun ScopeFacade.decimalToReal(value: Obj): Double =
ObjDecimalSupport.toDoubleOrNull(value)
?: raiseClassCastError("expected Decimal-compatible value, got ${value.objClass.className}")
}

View File

@ -54,16 +54,20 @@ object ObjDecimalSupport {
instance.kotlinInstanceData = zero
}
decimalClass.addFn("plus") {
newInstance(decimalClass, valueOf(thisObj).plus(coerceArg(requireScope(), args.firstAndOnly())))
OperatorInteropRegistry.invokeBinary(requireScope(), thisObj, args.firstAndOnly(), InteropOperator.Plus)
?: newInstance(decimalClass, valueOf(thisObj).plus(coerceArg(requireScope(), args.firstAndOnly())))
}
decimalClass.addFn("minus") {
newInstance(decimalClass, valueOf(thisObj).minus(coerceArg(requireScope(), args.firstAndOnly())))
OperatorInteropRegistry.invokeBinary(requireScope(), thisObj, args.firstAndOnly(), InteropOperator.Minus)
?: newInstance(decimalClass, valueOf(thisObj).minus(coerceArg(requireScope(), args.firstAndOnly())))
}
decimalClass.addFn("mul") {
newInstance(decimalClass, valueOf(thisObj).times(coerceArg(requireScope(), args.firstAndOnly())))
OperatorInteropRegistry.invokeBinary(requireScope(), thisObj, args.firstAndOnly(), InteropOperator.Mul)
?: newInstance(decimalClass, valueOf(thisObj).times(coerceArg(requireScope(), args.firstAndOnly())))
}
decimalClass.addFn("div") {
newInstance(decimalClass, divideWithContext(valueOf(thisObj), coerceArg(requireScope(), args.firstAndOnly()), currentDivisionMode(requireScope())))
OperatorInteropRegistry.invokeBinary(requireScope(), thisObj, args.firstAndOnly(), InteropOperator.Div)
?: newInstance(decimalClass, divideWithContext(valueOf(thisObj), coerceArg(requireScope(), args.firstAndOnly()), currentDivisionMode(requireScope())))
}
decimalClass.addFn("mod") {
newInstance(decimalClass, valueOf(thisObj).rem(coerceArg(requireScope(), args.firstAndOnly())))

View File

@ -73,4 +73,32 @@ class ComplexModuleTest {
""".trimIndent()
)
}
@Test
fun testInferences() = runTest {
eval(
$$"""
import lyng.decimal
import lyng.complex
assert( 1.i is Complex )
assert( 5 + 1.i is Complex )
""".trimIndent()
)
}
@Test
fun testDecimalInferences() = runTest {
eval(
$$"""
import lyng.decimal
import lyng.complex
assert( 1.d.i is Complex )
assert( 5 + 1.d.i is Complex )
assert( 5.d + 1.i is Complex )
assert( 5.d + 2.d.i is Complex )
""".trimIndent()
)
}
}