Python тілінің интерпретаторы құрылғысы

Python тілінің интерпретаторы құрылғысы.
Дәрістің мақсаты: Python тілінің интерпретаторы құрылғысын қарастыру.
Тақырып бойынша қарастырылатын сұрақтар:
1. Лексикалық талдау.
2. Синтаксистік талдау.
3. Байт-кодты алу.
4. Баптау.

1. Лексикалық талдау.

Программалау тілінің лексикалық анализаторы бағдарламаның бастапқы мәтінін (жалғыз таңбадан тұратын) лексемаларға — тілдің бөлінбейтін «сөздеріне» бөледі.

Python лексемасының негізгі категориялары: идентификаторлар және кілт сөздер (NAME), литералдар (STRING, NUMBER және т.б.), операциялар (OP), бөлгіштер, шегіністерді (INDENT, DEDENT) және жолдардың ұштарын (NEWLINE) белгілеу (өзгерту) үшін арнайы лексемалар, сондай-ақ түсініктемелер (COMMENT). Лексикалық анализатор tokenize модулі арқылы қол жетімді, ал лексемалардың кодтарын анықтау Python стандартты кітапханасының token модулінде болады. Келесі мысал лексикалық талдағышты іс-әрекетте көрсетеді:

import StringIO, token, tokenize

prog_example = «»»
for i in range(100): # comment
if i % 1 == 0: \
print «:», t**2
«»».strip()

rl = StringIO.StringIO(prog_example).readline

for t_type, t_str, (br,bc), (er,ec), logl in tokenize.generate_tokens(rl):
print «%3i %10s : %20r» % (t_type, token.tok_name[t_type], t_str)


Бірақ бұл бағдарламаны лексемаға бөліп, мысалдың бастапқы кодын шығарады:

prog_example:

1 NAME : ‘for’
1 NAME : ‘i’
1 NAME : ‘in’
1 NAME : ‘range’
50 OP : ‘(‘
2 NUMBER : ‘100’
50 OP : ‘)’
50 OP : ‘:’
52 COMMENT : ‘# comment’
4 NEWLINE : ‘\n’
5 INDENT : ‘ ‘
1 NAME : ‘if’
1 NAME : ‘i’
50 OP : ‘%’
2 NUMBER : ‘1’
50 OP : ‘==’
2 NUMBER : ‘0’
50 OP : ‘:’
1 NAME : ‘print’
3 STRING : ‘»:»‘
50 OP : ‘,’
1 NAME : ‘t’
50 OP : ‘**’
2 NUMBER : ‘2’
6 DEDENT : »
0 ENDMARKER : »

Іс жүзінде әртүрлі мақсаттар үшін пайдаланылуы мүмкін лексемдер ағыны алынды. Мысалы, Python тілінде синтаксистік «бояу» коды үшін. Token.tok_name сөздігі нөмірі бойынша лексема түріне арналған мнемоникалық атауларды алуға мүмкіндік береді.


2. Синтаксистік талдау.

Бағдарламаның бастапқы мәтінін интерпретатордың байт-кодына түрлендірудің екінші сатысы бастапқы мәтінді синтаксистік талдаудан тұрады. Parser модулі бағдарламалар коды мен Python өрнектері үшін сәйкесінше синтаксистік талдау ағаштарын құру үшін suite() және Express() функцияларын қамтиды. Symbol модулі Python грамматикасының символдарының нөмірлерін, Python грамматикасының символының атауын алу үшін сөздікті қамтиды.

Келесі бағдарлама қарапайым Python ( рrg ) кодын талдайды және синтаксистік талдау ағашын (AST-нысан) туындатады, ол бірден кортежге айналуға және pprint функциясымен әдемі шығаруға болады.pprint (). Бұдан әрі символдардың нөмірлерін грамматикада олардың мнемоникалық белгілеріне (аттарына) айналдыру функциясы анықталады:

import pprint, token, parser, symbol

prg = «»»print 2*2″»»

pprint.pprint(parser.suite(prg).totuple())

def pprint_ast(ast, level=0):
if type(ast) == type(()):
for a in ast:
pprint_ast(a, level+1)
elif type(ast) == type(«»):
print repr(ast)
else:
print » «*level,
try:
print symbol.sym_name[ast]
except:
print «token.»+token.tok_name[ast],

print
pprint_ast(parser.suite(prg).totuple())

Бұл бағдарлама келесілерді шығарады (ағаш құрылымы шегіністермен көрсетілген):

(257,
(264,
(265,
(266,
(269,
(1, ‘print’),
(292,
(293,
(294,
(295,
(297,
(298,
(299,
(300,
(301,
(302,
(303, (304, (305, (2, ‘2’)))),
(16, ‘*’),
(303, (304, (305, (2, ‘2’)))))))))))))))),
(4, »))),
(0, »))

file_input
stmt
simple_stmt
small_stmt
print_stmt
token.NAME ‘print’
test
and_test
not_test
comparison
expr
xor_expr
and_expr
shift_expr
arith_expr
term
factor
power
atom
token.NUMBER ‘2’
token.STAR ‘*’
factor
power
atom
token.NUMBER ‘2’
token.NEWLINE »
token.ENDMARKER »


3. Байт-кодты алу.

Синтаксистік талдау ағашын алғаннан кейін, компилятор оны интерпретатор орындау үшін жарамды байт-кодқа айналдыруы тиіс. Келесі бағдарламада жеке синтаксистік талдау, Python тілінде кодты (және өрнектерді) құрастыру және орындау (есептеу) жүргізіледі:

import parser

prg = «»»print 2*2″»»
ast = parser.suite(prg)
code = ast.compile(‘filename.py’)
exec code

prg = «»»2*2″»»
ast = parser.expr(prg)
code = ast.compile(‘filename1.py’)
print eval(code)

Parser функциясы.suite () (немесе parser.Express () compile () әдісімен Python байт кодына жинақталатын және code кодында сақталатын AST нысанын (синтаксистік талдау ағашы) қайтарады. Енді бұл кодты exec (немесе eval () функциясы) операторының көмегімен орындауға болады.

Мұнда Python-да соңғы кодты талдау және кодты генерациялау үшін модульдерді біріктіретін compiler пакеті жақында пайда болғанын ескеру қажет. Бұл дәрісте ол қарастырылмайды,бірақ осы процестерді тереңірек зерттегісі келетіндер Python бойынша құжаттарға жүгіне алады.

Байт-кодты зерттеу.

Python-бағдарламаның байт-кодын зерттеу үшін dos модулін («дизассемблерден» қысқару) пайдалануға болады, ол байт-кодты мнемоникалық түрде көруге мүмкіндік береді . Келесі мысал осы мүмкіндікті көрсетеді:

>>> def f():
… print 2*2

>>> dis.dis(f)
2 0 LOAD_CONST 1 (2)
3 LOAD_CONST 1 (2)
6 BINARY_MULTIPLY
7 PRINT_ITEM
8 PRINT_NEWLINE
9 LOAD_CONST 0 (None)
12 RETURN_VALUE


2*2 өрнегінің мәнін есептейтін және басып шығаратын f () функциясы анықталады. Dis() функциясы dis () функциясы Python байт коды мнемоникалық аттармен ұсынылған «ассемблер» түрінде f () функциясының кодын шығарады. Бұл жағдайда, егер де, егер де, егер де, егер де, егер де, егер де, егер де, егер де, егер де, егер де, онда, онда, онда, онда, онда, онда, егер де, онда, онда, онда, онда, егер де, егер де, онда, егер де, онда, онда, онда, егер де, онда, онда, онда, мысалы, онда, онда, онда, онда, онда, егер де, онда, егер де, онда, онда, егер де, онда, егер де, онда, егер де, Return операторсыз функциясы None мәнін қайтарады. Микропроцессор үшін кодтар сияқты кейбір байт-кодтар параметрлерді қабылдайды.

Мнемоникалық атауларды dis тізімінен көруге болады.opname (төменде тек іске қосылған атаулар басылады):

>>> import dis
>>> [n for n in dis.opname if n[0] != «<«]
[‘STOP_CODE’, ‘POP_TOP’, ‘ROT_TWO’, ‘ROT_THREE’, ‘DUP_TOP’, ‘ROT_FOUR’,
‘NOP’, ‘UNARY_POSITIVE’, ‘UNARY_NEGATIVE’, ‘UNARY_NOT’, ‘UNARY_CONVERT’,
‘UNARY_INVERT’, ‘LIST_APPEND’, ‘BINARY_POWER’, ‘BINARY_MULTIPLY’,
‘BINARY_DIVIDE’, ‘BINARY_MODULO’, ‘BINARY_ADD’, ‘BINARY_SUBTRACT’,
‘BINARY_SUBSCR’, ‘BINARY_FLOOR_DIVIDE’, ‘BINARY_TRUE_DIVIDE’,
‘INPLACE_FLOOR_DIVIDE’, ‘INPLACE_TRUE_DIVIDE’, ‘SLICE+0’, ‘SLICE+1’,
‘SLICE+2’, ‘SLICE+3’, ‘STORE_SLICE+0’, ‘STORE_SLICE+1’, ‘STORE_SLICE+2’,
‘STORE_SLICE+3’, ‘DELETE_SLICE+0’, ‘DELETE_SLICE+1’, ‘DELETE_SLICE+2’,
‘DELETE_SLICE+3’, ‘INPLACE_ADD’, ‘INPLACE_SUBTRACT’, ‘INPLACE_MULTIPLY’,
‘INPLACE_DIVIDE’, ‘INPLACE_MODULO’, ‘STORE_SUBSCR’, ‘DELETE_SUBSCR’,
‘BINARY_LSHIFT’, ‘BINARY_RSHIFT’, ‘BINARY_AND’, ‘BINARY_XOR’, ‘BINARY_OR’,
‘INPLACE_POWER’, ‘GET_ITER’, ‘PRINT_EXPR’, ‘PRINT_ITEM’, ‘PRINT_NEWLINE’,
‘PRINT_ITEM_TO’, ‘PRINT_NEWLINE_TO’, ‘INPLACE_LSHIFT’, ‘INPLACE_RSHIFT’,
‘INPLACE_AND’, ‘INPLACE_XOR’, ‘INPLACE_OR’, ‘BREAK_LOOP’, ‘LOAD_LOCALS’,
‘RETURN_VALUE’, ‘IMPORT_STAR’, ‘EXEC_STMT’, ‘YIELD_VALUE’, ‘POP_BLOCK’,
‘END_FINALLY’, ‘BUILD_CLASS’, ‘STORE_NAME’, ‘DELETE_NAME’,
‘UNPACK_SEQUENCE’, ‘FOR_ITER’, ‘STORE_ATTR’, ‘DELETE_ATTR’, ‘STORE_GLOBAL’,
‘DELETE_GLOBAL’, ‘DUP_TOPX’, ‘LOAD_CONST’, ‘LOAD_NAME’, ‘BUILD_TUPLE’,
‘BUILD_LIST’, ‘BUILD_MAP’, ‘LOAD_ATTR’, ‘COMPARE_OP’, ‘IMPORT_NAME’,
‘IMPORT_FROM’, ‘JUMP_FORWARD’, ‘JUMP_IF_FALSE’, ‘JUMP_IF_TRUE’,
‘JUMP_ABSOLUTE’, ‘LOAD_GLOBAL’, ‘CONTINUE_LOOP’, ‘SETUP_LOOP’,
‘SETUP_EXCEPT’, ‘SETUP_FINALLY’, ‘LOAD_FAST’, ‘STORE_FAST’, ‘DELETE_FAST’,
‘RAISE_VARARGS’, ‘CALL_FUNCTION’, ‘MAKE_FUNCTION’, ‘BUILD_SLICE’,
‘MAKE_CLOSURE’, ‘LOAD_CLOSURE’, ‘LOAD_DEREF’, ‘STORE_DEREF’,
‘CALL_FUNCTION_VAR’, ‘CALL_FUNCTION_KW’, ‘CALL_FUNCTION_VAR_KW’,
‘EXTENDED_ARG’]


LOAD мәндерді стекке жүктеу, STORE — түсіру, PRINT — басып шығару, BINARY — бинарлық операция және т. б. білдіреді.

4. Баптау.

Python тілінің и нтерпретаторында бағдарламаларды жөндеу мүмкіндіктері бар, ал стандартты жеткізуде қарапайым реттеуші — pdb бар. Келесі мысал баптауға жататын бағдарламаны және баптаудың типтік сессиясын көрсетеді:

# File myfun.py
def fun(s):
lst = []
for i in s:
lst.append(ord(i))
return lst

Сондықтан әдеттегі жөндеу процесі көрінуі мүмкін:

>>> import pdb, myfun
>>> pdb.runcall(myfun.fun, «ABCDE»)
> /examples/myfun.py(4)fun()
-> lst = []
(Pdb) n
> /examples/myfun.py(5)fun()
-> for i in s:
(Pdb) n
> /examples/myfun.py(6)fun()
-> lst.append(ord(i))
(Pdb) l
1 #!/usr/bin/python
2 # File myfun.py
3 def fun(s):
4 lst = []
5 for i in s:
6 -> lst.append(ord(i))
7 return lst
[EOF]
(Pdb) p lst
[]
(Pdb) p vars()
{‘i’: ‘A’, ‘s’: ‘ABCDE’, ‘lst’: []}
(Pdb) n
> /examples/myfun.py(5)fun()
-> for i in s:
(Pdb) p vars()
{‘i’: ‘A’, ‘s’: ‘ABCDE’, ‘lst’: [65]}
(Pdb) n
> /examples/myfun.py(6)fun()
-> lst.append(ord(i))
(Pdb) n
> /examples/myfun.py(5)fun()
-> for i in s:
(Pdb) p vars()
{‘i’: ‘B’, ‘s’: ‘ABCDE’, ‘lst’: [65, 66]}
(Pdb) r
— Return —
> /examples/myfun.py(7)fun()->[65, 66, 67, 68, 69]
-> return lst
(Pdb) n
[65, 66, 67, 68, 69]
>>>

Интерактивті реттеуші pdb функциясымен туындайды.runcall () және оның шақыруына (Pdb) командаларды енгізу керек. Бұл баптау сессиясының мысалында келесі командалардың кейбірі пайдаланылды: l (кодталған код фрагментін басып шығару), n (келесі жолға дейін бәрін орындау), s (әдіс немесе функцияны шақыруға тереңдей отырып, келесі қадам жасау), p (мәнді басып шығару), r (ағымдағы функциядан қайтарылғанға дейін бәрін орындау).

Әрине, Python үшін кейбір интерактивті әзірлеушінің қабықтары реттеуші функциясын ұсынады. Сонымен қатар, бағдарламаның негізгі жерлерінде print операторлары қызығушылық тудыратын параметрлерді шығару үшін ұйымдастыру оңай. Әдетте бұл мәселені оқшаулау үшін жеткілікті. CGI-сценарийлерде алдыңғы дәрістердің бірінде айтылған cgitb модулін пайдалануға болады.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *