পাইথন
যেহেতু পুরো পিসিআরই বাস্তবায়ন অত্যধিক তাই আমি এর একটি প্রয়োজনীয় উপসেটটি প্রয়োগ করেছি।
সমর্থন |.\.\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
)
- স্ট্রিংয়ের সমাপ্তি: পোস্ট == লেন (টেক্সট)
(
প্রথম == শেষ=)
পরবর্তী টোকেনটি কোথায় মিলবে তা পাঠ্যের ক্ষেত্রেও আপনাকে নতুন অবস্থানের প্রয়োজন হবে।
আরও জটিল উদাহরণগুলি হ'ল (মূল অক্ষরগুলি ব্লকের জন্য দাঁড়ায়)।
মিলবে বি +:
- নোড তৈরি করুন: ইউ, ভি (কিছুই মিলছে না)
- রূপান্তরগুলি তৈরি করুন: ইউ -> বিফার্স্ট, বিস্ট -> ভি, ভি -> ইউ
- আপনি যখন নোড পাবেন তখন ইতিমধ্যে আপনার বিয়ের সাথে মিল আছে Then তারপরে আপনার দুটি বিকল্প রয়েছে: আরও এগিয়ে যান, বা আবার বি এর সাথে মেলে দেখার চেষ্টা করুন।
মিলবে এ | বি | সি:
- নোড তৈরি করুন: ইউ, ভি (কিছুই মিলছে না)
- রূপান্তরগুলি তৈরি করুন: u -> এফার্স্ট, u -> সিফার্স্ট, u -> সিফার্স্ট,
- রূপান্তর তৈরি করুন: এ-> সর্বশেষ -> ভি, বি-> সর্বশেষ -> ভি, সি-> শেষ -> ভি,
- আপনার কাছ থেকে আপনি যে কোনও ব্লকে যেতে পারেন
সমস্ত রেজপ্লেক্স অপারেটরকে এভাবে রূপান্তর করা যায়। শুধু চেষ্টা করুন *
।
শেষ অংশটি রেজিএক্সপ্সকে পার্স করা যার জন্য খুব সাধারণ ব্যাকরণের প্রয়োজন:
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)