Loading commands/editor/sed.cpp +80 −3 Changes for commands/editor/sed.cpp: 80 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -113,8 +113,9 @@ namespace fresh { namespace cmd { }; // One parsed command: 's' (substitute), 'p' (print now), 'q' (quit), // or '{' (an address-gated group of nested commands -- recursive, same // grammar one level in). A std::vector<SedCmd> holding SedCmd itself is // 'y' (transliterate), or '{' (an address-gated group of nested // commands -- recursive, same grammar one level in). A // std::vector<SedCmd> holding SedCmd itself is // fine since C++17 (23.2.1): incomplete-type members are permitted for // vector/list/forward_list as long as the type is complete by the time // any operation actually needs it, which it always is here (this file Loading @@ -133,6 +134,10 @@ namespace fresh { namespace cmd { bool printFlag=false; char delimiter='/'; // whatever character followed 's' in this command // 'y' data: yFrom[k] is transliterated to yTo[k] for every k -- // always the same length, checked at parse time. std::string yFrom,yTo; // '{' data: std::vector<SedCmd> block; }; Loading Loading @@ -284,6 +289,66 @@ namespace fresh { namespace cmd { return true; } // Parses one "y<delim>SRC<delim>DST<delim>" starting at // text[i]=='y' -- same delimiter/escape handling as parseSCommand() // (any character may be the delimiter; "\\", "\<delim>" are taken // literally), but with no regex/flags: real sed's 'y' is a plain, // same-length character transliteration (e.g. scripts/syscallhdr. // sh's own "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/" // to uppercase a header guard name), and POSIX requires SRC and // DST to have exactly the same length. static bool parseYCommand(const std::string &text,size_t &i,SedCmd &cmd,std::string &err){ size_t n=text.length(); if(i+1>=n){ err="unterminated y command"; return false; } char delim=text[i+1]; i+=2; std::string from,to; int delimsSeen=0; std::string *target=&from; while(i<n && delimsSeen<2){ char c=text[i]; if(c=='\\' && i+1<n && text[i+1]=='\\'){ *target+='\\'; i+=2; continue; } if(c=='\\' && i+1<n && text[i+1]==delim){ *target+=delim; i+=2; continue; } if(c==delim){ ++delimsSeen; target=&to; ++i; continue; } if(c=='\n'){ err="unterminated y"+std::string(1,delim)+std::string(1,delim)+std::string(1,delim)+" command"; return false; } *target+=c; ++i; } if(delimsSeen<2){ err="unterminated y"+std::string(1,delim)+std::string(1,delim)+std::string(1,delim)+" command"; return false; } if(from.size()!=to.size()){ err="strings for 'y' command are different lengths"; return false; } cmd.yFrom=from; cmd.yTo=to; cmd.kind='y'; return true; } // Recursive-descent parse of a command list: zero or more // (optionally addressed) commands, each 's', 'p', 'q', or a // '{'-nested sub-list -- stopping at the matching '}' when Loading Loading @@ -361,8 +426,14 @@ namespace fresh { namespace cmd { out.push_back(std::move(cmd)); continue; } if(letter=='y'){ if(!parseYCommand(text,i,cmd,err)) return false; out.push_back(std::move(cmd)); continue; } err="only 's', 'p', 'q', and '{' commands are supported"; err="only 's', 'p', 'q', 'y', and '{' commands are supported"; return false; } } Loading Loading @@ -496,6 +567,12 @@ namespace fresh { namespace cmd { out << line << "\n"; }else if(cmd.kind=='q'){ return true; }else if(cmd.kind=='y'){ for(size_t c=0;c<line.size();++c){ size_t pos=cmd.yFrom.find(line[c]); if(pos!=std::string::npos) line[c]=cmd.yTo[pos]; } }else if(cmd.kind=='{'){ if(execCommands(cmd.block,line,out)) return true; Loading Loading
commands/editor/sed.cpp +80 −3 Changes for commands/editor/sed.cpp: 80 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -113,8 +113,9 @@ namespace fresh { namespace cmd { }; // One parsed command: 's' (substitute), 'p' (print now), 'q' (quit), // or '{' (an address-gated group of nested commands -- recursive, same // grammar one level in). A std::vector<SedCmd> holding SedCmd itself is // 'y' (transliterate), or '{' (an address-gated group of nested // commands -- recursive, same grammar one level in). A // std::vector<SedCmd> holding SedCmd itself is // fine since C++17 (23.2.1): incomplete-type members are permitted for // vector/list/forward_list as long as the type is complete by the time // any operation actually needs it, which it always is here (this file Loading @@ -133,6 +134,10 @@ namespace fresh { namespace cmd { bool printFlag=false; char delimiter='/'; // whatever character followed 's' in this command // 'y' data: yFrom[k] is transliterated to yTo[k] for every k -- // always the same length, checked at parse time. std::string yFrom,yTo; // '{' data: std::vector<SedCmd> block; }; Loading Loading @@ -284,6 +289,66 @@ namespace fresh { namespace cmd { return true; } // Parses one "y<delim>SRC<delim>DST<delim>" starting at // text[i]=='y' -- same delimiter/escape handling as parseSCommand() // (any character may be the delimiter; "\\", "\<delim>" are taken // literally), but with no regex/flags: real sed's 'y' is a plain, // same-length character transliteration (e.g. scripts/syscallhdr. // sh's own "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/" // to uppercase a header guard name), and POSIX requires SRC and // DST to have exactly the same length. static bool parseYCommand(const std::string &text,size_t &i,SedCmd &cmd,std::string &err){ size_t n=text.length(); if(i+1>=n){ err="unterminated y command"; return false; } char delim=text[i+1]; i+=2; std::string from,to; int delimsSeen=0; std::string *target=&from; while(i<n && delimsSeen<2){ char c=text[i]; if(c=='\\' && i+1<n && text[i+1]=='\\'){ *target+='\\'; i+=2; continue; } if(c=='\\' && i+1<n && text[i+1]==delim){ *target+=delim; i+=2; continue; } if(c==delim){ ++delimsSeen; target=&to; ++i; continue; } if(c=='\n'){ err="unterminated y"+std::string(1,delim)+std::string(1,delim)+std::string(1,delim)+" command"; return false; } *target+=c; ++i; } if(delimsSeen<2){ err="unterminated y"+std::string(1,delim)+std::string(1,delim)+std::string(1,delim)+" command"; return false; } if(from.size()!=to.size()){ err="strings for 'y' command are different lengths"; return false; } cmd.yFrom=from; cmd.yTo=to; cmd.kind='y'; return true; } // Recursive-descent parse of a command list: zero or more // (optionally addressed) commands, each 's', 'p', 'q', or a // '{'-nested sub-list -- stopping at the matching '}' when Loading Loading @@ -361,8 +426,14 @@ namespace fresh { namespace cmd { out.push_back(std::move(cmd)); continue; } if(letter=='y'){ if(!parseYCommand(text,i,cmd,err)) return false; out.push_back(std::move(cmd)); continue; } err="only 's', 'p', 'q', and '{' commands are supported"; err="only 's', 'p', 'q', 'y', and '{' commands are supported"; return false; } } Loading Loading @@ -496,6 +567,12 @@ namespace fresh { namespace cmd { out << line << "\n"; }else if(cmd.kind=='q'){ return true; }else if(cmd.kind=='y'){ for(size_t c=0;c<line.size();++c){ size_t pos=cmd.yFrom.find(line[c]); if(pos!=std::string::npos) line[c]=cmd.yTo[pos]; } }else if(cmd.kind=='{'){ if(execCommands(cmd.block,line,out)) return true; Loading