- TRRE रेगुलर एक्सप्रेशन में टेक्स्ट ट्रांसफॉर्मेशन को सीधे व्यक्त करने वाला
:ऑपरेटर जोड़ने वाला language extension है, और इसे प्रयोग करने के लिएgrep -Eजैसा CLI tooltrreउपलब्ध है - इसका मूल रूप transductive pair है, जैसे
a:b, जो input pattern को output pattern में बदलता है; deletion कोx:और insertion को:xकी तरह empty string के साथ transformation के रूप में व्यक्त किया जाता है - सामान्य रेगुलर एक्सप्रेशन की तरह alternative selection, repetition और character range transformation इस्तेमाल किए जा सकते हैं, और
cat:dog,[a:A-z:Z], Caesar cipher जैसे examples शामिल हैं - अंदरूनी implementation सामान्य रेगुलर एक्सप्रेशन के FSA के बजाय input-output pairs संभालने वाला Finite State Transducer(FST) बनाता है, और experimental on-the-fly determinization भी support करता है
- अभी pre-built binaries नहीं हैं और इसे खुद build करना पड़ता है; DFT stabilization, full Unicode support, ERE features completion, efficient range handling आदि TODO में बचे हैं
TRRE जिस समस्या को हल करना चाहता है
- सामान्य रेगुलर एक्सप्रेशन टेक्स्ट में pattern खोजने के लिए उपयोगी हैं, लेकिन text editing में group handling logic post-processing जैसा काम करता है और जटिल हो सकता है
- TRRE pattern matching और text modification को एक ही expression में डालने के लिए regular expression language को extend करता है
- मुख्य syntax
pattern-to-match:pattern-to-generateके रूप में है, और सबसे सरल examplea:bहै, जोaकोbमें बदलता है - CLI tool
trreइस concept को दिखाने वाली implementation है औरgrep -Eजैसा feel देकर काम करता है
बेसिक transformation syntax
- string substitution को
cat:dogकी तरह लिखा जाता हैecho 'cat' | ./trre 'cat:dog'dogoutput करता है(c:d)(a:o)(t:g)की तरह character-level transformation से भी वही result बनाया जा सकता है
sedकी तरह string के अंदर सभी matches को बदलने के लिए इस्तेमाल किया जा सकता हैMary had a little lamb.परlamb:catapply करने सेMary had a little cat.बनता है
- Deletion को right side खाली रखकर
string_to_delete:के रूप में व्यक्त किया जाता है(x:)or,xorसेxहटाकरorबनाता हैa:default scan mode में सभीaको empty symbol में बदलकर हटा देता है[aie]:की तरह bracket expression का उपयोग करके कई characters हटाए जा सकते हैं
- Insertion को left side खाली रखकर
:string_to_insertके रूप में व्यक्त किया जाता है(:x)or,orके आगेxडालकरxorबनाता हैhad a (:little )lambcontext के अंदरlittleinsert करता है
रेगुलर एक्सप्रेशन पर transformations
- TRRE सामान्य रेगुलर एक्सप्रेशन की तरह
|का उपयोग करके alternative selection support करता है(c:b)at|(d:h)og,cat dogकोbat hogमें बदलता है
- repetition operators भी transformations पर apply किए जा सकते हैं
(cat:dog)*,catcatcatकोdogdogdogमें बदलता है- default scan mode में केवल
cat:dogभी बार-बार apply होकर वही result बना सकता है
- left pattern में repetition इस्तेमाल करने पर कई inputs consume करके उन्हें एक output में बदला जा सकता है
(cat)*:dog,catcatcatकोdogमें बदलता है
- right pattern में
*या+इस्तेमाल करने पर infinite loop हो सकता है:a*जैसे expressions से बचना चाहिए- finite repetition चाहिए तो
:(repeat-10-times){10}की तरह count specify करें
Range transformations और generators
- character range transformation को
[a:A-z:Z]की तरह लिखा जाता हैregular expressionsकोREGULAR EXPRESSIONSमें बदला जा सकता है
- Caesar cipher example शामिल है
[a:b-y:zz:a],caesar cipherकोdbftbs djqifsमें बदलता है[a:zb:a-z:y]इसे फिरcaesar cipherमें वापस बदलता है
- generator की तरह एक input से कई outputs भी बनाए जा सकते हैं
- default में possible first match का उपयोग होता है
-aoption इस्तेमाल करने पर सभी possible outputs generate होते हैं
- example के तौर पर empty input पर
:(0|1){3}apply करने से000से111तक 3-bit binary sequences बनाए जा सकते हैं :(0|1){,3}?और-maको साथ इस्तेमाल करने पर length 3 या उससे कम के subset-form outputs generate होते हैं
Language spec और operator precedence
- अनौपचारिक रूप से TRRE को
pattern-to-match:pattern-to-generatepairs के रूप में define किया गया है - left
pattern-to-matchstring या regular expression हो सकता है - right
pattern-to-generateआम तौर पर string होता है, लेकिन regular expression भी हो सकता है :operator को फिलहाल non-associative माना जाता है, औरTRRE:TRREरूप syntax के हिसाब से allow नहीं है- इस रूप का स्वाभाविक अर्थ TRRE द्वारा define किए गए relations का composition है, लेकिन complexity बढ़ सकती है इसलिए अभी exclude किया गया है
- operator precedence high से low क्रम में इस तरह है
- escape character
\ - bracket expression
[] - grouping
() - repetition
* + ? {m,n} - concatenation
- Transduction
: - alternative selection
|
- escape character
Modes और greediness
trreदो modes support करता है- Scan Mode: default mode है और transformations को sequentially apply करता है
- Match Mode:
-mflag इस्तेमाल करता है और check करता है कि पूरी string expression से match होती है या नहीं
-aoption सभी possible outputs generate करता है?modifier*,+,{,}operators को non-greedy बनाता है<(.:)*><cat><dog>से<>output करता है<(.:)*?>उसी input से<><>output करता है
- tags या brackets के अंदर की content बदलने के examples भी शामिल हैं
<(.*?:cat)>,<dog> <mouse>को<cat> <cat>में बदलता है
FST-based implementation और determinization
- TRRE internally Finite State Transducer(FST) बनाता है
- FST सामान्य रेगुलर एक्सप्रेशन में इस्तेमाल होने वाले Finite State Automaton(FSA) जैसा है, लेकिन simple strings के बजाय input-output pairs संभालता है
- TRRE के मुख्य अंतर ये हैं
- दो regular languages के बीच binary relation define करता है
- inference में FSA के बजाय FST इस्तेमाल करता है
- performance के लिए experimental on-the-fly determinization support करता है
- सामान्य regex engine में determinization non-deterministic automata को deterministic automata में बदलकर input string length के हिसाब से linear-time inference संभव बनाता है
- TRRE में भी similar approach संभव है, लेकिन सभी non-deterministic transducers NFT को deterministic transducers DFT में नहीं बदला जा सकता
- एक ही input label वाले दो “bad” cycles हों तो state generation infinite loop में फंस सकता है
- ऐसे loops detect करने के तरीके हैं, लेकिन cost ज्यादा है
Performance और installation status
- basic non-deterministic version के लिए simple substitution में
sedसे थोड़ा धीमा example दिया गया है./trre '(vodka):(VODKA)': real0m0.046ssed 's/vodka/VODKA/': real0m0.024s
- complex tasks में deterministic version
trre_dftकेsedसे तेज होने का example हैsed -e 's/\(.*\)/\U\1/': real0m0.508s./trre_dft '[a:A-z:Z]': real0m0.131s
- pre-built binaries अभी उपलब्ध नहीं हैं
- installation में repository clone करने के बाद
make && sh test.shसे build और test किया जाता है - TODO में ये items बचे हैं
- stable DFT version
- full Unicode support
- ERE features completion
[]के अंदर negation^- character classes
$^anchor symbols
- efficient range handling
संदर्भित approaches
- regular expression matching approach Russ Cox के Regular Expression Matching Can Be Simple And Fast से काफी inspired है
- transducer determinization idea Cyril Allauzen, Mehryar Mohri के Finitely Subsequential Transducers से लिया गया है
- parsing approach Erik Eidt के Double-E algorithm का उपयोग करता है, और classic Shunting Yard algorithm के करीब है
1 टिप्पणियां
Hacker News टिप्पणियां
यह देखना दिलचस्प होगा कि यह project कहां जाता है। हालांकि operator precedence अप्राकृतिक लगता है, और लगता है इस thread में बाकी लोगों को भी कुछ ऐसा ही महसूस हुआ
cat:dogको स्वाभाविक रूप सेca(t:d)ogनहीं, बल्कि(cat):(dog)जैसा मानने की उम्मीद होती हैमुझे भी यह बात उलझाने वाली लगी कि
cat:dogको(cat):(dog)नहीं बल्किca(t:d)ogकी तरह parse किया जाता है, लेकिन जब यह याद आया कि हम सब regex का थोड़ा गलत इस्तेमाल कर रहे हैं, तो बात समझ में आई। regex को “मूल रूप से” matcher नहीं, बल्कि string generator के रूप में देखना चाहिए, इसलिएcat|dogको औपचारिक रूप से{catog,cadog}जैसे set में expand होता माना जा सकता हैmatching में इस string set को किसी बड़े text के against substring matching कर देना होता है। समस्या यह है कि असल regex engines में से ज्यादातर इस तरह काम नहीं करते, और अपेक्षाओं से मेल खाने या efficiency के लिए कई अजीब व्यवहार करते हैं
अलग-अलग regex tools आजमाने पर
(cat)|(dog)या(cat)|(dog)|(ca[td]og)जैसी variations निकलती हैं। इसलिए ज्यादा formal नजरिए से देखें तोcat:dogकाca(t:d)ogबनाना सही है, न कि(cat):(dog)। लेकिन दशकों से regex को user expectations के हिसाब से matching tool की तरह misuse करने के अनुभव के कारण अब हर कोई जिस expression को replace करना चाहता है, उसके चारों ओर parentheses लगा देता हैयह proposal दिलचस्प और अच्छी तरह design किया गया है, लेकिन अंत में यह regex को उसके मूल generator model में वापस ले जाने जैसा लगता है। समस्या grammar से ज्यादा tools की तरफ है
मैंने पहले इस field के आसपास काम किया है, और अगर आपने regex को string set generator के रूप में कभी नहीं सोचा है, तो यहां खेलकर देख सकते हैं: https://onlinestringtools.com/generate-string-from-regex
हालांकि ऐसे generation tools का behavior भी बहुत specific होता है। जिन tools का मैं इस्तेमाल करता था, उनमें closures आदि पर constraints specify करके generator को सीमित करने के कई तरीके थे
अगर इसे concatenation के बाद तक टाल दें, तो दूसरी समस्याएं पैदा हो सकती हैं। उदाहरण के लिए non-associative
:मेंcat:dog:mouseillegal होना पड़ सकता है, लेकिन इसे कैसे संभालना है, इस पर मुझे यकीन नहीं हैमौजूदा version में epsilon, यानी खाली string, insert की जाती है। उदाहरण के लिए हर दूसरे character को छोड़ते हुए हटाने के लिए technically
.(.:eps)यानी..:चला सकते हैंecho 'abcde' | ./trre '..:'का result'ace'हैदरअसल
:composition का अर्थ regular relations की composition भी हो सकता है, लेकिन फिलहाल मुझे यह बहुत complex लगा[a:A-z:Z]के बजाय[a-z:A-Z]बेहतर है, और[a:b-y:zz:a]के बजाय[a-y:b-z;z:a]जैसी form propose करना चाहूंगाअगर आपकी रुचि finite-state transducers और संबंधित tools में है, तो XFST(Xerox Finite-State Transducer) देखने लायक है। computational linguistics applications में इसका इस्तेमाल 20 साल से ज्यादा समय से होता आ रहा है
PARC के एक Finnish researcher UT की class में आए थे और उन्होंने FST से Finnish morphology handle करने का तरीका दिखाया था, जो देखने में भी काफी impressive था
PARC में हुए काम को explain करता है
OpenFst transducers के लिए वाकई शानदार library है। Johns Hopkins आदि द्वारा assignments के रूप में बनाए गए Pynini usage tutorials भी अच्छे हैं
[1] https://www.openfst.org/twiki/bin/view/GRM/Pynini
[2] https://www.openfst.org/
अगर आप standard regex के alternatives ढूंढ रहे हैं, खासकर जब group logic कठिन लगता हो या maintainable expressions चाहिए हों, तो Rosie Pattern Language उपयुक्त हो सकती है
https://gitlab.com/rosie-pattern-language/rosie/-/blob/maste...
https://rosie-lang.org/about/
शानदार। मैंने लगभग 1997 में computer science का Diplom thesis finite-state transducers पर लिखा था, और यह जितना लगता था उससे कहीं कम trivial था
assignment यह था कि जहां संभव हो composition और DFA implement किया जाए, और composed transducers भी शामिल थे। यह “finite-state transducers का algebra” था और use case morphology था। topic को काफी underestimate किया गया था, इसलिए मुझे बीच में ही खत्म करना पड़ा। इसलिए respect
grammar के बारे में, सच में यह जानना चाहूंगा कि क्या आप चाहते हैं कि
:concatenationabसे ज्यादा tightly bind करे20 साल बाद भी project जारी है, यह देखकर अच्छा लगा: https://www.openfst.org/twiki/bin/view/FST/WebHome
:को concatenation से ज्यादा tightly bind करना चाहिए या नहीं, इस पर अभी मुझे यकीन नहीं है। करीब 100 examples देखकर मुझे current approach, यानी:का.से lower precedence होना, ज्यादा natural लगा, लेकिन code में literally सिर्फ एक number बदलने से यह change हो सकता है। इसलिए इसे यहां post किया है, और असली feedback चाहिएजिस पल आप किसी तरह का structural substitution करना चाहते हैं, यह तरीका पर्याप्त नहीं लगता। उदाहरण के लिए, कभी-कभी
s/"([^"]*)"/'$1'/जैसा कुछ करना होता हैइसके अलावा, अगर
[^"]में से[']से match होने वाली चीज़ को\'में बदल सकें, तो यह और उपयोगी लगेगाअधिक सामान्य रूप से, regex match result के लिए असल में एक parse tree define करता है, इसलिए उस tree पर ज्यादा सामान्य transformations कर पाना उपयोगी होगा
":'(':(\\')|[^"'])*":'"..."block के अंदर की content बदलना चाहते हैं और quotes को single quotes'में बदलना चाहते हैंयह expression ऐसा कर सकता है:
echo '"hello world" "hello again!"' | ./trre "\":'.+?:-\":'"result है
'-' '-'यानी
".+?:-"expression""के अंदर के text को-symbol से replace करते हुए साथ-साथ आसपास के quotes भी बदल देता है। question mark का मतलब non-greedy mode हैऐसा लगता है कि पूरा project इस दावे पर टिका है कि “regex text में patterns ढूंढने के लिए शानदार tools हैं, लेकिन text editing के लिए हमेशा unnatural लगे”, मगर कोई example ही नहीं है
मुझे समझ नहीं आता कि editing के लिए regex unnatural क्यों है। यहां editing से क्या मतलब है, यह भी नहीं पता, और लोग groups में कठिनाई क्यों झेलते हैं, यह भी नहीं समझ आता
इस project में syntax examples बहुत हैं, लेकिन यह सामान्य regex से बेहतर क्यों है, समझ नहीं आता। अगर कुछ examples हों कि “basic regex version यह है, मेरा version यह है, और इसलिए यह आसान हो जाता है”, तो शायद project समझ आ जाए
उदाहरण के लिए,
xऔरzके बीच मौजूद सिर्फyकोYमें बदलना हो, तो Python में लगभग ऐसे करेंगे:pattern = r'(x)y(z)'replacement = r'\1Y\2'result = re.sub(pattern, replacement, text)मैं इसे
xy:Yzpattern से replace करना चाहता हूं:result = re.trre('xy:Yz', text)अगर
x,zज्यादा complex patterns हों या खुद regex हों, तो यह approach ज्यादा सुविधाजनक हो सकती हैअच्छा project है
C code पढ़ना वाकई मजेदार है। बहुत अच्छा है, और अभी पढ़ रहा हूं
बस एक छोटा comment: README में
theory.pdflink broken है। PDFdocs/directory में है, इसलिए URL में बसdocs/शामिल करना होगालिखा है कि right-hand side में
*या+इस्तेमाल करने से infinite loop हो सकता है, इसलिए बचें; तो इसे सीधे ban क्यों नहीं कर देते?समझता हूं कि grammar spec ज्यादा मुश्किल हो जाएगा, लेकिन इसे बनाए रखने की कोई अच्छी वजह नहीं दिखती
असली वजह यह थी कि मैं transducer composition नाम का एक मजेदार operation implement करना चाहता था। strings पर simple operations करके trre को filter की तरह compose किया जा सकता है, लेकिन अभी इसे पूरा नहीं कर पाया। इसलिए आपका point सही है
बढ़िया exploration है, लेकिन असल में यह क्यों बेहतर है, इसके examples कम हैं। हालांकि हो सकता है मैं regex का बहुत लंबे समय से आदी हो गया हूं
उदाहरण के लिए, trre का
(cat):(dog)s/cat/dogसे क्यों बेहतर है, या(x:)ors/xor/orसे कैसे बेहतर है, यह नहीं समझ आता। लगभग सभी examples मेरे दिमाग में तुलनात्मक रूप से आसान regex से map हो जाते हैंअगर कोई core advantage है, तो शायद वह group logic की तरफ होगा, इसलिए examples भी उसी पर focus करें तो अच्छा होगा। basic syntax समझाने से पहले ही यह बताना बेहतर लगता है कि यह बेहतर choice क्यों है
Caesar cipher example में “इसे reverse direction में apply करो” feature की बहुत जरूरत लगती है। कई text substitutions में यह common request है, और इस example में खास तौर पर साफ दिखता है। programmer का दिमाग तुरंत चिल्लाता है, “एक ही logic दो बार क्यों express करनी पड़े?”
अभी नहीं पता कि यह useful है या नहीं, लेकिन लंबे समय से जमे status quo के alternatives explore करना शानदार है। आम तौर पर ऐसे प्रयासों के सफल न होने की संभावना भी बड़ी होती है, फिर भी exploration अपने-आप में देखने लायक है
स्पेसिफिकेशन काफ़ी अधूरा लगता है। पहला उदाहरण ही अजीब है:
$ echo 'cat' | trre 'c:da:ot:g'dogसमझ नहीं आता कि यहाँ क्या हो रहा है। syntax इस तरह दिया है:
TRRE <- TRRE* TRRE|TRRE TRRE.TRRETRRE <- REGEX REGEX:REGEXयहाँ parse tree क्या है?
c,daमें क्यों नहीं बदलता? या फिरcहटकरda,otमें क्यों नहीं बदलता?group operator की तुलना में ज़्यादा intuitive search/replace semantics देने का idea अच्छा है। MS-DOS के दौर में
ren .log .txtजैसा कुछ कर सकते थे और वह काम करता था; modern bash-style सोच से यह बेतुका लगता है, लेकिन देखते ही intent बहुत साफ़ थाअगर उस operator को explicitly
~कहें, तो example ऐसा दिखेगा:$ echo 'cat' | trre 'c:d~a:o~t:g'dogअनावश्यक parentheses डालें तो ऐसा होगा:
$ echo 'cat' | trre '(c:d)~(a:o)~(t:g)'dogc,daमें क्यों नहीं बदलता, यह पूरा precedence की वजह से है। इस चर्चा को देखकर लगता है कि मैंने गलत precedence चुनी है, और वही confusion पैदा कर रही हैमौजूदा precedence table इस प्रकार है:
| 1 | escape character | \ || 2 | bracket expression | [] || 3 | grouping | () || 4 | single-character ERE repetition | * + ? {m,n} || 5 | transformation | : || 6 | concatenation | . (implicit) || 8 | alternation | | |इसलिए
:की binding.यानी implicit concatenation से ज़्यादा मजबूत हैइसके बजाय अगर requirement रखें कि regex खाली नहीं होनी चाहिए, तो deletion example टूट जाता है, लेकिन ambiguity concatenation की तरफ़ चली जाती है। यानी ambiguity रहती है कि यह
(((c:d)(a:o))(t:g))है या((c:d)((a:o)(d:g)))। associativity मान लें तो यह अंतर शायद मायने नहीं रखेगाc:d,a:यानी nothing, औरot:gजैसा लगता हैलेकिन दोबारा पढ़ने पर यह सच में confusing है, और theoretical रूप से आपत्ति सही है। repository पढ़ने के बाद तो मुझे भी लगने लगा कि
cकोdaमें बदलना चाहिए, लेकिन पक्का नहीं हूँ