added bitwise operators and shift support, updated parser, compiler, and docs. fixed copyright.

This commit is contained in:
sergeych committed 2025-11-12 12:57:35 +01:00
1 parent 852383e3b1
commit 76d89b43db
57 files changed
+996 -10

No files matched your search

@@ -1,3 +1,20 @@
/*
* Copyright 2025 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
import net.sergeych.lyng.obj.Obj
@@ -1,3 +1,20 @@
/*
* Copyright 2025 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
import net.sergeych.lyng.obj.Obj
@@ -277,6 +277,12 @@ class Compiler(
operand = UnaryOpRef(UnaryOp.NOT, op)
}
Token.Type.BITNOT -> {
if (operand != null) throw ScriptError(t.pos, "unexpected operator '~'")
val op = parseTerm() ?: throw ScriptError(t.pos, "Expecting expression after '~'")
operand = UnaryOpRef(UnaryOp.BITNOT, op)
}
Token.Type.DOT, Token.Type.NULL_COALESCE -> {
val isOptional = t.type == Token.Type.NULL_COALESCE
operand?.let { left ->
@@ -1867,6 +1873,16 @@ class Compiler(
Operator(Token.Type.AND, ++lastPriority) { _, a, b ->
LogicalAndRef(a, b)
},
// bitwise or/xor/and (tighter than &&, looser than equality)
Operator(Token.Type.BITOR, ++lastPriority) { _, a, b ->
BinaryOpRef(BinOp.BOR, a, b)
},
Operator(Token.Type.BITXOR, ++lastPriority) { _, a, b ->
BinaryOpRef(BinOp.BXOR, a, b)
},
Operator(Token.Type.BITAND, ++lastPriority) { _, a, b ->
BinaryOpRef(BinOp.BAND, a, b)
},
// equality/not equality and related
Operator(Token.Type.EQARROW, ++lastPriority) { _, a, b ->
BinaryOpRef(BinOp.EQARROW, a, b)
@@ -1924,6 +1940,13 @@ class Compiler(
Operator(Token.Type.SHUTTLE, ++lastPriority) { _, a, b ->
BinaryOpRef(BinOp.SHUTTLE, a, b)
},
// shifts (tighter than shuttle, looser than +/-)
Operator(Token.Type.SHL, ++lastPriority) { _, a, b ->
BinaryOpRef(BinOp.SHL, a, b)
},
Operator(Token.Type.SHR, lastPriority) { _, a, b ->
BinaryOpRef(BinOp.SHR, a, b)
},
// arithmetic
Operator(Token.Type.PLUS, ++lastPriority) { _, a, b ->
BinaryOpRef(BinOp.PLUS, a, b)
@@ -191,6 +191,10 @@ private class Parser(fromPos: Pos) {
} else {
Token("<=", from, Token.Type.LTE)
}
} else if (currentChar == '<') {
// Shift left <<
pos.advance()
Token("<<", from, Token.Type.SHL)
} else
Token("<", from, Token.Type.LT)
}
@@ -199,6 +203,10 @@ private class Parser(fromPos: Pos) {
if (currentChar == '=') {
pos.advance()
Token(">=", from, Token.Type.GTE)
} else if (currentChar == '>') {
// Shift right >>
pos.advance()
Token(">>", from, Token.Type.SHR)
} else
Token(">", from, Token.Type.GT)
}
@@ -253,6 +261,10 @@ private class Parser(fromPos: Pos) {
Token("&", from, Token.Type.BITAND)
}
'^' -> Token("^", from, Token.Type.BITXOR)
'~' -> Token("~", from, Token.Type.BITNOT)
'@' -> {
val label = loadChars(idNextChars)
if (label.isNotEmpty()) Token(label, from, Token.Type.ATLABEL)
@@ -1,3 +1,20 @@
/*
* Copyright 2025 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
/**
@@ -1,3 +1,20 @@
/*
* Copyright 2025 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
/**
@@ -1,3 +1,20 @@
/*
* Copyright 2025 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
/**
@@ -1,3 +1,20 @@
/*
* Copyright 2025 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
/**
@@ -1,3 +1,20 @@
/*
* Copyright 2025 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
import net.sergeych.lyng.obj.Obj
@@ -39,7 +39,8 @@ data class Token(val value: String, val pos: Pos, val type: Type) {
IN, NOTIN, IS, NOTIS,
EQ, NEQ, LT, LTE, GT, GTE, REF_EQ, REF_NEQ, MATCH, NOTMATCH,
SHUTTLE,
AND, BITAND, OR, BITOR, NOT, BITNOT, DOT, ARROW, EQARROW, QUESTION, COLONCOLON,
AND, BITAND, OR, BITOR, BITXOR, NOT, BITNOT, DOT, ARROW, EQARROW, QUESTION, COLONCOLON,
SHL, SHR,
SINLGE_LINE_COMMENT, MULTILINE_COMMENT,
LABEL, ATLABEL, // label@ at@label
//PUBLIC, PROTECTED, INTERNAL, EXPORT, OPEN, INLINE, OVERRIDE, ABSTRACT, SEALED, EXTERNAL, VAL, VAR, CONST, TYPE, FUN, CLASS, INTERFACE, ENUM, OBJECT, TRAIT, THIS,
@@ -183,6 +183,31 @@ open class Obj {
return operatorMatch(scope,other).logicalNot(scope)
}
// Bitwise ops default (override in numeric types that support them)
open suspend fun bitAnd(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented()
}
open suspend fun bitOr(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented()
}
open suspend fun bitXor(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented()
}
open suspend fun shl(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented()
}
open suspend fun shr(scope: Scope, other: Obj): Obj {
scope.raiseNotImplemented()
}
open suspend fun bitNot(scope: Scope): Obj {
scope.raiseNotImplemented()
}
open suspend fun assign(scope: Scope, other: Obj): Obj? = null
open fun getValue(scope: Scope) = this
@@ -118,6 +118,29 @@ class ObjInt(var value: Long, override val isConst: Boolean = false) : Obj(), Nu
return ObjInt(-value)
}
// Bitwise operations
override suspend fun bitAnd(scope: Scope, other: Obj): Obj =
if (other is ObjInt) ObjInt(this.value and other.value)
else scope.raiseIllegalArgument("bitwise and '&' requires Int, got ${other.objClass.className}")
override suspend fun bitOr(scope: Scope, other: Obj): Obj =
if (other is ObjInt) ObjInt(this.value or other.value)
else scope.raiseIllegalArgument("bitwise or '|' requires Int, got ${other.objClass.className}")
override suspend fun bitXor(scope: Scope, other: Obj): Obj =
if (other is ObjInt) ObjInt(this.value xor other.value)
else scope.raiseIllegalArgument("bitwise xor '^' requires Int, got ${other.objClass.className}")
override suspend fun shl(scope: Scope, other: Obj): Obj =
if (other is ObjInt) ObjInt(this.value shl (other.value.toInt() and 63))
else scope.raiseIllegalArgument("shift left '<<' requires Int, got ${other.objClass.className}")
override suspend fun shr(scope: Scope, other: Obj): Obj =
if (other is ObjInt) ObjInt(this.value shr (other.value.toInt() and 63))
else scope.raiseIllegalArgument("shift right '>>' requires Int, got ${other.objClass.className}")
override suspend fun bitNot(scope: Scope): Obj = ObjInt(this.value.inv())
override suspend fun lynonType(): LynonType = when (value) {
0L -> LynonType.Int0
else -> {
@@ -1,5 +1,5 @@
/*
* Copyright 2025 Sergey S. Chernov
* Copyright 2025 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.obj
@@ -40,7 +41,7 @@ class ValueFnRef(private val fn: suspend (Scope) -> ObjRecord) : ObjRef {
}
/** Unary operations supported by ObjRef. */
enum class UnaryOp { NOT, NEGATE }
enum class UnaryOp { NOT, NEGATE, BITNOT }
/** Binary operations supported by ObjRef. */
enum class BinOp {
@@ -50,6 +51,11 @@ enum class BinOp {
IN, NOTIN,
IS, NOTIS,
SHUTTLE,
// bitwise
BAND, BXOR, BOR,
// shifts
SHL, SHR,
// arithmetic
PLUS, MINUS, STAR, SLASH, PERCENT
}
@@ -60,6 +66,7 @@ class UnaryOpRef(private val op: UnaryOp, private val a: ObjRef) : ObjRef {
val r = when (op) {
UnaryOp.NOT -> v.logicalNot(scope)
UnaryOp.NEGATE -> v.negate(scope)
UnaryOp.BITNOT -> v.bitNot(scope)
}
return r.asReadonly
}
@@ -97,6 +104,11 @@ class BinaryOpRef(private val op: BinOp, private val left: ObjRef, private val r
BinOp.STAR -> ObjInt(av * bv)
BinOp.SLASH -> if (bv != 0L) ObjInt(av / bv) else null
BinOp.PERCENT -> if (bv != 0L) ObjInt(av % bv) else null
BinOp.BAND -> ObjInt(av and bv)
BinOp.BXOR -> ObjInt(av xor bv)
BinOp.BOR -> ObjInt(av or bv)
BinOp.SHL -> ObjInt(av shl (bv.toInt() and 63))
BinOp.SHR -> ObjInt(av shr (bv.toInt() and 63))
BinOp.EQ -> if (av == bv) ObjTrue else ObjFalse
BinOp.NEQ -> if (av != bv) ObjTrue else ObjFalse
BinOp.LT -> if (av < bv) ObjTrue else ObjFalse
@@ -210,6 +222,11 @@ class BinaryOpRef(private val op: BinOp, private val left: ObjRef, private val r
BinOp.IS -> ObjBool(a.isInstanceOf(b))
BinOp.NOTIS -> ObjBool(!a.isInstanceOf(b))
BinOp.SHUTTLE -> ObjInt(a.compareTo(scope, b).toLong())
BinOp.BAND -> a.bitAnd(scope, b)
BinOp.BXOR -> a.bitXor(scope, b)
BinOp.BOR -> a.bitOr(scope, b)
BinOp.SHL -> a.shl(scope, b)
BinOp.SHR -> a.shr(scope, b)
BinOp.PLUS -> a.plus(scope, b)
BinOp.MINUS -> a.minus(scope, b)
BinOp.STAR -> a.mul(scope, b)
@@ -1,3 +1,20 @@
/*
* Copyright 2025 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.tools
import kotlinx.datetime.Clock