added T is nullable support

This commit is contained in:
sergeych committed 2026-03-14 20:57:15 +03:00
1 parent c54d947f1c
commit 37d093817e
9 files changed
+171 -11

No files matched your search

@@ -2247,6 +2247,7 @@ class Compiler(
is WhenEqualsCondition -> containsDelegatedRefs(cond.expr)
is WhenInCondition -> containsDelegatedRefs(cond.expr)
is WhenIsCondition -> false
is WhenNullableCondition -> false
}
} || containsDelegatedRefs(case.block)
} ||
@@ -2520,12 +2521,22 @@ class Compiler(
CastRef(lvalue!!, typeRef, true, opToken.pos)
}
} else if (opToken.type == Token.Type.IS || opToken.type == Token.Type.NOTIS) {
val (typeDecl, _) = parseTypeExpressionWithMini()
val typeRef = net.sergeych.lyng.obj.TypeDeclRef(typeDecl, opToken.pos)
if (opToken.type == Token.Type.IS) {
BinaryOpRef(BinOp.IS, lvalue!!, typeRef)
val nullablePredicate = tryConsumeNullablePredicate()
if (nullablePredicate) {
val nullRef = ConstRef(ObjNull.asReadonly)
if (opToken.type == Token.Type.IS) {
BinaryOpRef(BinOp.IS, nullRef, lvalue!!)
} else {
BinaryOpRef(BinOp.NOTIS, nullRef, lvalue!!)
}
} else {
BinaryOpRef(BinOp.NOTIS, lvalue!!, typeRef)
val (typeDecl, _) = parseTypeExpressionWithMini()
val typeRef = net.sergeych.lyng.obj.TypeDeclRef(typeDecl, opToken.pos)
if (opToken.type == Token.Type.IS) {
BinaryOpRef(BinOp.IS, lvalue!!, typeRef)
} else {
BinaryOpRef(BinOp.NOTIS, lvalue!!, typeRef)
}
}
} else {
val rvalue = parseExpressionLevel(level + 1)
@@ -2557,6 +2568,17 @@ class Compiler(
return lvalue
}
private fun tryConsumeNullablePredicate(): Boolean {
val save = cc.savePos()
val t = cc.nextNonWhitespace()
return if (t.type == Token.Type.ID && t.value == "nullable") {
true
} else {
cc.restorePos(save)
false
}
}
private suspend fun parseTerm(): ObjRef? {
var operand: ObjRef? = null
var pendingCallTypeArgs: List<TypeDecl>? = null
@@ -6252,10 +6274,14 @@ class Compiler(
Token.Type.IS,
Token.Type.NOTIS -> {
val negated = t.type == Token.Type.NOTIS
val (typeDecl, _) = parseTypeExpressionWithMini()
val typeRef = net.sergeych.lyng.obj.TypeDeclRef(typeDecl, t.pos)
val caseType = ExpressionStatement(typeRef, t.pos)
currentConditions += WhenIsCondition(caseType, negated, t.pos)
if (tryConsumeNullablePredicate()) {
currentConditions += WhenNullableCondition(negated, t.pos)
} else {
val (typeDecl, _) = parseTypeExpressionWithMini()
val typeRef = net.sergeych.lyng.obj.TypeDeclRef(typeDecl, t.pos)
val caseType = ExpressionStatement(typeRef, t.pos)
currentConditions += WhenIsCondition(caseType, negated, t.pos)
}
}
Token.Type.COMMA ->
@@ -6268,7 +6294,9 @@ class Compiler(
break@outer
else -> {
if (t.value == "else") {
if (t.type == Token.Type.ID && t.value == "nullable") {
currentConditions += WhenNullableCondition(negated = false, t.pos)
} else if (t.value == "else") {
cc.skipTokens(Token.Type.ARROW)
if (elseCase != null) throw ScriptError(
cc.currentPos(),
@@ -1,5 +1,5 @@
/*
* Copyright 2026 Sergey S. Chernov
* 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.
@@ -12,6 +12,7 @@
* 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
@@ -55,6 +56,15 @@ class WhenIsCondition(
}
}
class WhenNullableCondition(
val negated: Boolean,
override val pos: Pos,
) : WhenCondition(ExpressionStatement(net.sergeych.lyng.obj.ConstRef(net.sergeych.lyng.obj.ObjVoid.asReadonly), pos), pos) {
override suspend fun matches(scope: Scope, value: Obj): Boolean {
return bytecodeOnly(scope)
}
}
data class WhenCase(val conditions: List<WhenCondition>, val block: Statement)
class WhenStatement(
@@ -4345,6 +4345,22 @@ class BytecodeCompiler(
CompiledValue(neg, SlotType.BOOL)
}
}
is WhenNullableCondition -> {
val nullSlot = allocSlot()
builder.emit(Opcode.CONST_NULL, nullSlot)
updateSlotType(nullSlot, SlotType.OBJ)
val baseDst = allocSlot()
builder.emit(Opcode.CHECK_IS, nullSlot, subject.slot, baseDst)
updateSlotType(baseDst, SlotType.BOOL)
if (!cond.negated) {
CompiledValue(baseDst, SlotType.BOOL)
} else {
val neg = allocSlot()
builder.emit(Opcode.NOT_BOOL, baseDst, neg)
updateSlotType(neg, SlotType.BOOL)
CompiledValue(neg, SlotType.BOOL)
}
}
}
}
@@ -443,6 +443,7 @@ class BytecodeStatement private constructor(
is WhenEqualsCondition -> WhenEqualsCondition(unwrapDeep(cond.expr), cond.pos)
is WhenInCondition -> WhenInCondition(unwrapDeep(cond.expr), cond.negated, cond.pos)
is WhenIsCondition -> WhenIsCondition(unwrapDeep(cond.expr), cond.negated, cond.pos)
is WhenNullableCondition -> WhenNullableCondition(cond.negated, cond.pos)
}
}
}