আপনি এটি দরকারী খুঁজে পেতে পারেন - পাইথন ইন্টার্নালস: এখানে উদ্ধৃত পাইথনে একটি নতুন বিবৃতি যুক্ত :
এই নিবন্ধটি পাইথনের সম্মুখ-প্রান্তটি কীভাবে কাজ করে তা আরও ভাল করে বোঝার একটি প্রচেষ্টা। কেবলমাত্র ডকুমেন্টেশন এবং উত্স কোড পড়া কিছুটা বিরক্তিকর হতে পারে, তাই আমি এখানে হাতছাড়া পন্থা নিচ্ছি: আমি until
পাইথনে একটি বিবৃতি যুক্ত করতে যাচ্ছি ।
এই নিবন্ধটির সমস্ত কোডিং পাইথন মার্কিউরিয়াল রিপোজিটরি আয়নাতে কাটিং-এজ পাই 3 কে শাখার বিপরীতে করা হয়েছিল ।
until
বিবৃতি
কিছু ভাষায়, রুবির মতো একটি until
বক্তব্য রয়েছে, যা পরিপূরক while
( until num == 0
সমতুল্য while num != 0
)। রুবিতে আমি লিখতে পারি:
num = 3
until num == 0 do
puts num
num -= 1
end
এবং এটি মুদ্রণ করবে:
3
2
1
সুতরাং, আমি পাইথনের সাথে অনুরূপ সামর্থ্য যুক্ত করতে চাই। এটি, লিখতে সক্ষম হচ্ছে:
num = 3
until num == 0:
print(num)
num -= 1
একটি ভাষা-অ্যাডভোকেসি ডিগ্রেশন
এই নিবন্ধটি until
পাইথনে একটি বিবৃতি যুক্ত করার চেষ্টা করার চেষ্টা করে না । যদিও আমি মনে করি যে এই জাতীয় বিবৃতিটি কিছু কোড পরিষ্কার করবে এবং এই নিবন্ধটি যুক্ত করা কত সহজ তা দেখায়, আমি পাইথনের সংক্ষিপ্ততার দর্শনের সম্পূর্ণ সম্মান করি। আমি এখানে যা করতে চাইছি তা হ'ল পাইথনের অভ্যন্তরীণ কার্যকারিতা সম্পর্কে কিছুটা অন্তর্দৃষ্টি অর্জন করা।
ব্যাকরণ পরিবর্তন করা
পাইথন একটি কাস্টম পার্সার জেনারেটর ব্যবহার করে pgen
। এটি একটি এলএল (1) পার্সার যা পাইথন উত্স কোডটিকে পার্স ট্রিে রূপান্তর করে। পার্সার জেনারেটরের ইনপুটটি ফাইল Grammar/Grammar
[1] । এটি একটি সাধারণ পাঠ্য ফাইল যা পাইথনের ব্যাকরণ নির্দিষ্ট করে।
[1] : এখান থেকে পাইথন উত্সের ফাইলগুলির উল্লেখগুলি উত্স গাছের মূলের তুলনায় তুলনামূলকভাবে দেওয়া হয়, এটি সেই ডিরেক্টরি যেখানে আপনি কনফিগার চালিয়ে পাইথন তৈরির জন্য তৈরি করেন make
ব্যাকরণ ফাইলে দুটি পরিবর্তন করতে হবে। প্রথমটি হল until
বিবৃতিটির জন্য একটি সংজ্ঞা যুক্ত করা । আমি খুঁজে পেয়েছি যেখানে while
বিবৃতিটি সংজ্ঞায়িত করা হয়েছিল ( while_stmt
), এবং until_stmt
নীচে যুক্ত করা হয়েছে [2] :
compound_stmt: if_stmt | while_stmt | until_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
until_stmt: 'until' test ':' suite
[২] : উত্স কোডটি সংশোধন করার সময় আমি যে সাধারণ ব্যবহার করি তা আমি প্রদর্শন করি: যার সাথে আমি পরিচিত নই: সাদৃশ্য অনুসারে কাজ করুন । এই নীতিটি আপনার সমস্ত সমস্যার সমাধান করবে না, তবে এটি অবশ্যই প্রক্রিয়াটি সহজ করতে পারে। যেহেতু যা করতে হবে তার while
জন্যও সমস্ত কিছু করা উচিত until
, এটি একটি দুর্দান্ত ভাল গাইডলাইন হিসাবে কাজ করে।
নোট করুন যে আমি এই else
সংজ্ঞাটি until
কিছুটা আলাদা করার জন্য আমার সংজ্ঞাটি থেকে বাদ দেওয়ার সিদ্ধান্ত নিয়েছি (এবং প্রকৃতপক্ষে আমি else
লুপগুলির ধারাটিকে অপছন্দ করি এবং মনে করি না যে এটি পাইথনের জেনের সাথে ভাল ফিট করে)।
দ্বিতীয় পরিবর্তনটি compound_stmt
অন্তর্ভুক্ত করার নিয়মটি সংশোধন করা until_stmt
, আপনি উপরের স্নিপেটে দেখতে পারেন। এটা ঠিক পরে while_stmt
, আবার।
আপনি চালাতে যখন make
পরিবর্তন পরে Grammar/Grammar
, বিজ্ঞপ্তি যে pgen
প্রোগ্রাম চালানো হয় পুনরায় জেনারেট Include/graminit.h
এবং Python/graminit.c
, এবং তারপর বিভিন্ন ফাইল পুনরায় কম্পাইল পেতে।
এএসটি প্রজন্মের কোডটি সংশোধন করা হচ্ছে
পাইথন পার্সার একটি পার্স গাছ তৈরি করার পরে, এই গাছটি একটি এএসটিতে রূপান্তরিত হয়, যেহেতু সংকলন প্রক্রিয়ার পরবর্তী পর্যায়ে এটিএসটি কাজ করার জন্য অনেক সহজ ।
সুতরাং, আমরা পরিদর্শন করতে Parser/Python.asdl
যা পাইথনের এএসটিগুলির গঠনকে সংজ্ঞায়িত করে এবং আমাদের নতুন until
বিবৃতিটির জন্য আবার এএসটি নোড যুক্ত করবে , ঠিক নীচে while
:
| While(expr test, stmt* body, stmt* orelse)
| Until(expr test, stmt* body)
আপনি যদি এখন চালনা করেন তবে make
লক্ষ্য করুন যে গুচ্ছ ফাইলগুলি সংকলনের আগে Parser/asdl_c.py
এএসটি সংজ্ঞা ফাইল থেকে সি কোড উত্পন্ন করতে চালিত হয়। Grammar/Grammar
প্রোগ্রামিংকে সহজ করার জন্য মিনি-ল্যাঙ্গুয়েজ (অন্য কথায়, একটি ডিএসএল) ব্যবহার করে পাইথন সোর্স কোডের এটি (যেমন ) অন্য উদাহরণ। এছাড়াও লক্ষ করুন যেহেতু Parser/asdl_c.py
পাইথন স্ক্রিপ্ট তাই এটি এক ধরণের বুটস্ট্র্যাপিং - স্ক্র্যাচ থেকে পাইথন তৈরি করতে পাইথন ইতিমধ্যে উপলব্ধ থাকতে হবে।
Parser/asdl_c.py
আমাদের নতুন সংজ্ঞায়িত এএসটি নোড (ফাইলগুলিতে Include/Python-ast.h
এবং Python/Python-ast.c
) পরিচালনা করার জন্য কোডটি উত্পন্ন করার সময় , আমাদের এখনও কোডটি লিখতে হবে যা কোনও প্রাসঙ্গিক গাছ নোডকে হাতে রূপান্তরিত করে। এটি ফাইলটিতে করা হয় Python/ast.c
। সেখানে, নামের একটি ফাংশন ast_for_stmt
পার্স গাছের নোডগুলিকে এএসটি নোডগুলিতে বিবৃতি দেওয়ার জন্য রূপান্তর করে। আবার, আমাদের পুরানো বন্ধু দ্বারা পরিচালিত while
, আমরা switch
যৌগিক বিবৃতিগুলি পরিচালনা করার জন্য ডানদিকে ঝাঁপিয়ে পড়ি এবং এর জন্য একটি ধারা যুক্ত করি until_stmt
:
case while_stmt:
return ast_for_while_stmt(c, ch);
case until_stmt:
return ast_for_until_stmt(c, ch);
এখন আমাদের বাস্তবায়ন করা উচিত ast_for_until_stmt
। এটা এখানে:
static stmt_ty
ast_for_until_stmt(struct compiling *c, const node *n)
{
/* until_stmt: 'until' test ':' suite */
REQ(n, until_stmt);
if (NCH(n) == 4) {
expr_ty expression;
asdl_seq *suite_seq;
expression = ast_for_expr(c, CHILD(n, 1));
if (!expression)
return NULL;
suite_seq = ast_for_suite(c, CHILD(n, 3));
if (!suite_seq)
return NULL;
return Until(expression, suite_seq, LINENO(n), n->n_col_offset, c->c_arena);
}
PyErr_Format(PyExc_SystemError,
"wrong number of tokens for 'until' statement: %d",
NCH(n));
return NULL;
}
আবার, এই কোডটি সমানভাবে ঘনিষ্ঠভাবে দেখার সময়, এই ast_for_while_stmt
পার্থক্যের সাথে যে until
আমি এই else
ধারাটিকে সমর্থন না করার সিদ্ধান্ত নিয়েছি with যেমনটি প্রত্যাশা করা হয়েছিল, এএসটিটি পুনরাবৃত্তভাবে তৈরি করা ast_for_expr
হয়েছে, শর্তটি প্রকাশের ast_for_suite
জন্য এবং until
বিবৃতিটির মূল অংশের মতো অন্যান্য এএসটি তৈরি করে ফাংশন ব্যবহার করে । অবশেষে, নামের একটি নতুন নোড Until
ফিরে আসে।
নোট করুন যে আমরা n
কিছু ম্যাক্রোর মতো NCH
এবং ব্যবহার করে পার্স-ট্রি নোড অ্যাক্সেস করি CHILD
। এগুলি বোঝার যোগ্য - তাদের কোডটি রয়েছে Include/node.h
।
ডাইগ্রেশন: এএসটি রচনা
আমি until
বিবৃতিটির জন্য একটি নতুন ধরণের এএসটি তৈরি করতে বেছে নিয়েছি , তবে আসলে এটি প্রয়োজনীয় নয়। বিদ্যমান কিছু এএসটি নোডের সংমিশ্রণটি ব্যবহার করে আমি কিছু কাজ সংরক্ষণ করতে এবং নতুন কার্যকারিতা বাস্তবায়ন করতে পারতাম:
until condition:
# do stuff
কার্যত সমান:
while not condition:
# do stuff
Until
নোড ইন তৈরি করার পরিবর্তে ast_for_until_stmt
, আমি Not
ছোটবেলায় নোডের সাথে একটি While
নোড তৈরি করতে পারতাম । যেহেতু এএসটি সংকলক ইতিমধ্যে এই নোডগুলি পরিচালনা করতে জানে তাই প্রক্রিয়াটির পরবর্তী ধাপগুলি এড়ানো যায়।
এএসটিগুলি বাইটোকোডে সংকলন করছে
পরবর্তী পদক্ষেপটি পাইথন বাইকোডে এএসটি সংকলন করছে। সংকলনের একটি মধ্যবর্তী ফলাফল রয়েছে যা একটি সিএফজি (কন্ট্রোল ফ্লো গ্রাফ), তবে যেহেতু একই কোডটি এটি পরিচালনা করে আমি আপাতত এই বিশদটি উপেক্ষা করব এবং এটি অন্য একটি লেখার জন্য রেখে দেব।
আমরা পরবর্তী কোডটি দেখতে যাচ্ছি Python/compile.c
। এর নেতৃত্ব অনুসরণ করে while
, আমরা ফাংশনটি খুঁজে পাই compiler_visit_stmt
, যা বাইটোকোডে বিবৃতি সংকলনের জন্য দায়ী। আমরা এর জন্য একটি ধারা যুক্ত করি Until
:
case While_kind:
return compiler_while(c, s);
case Until_kind:
return compiler_until(c, s);
আপনি যদি অবাক Until_kind
হন তবে _stmt_kind
এটি এএসটি সংজ্ঞা ফাইল থেকে স্বয়ংক্রিয়ভাবে উত্পন্ন একটি ধ্রুবক (আসলে গণনার মান ) Include/Python-ast.h
। যাইহোক, আমরা কল করি compiler_until
যা অবশ্যই, এখনও বিদ্যমান নেই। আমি এক মুহুর্তে এটি পেয়ে যাব।
আপনি যদি আমার মতো কৌতূহলী হন তবে আপনি দেখতে পাবেন যে compiler_visit_stmt
এটি অদ্ভুত। grep
উত্স গাছের পরিমাণ নির্ধারণ করে না যেখানে এটি বলা হয়। যখন এটি হয়, কেবলমাত্র একটি বিকল্প থাকে - সি ম্যাক্রো-ফু। আসলে, একটি সংক্ষিপ্ত তদন্ত আমাদের VISIT
ম্যাক্রোতে সংজ্ঞায়িত করে Python/compile.c
:
#define VISIT(C, TYPE, V) {\
if (!compiler_visit_ ## TYPE((C), (V))) \
return 0; \
এটা তোলে ডাকা ব্যবহৃত হচ্ছে compiler_visit_stmt
মধ্যে compiler_body
। আমাদের ব্যবসায় ফিরে যান, তবে ...
প্রতিশ্রুতি হিসাবে, এখানে compiler_until
:
static int
compiler_until(struct compiler *c, stmt_ty s)
{
basicblock *loop, *end, *anchor = NULL;
int constant = expr_constant(s->v.Until.test);
if (constant == 1) {
return 1;
}
loop = compiler_new_block(c);
end = compiler_new_block(c);
if (constant == -1) {
anchor = compiler_new_block(c);
if (anchor == NULL)
return 0;
}
if (loop == NULL || end == NULL)
return 0;
ADDOP_JREL(c, SETUP_LOOP, end);
compiler_use_next_block(c, loop);
if (!compiler_push_fblock(c, LOOP, loop))
return 0;
if (constant == -1) {
VISIT(c, expr, s->v.Until.test);
ADDOP_JABS(c, POP_JUMP_IF_TRUE, anchor);
}
VISIT_SEQ(c, stmt, s->v.Until.body);
ADDOP_JABS(c, JUMP_ABSOLUTE, loop);
if (constant == -1) {
compiler_use_next_block(c, anchor);
ADDOP(c, POP_BLOCK);
}
compiler_pop_fblock(c, LOOP, loop);
compiler_use_next_block(c, end);
return 1;
}
আমার কাছে একটি স্বীকারোক্তি আছে: পাইথন বাইকোডের গভীর বোঝার ভিত্তিতে এই কোডটি লেখা হয়নি। নিবন্ধের বাকী অংশগুলির মতো এটিও আত্মীয় compiler_while
কার্যের অনুকরণে করা হয়েছিল । এটিকে সাবধানে পড়ার মাধ্যমে, পাইথন ভিএম স্ট্যাক-ভিত্তিক এবং dis
মডিউলের ডকুমেন্টেশনের দিকে ঝুঁকছেন, যেখানে বিবরণ সহ পাইথন বাইটকোডগুলির একটি তালিকা রয়েছে , এটি বোঝা যায় যে কী চলছে।
এটাই, আমরা শেষ করেছি ... আমরা না?
সমস্ত পরিবর্তন এবং চলমান পরে make
, আমরা নতুন সংকলিত পাইথন চালাতে এবং আমাদের নতুন until
বিবৃতি চেষ্টা করতে পারেন :
>>> until num == 0:
... print(num)
... num -= 1
...
3
2
1
ভয়েলা, কাজ করে! নীচে dis
মডিউলটি ব্যবহার করে নতুন বিবৃতিটির জন্য তৈরি বাইটকোডটি দেখুন :
import dis
def myfoo(num):
until num == 0:
print(num)
num -= 1
dis.dis(myfoo)
ফলাফল এখানে:
4 0 SETUP_LOOP 36 (to 39)
>> 3 LOAD_FAST 0 (num)
6 LOAD_CONST 1 (0)
9 COMPARE_OP 2 (==)
12 POP_JUMP_IF_TRUE 38
5 15 LOAD_NAME 0 (print)
18 LOAD_FAST 0 (num)
21 CALL_FUNCTION 1
24 POP_TOP
6 25 LOAD_FAST 0 (num)
28 LOAD_CONST 2 (1)
31 INPLACE_SUBTRACT
32 STORE_FAST 0 (num)
35 JUMP_ABSOLUTE 3
>> 38 POP_BLOCK
>> 39 LOAD_CONST 0 (None)
42 RETURN_VALUE
সর্বাধিক আকর্ষণীয় অপারেশনটি 12 নম্বর: শর্তটি সত্য হলে আমরা লুপের পরে ঝাঁপ দাও। এটি সঠিক শব্দার্থবিজ্ঞানের জন্য until
। যদি লাফটি কার্যকর না করা হয়, অপারেশন ৩৫-এর শর্তে ফিরে না আসা পর্যন্ত লুপ বডিটি চলতে থাকে।
আমার পরিবর্তন সম্পর্কে ভাল লাগছে, আমি তখন ফাংশনটি চালানোর চেষ্টা করেছি (সম্পাদন myfoo(3)
) এর বাইকোড না দেখানোর পরিবর্তে। ফলাফল উত্সাহ দেওয়ার চেয়ে কম ছিল:
Traceback (most recent call last):
File "zy.py", line 9, in
myfoo(3)
File "zy.py", line 5, in myfoo
print(num)
SystemError: no locals when loading 'print'
ওহো ... এটা ভাল হতে পারে না। তাহলে কি ভুল হয়েছে?
নিখোঁজ প্রতীক টেবিল কেস
এএসটি সংকলন করার সময় পাইথন সংকলক যে পদক্ষেপগুলি সম্পাদন করে তার মধ্যে একটি হল কোডটি সংকলন করে তার জন্য একটি সারণী সারণী তৈরি করা। থেকে কল PySymtable_Build
মধ্যে PyAST_Compile
প্রতীক টেবিল মডিউল (মধ্যে কল Python/symtable.c
), যা একটি পদ্ধতিতে কোড নির্মাণ ফাংশন অনুরূপ এবং AST দিয়ে হেঁটে যাচ্ছে। প্রতিটি স্কোপের জন্য একটি সারণী সারণী থাকা সংকলকটিকে কিছু মূল তথ্য বের করতে সহায়তা করে, যেমন কোন ভেরিয়েবলগুলি বিশ্বব্যাপী এবং কোন স্কোপে স্থানীয়।
সমস্যার সমাধানের জন্য, বিবৃতিগুলির জন্য একই কোডের পরে, আমাদের বিবৃতি পরিচালনা করতে কোড যুক্ত করে symtable_visit_stmt
ফাংশনটি সংশোধন করতে হবে [3] :Python/symtable.c
until
while
case While_kind:
VISIT(st, expr, s->v.While.test);
VISIT_SEQ(st, stmt, s->v.While.body);
if (s->v.While.orelse)
VISIT_SEQ(st, stmt, s->v.While.orelse);
break;
case Until_kind:
VISIT(st, expr, s->v.Until.test);
VISIT_SEQ(st, stmt, s->v.Until.body);
break;
[3] : যাইহোক, এই কোড ব্যতীত এর জন্য একটি সংকলক সতর্কতা রয়েছে Python/symtable.c
। সংকলকটি লক্ষ্য করে যে Until_kind
গণনার মানটি স্যুইচ বিবৃতিতে symtable_visit_stmt
অভিযোগ করা হয় না এবং অভিযোগ করে না। সংকলক সতর্কতাগুলির জন্য এটি পরীক্ষা করা সবসময় গুরুত্বপূর্ণ!
এবং এখন আমরা সত্যিই সম্পন্ন। এই পরিবর্তনের পরে উত্সটি সংকলন myfoo(3)
করা প্রত্যাশার মতো করে কাজ সম্পাদন করে ।
উপসংহার
এই নিবন্ধে আমি দেখিয়েছি কীভাবে পাইথনে একটি নতুন বিবৃতি যুক্ত করতে হয়। পাইথন সংকলকের কোডে যদিও বেশ কিছুটা ঝোঁক প্রয়োজন, পরিবর্তনটি বাস্তবায়ন করা খুব কঠিন ছিল না, কারণ আমি গাইডলাইন হিসাবে অনুরূপ এবং বিদ্যমান বক্তব্যটি ব্যবহার করেছি।
পাইথন সংকলকটি সফ্টওয়্যারটির একটি পরিশীলিত অংশ এবং আমি এটিতে বিশেষজ্ঞ হিসাবে দাবি করি না। তবে আমি পাইথনের অভ্যন্তরীণ ক্ষেত্রগুলিতে এবং বিশেষত এর ফ্রন্ট-এন্ডে আগ্রহী। অতএব, আমি এই অনুশীলনটি সংকলকের নীতি এবং উত্স কোডের তাত্ত্বিক অধ্যয়নের জন্য খুব দরকারী সহযোগী হিসাবে পেয়েছি। এটি ভবিষ্যতের নিবন্ধগুলির ভিত্তি হিসাবে কাজ করবে যা সংকলকটির আরও গভীর হবে।
তথ্যসূত্র
আমি এই নিবন্ধটি নির্মাণের জন্য কয়েকটি দুর্দান্ত রেফারেন্স ব্যবহার করেছি। এখানে তারা কোনও নির্দিষ্ট ক্রমে নয়:
- পিইপি 339: সিপথন সংকলকটির ডিজাইন - পাইথন সংকলকটির পক্ষে সম্ভবত অফিসিয়াল ডকুমেন্টেশনের সবচেয়ে গুরুত্বপূর্ণ এবং বিস্তৃত অংশ । খুব সংক্ষিপ্ত হওয়ার কারণে এটি পাইথনের ইন্টার্নালগুলির ভাল ডকুমেন্টেশনের অভাবটি বেদনাদায়কভাবে প্রদর্শন করে।
- "পাইথন সংকলক অভ্যন্তরীণ" - টমাস লি-র একটি নিবন্ধ
- "পাইথন: ডিজাইন এবং বাস্তবায়ন" - গাইডো ভ্যান রসমের উপস্থাপনা
- পাইথন (2.5) ভার্চুয়াল মেশিন, একটি গাইড ট্যুর - পিটার ট্র্যাজারের উপস্থাপনা
মূল উৎস