عالم البرمجه
اليوم موضوعنا اليوم عن
وايضا كيف يتم التعامل مع Lex و Yacc
الشروع لتثبيت رقائق على جهازك ل
PHTHON2 / 3، اتبع الخطوات الموضحة أدناه:
في الأمر الخاص بك: بيث
ون Setup.py تثبيت إذا كنت قد أكملت كل ما سبق، يجب أن تكون الآن قادرة على استخدام وحدة التردد. يمكنك اختباره عن طريق فتح مترجم بيثون و الكتابة على استيراد الشراء والملاءمة وليبة الكتابة ليلي تثبيت، و "سيتم تشغيل التوزيع المكسورة على الجهاز الخاص بك." مرحبا، العالم! "من رقائق - آلة حاسبة بسيطة دعونا نثبت قوة بلي مع مثال بسيط: هذا البرنامج سيأخذ التعبير الحسابي كمدخلات سلسلة، وحاول حلها. ويفتح حلها على الانترنت.
from ply import lex
import ply.yacc as yacc
tokens = (
'PLUS',
'MINUS',
'TIMES',
'DIV',
'LPAREN',
'RPAREN',
'NUMBER',
)
t_ignore = ' '
t_PLUS = r'+'
t_MINUS = r'-'
t_TIMES = r'*'
t_DIV = r'/'
t_LPAREN = r'('
t_RPAREN = r')'
def t_NUMBER( t ) :
r'[0-9]+'
t.value = int( t.value )
return t
def t_newline( t ):
r'
+'
t.lexer.lineno += len( t.value )
def t_error( t ):
print("Invalid Token:",t.value[0])
t.lexer.skip( 1 )
lexer = lex.lex()
precedence = (
( 'left', 'PLUS', 'MINUS' ),
( 'left', 'TIMES', 'DIV' ),
( 'nonassoc', 'UMINUS' )
)
def p_add( p ) :
'expr : expr PLUS expr'
p[0] = p[1] + p[3]
def p_sub( p ) :
'expr : expr MINUS expr'
p[0] = p[1] - p[3]
def p_expr2uminus( p ) :
'expr : MINUS expr %prec UMINUS'
p[0] = - p[2]
def p_mult_div( p ) :
'''expr : expr TIMES expr
| expr DIV expr'''
if p[2] == '*' :
p[0] = p[1] * p[3]
else :
if p[3] == 0 :
print("Can't divide by 0")
raise ZeroDivisionError('integer division by 0')
p[0] = p[1] / p[3]
def p_expr2NUM( p ) :
'expr : NUMBER'
p[0] = p[1]
def p_parens( p ) :
'expr : LPAREN expr RPAREN'
p[0] = p[2]
def p_error( p ):
print("Syntax error in input!")
parser = yacc.yacc()
res = parser.parse("-4*-(3-5)") # المدخلات
print(res)
حفظ هذا الملف كال cple.py وتشغيله. الإخراج:
-8
الذي هو الجواب الصحيح ل -4 * - (3 - 5)
.
الجزء 1: الإمكانات المدخلات مع ليكس هناك خطوتين أن التعليمات البرمجية من المثال 1 نفذت: واحد كان توكينينغ المقدمة، مما يعني أنه نظر إلى الرموز التي تشكل التعبير الحسابي، وكانت الخطوة الثانية تحليل، والتي تنطوي على تحليل الرموز المستخرجة وتقييم النتيجة. يوفر هذا القسم مثالا بسيطا على كيفية إدخال تذكير المستخدمين، ثم يكسره خط أسفل خط.
import ply.lex as lex
# قائمة أسماء رمز الملكية. هذا مطلوب دائما
tokens = [
'NUMBER',
'PLUS',
'MINUS',
'TIMES',
'DIVIDE',
'LPAREN',
'RPAREN',
]
# قواعد التعبير العادية للرموز البسيطة
t_PLUS = r'+'
t_MINUS = r'-'
t_TIMES = r'*'
t_DIVIDE = r'/'
t_LPAREN = r'('
t_RPAREN = r')'
# قاعدة تعبير منتظمة مع بعض شهادات الإجراءات
def t_NUMBER(t):
r'd+'
t.value = int(t.value)
return t
# تحديد قاعدة حتى نتمكن من تتبع أرقام الخط
def t_newline(t):
r'
+'
t.lexer.lineno += len(t.value)
# سلسلة تحتوي على أحرف تجاهل (المساحات وعلامات التبويب)
t_ignore = ' '
# خطأ في التعامل مع الحكم
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
lexer = lex.lex()
lexer.input(data)
while True:
tok = lexer.token()
if not tok:
break
print(tok)
حفظ هذا الملف كالكسل.بي. سنستخدم هذا عند بناء محلل ياك.
انهيار استيراد الوحدة باستخدام استيراد PLY.LEX يجب أن تختص كل سيرك قائمة تسمى توكينستات يعرف كل من أسماء الرمز الممكنة التي يمكن أن تنتجها ليكسر. هذه المطلوبة دائما مطلوبة.
tokens = [ 'NUMBER', 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'LPAREN', 'RPAREN', ]
يمكن أن تكون الرموز أيضا أن تكون هناك تواضع من السلاسل (بدلا من سلسلة)، حيث يظهر كل سلسلة من الرمز المميز كما هو الحال في. ويمكن تعريف قاعدة ريجيكس لكل سلسلة إما كمسلنة أو كحدالة. في كلتا الحالتين، يجب أن يكون مسبقا اسم متغير ب "T- ويندوز هو قاعدة للحصول على مطابقة الرموز. للحصول على الرموز البسيطة، يمكن تحديد التعبير العادي كسلاسل: T_Plus = R ' +' إذا كان هناك نوع من الإجراء الذي يجب القيام به، يمكن تحديد قاعدة رمز رمز كوظيفة.
def t_NUMBER(t): r'd+' t.value = int(t.value) return t
ملاحظة، يتم تحديد القاعدة كمساعدة دوك ضمن الدالة. تقبل الدالة حجة واحدة مثيل من ليكستوكين، يؤدي بعض الإجراءات ثم يعود إلى الحجة. إذا كنت ترغب في استخدام سلسلة خارجية كمحطة ريجيكس للوظيفة بدلا من تحديد سلسلة دوك، والنظر في المثال التالي:
@TOKEN(identifier) # المعرف هو سلسلة عقد الإيجاف def t_ID(t): ... # الإجراءات
مثيل كائن ليكستوكين (دعونا ندعو هذا الكائن ر) لديه السمات التالية: t.type هو نوع رمز الرمز (كما سلسلة) (على سبيل المثال: "عدد"، "زائد"، الخ). افتراضيا، تعيين T.Typece إلى اسم عادم البترفة T.الفاك الذي هو ليكسيم (النص الفعلي المتطابقة) T.Lineno الذي هو رقم السطر الحالي (هذا لا يتم تحديثها تلقائيا، كما لا يكون لكسير لا شيء من الأرقام الخطية). تحديث لينينو باستخدام وظيفة تسمى T_Newline.
def t_newline(t): r' +' t.lexer.lineno += len(t.value)
- t.lexpos الذي هو موقف الرمز المميز إلى بداية نص الإدخال. إذا لم يتم إرجاع أي شيء من وظيفة قاعدة إعادة تثبيت، يتم تجاهل الرمز المميز. إذا كنت تريد تجاهل رمز، يمكنك إضافة T_INGORE_ بادفيكس إلى متغير قاعدة إعادة التثبيت إلى تحديد بدلا من تحديد وظيفة لنفس الحكم.
- t.lexpos الذي هو موقف الرمز المميز إلى بداية نص الإدخال. إذا لم يتم إرجاع أي شيء من وظيفة قاعدة إعادة تثبيت، يتم تجاهل الرمز المميز. إذا كنت تريد تجاهل رمز، يمكنك إضافة T_INGORE_ بادفيكس إلى متغير قاعدة إعادة التثبيت إلى تحديد بدلا من تحديد وظيفة لنفس الحكم.
def t_COMMENT(t): r'#.*' pass # قيمة قيمة. رموز التخلص منها
...هو نفسه:
t_ignore_COMMENT = r'#.*'
هذا بالطبع غير صالح إذا كنت تنفذ بعض الإجراءات عند رؤية تعليق. في حالة الحالة، استخدم وظيفة لتحديد قاعدة إعادة تثبيت. إذا لم تكن قد حددت رمزا لبعض الأحرف ولكن لا يزال يريد تجاهله، استخدم T_INGORE = "<أحرف إلى تجاهل>" (هذه البادئات ضرورية):
t_ignore_COMMENT = r'#.*' t_ignore = ' ' # يتجاهل المساحات وعلامات التبويب
عند بناء ميداع ريكجس، سوف ليكس إضافة مجلدات المحددة في الملف كما يلي: يتم إضافة الرموز تعريف المحدد في نفس النظام كما تظهر في الملف. يتم إضافة الرموز تعريف الحالات التي تم تحديدها من خلال سلسلة من سلسلة السلسلة التي تحدد ريجيكس لهذا الرمز المميز. إذا كنت مطابقة == و = في نفس الملف، والاستفادة من هذه القواعد. الحرفية هي الرموز التي يتم إرجاعها كما هي. يتم تعيين كل من T.Type و T.VAWEWIL الحرف إلى الحرف نفسه. تحديد قائمة من الحرفية مثل هذا:
انضر
literals = [ '+', '-', '*', '/' ]
او....,
literals = "+-*/"
فمن الممكن كتابة وظائف رمز مائية التي تؤدي إجراءات إضافية عندما يتم تطابق الحرف اليدوية. ومع ذلك، ستحتاج إلى تعيين نوع رمز الرمل بشكل مناسب. على سبيل المثال:
literals = [ '{', '}' ] def t_lbrace(t): r'{' t.type = '{' # تعيين نوع الرمز إلى الحرفية المتوقع (المطلق يجب إذا كان هذا حرفيا) return t
التعامل مع أخطاء مع وظيفة T_ERROR.
# def t_error(t): print("Illegal character '%s'" % t.value[0]) t.lexer.skip(1)
بشكل عام، T.LEXER.SKIP (N) تخطي أحرف N في سلسلة الإدخال. الاستعدادات النهائية: بناء ليكسر باستخدام ليكسر = LEX.LEX () يمكنك أيضا وضع كل شيء داخل مثال استخدام واستعمال استخدام الفئة لتحديد ليكسر. إغ:
import ply.lex as lex class MyLexer(object): ... # كل شيء يتعلق بقواعد رمز ويمكن التعامل مع الخطأ هنا كالمعتاد # بناء ليكسر def build(self, **kwargs): self.lexer = lex.lex(module=self, **kwargs) def test(self, data): self.lexer.input(data) for token in self.lexer.token(): print(token) # بناء ليكسر ومحاولة بها m = MyLexer() m.build() # بناء ليكسر m.test("3 + 4") #
توفير المدخلات باستخدام Lexer.Input (البيانات) حيث البيانات هي سلسلة للحصول على الرموز، واستخدام Lexer.Token () التي تعود تافكر مطابقة. يمكنك تكرار أكثر من ليكسر في حلقة كما في:
for i in lexer: print(i)
الجزء 2: تحليل الإمكانية التكوين مع ياك هذا القسم يشرح كيف يتم معالجة المدخلات التكوينية من الجزء 1 - يتم ذلك باستخدام غرام السائقين الحرفية (سفغس). يجب تحديد قواعد اللغة، ويتم معالجة الرموز وفقا لحبر القواعد. تحت غطاء محرك السيارة، تستخدم محلل محلل لالر.
# Yacc مثال
import ply.yacc as yacc
# الحصول على خريطة ملكية من ليكسر. هذا مطلوب.
from calclex import tokens
def p_expression_plus(p):
'expression : expression PLUS term'
p[0] = p[1] + p[3]
def p_expression_minus(p):
'expression : expression MINUS term'
p[0] = p[1] - p[3]
def p_expression_term(p):
'expression : term'
p[0] = p[1]
def p_term_times(p):
'term : term TIMES factor'
p[0] = p[1] * p[3]
def p_term_div(p):
'term : term DIVIDE factor'
p[0] = p[1] / p[3]
def p_term_factor(p):
'term : factor'
p[0] = p[1]
def p_factor_num(p):
'factor : NUMBER'
p[0] = p[1]
def p_factor_expr(p):
'factor : LPAREN expression RPAREN'
p[0] = p[2]
# قاعدة خطأ لإصلاح الأخطاء
def p_error(p):
print("Syntax error in input!")
# بناء محلل
parser = yacc.yacc()
while True:
try:
s = raw_input('calc > ')
except EOFError:
break
if not s: continue
result = parser.parse(s)
print(result)
انهيار
يتم تعريف كل قاعدة قواعد مملة من قبل وظيفة حيث يحتوي دوكسترينغ على تلك الوظيفة على مواصفات قواعد اللغة السياحية الموضعية. وبيانات تشكل الجسم وظيفة تنفيذ الإجراءات الدلالية للحكم. يقبل كل وظيفة حجة واحدة صادرة عن تكلفة تسلق كل قسم قواعد اللغة في القاعدة المقابلة. يتم تعيين القيم [1] إلى الرموز النحوية كما هو مبين أدناه:
def p_expression_plus(p): 'expression : expression PLUS term' # ^ ^ ^ ^ # p[0] p[1] p[2] p[3] p[0] = p[1] + p[3]
للتكيف، "قيمة" من المادة P] [هي نفسها من السمة thep.value المخصصة في وحدة ليكسر. لذلك، بالإضافة إلى سيكون القيمة +.
بالنسبة إلى غير المحطات، يتم تحديد القيمة من قبل ما يجري وضعها في ص [0]. إذا كان لا شيء لا يتم وضعها، والقيمة ليست أيا. أيضا، [1] ليست هي نفس P [3]، ولكن p ليست قائمة بسيطة (P [-1] يمكن تحديد الإجراءات المضمنة (لا مناقشة هنا)). لاحظ أن الدالة يمكن أن يكون أي اسم، طالما يتم فحسب بواسطة P_.
يتم تعريف قاعدة P_ERROR (P) بالصيت أخطاء بناء الجملة (نفس ييرور في ياك / بيسون). ويمكن دمج قواعد قواعد متعددة في وظيفة واحدة، وهو فكرة جيدة إذا كانت الإنتاج لها هيكل مماثل.
def p_binary_operators(p): '''expression : expression PLUS term | expression MINUS term term : term TIMES factor | term DIVIDE factor''' if p[2] == '+': p[0] = p[1] + p[3] elif p[2] == '-': p[0] = p[1] - p[3] elif p[2] == '*': p[0] = p[1] * p[3] elif p[2] == '/': p[0] = p[1] / p[3]
يمكن استخدام الحرفية الحرفية بدلا من الرموز.
def p_binary_operators(p): '''expression : expression '+' term | expression '-' term term : term '*' factor | term '/' factor''' if p[2] == '+': p[0] = p[1] + p[3] elif p[2] == '-': p[0] = p[1] - p[3] elif p[2] == '*': p[0] = p[1] * p[3] elif p[2] == '/': p[0] = p[1] / p[3]
وبطبيعة الحال، يجب تحديد الحرف اليدوية في وحدة ليكسر. إنتاج فارغة لديها شكل '' 'رمز:' '' تعيين صراحة رمز البدء، واستخدام ستارت = 'فو "، حيث فو هو بعض غير المحطة. إعداد الأسبقية والأنتم يمكن أن يتم استخدامها باستخدام متغير الأسبقية.
precedence = ( ('nonassoc', 'LESSTHAN', 'GREATERTHAN'), # المشغلين غير اللائقين ('left', 'PLUS', 'MINUS'), ('left', 'TIMES', 'DIVIDE'), ('right', 'UMINUS'), # مشغل ناقش لا يوناري )
يتم ترتيب الرموز من أدنى لأسباب قصوى. لا يمكن ل نوساس أن تلك الرموز لا تؤدي. وهذا يعني أن شيئا مثل
parser.out هو ملف تصحيح يتم إنشاؤه عند تنفيذ برنامج ياك للمرة الأولى. كلما حدث تحول / تقليل الصراع، تتحول محلل دائما.
تعليقات
إرسال تعليق