From 60e6da2cb84029d9b39c3aa1a22fa525869122b6 Mon Sep 17 00:00:00 2001 From: Shaw Summa Date: Mon, 9 Mar 2026 20:23:20 -0400 Subject: [PATCH 01/10] ai Signed-off-by: Shaw Summa --- .../temper/be/csharp/temper-core/Float64.cs | 4 +-- .../be/csharp/temper-core/StringUtil.cs | 4 +-- .../lang/temper/be/js/temper-core/float.js | 2 +- .../lang/temper/be/js/temper-core/listed.js | 2 +- .../lang/temper/be/js/temper-core/string.js | 4 +-- .../be/py/temper-core/temper_core/__init__.py | 32 +++++++++++-------- .../kotlin/lang/temper/common/Memoized.kt | 3 ++ .../lang/temper/compile/fetch/GitCache.kt | 2 +- .../temper/frontend/staging/ModuleAdvancer.kt | 4 +++ .../kotlin/lang/temper/lexer/Lexer.kt | 2 +- .../temper/result/junit/JUnitResultParser.kt | 2 +- .../lang/temper/tooling/buildrun/Runner.kt | 1 + 12 files changed, 38 insertions(+), 24 deletions(-) diff --git a/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/Float64.cs b/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/Float64.cs index b53d8220..104a1be0 100644 --- a/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/Float64.cs +++ b/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/Float64.cs @@ -73,7 +73,7 @@ public static bool Near( var rel = relTol ?? 1e-9; var abs = absTol ?? 0.0; var margin = Math.Max(Math.Max(Math.Abs(x), Math.Abs(y)) * rel, abs); - return Math.Abs(x - y) < margin; + return Math.Abs(x - y) <= margin; } public static double Sign(this double x) @@ -109,7 +109,7 @@ public static double ToFloat64(this string s) ? double.PositiveInfinity : trimmed == "-Infinity" ? double.NegativeInfinity - : double.Parse(s); + : double.Parse(trimmed); } public static int ToInt(double value) diff --git a/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/StringUtil.cs b/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/StringUtil.cs index c4b62a5b..d9a4a565 100644 --- a/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/StringUtil.cs +++ b/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/StringUtil.cs @@ -62,7 +62,7 @@ public static int CountBetween(string s, int left, int right) for (int i = Math.Max(0, left); i < limit; ++i) { count += 1; - if (i + 1 < right) + if (i + 1 < limit) { char c = s[i]; if ('\uD800' <= c && c <= '\uDBFF') @@ -195,7 +195,7 @@ public static int RequireNoStringIndex(int i) { if (i < 0) { - return -1; + return i; } throw new ArgumentOutOfRangeException("i"); } diff --git a/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/float.js b/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/float.js index f921f43c..d431d5f0 100644 --- a/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/float.js +++ b/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/float.js @@ -19,7 +19,7 @@ export const float64Near = (x, y, relTol, absTol) => { absTol = 0; } const margin = Math.max(Math.max(Math.abs(x), Math.abs(y)) * relTol, absTol); - return Math.abs(x - y) < margin; + return Math.abs(x - y) <= margin; } /** diff --git a/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/listed.js b/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/listed.js index bf05d9ff..a720953a 100644 --- a/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/listed.js +++ b/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/listed.js @@ -238,7 +238,7 @@ export const listBuilderReverse = (ls) => { * @param {T} newValue */ export const listBuilderSet = (ls, i, newValue) => { - if (0 <= i && i <= ls.length) { + if (0 <= i && i < ls.length) { ls[i] = newValue; } } diff --git a/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/string.js b/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/string.js index 91277c98..49b7aeea 100644 --- a/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/string.js +++ b/be-js/src/commonMain/resources/lang/temper/be/js/temper-core/string.js @@ -159,7 +159,7 @@ export const stringHasAtLeast = (s, begin, end, minCount) => { * @returns {number} */ export const stringNext = (s, i) => { - let iNext = Math.min(s.length, i); + let iNext = Math.min(s.length, Math.max(0, i)); let cp = s.codePointAt(i); if (cp !== undefined) { iNext += 1 + !!(cp >>> 16); @@ -173,7 +173,7 @@ export const stringNext = (s, i) => { * @returns {number} */ export const stringPrev = (s, i) => { - let iPrev = Math.min(s.length, i); + let iPrev = Math.min(s.length, Math.max(0, i)); if (iPrev) { iPrev -= 1; if (iPrev && s.codePointAt(iPrev - 1) >>> 16) { diff --git a/be-py/src/commonMain/resources/lang/temper/be/py/temper-core/temper_core/__init__.py b/be-py/src/commonMain/resources/lang/temper/be/py/temper-core/temper_core/__init__.py index 8f783880..b91313df 100644 --- a/be-py/src/commonMain/resources/lang/temper/be/py/temper-core/temper_core/__init__.py +++ b/be-py/src/commonMain/resources/lang/temper/be/py/temper-core/temper_core/__init__.py @@ -180,7 +180,7 @@ def float_gt_eq(left: float, right: float) -> bool: def float_gt(left: float, right: float) -> bool: - "Checks if left <= right, caring about sign of zeros." + "Checks if left > right, caring about sign of zeros." return float_cmp(left, right) > 0 @@ -216,7 +216,7 @@ def generic_lt_eq(left: C, right: C) -> bool: def generic_lt(left: C, right: C) -> bool: - "Checks if two left <=right, caring about the sign of zeros of floats." + "Checks if left < right, caring about the sign of zeros of floats." if isinstance(left, float) and isinstance(right, float): return float_lt(left, right) return left < right @@ -251,9 +251,12 @@ def arith_int_mod(dividend: int, divisor: int) -> int: arith_int_mod(5, 3) == 2 arith_int_mod(-5, -3) == -2 arith_int_mod(5, -3) == 2 - arith_int_mod(-5, -3) == -2 + arith_int_mod(-5, 3) == -2 """ - return dividend - divisor * int(dividend / divisor) + q = dividend // divisor + if (dividend ^ divisor) < 0 and q * divisor != dividend: + q += 1 + return dividend - divisor * q def isinstance_int(val: T) -> bool: @@ -387,7 +390,7 @@ def __init__(self, capacity: int): def __bool__(self) -> bool: "Test if any bit is set." - return bool(rb"\0" in self._bytearray) + return any(b != 0 for b in self._bytearray) def __bytes__(self) -> bytes: "Convert the bit vector into a read-only bytes value." @@ -496,7 +499,10 @@ def int64_div(a: int, b: int) -> int: # Mostly concerned with b == -1, but maybe evil `a` snuck in from outside? if a <= -0x8000_0000_0000_0000 and b < 0: return int64_clamp(int(a / b)) - return int(a / b) + r = a // b + if (a ^ b) < 0 and r * b != a: + r += 1 + return r def int64_mul(a: int, b: int) -> int: @@ -520,14 +526,14 @@ def int64_to_float64(value: int) -> float: raise OverflowError() -def int64_to_int32(value: int) -> float: +def int64_to_int32(value: int) -> int: "Implements connected method Int64::toInt32." if -0x8000_0000 <= value <= 0x7FFF_FFFF: return int(value) raise OverflowError() -def int64_to_int32_unsafe(value: int) -> float: +def int64_to_int32_unsafe(value: int) -> int: "Implements connected method Int64::toInt32Unsafe." return int_clamp(int(value)) @@ -549,7 +555,7 @@ def float64_near( y: float, rel_tol: Optional[float] = Unset, abs_tol: Optional[float] = Unset, -) -> float: +) -> bool: "Implements connected method Float64::near." # This exactly matches isclose behavior, but matching our forwarding our # optionals to python named args is awkward, so duplicate the logic. @@ -631,7 +637,7 @@ def float64_to_string(value: float) -> str: def boolean_to_string(value: bool) -> str: - "Turns a stirng into a boolean (lowercase like temper)." + "Turns a boolean into a string (lowercase like temper)." return "true" if value else "false" @@ -722,7 +728,7 @@ def require_string_index(i: int) -> int: def require_no_string_index(i: int) -> int: "Checked cast from i to NoStringIndex, a negative int" if i < 0: - return -1 + return i raise AssertionError(f"require_string_index; {i!r} not < 0 ") @@ -759,7 +765,7 @@ def string_to_float64(string: str) -> float: return result -def string_to_int32(string: str, radix: Optional[int] = None) -> float: +def string_to_int32(string: str, radix: Optional[int] = None) -> int: if radix == 0: # Other values we reject are checked already. raise ValueError() @@ -769,7 +775,7 @@ def string_to_int32(string: str, radix: Optional[int] = None) -> float: raise OverflowError() -def string_to_int64(string: str, radix: Optional[int] = None) -> float: +def string_to_int64(string: str, radix: Optional[int] = None) -> int: if radix == 0: # Other values we reject are checked already. raise ValueError() diff --git a/common/src/commonMain/kotlin/lang/temper/common/Memoized.kt b/common/src/commonMain/kotlin/lang/temper/common/Memoized.kt index 3e5fb790..d74bec6a 100644 --- a/common/src/commonMain/kotlin/lang/temper/common/Memoized.kt +++ b/common/src/commonMain/kotlin/lang/temper/common/Memoized.kt @@ -1,5 +1,7 @@ package lang.temper.common +import kotlin.jvm.Synchronized + /** * A thunk that calls its [function argument][f] at most once. * It may be called like a function, and the first time it is called, it delegates to [f], @@ -10,6 +12,7 @@ class Memoized( ) { private var cached: List? = null + @Synchronized operator fun invoke(): T = ( cached ?: run { diff --git a/compile/src/commonMain/kotlin/lang/temper/compile/fetch/GitCache.kt b/compile/src/commonMain/kotlin/lang/temper/compile/fetch/GitCache.kt index d87f9525..fc796738 100644 --- a/compile/src/commonMain/kotlin/lang/temper/compile/fetch/GitCache.kt +++ b/compile/src/commonMain/kotlin/lang/temper/compile/fetch/GitCache.kt @@ -116,7 +116,7 @@ internal fun makeWritable(root: Path) { var exception: Throwable? = null repeat(2) { runCatching { - Files.walk(root).forEach { it.toFile().setWritable(true, true) } + Files.walk(root).use { it.forEach { path -> path.toFile().setWritable(true, true) } } // It worked, so get out of here. return@makeWritable }.onFailure { exception = it } diff --git a/frontend/src/commonMain/kotlin/lang/temper/frontend/staging/ModuleAdvancer.kt b/frontend/src/commonMain/kotlin/lang/temper/frontend/staging/ModuleAdvancer.kt index 8bb5e221..5e4b057f 100644 --- a/frontend/src/commonMain/kotlin/lang/temper/frontend/staging/ModuleAdvancer.kt +++ b/frontend/src/commonMain/kotlin/lang/temper/frontend/staging/ModuleAdvancer.kt @@ -1081,6 +1081,7 @@ private fun checkForImportCycles( if (pendingByImporter.isEmpty()) { return null } for (importer in pendingByImporter.keys) { val loc = importer.loc + val visited = mutableSetOf() fun lookForCycle(possibleCycle: Cons.NotEmpty): Cons.NotEmpty? { val import = possibleCycle.head val exporter = import.exporter @@ -1088,6 +1089,9 @@ private fun checkForImportCycles( if (exporterLoc == loc) { return possibleCycle } + if (!visited.add(exporterLoc)) { + return null + } // For Modules, importers are also exporters val importsForExporter = pendingByImporter[ exporter as? Importer, diff --git a/lexer/src/commonMain/kotlin/lang/temper/lexer/Lexer.kt b/lexer/src/commonMain/kotlin/lang/temper/lexer/Lexer.kt index cc683e5e..fddc012c 100644 --- a/lexer/src/commonMain/kotlin/lang/temper/lexer/Lexer.kt +++ b/lexer/src/commonMain/kotlin/lang/temper/lexer/Lexer.kt @@ -1107,7 +1107,7 @@ class Lexer( // If a '<' is adjacent to a '<' or '=', treat it as part of a // larger shift or comparison operator, or close tag start marker. val before = text[end - 1] - val after = if (end + 1 < limit) { text[end - 1] } else { '\u0000' } + val after = if (end + 1 < limit) { text[end + 1] } else { '\u0000' } if ( !(before == '=' || before == '<' || after == '=' || after == '<') ) { diff --git a/result-helpers/src/commonMain/kotlin/lang/temper/result/junit/JUnitResultParser.kt b/result-helpers/src/commonMain/kotlin/lang/temper/result/junit/JUnitResultParser.kt index 8b3d390d..6a105e39 100644 --- a/result-helpers/src/commonMain/kotlin/lang/temper/result/junit/JUnitResultParser.kt +++ b/result-helpers/src/commonMain/kotlin/lang/temper/result/junit/JUnitResultParser.kt @@ -125,7 +125,7 @@ data class FailureInfo( ) { // Where luaunit uses type instead of message, so use that as a fallback, // and we do some conversion there but not full. - val cause: String get() = message ?: type!! + val cause: String get() = message ?: type ?: "unknown" } @Serializable diff --git a/tooling/src/commonMain/kotlin/lang/temper/tooling/buildrun/Runner.kt b/tooling/src/commonMain/kotlin/lang/temper/tooling/buildrun/Runner.kt index e8f3ff6d..73bdc08b 100644 --- a/tooling/src/commonMain/kotlin/lang/temper/tooling/buildrun/Runner.kt +++ b/tooling/src/commonMain/kotlin/lang/temper/tooling/buildrun/Runner.kt @@ -157,5 +157,6 @@ internal class SynchronizedPrintBuffer { @Synchronized fun append(str: String) { buffer.append(str) } + @Synchronized override fun toString(): String = "$buffer" } From 4b1b76dbff11e6ece40fee5a8a67aa89fa57b37d Mon Sep 17 00:00:00 2001 From: Shaw Summa Date: Tue, 10 Mar 2026 16:26:42 -0400 Subject: [PATCH 02/10] ai fixes to type stuff Signed-off-by: Shaw Summa --- .../kotlin/lang/temper/be/tmpl/TmpLHelpers.kt | 26 ++++++++++++++++++- .../InlineToRepairUnrealizedGoals.kt | 1 - .../lang/temper/frontend/typestage/Typer.kt | 2 +- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/be/src/commonMain/kotlin/lang/temper/be/tmpl/TmpLHelpers.kt b/be/src/commonMain/kotlin/lang/temper/be/tmpl/TmpLHelpers.kt index 7dc0a68b..4cb2ffbc 100644 --- a/be/src/commonMain/kotlin/lang/temper/be/tmpl/TmpLHelpers.kt +++ b/be/src/commonMain/kotlin/lang/temper/be/tmpl/TmpLHelpers.kt @@ -529,7 +529,31 @@ val TmpL.Type.withoutBubbleOrNull: TmpL.Type get() = this.withoutAtom { } fun TmpL.Type.withoutAtom(predicate: (TmpL.Type) -> Boolean): TmpL.Type = when (this) { - is TmpL.TypeIntersection -> this + is TmpL.TypeIntersection -> { + var hasDifferences = false + val typesWithout: List = buildList { + types.mapNotNullTo(this@buildList) { + val t = it.withoutAtom(predicate) + if (t is TmpL.NeverType) { + hasDifferences = true + null + } else { + if (t !== it) { hasDifferences = true } + t + } + } + } + + if (hasDifferences) { + when (typesWithout.size) { + 0 -> TmpL.NeverType(pos) + 1 -> typesWithout.first().deepCopy() + else -> TmpL.TypeIntersection(pos, typesWithout.map { it.deepCopy() }) + } + } else { + this + } + } is TmpL.TypeUnion -> { var hasDifferences = false val typesWithout: List = buildList { diff --git a/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/InlineToRepairUnrealizedGoals.kt b/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/InlineToRepairUnrealizedGoals.kt index 7b189cc1..bddb2fda 100644 --- a/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/InlineToRepairUnrealizedGoals.kt +++ b/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/InlineToRepairUnrealizedGoals.kt @@ -310,7 +310,6 @@ private class InlineToRepairUnrealizedGoals( val calleeTree = callToInline.definition val formalNameToArg = callToInline.formalNameToFunArg val thisBindings = callToInline.thisTypeBindings - callToInline.thisTypeBindings val combinedCallArgs = callToInline.combinedCallArgs val callEdge = call.incoming!! diff --git a/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/Typer.kt b/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/Typer.kt index d5300b09..4bfe600c 100644 --- a/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/Typer.kt +++ b/frontend/src/commonMain/kotlin/lang/temper/frontend/typestage/Typer.kt @@ -3234,7 +3234,7 @@ private fun mergeTypeArgs(from: StaticType?, into: StaticType): StaticType { type.definition === WellKnownTypes.nullTypeDefinition -> null else -> type } - is OrType -> type.members.firstNotNullOf { findNominal(it) } + is OrType -> type.members.firstNotNullOfOrNull { findNominal(it) } else -> null } } From d71c325e3debd4015aff681f925119546334fca1 Mon Sep 17 00:00:00 2001 From: Shaw Summa Date: Tue, 10 Mar 2026 16:45:04 -0400 Subject: [PATCH 03/10] ai fixes for rust Signed-off-by: Shaw Summa --- .../lang/temper/be/rust/RustOperator.kt | 2 + .../lang/temper/be/rust/RustTranslator.kt | 48 ++++++++++++++----- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustOperator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustOperator.kt index dd26412b..6ea6a3ad 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustOperator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustOperator.kt @@ -7,6 +7,8 @@ enum class RustOperator( val operatorDefinition: RustOperatorDefinition, ) { Assign("=", RustOperatorDefinition.Assignment), + LogicalAnd("&&", RustOperatorDefinition.LogicalAnd), + LogicalOr("||", RustOperatorDefinition.LogicalOr), And("&", RustOperatorDefinition.And), Or("|", RustOperatorDefinition.InclusiveOr), As("as", RustOperatorDefinition.As), diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index 6e6d975b..c96ff7fe 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -1985,7 +1985,7 @@ class RustTranslator( is TmpL.GetProperty -> translateGetProperty(expression, avoidClone = avoidClone) is TmpL.InstanceOfExpression -> translateInstanceOfExpression(expression) is TmpL.InfixOperation -> translateInfixOperation(expression) - is TmpL.PrefixOperation -> TODO() + is TmpL.PrefixOperation -> translatePrefixOperation(expression) is TmpL.Reference -> translateReference(expression, avoidClone = avoidClone) is TmpL.RestParameterCountExpression -> TODO() is TmpL.RestParameterExpression -> TODO() @@ -2339,8 +2339,8 @@ class RustTranslator( return Rust.Operator( op.pos, operator = when (op.tmpLOperator) { - TmpLOperator.AmpAmp -> TODO() // RustOperator.LogicalAnd - TmpLOperator.BarBar -> TODO() // RustOperator.LogicalOr + TmpLOperator.AmpAmp -> RustOperator.LogicalAnd + TmpLOperator.BarBar -> RustOperator.LogicalOr TmpLOperator.EqEqInt -> RustOperator.Equals TmpLOperator.GeInt -> RustOperator.GreaterEquals TmpLOperator.GtInt -> RustOperator.GreaterThan @@ -2351,6 +2351,18 @@ class RustTranslator( ) } + private fun translatePrefixOperation(expr: TmpL.PrefixOperation): Rust.Expr { + val operator = when (expr.op.tmpLOperator) { + TmpLOperator.Bang -> RustOperator.BoolComplement + } + return Rust.Operation( + expr.pos, + left = null, + operator = Rust.Operator(expr.op.pos, operator), + right = translateExpression(expr.operand), + ) + } + private fun translateInstanceOfExpression(expression: TmpL.InstanceOfExpression): Rust.Expr { // TODO Bubbly found. Maybe some map expression? We don't ever expect bubbly wanted. val found = expression.expr.type.described() @@ -2919,27 +2931,33 @@ class RustTranslator( try { return when (statement) { is TmpL.Assignment -> return translateAssignment(statement) - is TmpL.BoilerplateCodeFoldEnd -> TODO() - is TmpL.BoilerplateCodeFoldStart -> TODO() + is TmpL.BoilerplateCodeFoldEnd -> return listOf() + is TmpL.BoilerplateCodeFoldStart -> return listOf() is TmpL.BreakStatement -> translateBreakStatement(statement) is TmpL.ContinueStatement -> translateContinueStatement(statement) - is TmpL.EmbeddedComment -> TODO() + is TmpL.EmbeddedComment -> return listOf() is TmpL.ExpressionStatement -> translateExpressionStatement(statement) - is TmpL.GarbageStatement -> TODO() + is TmpL.GarbageStatement -> Rust.ExprStatement(statement.pos, translateGarbage(statement)) is TmpL.HandlerScope -> error("handled elsewhere") is TmpL.LocalDeclaration -> return translateModuleOrLocalDeclaration(statement) - is TmpL.LocalFunctionDeclaration -> TODO() // handled elsewhere + is TmpL.LocalFunctionDeclaration -> error("handled elsewhere") is TmpL.ModuleInitFailed -> translateModuleInitFailed(statement) is TmpL.BlockStatement -> translateBlock(statement) is TmpL.ComputedJumpStatement -> translateComputedJumpStatement(statement) is TmpL.IfStatement -> return translateIfStatement(statement) is TmpL.LabeledStatement -> return translateLabeledStatement(statement) - is TmpL.TryStatement -> TODO() + is TmpL.TryStatement -> return translateTryStatement(statement) is TmpL.WhileStatement -> translateWhileStatement(statement) is TmpL.ReturnStatement -> return translateReturnStatement(statement) is TmpL.SetProperty -> translateSetProperty(statement) - is TmpL.ThrowStatement -> TODO() - is TmpL.YieldStatement -> TODO() + is TmpL.ThrowStatement -> Rust.ExprStatement( + statement.pos, + "panic!".toId(statement.pos).call(listOf(Rust.StringLiteral(statement.pos, "bubble"))), + ) + is TmpL.YieldStatement -> Rust.ExprStatement( + statement.pos, + "yield".toKeyId(statement.pos).call(listOf("()".toId(statement.pos))), + ) }.let { listOf(it) } } catch (_: NeverRefException) { // No statements referencing things that are typed never. @@ -3345,6 +3363,14 @@ class RustTranslator( } } + private fun translateTryStatement(statement: TmpL.TryStatement): List { + // TryStatement is generated by CatchBubble strategy, which Rust doesn't use + // (Rust uses IfHandlerScopeVar). Handle gracefully by inlining both blocks. + val tried = translateStatement(statement.tried) + val recover = translateStatement(statement.recover) + return tried + recover + } + private fun translateWhileStatement(loop: TmpL.WhileStatement): Rust.Statement { val test = loop.test // Always label in case we need to add labels to breaks or continues inside. From 00660a462591913190a0003610d47a4ef9359323 Mon Sep 17 00:00:00 2001 From: Shaw Summa Date: Tue, 10 Mar 2026 18:21:30 -0400 Subject: [PATCH 04/10] ai fixes for rust Signed-off-by: Shaw Summa --- .../lang/temper/be/rust/RustSupportNetwork.kt | 57 +++++++++++++++++-- .../lang/temper/be/rust/RustTranslator.kt | 48 ++++++++-------- 2 files changed, 77 insertions(+), 28 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt index 9d6d29e4..4bce933e 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt @@ -146,13 +146,13 @@ private fun supportCodeByOperatorId(builtinOperatorId: BuiltinOperatorId?): Supp BuiltinOperatorId.NeIntInt -> neIntInt BuiltinOperatorId.NeStrStr -> neStrStr BuiltinOperatorId.NeGeneric -> neGeneric - BuiltinOperatorId.CmpFltFlt -> TODO() - BuiltinOperatorId.CmpIntInt -> TODO() - BuiltinOperatorId.CmpStrStr -> TODO() + BuiltinOperatorId.CmpFltFlt -> cmpFltFlt + BuiltinOperatorId.CmpIntInt -> CmpIntInt + BuiltinOperatorId.CmpStrStr -> CmpStrStrOrdering BuiltinOperatorId.CmpGeneric -> CmpGeneric - BuiltinOperatorId.Bubble -> TODO() // bubble + BuiltinOperatorId.Bubble -> bubble BuiltinOperatorId.Panic -> panic - BuiltinOperatorId.Print -> TODO() + BuiltinOperatorId.Print -> print BuiltinOperatorId.StrCat -> StrCat BuiltinOperatorId.Listify -> Listify BuiltinOperatorId.Async -> async @@ -583,6 +583,53 @@ private object CmpGeneric : MethodCall( } } +private val cmpFltFlt = FunctionCall("CmpFltFlt", "temper_core::float64::cmp", BuiltinOperatorId.CmpFltFlt) + +private object CmpIntInt : RustInlineSupportCode("CmpIntInt", BuiltinOperatorId.CmpIntInt, cloneEvenIfFirst = true) { + override fun inlineToTree( + pos: Position, + arguments: List>, + returnType: Type2, + translator: RustTranslator, + ): Rust.Expr { + // (a).cmp(&b) as i32 + val left = arguments[0].expr as Rust.Expr + val right = (arguments[1].expr as Rust.Expr).ref() + return left.methodCall("cmp", listOf(right)).infix(RustOperator.As, "i32".toId(pos)) + } +} + +private object CmpStrStrOrdering : + RustInlineSupportCode("CmpStrStr", BuiltinOperatorId.CmpStrStr, cloneEvenIfFirst = true) { + override fun inlineToTree( + pos: Position, + arguments: List>, + returnType: Type2, + translator: RustTranslator, + ): Rust.Expr { + val left = (arguments[0].expr as Rust.Expr).methodCall("as_str") + val right = (arguments[1].expr as Rust.Expr).methodCall("as_str") + return left.methodCall("cmp", listOf(right.ref())).infix(RustOperator.As, "i32".toId(pos)) + } +} + +private val bubble = FunctionCall("Bubble", "panic!", BuiltinOperatorId.Bubble) + +private object PrintCode : RustInlineSupportCode("Print", BuiltinOperatorId.Print) { + override fun inlineToTree( + pos: Position, + arguments: List>, + returnType: Type2, + translator: RustTranslator, + ): Rust.Expr { + // Print takes a single string argument and outputs it. + val arg = arguments[0].expr as Rust.Expr + return "println!".toId(pos).call(listOf(Rust.StringLiteral(pos, "{}"), arg)) + } +} + +private val print: SupportCode = PrintCode + private val dateToday = FunctionCall("Date::today", "temper_std::temporal::today") private val denseBitVectorConstructor = FunctionCall("DenseBitVector::constructor", "temper_core::DenseBitVector::with_capacity") diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index c96ff7fe..8ea36bfc 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -2600,8 +2600,8 @@ class RustTranslator( return when (member) { is TmpL.Getter -> translateGetterId(member) is TmpL.Setter -> translateSetterId(member) - is TmpL.NormalMethod -> translateNormalishMethodId(member) - else -> TODO() + is TmpL.Method -> translateNormalishMethodId(member) + else -> error("unexpected member type for translateMethodId: $member") } } @@ -2615,7 +2615,7 @@ class RustTranslator( typePub: Rust.VisibilityPub? = null, ): List { return when (member) { - is TmpL.GarbageStatement -> TODO() + is TmpL.GarbageStatement -> return listOf() // So far only bother with returnType forwarding for instance methods. Will we need more later? is TmpL.Getter -> translateGetter(member, block = block, forTrait = forTrait, returnType = returnType) is TmpL.Setter -> translateSetter(member, block = block, forTrait = forTrait) // don't expect return type @@ -2904,7 +2904,10 @@ class RustTranslator( return when (statement.left.property) { is TmpL.ExternalPropertyId -> { val ref = statement.left - val subject = translateExpression((ref.subject as? TmpL.Expression) ?: TODO(), avoidClone = true) + val subject = when (val subj = ref.subject) { + is TmpL.Expression -> translateExpression(subj, avoidClone = true) + is TmpL.TypeName -> translateTypeName(subj) + } val setter = "set_${translatePropertyId(ref.property)}" subject.methodCall(setter, listOf(value)) } @@ -2946,18 +2949,18 @@ class RustTranslator( is TmpL.ComputedJumpStatement -> translateComputedJumpStatement(statement) is TmpL.IfStatement -> return translateIfStatement(statement) is TmpL.LabeledStatement -> return translateLabeledStatement(statement) - is TmpL.TryStatement -> return translateTryStatement(statement) + // TryStatement and ThrowStatement are only generated by CatchBubble strategy; + // Rust uses IfHandlerScopeVar, so these should never appear. + is TmpL.TryStatement -> error("unexpected TryStatement: Rust uses IfHandlerScopeVar, not CatchBubble") is TmpL.WhileStatement -> translateWhileStatement(statement) is TmpL.ReturnStatement -> return translateReturnStatement(statement) is TmpL.SetProperty -> translateSetProperty(statement) - is TmpL.ThrowStatement -> Rust.ExprStatement( - statement.pos, - "panic!".toId(statement.pos).call(listOf(Rust.StringLiteral(statement.pos, "bubble"))), - ) - is TmpL.YieldStatement -> Rust.ExprStatement( - statement.pos, - "yield".toKeyId(statement.pos).call(listOf("()".toId(statement.pos))), - ) + is TmpL.ThrowStatement -> + error("unexpected ThrowStatement: Rust uses IfHandlerScopeVar, not CatchBubble") + // YieldStatement is only generated by TranslateToGenerator strategy; + // Rust uses TranslateToRegularFunction, which deletes yields during conversion. + is TmpL.YieldStatement -> + error("unexpected YieldStatement: Rust uses TranslateToRegularFunction, not TranslateToGenerator") }.let { listOf(it) } } catch (_: NeverRefException) { // No statements referencing things that are typed never. @@ -3065,11 +3068,18 @@ class RustTranslator( // Otherwise handle non-connected types. return when (type) { is TmpL.FunctionType -> translateFunctionType(type) - is TmpL.TypeIntersection -> TODO() + is TmpL.TypeIntersection -> when { + type.types.isEmpty() -> ANY_NAME.toId(pos) + type.types.size == 1 -> translateType(type.types.first(), inExpr = inExpr, isFlex = isFlex) + else -> Rust.ImplTraitType( + pos, + bounds = type.types.map { translateType(it, inExpr = inExpr, isFlex = isFlex) as Rust.Path }, + ) + } is TmpL.TypeUnion -> translateTypeUnion(type, isFlex = isFlex) is TmpL.GarbageType -> "()".toId(pos) is TmpL.NominalType -> translateTypeNominal(type, inExpr = inExpr, isFlex = isFlex) - is TmpL.BubbleType -> TODO() + is TmpL.BubbleType -> "()".toId(pos) is TmpL.NeverType -> "!".toId(pos) // except not actually supported in stable rust is TmpL.TopType -> ANY_NAME.toId(pos) } @@ -3363,14 +3373,6 @@ class RustTranslator( } } - private fun translateTryStatement(statement: TmpL.TryStatement): List { - // TryStatement is generated by CatchBubble strategy, which Rust doesn't use - // (Rust uses IfHandlerScopeVar). Handle gracefully by inlining both blocks. - val tried = translateStatement(statement.tried) - val recover = translateStatement(statement.recover) - return tried + recover - } - private fun translateWhileStatement(loop: TmpL.WhileStatement): Rust.Statement { val test = loop.test // Always label in case we need to add labels to breaks or continues inside. From 8d4270154d7a91b771ffa8bdb9e77c1f5b8fedd9 Mon Sep 17 00:00:00 2001 From: Shaw Summa Date: Tue, 10 Mar 2026 19:10:09 -0400 Subject: [PATCH 05/10] ai fixes for lua Signed-off-by: Shaw Summa --- .../lang/temper/be/lua/LuaLocalRemover.kt | 46 ++++++---- .../lang/temper/be/lua/LuaSupportNetwork.kt | 83 ++++++++++++++++--- .../lang/temper/be/lua/temper-core/init.lua | 66 +++++++++++++-- 3 files changed, 162 insertions(+), 33 deletions(-) diff --git a/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaLocalRemover.kt b/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaLocalRemover.kt index 4b49bb16..1d6897e1 100644 --- a/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaLocalRemover.kt +++ b/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaLocalRemover.kt @@ -3,11 +3,16 @@ package lang.temper.be.lua // Could go up to 200 in theory, but smaller numbers allow for larger closures. const val MAX_ALLOWABLE_LOCALS = 128 +// When wrapping is needed, keep first N locals as real locals for performance. +// The rest overflow into the env table. Budget: LOCALS_TO_KEEP + 1 (env table) < MAX_ALLOWABLE_LOCALS. +private const val LOCALS_TO_KEEP = 100 + class LuaLocalRemover { private var locals = mutableMapOf() private var luaName = LuaName("_G") private var numLocalTables = 0 - private var shouldRewrite = false + private var needsEnvTable = false + private var localsKept = 0 private var localsToDecl = mutableListOf() private fun allocLocalTableName(): LuaName = LuaName("env_t${++numLocalTables}") @@ -15,13 +20,15 @@ class LuaLocalRemover { private fun scoped(cb: () -> Lua.Chunk): Lua.Chunk { val lastLocals = locals.toMutableMap() val lastLuaName = luaName - val lastShouldRewrite = shouldRewrite + val lastNeedsEnvTable = needsEnvTable + val lastLocalsKept = localsKept val lastLocalsToDecl = localsToDecl localsToDecl = mutableListOf() val ret = cb() locals = lastLocals luaName = lastLuaName - shouldRewrite = lastShouldRewrite + needsEnvTable = lastNeedsEnvTable + localsKept = lastLocalsKept val localsToWrap = localsToDecl localsToDecl = lastLocalsToDecl return luaChunk( @@ -50,12 +57,12 @@ class LuaLocalRemover { ) } - private fun define(target: Lua.SetTarget) { + private fun define(target: Lua.SetTarget, asEnvField: Boolean) { when (target) { is Lua.DotSetTarget -> TODO() is Lua.IndexSetTarget -> TODO() is Lua.NameSetTarget -> { - if (shouldRewrite) { + if (asEnvField) { locals[target.target.id] = luaName } else { locals.remove(target.target.id) @@ -64,6 +71,10 @@ class LuaLocalRemover { } } + // Decide whether a batch of N new locals should overflow to the env table. + private fun shouldOverflow(count: Int): Boolean = + needsEnvTable && localsKept + count > LOCALS_TO_KEEP + private fun set(target: Lua.SetTarget): Lua.SetTarget = when (target) { is Lua.DotSetTarget -> when (val obj = target.obj) { is Lua.SetTarget -> Lua.DotSetTarget( @@ -230,8 +241,9 @@ class LuaLocalRemover { private fun wrapLocals(chunk: Lua.Chunk): List { luaName = allocLocalTableName() val numLocals = countLocalsDirectlyIn(chunk) - shouldRewrite = numLocals >= MAX_ALLOWABLE_LOCALS - if (shouldRewrite) { + needsEnvTable = numLocals >= MAX_ALLOWABLE_LOCALS + if (needsEnvTable) { + localsKept = 0 return listOf( Lua.LocalStmt( chunk.pos, @@ -355,17 +367,17 @@ class LuaLocalRemover { Lua.Name(stmt.name.pos, stmt.name.id), ) is Lua.LocalDeclStmt -> { - stmt.targets.targets.forEach(::define) - if (shouldRewrite) { - null - } else { + val overflow = shouldOverflow(stmt.targets.targets.size) + stmt.targets.targets.forEach { define(it, asEnvField = overflow) } + if (!overflow) { + if (needsEnvTable) localsKept += stmt.targets.targets.size stmt.targets.targets.forEach { localsToDecl.add((it as Lua.NameSetTarget).target.id) } - null } + null } - is Lua.LocalFunctionStmt -> if (shouldRewrite) { + is Lua.LocalFunctionStmt -> if (shouldOverflow(1)) { locals[stmt.name.id] = luaName Lua.SetStmt( stmt.pos, @@ -405,6 +417,7 @@ class LuaLocalRemover { ), ) } else { + if (needsEnvTable) localsKept++ localsToDecl.add(stmt.name.id) Lua.SetStmt( stmt.pos, @@ -440,7 +453,7 @@ class LuaLocalRemover { ), ) } - is Lua.LocalStmt -> if (shouldRewrite) { + is Lua.LocalStmt -> if (shouldOverflow(stmt.targets.targets.size)) { val ret = Lua.SetStmt( stmt.pos, Lua.SetTargets( @@ -461,9 +474,10 @@ class LuaLocalRemover { stmt.exprs.exprs.map(::scan), ), ) - stmt.targets.targets.forEach(::define) + stmt.targets.targets.forEach { define(it, asEnvField = true) } ret } else { + if (needsEnvTable) localsKept += stmt.targets.targets.size val ret = Lua.SetStmt( stmt.pos, Lua.SetTargets( @@ -479,7 +493,7 @@ class LuaLocalRemover { stmt.exprs.exprs.map(::scan), ), ) - stmt.targets.targets.forEach(::define) + stmt.targets.targets.forEach { define(it, asEnvField = false) } ret } is Lua.SetStmt -> { diff --git a/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaSupportNetwork.kt b/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaSupportNetwork.kt index c7737f63..27d889d7 100644 --- a/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaSupportNetwork.kt +++ b/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaSupportNetwork.kt @@ -35,7 +35,7 @@ internal fun operatorToName( BuiltinOperatorId.BitwiseAnd -> "band" BuiltinOperatorId.BitwiseOr -> "bor" BuiltinOperatorId.IsNull -> "is_null" - BuiltinOperatorId.NotNull -> TODO() + BuiltinOperatorId.NotNull -> "not_null" BuiltinOperatorId.DivFltFlt -> "fdiv" BuiltinOperatorId.DivIntInt -> "int32_div" BuiltinOperatorId.DivIntInt64 -> "int64_div" @@ -51,36 +51,36 @@ internal fun operatorToName( BuiltinOperatorId.MinusInt -> "int32_unm" BuiltinOperatorId.MinusInt64 -> "int64_unm" // TODO Just use `-x` because either standard int64 or metatabled? BuiltinOperatorId.MinusIntInt -> "int32_sub" - BuiltinOperatorId.MinusIntInt64 -> TODO() + BuiltinOperatorId.MinusIntInt64 -> "int64_sub" BuiltinOperatorId.PlusFltFlt -> "add" BuiltinOperatorId.PlusIntInt -> "int32_add" - BuiltinOperatorId.PlusIntInt64 -> TODO() + BuiltinOperatorId.PlusIntInt64 -> "int64_add" BuiltinOperatorId.PowFltFlt -> "pow" BuiltinOperatorId.TimesIntInt -> "int32_mul" - BuiltinOperatorId.TimesIntInt64 -> TODO() + BuiltinOperatorId.TimesIntInt64 -> "int64_mul" BuiltinOperatorId.TimesFltFlt -> "mul" BuiltinOperatorId.LtFltFlt -> "float_lt" - BuiltinOperatorId.LtIntInt -> TODO() + BuiltinOperatorId.LtIntInt -> "generic_lt" BuiltinOperatorId.LtStrStr -> "str_lt" BuiltinOperatorId.LtGeneric -> "generic_lt" BuiltinOperatorId.LeFltFlt -> "float_le" - BuiltinOperatorId.LeIntInt -> TODO() + BuiltinOperatorId.LeIntInt -> "generic_le" BuiltinOperatorId.LeStrStr -> "str_le" BuiltinOperatorId.LeGeneric -> "generic_le" BuiltinOperatorId.GtFltFlt -> "float_gt" - BuiltinOperatorId.GtIntInt -> TODO() + BuiltinOperatorId.GtIntInt -> "generic_gt" BuiltinOperatorId.GtStrStr -> "str_gt" BuiltinOperatorId.GtGeneric -> "generic_gt" BuiltinOperatorId.GeFltFlt -> "float_ge" - BuiltinOperatorId.GeIntInt -> TODO() + BuiltinOperatorId.GeIntInt -> "generic_ge" BuiltinOperatorId.GeStrStr -> "str_ge" BuiltinOperatorId.GeGeneric -> "generic_ge" BuiltinOperatorId.EqFltFlt -> "float_eq" - BuiltinOperatorId.EqIntInt -> TODO() + BuiltinOperatorId.EqIntInt -> "generic_eq" BuiltinOperatorId.EqStrStr -> "str_eq" BuiltinOperatorId.EqGeneric -> "generic_eq" BuiltinOperatorId.NeFltFlt -> "float_ne" - BuiltinOperatorId.NeIntInt -> TODO() + BuiltinOperatorId.NeIntInt -> "generic_ne" BuiltinOperatorId.NeStrStr -> "str_ne" BuiltinOperatorId.NeGeneric -> "generic_ne" BuiltinOperatorId.CmpFltFlt -> "float_cmp" @@ -91,7 +91,7 @@ internal fun operatorToName( BuiltinOperatorId.Print -> "print" BuiltinOperatorId.StrCat -> "concat" BuiltinOperatorId.Listify -> "listof" - BuiltinOperatorId.Async -> "TODO" // TODO + BuiltinOperatorId.Async -> "async" // should not be used with CoroutineStrategy.TranslateToGenerator BuiltinOperatorId.AdaptGeneratorFn, BuiltinOperatorId.SafeAdaptGeneratorFn, @@ -289,6 +289,67 @@ internal object LuaSupportNetwork : SupportNetwork { args[1], ) } + // Inline float arithmetic as native Lua operators (Lua numbers are doubles). + BuiltinOperatorId.PlusFltFlt -> InlineLua( + builtin.builtinOperatorId.toString(), + builtin.builtinOperatorId, + ) { pos, args -> + Lua.BinaryExpr( + pos, args[0], + Lua.BinaryOp(pos, BinaryOpEnum.Add, LuaOperatorDefinition.Add), + args[1], + ) + } + BuiltinOperatorId.MinusFltFlt -> InlineLua( + builtin.builtinOperatorId.toString(), + builtin.builtinOperatorId, + ) { pos, args -> + Lua.BinaryExpr( + pos, args[0], + Lua.BinaryOp(pos, BinaryOpEnum.Sub, LuaOperatorDefinition.Sub), + args[1], + ) + } + BuiltinOperatorId.TimesFltFlt -> InlineLua( + builtin.builtinOperatorId.toString(), + builtin.builtinOperatorId, + ) { pos, args -> + Lua.BinaryExpr( + pos, args[0], + Lua.BinaryOp(pos, BinaryOpEnum.Mul, LuaOperatorDefinition.Mul), + args[1], + ) + } + BuiltinOperatorId.DivFltFlt -> InlineLua( + builtin.builtinOperatorId.toString(), + builtin.builtinOperatorId, + ) { pos, args -> + Lua.BinaryExpr( + pos, args[0], + Lua.BinaryOp(pos, BinaryOpEnum.Div, LuaOperatorDefinition.Div), + args[1], + ) + } + BuiltinOperatorId.PowFltFlt -> InlineLua( + builtin.builtinOperatorId.toString(), + builtin.builtinOperatorId, + ) { pos, args -> + Lua.BinaryExpr( + pos, args[0], + Lua.BinaryOp(pos, BinaryOpEnum.Pow, LuaOperatorDefinition.Pow), + args[1], + ) + } + BuiltinOperatorId.MinusFlt -> InlineLua( + builtin.builtinOperatorId.toString(), + builtin.builtinOperatorId, + ) { pos, args -> + Lua.UnaryExpr( + pos, + Lua.UnaryOp(pos.leftEdge, UnaryOpEnum.UnaryAdd, LuaOperatorDefinition.Unm), + args[0], + ) + } else -> InlineLua(builtin.builtinOperatorId.toString(), builtin.builtinOperatorId) { pos, args -> Lua.FunctionCallExpr( pos, diff --git a/be-lua/src/commonMain/resources/lang/temper/be/lua/temper-core/init.lua b/be-lua/src/commonMain/resources/lang/temper/be/lua/temper-core/init.lua index 98d348f9..54252229 100644 --- a/be-lua/src/commonMain/resources/lang/temper/be/lua/temper-core/init.lua +++ b/be-lua/src/commonMain/resources/lang/temper/be/lua/temper-core/init.lua @@ -93,7 +93,7 @@ function temper.codepoint_fallback(s, i) b1 = string.byte(s, i + 1) -- TODO: these checks should really be (b1 & 192) == 128 if b1 == nil or b1 >= 192 then return 0xFFFD; end - return (b0 - 192)*64 + b1 + return (b0 - 192)*64 + b1%64 end if b0 < 240 then @@ -1077,6 +1077,36 @@ function temper.generic_ge(a, b) return a >= b end +function temper.float_cmp(a, b) + if temper.float_lt(a, b) then + return -1 + elseif temper.float_gt(a, b) then + return 1 + else + return 0 + end +end + +function temper.int_cmp(a, b) + if a < b then + return -1 + elseif a > b then + return 1 + else + return 0 + end +end + +function temper.str_cmp(a, b) + if a < b then + return -1 + elseif a > b then + return 1 + else + return 0 + end +end + function temper.str_eq(a, b) return a == b end @@ -1470,7 +1500,7 @@ function temper.string_hasindex(str, i) end function temper.string_indexof(str, target, i) - return string.find(str, target, i, true) or 0 + return string.find(str, target, i, true) or -1 end function temper.string_next(str, i) @@ -1556,8 +1586,12 @@ function temper.require_string_index(i) end function temper.string_foreach(str, f) - for _, c in utf8.codes(str) do - f(c) + local i = 1 + local len = string_len(str) + while i <= len do + local cp = temper.codepoint_fallback(str, i) + f(cp) + i = i + utf8len(str, i) end end @@ -1635,7 +1669,14 @@ function temper.mapbuilder_remove(builder, key) if got == nil then temper.bubble("MapBuilder::remove key not found: " .. tostring(key)) end - builder[key] = nil + rawset(builder, key, nil) + local key_order_list = rawget(builder, map_key_order) + for i = #key_order_list, 1, -1 do + if key_order_list[i] == key then + table_remove(key_order_list, i) + break + end + end return got end @@ -1748,7 +1789,7 @@ do local sign if string_byte(str, 1) == 45 then sign = "-" - str = string_sub(pad, 2, string_len(str)) + str = string_sub(str, 2) else sign = "" end @@ -1985,4 +2026,17 @@ do end end +function temper.listed_mapdropping(list, f) + local ret = {} + local head = 1 + for i = 1, #list do + local ok, val = pcall(f, list[i]) + if ok then + ret[head] = val + head = head + 1 + end + end + return ret +end + return temper From 8eaf9fdd5c34e736e32e5cdb9338fd6d3ac97f0e Mon Sep 17 00:00:00 2001 From: Shaw Summa Date: Tue, 10 Mar 2026 19:10:45 -0400 Subject: [PATCH 06/10] ai fixes to rust Signed-off-by: Shaw Summa --- .../kotlin/lang/temper/be/rust/RustExt.kt | 2 +- .../lang/temper/be/rust/RustSupportNetwork.kt | 48 +++++++++++++++++++ .../temper/be/rust/temper-core/src/listed.rs | 16 +++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustExt.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustExt.kt index a75e8540..09aa379f 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustExt.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustExt.kt @@ -381,7 +381,7 @@ internal fun Rust.GenericParam.toArg(): Rust.Id { return when (this) { is Rust.Id -> this is Rust.TypeParam -> id - is Rust.PathSegments -> TODO() // needed? + is Rust.PathSegments -> segments.last() as Rust.Id }.deepCopy() } diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt index 4bce933e..329de67a 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt @@ -683,12 +683,29 @@ private class EqNeGeneric( private val eqGeneric = EqNeGeneric("EqGeneric", BuiltinOperatorId.EqGeneric, RustOperator.Equals) private val eqIntInt = Infix("EqIntInt", BuiltinOperatorId.EqIntInt, RustOperator.Equals) private val eqStrStr = CmpStrStr("EqStrStr", BuiltinOperatorId.EqStrStr, RustOperator.Equals) +private val float64Abs = MethodCall("Float64::abs", "abs") +private val float64Acos = MethodCall("Float64::acos", "acos") +private val float64Asin = MethodCall("Float64::asin", "asin") +private val float64Atan = MethodCall("Float64::atan", "atan") +private val float64Atan2 = MethodCall("Float64::atan2", "atan2") +private val float64Ceil = MethodCall("Float64::ceil", "ceil") +private val float64Cos = MethodCall("Float64::cos", "cos") +private val float64Cosh = MethodCall("Float64::cosh", "cosh") +private val float64Exp = MethodCall("Float64::exp", "exp") private val float64Expm1 = MethodCall("Float64::expm1", "exp_m1") +private val float64Floor = MethodCall("Float64::floor", "floor") private val float64Log = MethodCall("Float64::log", "ln") +private val float64Log10 = MethodCall("Float64::log10", "log10") private val float64Log1p = MethodCall("Float64::log1p", "ln_1p") private val float64Max = FunctionCall("Float64::max", "temper_core::float64::max") private val float64Min = FunctionCall("Float64::min", "temper_core::float64::min") private val float64Near = FunctionCall("Float64::near", "temper_core::float64::near") +private val float64Round = MethodCall("Float64::round", "round") +private val float64Sin = MethodCall("Float64::sin", "sin") +private val float64Sinh = MethodCall("Float64::sinh", "sinh") +private val float64Sqrt = MethodCall("Float64::sqrt", "sqrt") +private val float64Tan = MethodCall("Float64::tan", "tan") +private val float64Tanh = MethodCall("Float64::tanh", "tanh") private object Float64E : Constant("Float64::e") { override fun value(pos: Position) = makePath(pos, "std", "f64", "consts", "E") @@ -737,6 +754,10 @@ private object Int64ToInt32Unsafe : Cast("Int64::toInt32Unsafe") { override fun buildType(pos: Position) = "i32".toId(pos) } +private val int32Max = MethodCall("Int32::max", "max") +private val int32Min = MethodCall("Int32::min", "min") +private val int64Max = MethodCall("Int64::max", "max") +private val int64Min = MethodCall("Int64::min", "min") internal val intToString = FunctionCall("Int32::toString", "temper_core::int_to_string") private val int64ToFloat64 = FunctionCall("Int64::toFloat64", "temper_core::int64_to_float64") private val int64ToInt32 = FunctionCall("Int64::toInt32", "temper_core::int64_to_int32") @@ -771,6 +792,8 @@ private val listedIsEmpty = private val listedJoin = FunctionCall("Listed::join", "temper_core::listed::join", hasGeneric = true, fnIndex = -1) private val listedLength = FunctionCall(listedTypes.map { "$it::length" }, "$LISTED_TRAIT_NAME::len", hasGeneric = true) private val listedMap = FunctionCall("Listed::map", "temper_core::listed::map", hasGeneric = true, fnIndex = -1) +private val listedMapDropping = + FunctionCall("Listed::mapDropping", "temper_core::listed::map_dropping", hasGeneric = true, fnIndex = -1) private val listedReduce = FunctionCall("Listed::reduce", "temper_core::listed::reduce", hasGeneric = true, fnIndex = -1) private val listedReduceFrom = @@ -973,6 +996,8 @@ private val stringBuilderAppendCodePoint = private val stringBuilderToString = FunctionCall("StringBuilder::toString", "temper_core::string::builder::to_string") +private val stringIsEmpty = MethodCall("String::isEmpty", "is_empty") + private object StringIndexNone : Constant("StringIndex::none") { override fun value(pos: Position) = "()".toId(pos) } @@ -1009,13 +1034,30 @@ private val connectedReferences = listOf( dequeRemoveFirst, DoneResult, Empty, + float64Abs, + float64Acos, + float64Asin, + float64Atan, + float64Atan2, + float64Ceil, + float64Cos, + float64Cosh, + float64Exp, float64Expm1, + float64Floor, float64Log, + float64Log10, float64Log1p, float64Min, float64Max, float64Near, + float64Round, float64Sign, + float64Sin, + float64Sinh, + float64Sqrt, + float64Tan, + float64Tanh, Float64E, Float64Pi, float64ToInt, @@ -1025,6 +1067,10 @@ private val connectedReferences = listOf( float64ToString, GetConsole, ignore, + int32Max, + int32Min, + int64Max, + int64Min, IntToFloat64, IntToInt64, intToString, @@ -1050,6 +1096,7 @@ private val connectedReferences = listOf( listedJoin, listedLength, listedMap, + listedMapDropping, listedReduce, listedReduceFrom, listedSlice, @@ -1103,6 +1150,7 @@ private val connectedReferences = listOf( stringToInt, stringToInt64, stringToString, + stringIsEmpty, StringBuilderConstructor, stringBuilderAppend, stringBuilderAppendBetween, diff --git a/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/listed.rs b/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/listed.rs index 592c8c6d..d95e7b89 100644 --- a/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/listed.rs +++ b/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/listed.rs @@ -143,6 +143,22 @@ mod listed { Arc::new(result) } + pub fn map_dropping( + listed: &dyn ListedTrait, + transform: &dyn Fn(T) -> Option, + ) -> List + where + T: Clone + Sync + Send, + { + let mut result = vec![]; + for i in 0..listed.len() { + if let Some(value) = transform(listed.get(i)) { + result.push(value); + } + } + Arc::new(result) + } + pub fn reduce(listed: &dyn ListedTrait, accumulate: &dyn Fn(T, T) -> T) -> T where T: Clone + Sync + Send, From f06fc9fb31c235808cfdda53c9837d171996b0bc Mon Sep 17 00:00:00 2001 From: Shaw Summa Date: Tue, 10 Mar 2026 19:50:38 -0400 Subject: [PATCH 07/10] ai fixes to lua Signed-off-by: Shaw Summa --- .../lang/temper/be/lua/LuaSupportNetwork.kt | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaSupportNetwork.kt b/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaSupportNetwork.kt index 27d889d7..f2829a88 100644 --- a/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaSupportNetwork.kt +++ b/be-lua/src/commonMain/kotlin/lang/temper/be/lua/LuaSupportNetwork.kt @@ -350,6 +350,31 @@ internal object LuaSupportNetwork : SupportNetwork { args[0], ) } + // Inline string comparisons as native Lua operators (lexicographic, same as Temper). + BuiltinOperatorId.EqStrStr -> inlineBinaryOp( + builtin.builtinOperatorId.toString(), BinaryOpEnum.Eq, LuaOperatorDefinition.Eq, + builtin.builtinOperatorId, + ) + BuiltinOperatorId.NeStrStr -> inlineBinaryOp( + builtin.builtinOperatorId.toString(), BinaryOpEnum.NotEq, LuaOperatorDefinition.Ne, + builtin.builtinOperatorId, + ) + BuiltinOperatorId.LtStrStr -> inlineBinaryOp( + builtin.builtinOperatorId.toString(), BinaryOpEnum.Lt, LuaOperatorDefinition.Lt, + builtin.builtinOperatorId, + ) + BuiltinOperatorId.LeStrStr -> inlineBinaryOp( + builtin.builtinOperatorId.toString(), BinaryOpEnum.LtEq, LuaOperatorDefinition.Le, + builtin.builtinOperatorId, + ) + BuiltinOperatorId.GtStrStr -> inlineBinaryOp( + builtin.builtinOperatorId.toString(), BinaryOpEnum.Gt, LuaOperatorDefinition.Gt, + builtin.builtinOperatorId, + ) + BuiltinOperatorId.GeStrStr -> inlineBinaryOp( + builtin.builtinOperatorId.toString(), BinaryOpEnum.GtEq, LuaOperatorDefinition.Ge, + builtin.builtinOperatorId, + ) else -> InlineLua(builtin.builtinOperatorId.toString(), builtin.builtinOperatorId) { pos, args -> Lua.FunctionCallExpr( pos, @@ -417,6 +442,75 @@ internal object LuaSupportNetwork : SupportNetwork { Lua.Args(pos, Lua.Exprs(pos, args)), ) } + + // Int32::toFloat64 is identity in Lua (all numbers are doubles). + "Int32::toFloat64" -> inlineIdentity(connectedKey) + + // Math functions → Lua's math.* standard library (available in 5.1+). + "Float64::abs" -> inlineGlobalCall(connectedKey, "math", "abs") + "Float64::ceil" -> inlineGlobalCall(connectedKey, "math", "ceil") + "Float64::floor" -> inlineGlobalCall(connectedKey, "math", "floor") + "Float64::sqrt" -> inlineGlobalCall(connectedKey, "math", "sqrt") + "Float64::sin" -> inlineGlobalCall(connectedKey, "math", "sin") + "Float64::cos" -> inlineGlobalCall(connectedKey, "math", "cos") + "Float64::tan" -> inlineGlobalCall(connectedKey, "math", "tan") + "Float64::asin" -> inlineGlobalCall(connectedKey, "math", "asin") + "Float64::acos" -> inlineGlobalCall(connectedKey, "math", "acos") + "Float64::atan" -> inlineGlobalCall(connectedKey, "math", "atan") + "Float64::exp" -> inlineGlobalCall(connectedKey, "math", "exp") + "Float64::log" -> inlineGlobalCall(connectedKey, "math", "log") + // Float64::max/min have NaN propagation semantics; cannot use math.max/min directly. + "Int32::max" -> inlineGlobalCall(connectedKey, "math", "max") + "Int32::min" -> inlineGlobalCall(connectedKey, "math", "min") + + // Math constants. + "Float64::pi" -> inlineGlobalProp(connectedKey, "math", "pi") + "Float64::infinity" -> inlineGlobalProp(connectedKey, "math", "huge") + + // String::isEmpty → #str == 0 (byte-length check, correct for empty strings). + "String::isEmpty" -> InlineLua(connectedKey) { pos, args -> + Lua.BinaryExpr( + pos, + Lua.UnaryExpr( + pos, + Lua.UnaryOp(pos, UnaryOpEnum.UnarySub, LuaOperatorDefinition.Unm), + args[0], + ), + Lua.BinaryOp(pos, BinaryOpEnum.Eq, LuaOperatorDefinition.Eq), + Lua.Num(pos, 0.0), + ) + } + + // List::isEmpty → #list == 0. + "List::isEmpty", + "Listed::isEmpty", + -> InlineLua(connectedKey) { pos, args -> + Lua.BinaryExpr( + pos, + Lua.UnaryExpr( + pos, + Lua.UnaryOp(pos, UnaryOpEnum.UnarySub, LuaOperatorDefinition.Unm), + args[0], + ), + Lua.BinaryOp(pos, BinaryOpEnum.Eq, LuaOperatorDefinition.Eq), + Lua.Num(pos, 0.0), + ) + } + + // Boolean::toString → tostring(). (Int32::toString has a radix param, can't inline.) + "Boolean::toString" -> inlineBuiltinCall(connectedKey, "tostring") + + // StringIndex comparisons → native Lua operators (they're just integers). + "StringIndexOption::compareTo" -> InlineLua(connectedKey) { pos, args -> + Lua.BinaryExpr(pos, args[0], Lua.BinaryOp(pos, BinaryOpEnum.Sub, LuaOperatorDefinition.Sub), args[1]) + } + "StringIndexOption::compareTo::eq" -> inlineBinaryOp(connectedKey, BinaryOpEnum.Eq, LuaOperatorDefinition.Eq) + "StringIndexOption::compareTo::ne" -> inlineBinaryOp(connectedKey, BinaryOpEnum.NotEq, LuaOperatorDefinition.Ne) + "StringIndexOption::compareTo::lt" -> inlineBinaryOp(connectedKey, BinaryOpEnum.Lt, LuaOperatorDefinition.Lt) + "StringIndexOption::compareTo::le" -> inlineBinaryOp(connectedKey, BinaryOpEnum.LtEq, LuaOperatorDefinition.Le) + "StringIndexOption::compareTo::gt" -> inlineBinaryOp(connectedKey, BinaryOpEnum.Gt, LuaOperatorDefinition.Gt) + "StringIndexOption::compareTo::ge" -> inlineBinaryOp(connectedKey, BinaryOpEnum.GtEq, LuaOperatorDefinition.Ge) + else -> temperMethod( connectedKey, connectedKey @@ -514,3 +608,52 @@ internal fun temperMethod( ) }, ) + +// --- Inline helper factories for systematic operator/method inlining --- + +/** Inline a binary operator: `args[0] op args[1]` */ +private fun inlineBinaryOp( + key: String, + op: BinaryOpEnum, + opDef: LuaOperatorDefinition, + builtinOp: BuiltinOperatorId? = null, +) = InlineLua(key, builtinOp) { pos, args -> + Lua.BinaryExpr(pos, args[0], Lua.BinaryOp(pos, op, opDef), args[1]) +} + +/** Inline a call to a Lua standard library function: `module.fn(args)` */ +private fun inlineGlobalCall( + key: String, + module: String, + fn: String, +) = InlineLua(key) { pos, args -> + Lua.FunctionCallExpr( + pos, + Lua.DotIndexExpr(pos, Lua.Name(pos, name(module)), Lua.Name(pos, name(fn))), + Lua.Args(pos, Lua.Exprs(pos, args)), + ) +} + +/** Inline a call to a Lua global function: `fn(args)` */ +private fun inlineBuiltinCall( + key: String, + fn: String, +) = InlineLua(key) { pos, args -> + Lua.FunctionCallExpr( + pos, + Lua.Name(pos, name(fn)), + Lua.Args(pos, Lua.Exprs(pos, args)), + ) +} + +/** Inline identity: returns `args[0]` unchanged. */ +private fun inlineIdentity(key: String) = InlineLua(key) { _, args -> args[0] } + +/** Inline a global property access: `module.prop` */ +private fun inlineGlobalProp( + key: String, + module: String, + prop: String, +) = InlineLua(key) { pos, _ -> + Lua.DotIndexExpr(pos, Lua.Name(pos, name(module)), Lua.Name(pos, name(prop))) +} From 94d7e194ad11170835066a861145cb7e8abcafae Mon Sep 17 00:00:00 2001 From: Shaw Summa Date: Thu, 19 Mar 2026 14:21:34 -0400 Subject: [PATCH 08/10] revert things as per tom's suggestions Signed-off-by: Shaw Summa --- .../resources/lang/temper/be/csharp/temper-core/StringUtil.cs | 2 +- .../kotlin/lang/temper/result/junit/JUnitResultParser.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/StringUtil.cs b/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/StringUtil.cs index d9a4a565..8ce58331 100644 --- a/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/StringUtil.cs +++ b/be-csharp/src/commonMain/resources/lang/temper/be/csharp/temper-core/StringUtil.cs @@ -195,7 +195,7 @@ public static int RequireNoStringIndex(int i) { if (i < 0) { - return i; + return -1; } throw new ArgumentOutOfRangeException("i"); } diff --git a/result-helpers/src/commonMain/kotlin/lang/temper/result/junit/JUnitResultParser.kt b/result-helpers/src/commonMain/kotlin/lang/temper/result/junit/JUnitResultParser.kt index 6a105e39..8b3d390d 100644 --- a/result-helpers/src/commonMain/kotlin/lang/temper/result/junit/JUnitResultParser.kt +++ b/result-helpers/src/commonMain/kotlin/lang/temper/result/junit/JUnitResultParser.kt @@ -125,7 +125,7 @@ data class FailureInfo( ) { // Where luaunit uses type instead of message, so use that as a fallback, // and we do some conversion there but not full. - val cause: String get() = message ?: type ?: "unknown" + val cause: String get() = message ?: type!! } @Serializable From 368455d40204169d016b8393dc303129b5cdb1f1 Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 24 Mar 2026 10:12:46 -0600 Subject: [PATCH 09/10] Finish changing and testing near maybe (#386) Signed-off-by: Tom --- .../temper-core/src/main/java/temper/core/Core.java | 2 +- .../resources/lang/temper/be/lua/temper-core/init.lua | 2 +- .../lang/temper/be/rust/temper-core/src/float64.rs | 2 +- .../commonMain/resources/implicits/Implicits.temper | 2 +- .../commonMain/resources/types/float/ops/ops.temper.md | 10 +++++++--- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/be-java/src/commonMain/resources/lang/temper/be/java/temper-core/src/main/java/temper/core/Core.java b/be-java/src/commonMain/resources/lang/temper/be/java/temper-core/src/main/java/temper/core/Core.java index 4d5dfcdd..b63daec1 100644 --- a/be-java/src/commonMain/resources/lang/temper/be/java/temper-core/src/main/java/temper/core/Core.java +++ b/be-java/src/commonMain/resources/lang/temper/be/java/temper-core/src/main/java/temper/core/Core.java @@ -318,7 +318,7 @@ public static boolean float64Near(double x, double y, Double relTol, Double absT double rel = relTol == null ? 1e-9 : relTol; double abs = absTol == null ? 0.0 : absTol; double margin = Math.max(Math.max(Math.abs(x), Math.abs(y)) * rel, abs); - return Math.abs(x - y) < margin; + return Math.abs(x - y) <= margin; } /** diff --git a/be-lua/src/commonMain/resources/lang/temper/be/lua/temper-core/init.lua b/be-lua/src/commonMain/resources/lang/temper/be/lua/temper-core/init.lua index 54252229..24967b58 100644 --- a/be-lua/src/commonMain/resources/lang/temper/be/lua/temper-core/init.lua +++ b/be-lua/src/commonMain/resources/lang/temper/be/lua/temper-core/init.lua @@ -429,7 +429,7 @@ function temper.float64_near(x, y, rel_tol, abs_tol) abs_tol = temper.null_to_nil(abs_tol) or 0.0 local scale = temper.float64_max(temper.float64_abs(x), temper.float64_abs(y)) local margin = temper.float64_max(scale * rel_tol, abs_tol) - return temper.float64_abs(x - y) < margin + return temper.float64_abs(x - y) <= margin end function temper.float64_round(x) diff --git a/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/float64.rs b/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/float64.rs index dfdae2f6..0a4e90fd 100644 --- a/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/float64.rs +++ b/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/float64.rs @@ -44,7 +44,7 @@ pub fn near(x: f64, y: f64, rel_tol: Option, abs_tol: Option) -> bool let rel_tol = rel_tol.unwrap_or(1e-9); let abs_tol = abs_tol.unwrap_or(0.0); let margin = (x.abs().max(y.abs()) * rel_tol).max(abs_tol); - (x - y).abs() < margin + (x - y).abs() <= margin } pub fn rem(x: f64, y: f64) -> Result { diff --git a/frontend/src/commonMain/resources/implicits/Implicits.temper b/frontend/src/commonMain/resources/implicits/Implicits.temper index 9f09ce75..063075fd 100644 --- a/frontend/src/commonMain/resources/implicits/Implicits.temper +++ b/frontend/src/commonMain/resources/implicits/Implicits.temper @@ -1581,7 +1581,7 @@ export class Float64 extends Equatable { absTol: builtins.Float64 = 0.0, ): builtins.Boolean { let margin = (content.abs().max(other.abs()) * relTol).max(absTol); - (content - other).abs() < margin + (content - other).abs() <= margin } @connected("Float64::round") diff --git a/functional-test-suite/src/commonMain/resources/types/float/ops/ops.temper.md b/functional-test-suite/src/commonMain/resources/types/float/ops/ops.temper.md index 39e34700..93735206 100644 --- a/functional-test-suite/src/commonMain/resources/types/float/ops/ops.temper.md +++ b/functional-test-suite/src/commonMain/resources/types/float/ops/ops.temper.md @@ -77,8 +77,11 @@ But also a bit of testing of `near` params. We have both `relTol` and `absTol` tolerance options, in that order. Either or both can be specified, although we try only one or the other here. - console.log("one.near(one + 0.1, absTol = 0.11): ${ - one.near(one + 0.1, null, 0.11).toString() + console.log("one.near(one + 0.25, absTol = 0.25): ${ + one.near(one + 0.25, null, 0.25).toString() + }"); + console.log("one.near(one + 0.1 + 1e-9, absTol = 0.1): ${ + one.near(one + 0.1 + 1e-9, null, 0.1).toString() }"); console.log("one.near(one - 0.1, absTol = 0.11): ${ one.near(one - 0.1, null, 0.11).toString() @@ -127,7 +130,8 @@ one.sinh(): ✅ (three * pi / four).tan(): ✅ one.tanh(): ✅ one.near(one + 0.1): false -one.near(one + 0.1, absTol = 0.11): true +one.near(one + 0.25, absTol = 0.25): true +one.near(one + 0.1 + 1e-9, absTol = 0.1): false one.near(one - 0.1, absTol = 0.11): true ten.near(ten + 0.1, absTol = 0.011): false ten.near(ten + 0.1, relTol = 0.011): true From e29d3939c870cc8a1eb024ee4810642bf72f2ab1 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 3 Apr 2026 08:53:19 -0700 Subject: [PATCH 10/10] Clean up rust changes (#395) * Maybe address RustTranslator concerns * Take out redundant rust things, also mapDropping Signed-off-by: Tom --- .../lang/temper/be/rust/RustSupportNetwork.kt | 48 ------------------- .../lang/temper/be/rust/RustTranslator.kt | 34 +++++++++---- .../temper/be/rust/temper-core/src/listed.rs | 16 ------- 3 files changed, 25 insertions(+), 73 deletions(-) diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt index 329de67a..4bce933e 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustSupportNetwork.kt @@ -683,29 +683,12 @@ private class EqNeGeneric( private val eqGeneric = EqNeGeneric("EqGeneric", BuiltinOperatorId.EqGeneric, RustOperator.Equals) private val eqIntInt = Infix("EqIntInt", BuiltinOperatorId.EqIntInt, RustOperator.Equals) private val eqStrStr = CmpStrStr("EqStrStr", BuiltinOperatorId.EqStrStr, RustOperator.Equals) -private val float64Abs = MethodCall("Float64::abs", "abs") -private val float64Acos = MethodCall("Float64::acos", "acos") -private val float64Asin = MethodCall("Float64::asin", "asin") -private val float64Atan = MethodCall("Float64::atan", "atan") -private val float64Atan2 = MethodCall("Float64::atan2", "atan2") -private val float64Ceil = MethodCall("Float64::ceil", "ceil") -private val float64Cos = MethodCall("Float64::cos", "cos") -private val float64Cosh = MethodCall("Float64::cosh", "cosh") -private val float64Exp = MethodCall("Float64::exp", "exp") private val float64Expm1 = MethodCall("Float64::expm1", "exp_m1") -private val float64Floor = MethodCall("Float64::floor", "floor") private val float64Log = MethodCall("Float64::log", "ln") -private val float64Log10 = MethodCall("Float64::log10", "log10") private val float64Log1p = MethodCall("Float64::log1p", "ln_1p") private val float64Max = FunctionCall("Float64::max", "temper_core::float64::max") private val float64Min = FunctionCall("Float64::min", "temper_core::float64::min") private val float64Near = FunctionCall("Float64::near", "temper_core::float64::near") -private val float64Round = MethodCall("Float64::round", "round") -private val float64Sin = MethodCall("Float64::sin", "sin") -private val float64Sinh = MethodCall("Float64::sinh", "sinh") -private val float64Sqrt = MethodCall("Float64::sqrt", "sqrt") -private val float64Tan = MethodCall("Float64::tan", "tan") -private val float64Tanh = MethodCall("Float64::tanh", "tanh") private object Float64E : Constant("Float64::e") { override fun value(pos: Position) = makePath(pos, "std", "f64", "consts", "E") @@ -754,10 +737,6 @@ private object Int64ToInt32Unsafe : Cast("Int64::toInt32Unsafe") { override fun buildType(pos: Position) = "i32".toId(pos) } -private val int32Max = MethodCall("Int32::max", "max") -private val int32Min = MethodCall("Int32::min", "min") -private val int64Max = MethodCall("Int64::max", "max") -private val int64Min = MethodCall("Int64::min", "min") internal val intToString = FunctionCall("Int32::toString", "temper_core::int_to_string") private val int64ToFloat64 = FunctionCall("Int64::toFloat64", "temper_core::int64_to_float64") private val int64ToInt32 = FunctionCall("Int64::toInt32", "temper_core::int64_to_int32") @@ -792,8 +771,6 @@ private val listedIsEmpty = private val listedJoin = FunctionCall("Listed::join", "temper_core::listed::join", hasGeneric = true, fnIndex = -1) private val listedLength = FunctionCall(listedTypes.map { "$it::length" }, "$LISTED_TRAIT_NAME::len", hasGeneric = true) private val listedMap = FunctionCall("Listed::map", "temper_core::listed::map", hasGeneric = true, fnIndex = -1) -private val listedMapDropping = - FunctionCall("Listed::mapDropping", "temper_core::listed::map_dropping", hasGeneric = true, fnIndex = -1) private val listedReduce = FunctionCall("Listed::reduce", "temper_core::listed::reduce", hasGeneric = true, fnIndex = -1) private val listedReduceFrom = @@ -996,8 +973,6 @@ private val stringBuilderAppendCodePoint = private val stringBuilderToString = FunctionCall("StringBuilder::toString", "temper_core::string::builder::to_string") -private val stringIsEmpty = MethodCall("String::isEmpty", "is_empty") - private object StringIndexNone : Constant("StringIndex::none") { override fun value(pos: Position) = "()".toId(pos) } @@ -1034,30 +1009,13 @@ private val connectedReferences = listOf( dequeRemoveFirst, DoneResult, Empty, - float64Abs, - float64Acos, - float64Asin, - float64Atan, - float64Atan2, - float64Ceil, - float64Cos, - float64Cosh, - float64Exp, float64Expm1, - float64Floor, float64Log, - float64Log10, float64Log1p, float64Min, float64Max, float64Near, - float64Round, float64Sign, - float64Sin, - float64Sinh, - float64Sqrt, - float64Tan, - float64Tanh, Float64E, Float64Pi, float64ToInt, @@ -1067,10 +1025,6 @@ private val connectedReferences = listOf( float64ToString, GetConsole, ignore, - int32Max, - int32Min, - int64Max, - int64Min, IntToFloat64, IntToInt64, intToString, @@ -1096,7 +1050,6 @@ private val connectedReferences = listOf( listedJoin, listedLength, listedMap, - listedMapDropping, listedReduce, listedReduceFrom, listedSlice, @@ -1150,7 +1103,6 @@ private val connectedReferences = listOf( stringToInt, stringToInt64, stringToString, - stringIsEmpty, StringBuilderConstructor, stringBuilderAppend, stringBuilderAppendBetween, diff --git a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt index 8ea36bfc..5c8ed3b2 100644 --- a/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt +++ b/be-rust/src/commonMain/kotlin/lang/temper/be/rust/RustTranslator.kt @@ -2090,9 +2090,12 @@ class RustTranslator( ).wrapArcType() } - private fun translateGarbage(garbage: TmpL.Garbage): Rust.Call { - val pos = garbage.pos - return "panic!".toId(pos).call(listOf(Rust.StringLiteral(pos, "Garbage"))) + private fun translateGarbage(garbage: TmpL.Garbage): Rust.Expr = run { + translateUnsupported(garbage.pos, garbage.toString()) + } + + private fun translateGarbageStatement(garbage: TmpL.GarbageStatement): Rust.ExprStatement = run { + translateUnsupportedStatement(garbage) } private fun translateGetProperty(expression: TmpL.GetProperty, avoidClone: Boolean): Rust.Expr { @@ -2615,7 +2618,7 @@ class RustTranslator( typePub: Rust.VisibilityPub? = null, ): List { return when (member) { - is TmpL.GarbageStatement -> return listOf() + is TmpL.GarbageStatement -> translateGarbageStatement(member).toItem() // won't compile but that's ok // So far only bother with returnType forwarding for instance methods. Will we need more later? is TmpL.Getter -> translateGetter(member, block = block, forTrait = forTrait, returnType = returnType) is TmpL.Setter -> translateSetter(member, block = block, forTrait = forTrait) // don't expect return type @@ -2906,7 +2909,7 @@ class RustTranslator( val ref = statement.left val subject = when (val subj = ref.subject) { is TmpL.Expression -> translateExpression(subj, avoidClone = true) - is TmpL.TypeName -> translateTypeName(subj) + is TmpL.TypeName -> translateTypeName(subj) // wrong but also shouldn't happen } val setter = "set_${translatePropertyId(ref.property)}" subject.methodCall(setter, listOf(value)) @@ -2934,13 +2937,13 @@ class RustTranslator( try { return when (statement) { is TmpL.Assignment -> return translateAssignment(statement) - is TmpL.BoilerplateCodeFoldEnd -> return listOf() - is TmpL.BoilerplateCodeFoldStart -> return listOf() + is TmpL.BoilerplateCodeFoldEnd -> translateUnsupportedStatement(statement) + is TmpL.BoilerplateCodeFoldStart -> translateUnsupportedStatement(statement) is TmpL.BreakStatement -> translateBreakStatement(statement) is TmpL.ContinueStatement -> translateContinueStatement(statement) - is TmpL.EmbeddedComment -> return listOf() + is TmpL.EmbeddedComment -> translateUnsupportedStatement(statement) is TmpL.ExpressionStatement -> translateExpressionStatement(statement) - is TmpL.GarbageStatement -> Rust.ExprStatement(statement.pos, translateGarbage(statement)) + is TmpL.GarbageStatement -> translateGarbageStatement(statement) is TmpL.HandlerScope -> error("handled elsewhere") is TmpL.LocalDeclaration -> return translateModuleOrLocalDeclaration(statement) is TmpL.LocalFunctionDeclaration -> error("handled elsewhere") @@ -3069,6 +3072,7 @@ class RustTranslator( return when (type) { is TmpL.FunctionType -> translateFunctionType(type) is TmpL.TypeIntersection -> when { + // This branch is never expected, so some this translation is untested. type.types.isEmpty() -> ANY_NAME.toId(pos) type.types.size == 1 -> translateType(type.types.first(), inExpr = inExpr, isFlex = isFlex) else -> Rust.ImplTraitType( @@ -3337,6 +3341,18 @@ class RustTranslator( ) } + private fun translateUnsupported(pos: Position, diagnostic: String): Rust.Expr = run { + "panic!".toId(pos).call(listOf(Rust.StringLiteral(pos, "Unsupported: $diagnostic"))) + } + + private fun translateUnsupported(tree: TmpL.Tree): Rust.Expr = run { + translateUnsupported(tree.pos, tree.toString()) + } + + private fun translateUnsupportedStatement(statement: TmpL.Statement): Rust.ExprStatement = run { + Rust.ExprStatement(statement.pos, translateUnsupported(statement)) + } + private fun translateValueReference(expression: TmpL.ValueReference): Rust.Expr { val pos = expression.pos return when (expression.value.typeTag) { diff --git a/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/listed.rs b/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/listed.rs index d95e7b89..592c8c6d 100644 --- a/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/listed.rs +++ b/be-rust/src/commonMain/resources/lang/temper/be/rust/temper-core/src/listed.rs @@ -143,22 +143,6 @@ mod listed { Arc::new(result) } - pub fn map_dropping( - listed: &dyn ListedTrait, - transform: &dyn Fn(T) -> Option, - ) -> List - where - T: Clone + Sync + Send, - { - let mut result = vec![]; - for i in 0..listed.len() { - if let Some(value) = transform(listed.get(i)) { - result.push(value); - } - } - Arc::new(result) - } - pub fn reduce(listed: &dyn ListedTrait, accumulate: &dyn Fn(T, T) -> T) -> T where T: Clone + Sync + Send,