अपनी भाषा में पीसीआरई लागू करें।


13

नोट: यह खुद की कोशिश करने के बाद, मुझे जल्द ही एहसास हुआ कि यह क्या गलती थी। इसके अलावा, मैं नियमों को थोड़ा संशोधित कर रहा हूं।

न्यूनतम आवश्यक कार्यक्षमता:

  • चरित्र वर्गों ( ., \w, \W, आदि)
  • गुणक ( +, *और ?)
  • साधारण कब्जा समूह

आपकी चुनौती निम्नलिखित शर्तों के अधीन अपनी पसंद की भाषा में पीसीआरई को लागू करना है :

  • आप किसी भी तरह से अपनी भाषा के मूल RegEx सुविधाओं का उपयोग नहीं कर सकते हैं । आप तृतीय पक्ष RegEx लाइब्रेरी का उपयोग नहीं कर सकते हैं।
  • आपका प्रवेश PCRE युक्ति के रूप में लागू होना चाहिए। यथासंभव।
  • आपके कार्यक्रम को इनपुट, 2 लाइनों के रूप में स्वीकार करना चाहिए:

    • नियमित अभिव्यक्ति
    • के खिलाफ मिलान करने के लिए स्ट्रिंग इनपुट
  • आपका कार्यक्रम इसके आउटपुट में इंगित करना चाहिए:

    • क्या RegEx इनपुट स्ट्रिंग में कहीं भी मेल खाता है
    • किसी भी कैप्चरिंग समूहों के परिणाम
  • विजेता वह प्रविष्टि होगी जो कल्पना के रूप में अधिक लागू होती है। यथासंभव। एक टाई के मामले में, विजेता सबसे रचनात्मक प्रविष्टि होगी, जैसा कि मेरे द्वारा जज किया गया है।


संपादित करें: कुछ चीजों को स्पष्ट करने के लिए, यहां इनपुट और अपेक्षित आउटपुट के कुछ उदाहरण दिए गए हैं:


  • इनपुट:
^ \ S * (\ w +) $
         नमस्कार
  • आउटपुट:
मैच: हाँ
समूह 1: 'हैलो'

  • इनपुट:
(\ W +) @ (\ w +) (:। \ कॉम | \ .net)
sam@test.net
  • आउटपुट:
मैच: हाँ
समूह 1: 'सैम'
समूह 2: 'परीक्षण'


पीसीआरई में सुविधाओं की मात्रा को देखते हुए यह एक बहुत ही चुनौतीपूर्ण चुनौती है। रिकर्सियन, बैकट्रैकिंग, लुकहेड / एसेसरीज, यूनिकोड, कंडीशनल सबपैटर्न, ...
अरनौद ले ब्लांक

1
PCRE डॉक्स देखें ; पर्ल आरई ; PHP PCRE डॉक्स बहुत अच्छे हैं।
अरनौद ले ब्लैंक

@ user300: लक्ष्य जितना संभव हो उतना लागू करना है। जाहिर है सब कुछ थोड़ा बहुत कठिन होगा।
नाथन उस्मान

2
@ जॉर्ज: आप अपने बारे में उन विशेषताओं को सूचीबद्ध करते हैं, जिन्हें आप चाहते हैं और कुछ परीक्षण के मामले दें, बस इतना कि हम सब जमीन पर भी हैं।
मार्को डुमिक

1
@ जॉर्ज: मुझे लगता है कि @Marko विशिष्ट सुविधाओं के बाद था, या यूँ कहें कि आप जिस न्यूनतम सब्मिट को लोगों को पहले लागू करना चाहते थे। कुल मिलाकर, हालांकि, पीसीआरई एक आकस्मिक कोडिंग प्रतियोगिता के लिए वास्तव में बहुत कठिन चुनौती है। मैं इसे बहुत छोटे, विशिष्ट आरई सबसेट में बदलने का सुझाव देता हूं और इसे लागू करने की चुनौती देता हूं।
MtnViewMark

जवाबों:


10

अजगर

चूंकि पूर्ण PCRE को लागू करना बहुत अधिक है, इसलिए मैंने केवल एक आवश्यक उपसमुच्चय को लागू किया है।

का समर्थन करता है |.\.\w\W\s+*()। इनपुट regexp सही होना चाहिए।

उदाहरण:

$ python regexp.py 
^\s*(\w+)$
   hello
Matches:     hello
Group 1 hello

$ python regexp.py
(a*)+
infinite loop

$ python regexp.py 
(\w+)@(\w+)(\.com|\.net)
sam@test.net
Matches:  sam@test.net
Group 1 sam
Group 2 test
Group 3 .net

यह काम किस प्रकार करता है:

विस्तृत सिद्धांत के लिए यह परिचय ऑटोमेटा थ्योरी, भाषा और संगणना के लिए पढ़ें ।

यह विचार मूल नियमित अभिव्यक्ति को एक नॉडटर्मिनिस्ट परिमित ऑटोमेटा (एनएफए) में बदलने का है। दरअसल, पीसीआरई के नियमित भाव कम से कम संदर्भ मुक्त व्याकरण हैं, जिनके लिए हमें पुश-डाउन ऑटोमेटा की आवश्यकता है, लेकिन हम खुद को पीसीआरई के सबसेट में सीमित कर देंगे।

परिमित ऑटोमेटा निर्देशित ग्राफ होते हैं जिनमें नोड्स अवस्थाएं होती हैं, किनारे संक्रमण होते हैं और प्रत्येक संक्रमण में एक मिलान इनपुट होता है। प्रारंभ में आप पूर्वनिर्धारित प्रारंभ नोड से शुरू करते हैं। जब भी आपको एक इनपुट प्राप्त होता है जो आपके द्वारा किए गए संक्रमण से मेल खाता है तो आप उस संक्रमण को एक नए राज्य में ले जाते हैं। यदि आप एक टर्मिनल नोड पर पहुंच गए, तो इसे ऑटोमेटा स्वीकृत इनपुट कहा जाता है। हमारे मामले में इनपुट एक मेल खाने वाला फ़ंक्शन है जो सही रिटर्न देता है।

उन्हें नोंडेटर्मिनिस्ट ऑटोमेटा कहा जाता है क्योंकि कभी-कभी अधिक मिलान संक्रमण होते हैं जो आप एक ही राज्य से ले सकते हैं। मेरे कार्यान्वयन में एक ही राज्य में सभी संक्रमण एक ही चीज़ से मेल खाना चाहिए, इसलिए मैंने गंतव्य राज्य ( states[dest][0]) के साथ मिलान फ़ंक्शन को संग्रहीत किया ।

हम बिल्डिंग ब्लॉक का उपयोग करके अपने रेगेक्सप को एक परिमित ऑटोमेटा में बदल देते हैं। एक बिल्डिंग ब्लॉक में एक स्टार्ट नोड ( first) और एक एंड नोड ( last) होता है और टेक्स्ट से कुछ से मेल खाता है (संभव खाली स्ट्रिंग)।

सबसे सरल उदाहरणों में शामिल हैं

  • कुछ भी नहीं मिलान: True( first == last)
  • एक चरित्र का मिलान: c == txt[pos]( first == last)
  • स्ट्रिंग का मिलान अंत: pos == len (txt) (पहले == last`)

आपको पाठ में नई स्थिति की भी आवश्यकता होगी जहां अगले टोकन से मिलान करना है।

अधिक जटिल उदाहरण हैं (ब्लॉक के लिए कैपिटल लेटर्स स्टैंड)।

  • मिलान B +:

    • नोड बनाएँ: यू, वी (कुछ भी नहीं मिलान)
    • संक्रमण बनाएँ: u -> B.first, B.last -> v, v -> u
    • जब आप नोड वी से पहले ही मिल जाते हैं, तब आपके पास बी दो विकल्प होते हैं: आगे जाएं, या फिर बी से मिलान करने का प्रयास करें।
  • मिलान ए | बी | सी:

    • नोड बनाएँ: यू, वी (कुछ भी नहीं मिलान)
    • संक्रमण बनाएँ: u -> A.first, u -> C.first, u -> C.first, आदि
    • संक्रमण बनाएँ: A-> अंतिम -> v, B-> अंतिम -> v, C-> अंतिम -> v,
    • यू से आप किसी भी ब्लॉक में जा सकते हैं

सभी regexp ऑपरेटरों को इस तरह से बदला जा सकता है। बस के लिए एक कोशिश दे *

अंतिम भाग regexp को पार्स करना है जिसके लिए एक बहुत ही सरल व्याकरण की आवश्यकता होती है:

 or: seq ('|' seq)*
 seq: empty
 seq: atom seq
 seq: paran seq
 paran: '(' or ')'

उम्मीद है कि एक साधारण व्याकरण को लागू करना (मुझे लगता है कि एलएल (1) है, लेकिन मुझे गलत होने पर सही करें) एनएफए बनाने की तुलना में बहुत आसान है।

एक बार जब आपके पास एनएफए हो जाता है तो आपको टर्मिनल नोड तक पहुंचने के लिए पीछे हटना पड़ता है।

स्रोत कोड (या यहाँ ):

from functools import *

WORDCHAR = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_'


def match_nothing(txt, pos):
  return True, pos

def match_character(c, txt, pos):
  return pos < len(txt) and txt[pos] == c, pos + 1

def match_space(txt, pos):
  return pos < len(txt) and txt[pos].isspace(), pos + 1

def match_word(txt, pos):
  return pos < len(txt) and txt[pos] in WORDCHAR, pos + 1

def match_nonword(txt, pos):
  return pos < len(txt) and txt[pos] not in WORDCHAR, pos + 1

def match_dot(txt, pos):
  return pos < len(txt), pos + 1

def match_start(txt, pos):
  return pos == 0, pos

def match_end(txt, pos):
  return pos == len(txt), pos


def create_state(states, match=None, last=None, next=None, name=None):
  if next is None: next = []
  if match is None: match = match_nothing

  state = len(states)
  states[state] = (match, next, name)
  if last is not None:
    states[last][1].append(state)

  return state


def compile_or(states, last, regexp, pos):
  mfirst = create_state(states, last=last, name='or_first')
  mlast = create_state(states, name='or_last')

  while True:
    pos, first, last = compile_seq(states, mfirst, regexp, pos)
    states[last][1].append(mlast)
    if pos != len(regexp) and regexp[pos] == '|':
      pos += 1
    else:
      assert pos == len(regexp) or regexp[pos] == ')'
      break

  return pos, mfirst, mlast


def compile_paren(states, last, regexp, pos):
  states.setdefault(-2, [])   # stores indexes
  states.setdefault(-1, [])   # stores text

  group = len(states[-1])
  states[-2].append(None)
  states[-1].append(None)

  def match_pfirst(txt, pos):
    states[-2][group] = pos
    return True, pos

  def match_plast(txt, pos):
    old = states[-2][group]
    states[-1][group] = txt[old:pos]
    return True, pos

  mfirst = create_state(states, match=match_pfirst, last=last, name='paren_first')
  mlast = create_state(states, match=match_plast, name='paren_last')

  pos, first, last = compile_or(states, mfirst, regexp, pos)
  assert regexp[pos] == ')'

  states[last][1].append(mlast)
  return pos + 1, mfirst, mlast


def compile_seq(states, last, regexp, pos):
  first = create_state(states, last=last, name='seq')
  last = first

  while pos < len(regexp):
    p = regexp[pos]
    if p == '\\':
      pos += 1
      p += regexp[pos]

    if p in '|)':
      break

    elif p == '(':
      pos, first, last = compile_paren(states, last, regexp, pos + 1)

    elif p in '+*':
      # first -> u ->...-> last -> v -> t
      # v -> first (matches at least once)
      # first -> t (skip on *)
      # u becomes new first
      # first is inserted before u

      u = create_state(states)
      v = create_state(states, next=[first])
      t = create_state(states, last=v)

      states[last][1].append(v)
      states[u] = states[first]
      states[first] = (match_nothing, [[u], [u, t]][p == '*'])

      last = t
      pos += 1

    else:  # simple states
      if p == '^':
    state = create_state(states, match=match_start, last=last, name='begin')
      elif p == '$':
    state = create_state(states, match=match_end, last=last, name='end')
      elif p == '.':
    state = create_state(states, match=match_dot, last=last, name='dot')
      elif p == '\\.':
    state = create_state(states, match=partial(match_character, '.'), last=last, name='dot')
      elif p == '\\s':
    state = create_state(states, match=match_space, last=last, name='space')
      elif p == '\\w':
    state = create_state(states, match=match_word, last=last, name='word')
      elif p == '\\W':
    state = create_state(states, match=match_nonword, last=last, name='nonword')
      elif p.isalnum() or p in '_@':
    state = create_state(states, match=partial(match_character, p), last=last, name='char_' + p)
      else:
    assert False

      first, last = state, state
      pos += 1

  return pos, first, last


def compile(regexp):
  states = {}
  pos, first, last = compile_or(states, create_state(states, name='root'), regexp, 0)
  assert pos == len(regexp)
  return states, last


def backtrack(states, last, string, start=None):
  if start is None:
    for i in range(len(string)):
      if backtrack(states, last, string, i):
    return True
    return False

  stack = [[0, 0, start]]   # state, pos in next, pos in text
  while stack:
    state = stack[-1][0]
    pos = stack[-1][2]
    #print 'in state', state, states[state]

    if state == last:
      print 'Matches: ', string[start:pos]
      for i in xrange(len(states[-1])):
    print 'Group', i + 1, states[-1][i]
      return True

    while stack[-1][1] < len(states[state][1]):
      nstate = states[state][1][stack[-1][1]]
      stack[-1][1] += 1

      ok, npos = states[nstate][0](string, pos)
      if ok:
    stack.append([nstate, 0, npos])
    break
      else:
    pass
    #print 'not matched', states[nstate][2]
    else:
      stack.pop()

  return False



# regexp = '(\\w+)@(\\w+)(\\.com|\\.net)'
# string = 'sam@test.net'
regexp = raw_input()
string = raw_input()

states, last = compile(regexp)
backtrack(states, last, string)

1
+1 वाह ... मैंने खुद को PHP के साथ करने की कोशिश की और पूरी तरह से विफल रहा।
नाथन उस्मान

टीआईएल (a+b)+मैच abaabaaabaaaab
अलेक्जेंड्रू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.