@@ -446,7 +446,124 @@ def build_lua_char_class(chars):
446446 ordered = ordered .replace ("-" , "" ) + "-"
447447 return f"[{ ordered } ]"
448448
449+ def _stage_to_lua_fragment (stage ):
450+ """Convert a single pipeline stage to Lua code that transforms 'v'."""
451+ cut = parse_cut (stage )
452+ if cut :
453+ mode , start , end , delim = cut
454+ if mode == "bytes" :
455+ if not str (start ).isdigit ():
456+ return None
457+ if end is not None :
458+ if not str (end ).isdigit ():
459+ return None
460+ return f"v=v:sub({ start } ,{ end } )"
461+ return f"v=v:sub({ start } )"
462+ elif mode == "field" :
463+ esc = lua_string_escape (lua_pattern_escape (delim ))
464+ return (
465+ f"do local t={{}} "
466+ f'for f in v:gmatch("[^{ esc } ]+") do t[#t+1]=f end '
467+ f'v=t[{ start } ] or "" end'
468+ )
469+ elif mode == "range" :
470+ esc = lua_string_escape (lua_pattern_escape (delim ))
471+ return (
472+ f"do local t={{}} "
473+ f'for f in v:gmatch("[^{ esc } ]+") do t[#t+1]=f end '
474+ f'v=table.concat(t,"{ lua_string_escape (delim )} ",'
475+ f"{ start } ,math.min(#t,{ end } )) end"
476+ )
477+ if _RE_TR_LOWER .match (stage ):
478+ return "v=v:lower()"
479+ if _RE_TR_UPPER .match (stage ):
480+ return "v=v:upper()"
481+ m = _RE_TR_DELETE .match (stage )
482+ if m :
483+ chars = m .group (1 )
484+ if len (chars ) == 1 :
485+ pat = lua_pattern_escape (chars )
486+ else :
487+ pat = build_lua_char_class (chars )
488+ return f'v=(v:gsub("{ lua_string_escape (pat )} ", ""))'
489+ m = _RE_TR_DELETE_BARE .match (stage )
490+ if m :
491+ pat = lua_string_escape (lua_pattern_escape (m .group (1 )))
492+ return f'v=(v:gsub("{ pat } ", ""))'
493+ m = _RE_TR_REPLACE .match (stage )
494+ if m :
495+ pat = lua_string_escape (lua_pattern_escape (m .group (1 )))
496+ repl = lua_string_escape (lua_gsub_repl_escape (m .group (2 )))
497+ return f'v=(v:gsub("{ pat } ", "{ repl } "))'
498+ m = _RE_AWK_F .match (stage )
499+ if m :
500+ delim = m .group (1 )
501+ print_args = m .group (2 )
502+ parts = _RE_AWK_FIELDS .findall (print_args )
503+ if parts :
504+ if not all (is_safe_for_expand (sep ) for _ , sep in parts if sep ):
505+ return None
506+ lua_parts = []
507+ for field_num , separator in parts :
508+ if field_num :
509+ lua_parts .append (f'(t[{ field_num } ] or "")' )
510+ elif separator is not None :
511+ lua_parts .append (f'"{ lua_string_escape (separator )} "' )
512+ if lua_parts :
513+ lua_expr = " .. " .join (lua_parts )
514+ esc = lua_string_escape (lua_pattern_escape (delim ))
515+ return (
516+ f"do local t={{}} "
517+ f'for f in v:gmatch("[^{ esc } ]+") do t[#t+1]=f end '
518+ f"v={ lua_expr } end"
519+ )
520+ substs = parse_sed_substs (stage )
521+ if substs :
522+ if all (is_safe_for_expand (repl ) for _ , repl , _ in substs ):
523+ parts = []
524+ for pattern , repl , is_global in substs :
525+ esc_pat = lua_string_escape (sed_pattern_to_lua (pattern ))
526+ esc_repl = lua_string_escape (lua_gsub_repl_escape (repl ))
527+ count_arg = "" if is_global else ", 1"
528+ parts .append (
529+ f'v=(v:gsub("{ esc_pat } ", "{ esc_repl } "{ count_arg } ))'
530+ )
531+ return " " .join (parts )
532+ return None
533+
449534 def convert_string_op (expr , cmd ):
535+ # Handle pipelines: split cmd into stages and compose Lua
536+ try :
537+ tokens = shlex .split (cmd )
538+ except ValueError :
539+ tokens = None
540+ if tokens :
541+ stages = []
542+ current = []
543+ for token in tokens :
544+ if token == "|" :
545+ if current :
546+ stages .append (shlex .join (current ))
547+ current = []
548+ else :
549+ current .append (token )
550+ if current :
551+ stages .append (" " .join (current ))
552+ if len (stages ) > 1 :
553+ esc_expr = lua_string_escape (expr )
554+ fragments = []
555+ for stage in stages :
556+ fragment = _stage_to_lua_fragment (stage )
557+ if fragment is None :
558+ break
559+ fragments .append (fragment )
560+ else :
561+ lua_code = f'local v=rpm.expand("{ esc_expr } ")'
562+ for fragment in fragments :
563+ lua_code += f" { fragment } "
564+ lua_code += " print(v)"
565+ return f"%{{lua:{ lua_code } }}"
566+
450567 # -- cut --
451568 cut = parse_cut (cmd )
452569 if cut :
0 commit comments