@@ -223,6 +223,152 @@ pub(crate) const ARRAY_HAS_SPEC: BuiltinSpec = BuiltinSpec {
223223 needs_failure_exit : false ,
224224} ;
225225
226+ // ── Family 2: string/bytes pure transformations ─────────────────────
227+
228+ /// `string.slice(start, length)` — pure transformation.
229+ pub ( crate ) const STRING_SLICE_SPEC : BuiltinSpec = BuiltinSpec {
230+ name : "string_slice" ,
231+ arity : 3 ,
232+ inputs : & [
233+ InputRepr :: Int , // length (popped third)
234+ InputRepr :: Int , // start (popped second)
235+ InputRepr :: HeapPtr ( HeapInputKind :: String ) , // text (popped first)
236+ ] ,
237+ output : OutputKind :: Tagged ( ValueType :: String ) ,
238+ effect : BuiltinEffect :: Pure ,
239+ needs_failure_exit : false ,
240+ } ;
241+
242+ /// `bytes.slice(start, length)` — pure transformation.
243+ pub ( crate ) const BYTES_SLICE_SPEC : BuiltinSpec = BuiltinSpec {
244+ name : "bytes_slice" ,
245+ arity : 3 ,
246+ inputs : & [
247+ InputRepr :: Int , // length (popped third)
248+ InputRepr :: Int , // start (popped second)
249+ InputRepr :: HeapPtr ( HeapInputKind :: Bytes ) , // bytes (popped first)
250+ ] ,
251+ output : OutputKind :: Tagged ( ValueType :: Bytes ) ,
252+ effect : BuiltinEffect :: Pure ,
253+ needs_failure_exit : false ,
254+ } ;
255+
256+ /// `string.get(index)` — pure read, string result.
257+ pub ( crate ) const STRING_GET_SPEC : BuiltinSpec = BuiltinSpec {
258+ name : "string_get" ,
259+ arity : 2 ,
260+ inputs : & [
261+ InputRepr :: Int , // index (popped second)
262+ InputRepr :: HeapPtr ( HeapInputKind :: String ) , // text (popped first)
263+ ] ,
264+ output : OutputKind :: Tagged ( ValueType :: String ) ,
265+ effect : BuiltinEffect :: Pure ,
266+ needs_failure_exit : false ,
267+ } ;
268+
269+ /// `bytes.get(index)` — pure read, int result.
270+ pub ( crate ) const BYTES_GET_SPEC : BuiltinSpec = BuiltinSpec {
271+ name : "bytes_get" ,
272+ arity : 2 ,
273+ inputs : & [
274+ InputRepr :: Int , // index (popped second)
275+ InputRepr :: HeapPtr ( HeapInputKind :: Bytes ) , // bytes (popped first)
276+ ] ,
277+ output : OutputKind :: Int ,
278+ effect : BuiltinEffect :: Pure ,
279+ needs_failure_exit : false ,
280+ } ;
281+
282+ /// `bytes.has(index)` — pure read, bool result.
283+ pub ( crate ) const BYTES_HAS_SPEC : BuiltinSpec = BuiltinSpec {
284+ name : "bytes_has" ,
285+ arity : 2 ,
286+ inputs : & [
287+ InputRepr :: Int , // index (popped second)
288+ InputRepr :: HeapPtr ( HeapInputKind :: Bytes ) , // bytes (popped first)
289+ ] ,
290+ output : OutputKind :: Bool ,
291+ effect : BuiltinEffect :: Pure ,
292+ needs_failure_exit : false ,
293+ } ;
294+
295+ /// `string.replace_literal(needle, replacement)` — pure transformation.
296+ pub ( crate ) const STRING_REPLACE_LITERAL_SPEC : BuiltinSpec = BuiltinSpec {
297+ name : "string_replace_literal" ,
298+ arity : 3 ,
299+ inputs : & [
300+ InputRepr :: HeapPtr ( HeapInputKind :: String ) , // replacement (popped third)
301+ InputRepr :: HeapPtr ( HeapInputKind :: String ) , // needle (popped second)
302+ InputRepr :: HeapPtr ( HeapInputKind :: String ) , // text (popped first)
303+ ] ,
304+ output : OutputKind :: Tagged ( ValueType :: String ) ,
305+ effect : BuiltinEffect :: Pure ,
306+ needs_failure_exit : false ,
307+ } ;
308+
309+ /// `string.lower_ascii()` — pure transformation.
310+ pub ( crate ) const STRING_LOWER_ASCII_SPEC : BuiltinSpec = BuiltinSpec {
311+ name : "string_lower_ascii" ,
312+ arity : 1 ,
313+ inputs : & [ InputRepr :: HeapPtr ( HeapInputKind :: String ) ] ,
314+ output : OutputKind :: Tagged ( ValueType :: String ) ,
315+ effect : BuiltinEffect :: Pure ,
316+ needs_failure_exit : false ,
317+ } ;
318+
319+ /// `string.split_literal(delimiter)` — pure transformation, array result.
320+ pub ( crate ) const STRING_SPLIT_LITERAL_SPEC : BuiltinSpec = BuiltinSpec {
321+ name : "string_split_literal" ,
322+ arity : 2 ,
323+ inputs : & [
324+ InputRepr :: HeapPtr ( HeapInputKind :: String ) , // delimiter (popped second)
325+ InputRepr :: HeapPtr ( HeapInputKind :: String ) , // text (popped first)
326+ ] ,
327+ output : OutputKind :: Tagged ( ValueType :: Array ) ,
328+ effect : BuiltinEffect :: Pure ,
329+ needs_failure_exit : false ,
330+ } ;
331+
332+ /// `bytes.from_array_u8(array)` — pure transformation.
333+ pub ( crate ) const BYTES_FROM_ARRAY_U8_SPEC : BuiltinSpec = BuiltinSpec {
334+ name : "bytes_from_array_u8" ,
335+ arity : 1 ,
336+ inputs : & [ InputRepr :: HeapPtr ( HeapInputKind :: Array ) ] ,
337+ output : OutputKind :: Tagged ( ValueType :: Bytes ) ,
338+ effect : BuiltinEffect :: Pure ,
339+ needs_failure_exit : false ,
340+ } ;
341+
342+ /// `bytes.to_utf8_ascii()` — pure transformation.
343+ pub ( crate ) const BYTES_TO_UTF8_ASCII_SPEC : BuiltinSpec = BuiltinSpec {
344+ name : "bytes_to_utf8_ascii" ,
345+ arity : 1 ,
346+ inputs : & [ InputRepr :: HeapPtr ( HeapInputKind :: Bytes ) ] ,
347+ output : OutputKind :: Tagged ( ValueType :: String ) ,
348+ effect : BuiltinEffect :: Pure ,
349+ needs_failure_exit : false ,
350+ } ;
351+
352+ /// `bytes.to_array_u8()` — pure transformation.
353+ pub ( crate ) const BYTES_TO_ARRAY_U8_SPEC : BuiltinSpec = BuiltinSpec {
354+ name : "bytes_to_array_u8" ,
355+ arity : 1 ,
356+ inputs : & [ InputRepr :: HeapPtr ( HeapInputKind :: Bytes ) ] ,
357+ output : OutputKind :: Tagged ( ValueType :: Array ) ,
358+ effect : BuiltinEffect :: Pure ,
359+ needs_failure_exit : false ,
360+ } ;
361+
362+ /// `to_string(value)` — pure transformation.
363+ pub ( crate ) const TO_STRING_SPEC : BuiltinSpec = BuiltinSpec {
364+ name : "to_string" ,
365+ arity : 1 ,
366+ inputs : & [ InputRepr :: Any ] ,
367+ output : OutputKind :: Tagged ( ValueType :: String ) ,
368+ effect : BuiltinEffect :: Pure ,
369+ needs_failure_exit : false ,
370+ } ;
371+
226372/// Look up the spec for a specialized builtin kind, if one exists.
227373///
228374/// Returns `None` for builtins not yet covered by the spec-driven
@@ -241,6 +387,26 @@ pub(crate) fn spec_for(
241387 super :: recorder:: SpecializedBuiltinKind :: TypeOf => Some ( & TYPE_OF_SPEC ) ,
242388 super :: recorder:: SpecializedBuiltinKind :: StringContains => Some ( & STRING_CONTAINS_SPEC ) ,
243389 super :: recorder:: SpecializedBuiltinKind :: ArrayHas => Some ( & ARRAY_HAS_SPEC ) ,
390+ super :: recorder:: SpecializedBuiltinKind :: StringSlice => Some ( & STRING_SLICE_SPEC ) ,
391+ super :: recorder:: SpecializedBuiltinKind :: BytesSlice => Some ( & BYTES_SLICE_SPEC ) ,
392+ super :: recorder:: SpecializedBuiltinKind :: StringGet => Some ( & STRING_GET_SPEC ) ,
393+ super :: recorder:: SpecializedBuiltinKind :: BytesGet => Some ( & BYTES_GET_SPEC ) ,
394+ super :: recorder:: SpecializedBuiltinKind :: BytesHas => Some ( & BYTES_HAS_SPEC ) ,
395+ super :: recorder:: SpecializedBuiltinKind :: StringReplaceLiteral => {
396+ Some ( & STRING_REPLACE_LITERAL_SPEC )
397+ }
398+ super :: recorder:: SpecializedBuiltinKind :: StringLowerAscii => Some ( & STRING_LOWER_ASCII_SPEC ) ,
399+ super :: recorder:: SpecializedBuiltinKind :: StringSplitLiteral => {
400+ Some ( & STRING_SPLIT_LITERAL_SPEC )
401+ }
402+ super :: recorder:: SpecializedBuiltinKind :: BytesFromArrayU8 => {
403+ Some ( & BYTES_FROM_ARRAY_U8_SPEC )
404+ }
405+ super :: recorder:: SpecializedBuiltinKind :: BytesToUtf8Ascii => {
406+ Some ( & BYTES_TO_UTF8_ASCII_SPEC )
407+ }
408+ super :: recorder:: SpecializedBuiltinKind :: BytesToArrayU8 => Some ( & BYTES_TO_ARRAY_U8_SPEC ) ,
409+ super :: recorder:: SpecializedBuiltinKind :: ToString => Some ( & TO_STRING_SPEC ) ,
244410 _ => None ,
245411 }
246412}
0 commit comments