شجرة التركيب المجردة (AST)، وتُسمى أيضًا التعبير المقتبس، هي طريقة لتمثيل الكود كبيانات.
كل عقدة في AST هي صف ثلاثي العناصر.
# AST representation of:
# 2 + 3
{:+, [], [2, 3]}
العنصر الأول، وهو ذرة، هو العملية. والعنصر الثاني، وهو قائمة كلمات مفتاحية، هو البيانات الوصفية. والعنصر الثالث قائمة وسائط تحتوي على عقد أخرى. أما القيم الحرفية مثل الأعداد الصحيحة والذرات والسلاسل النصية فتمثَّل في AST كما هي بدلًا من صفوف ثلاثية العناصر.
يُعد تحويل كود Elixir إلى AST وإعادة تحويل AST إلى كود جزءًا من المكتبة القياسية. يمكنك أن تجد دوالًا للتعامل مع AST في الوحدتين Code (مثلًا لتحويل سلسلة نصية تحتوي كودًا إلى AST) وMacro (مثلًا للتنقل في AST أو تحويله إلى سلسلة نصية).
لاحظ أن جميع الدوال في المكتبة القياسية تستخدم اسم quoted للتعبير عن AST (وهو اختصار التعبير المقتبس).
الصيغة الخاصة لتحويل الكود إلى AST تسمى quote. وهي تقبل كتلة كود وتُرجع AST المقابلة لها.
quote do
2 + 3 - 1
end
# => {:-, [], [
# {:+, [], [2, 3]},
# 1
# ]}
القدرة على تمثيل الكود في صورة AST هي جوهر البرمجة الفوقية في Elixir. تعمل الماكروز، وهي طريقة لكتابة كود Elixir يُنتج كود Elixir، عبر إرجاع AST كمُخرَج.
ومن الاستخدامات الأخرى لشجرة التركيب المجردة التحليل الساكن للكود، مثل أداة Exercism نفسها، Analyzer، التي ربما تعرفها بالفعل باسم الروبوت الصغير الذي يترك تعليقات على حلولك.
أنت عضو في فرقة عمل تكافح التجسس على الشركات. لديك مخبر سري في شركة Shady Company X، التي تشك في أنها تسرق أسرار منافسيها.
مخبرك، العميل Ex، مطوّرة بلغة Elixir. وهي ترمّز رسائل سرية داخل كودها.
لفكّ تشفير رسائلها السرية:
n حرفًا من اسمها، حيث n هو عدد معاملات الدالة.نفّذ دالة TopSecret.to_ast/1. يجب أن تأخذ سلسلة نصية تحتوي على كود Elixir وتُرجع شجرة AST الخاصة به.
TopSecret.to_ast("div(4, 3)")
# => {:div, [line: 1], [4, 3]}
نفّذ دالة TopSecret.decode_secret_message_part/2. يجب أن تأخذ عقدة من شجرة AST ومجمّعًا للرسالة السرية (قائمة). ويجب أن تُرجع مجموعة مرتّبة تبقى فيها عقدة الشجرة كما هي كعنصر أول، ويكون المجمّع هو العنصر الثاني.
إذا كانت عملية عقدة الشجرة هي تعريف دالة (def أو defp)، فأضف اسم الدالة (بعد تحويله إلى سلسلة نصية) إلى مقدمة المجمّع. وإذا كانت العملية شيئًا آخر، فأرجع المجمّع كما هو.
ast_node = TopSecret.to_ast("defp cat(a, b, c), do: nil")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["cat", "day"]}
ast_node = TopSecret.to_ast("10 + 3")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["day"]}
لا تحتاج هذه الدالة إلى إجراء أي استدعاءات تكرارية لفحص الشجرة كاملة، بل العقدة المعطاة فقط. سنمرّ على الشجرة كاملة بأدوات مدمجة في الخطوة الأخيرة.
وسّع دالة TopSecret.decode_secret_message_part/2. إذا كانت العملية في عقدة الشجرة هي تعريف دالة، فلا تُرجع اسم الدالة كاملًا. بل افحص عدد معاملات الدالة، ثم أرجع أول n حرف من الاسم فقط، حيث n هو عدد المعاملات.
ast_node = TopSecret.to_ast("defp cat(a, b), do: nil")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["ca", "day"]}
ast_node = TopSecret.to_ast("defp cat(), do: nil")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["", "day"]}
وسّع دالة TopSecret.decode_secret_message_part/2. تأكد من اكتشاف اسم الدالة وعدد معاملاتها بشكل صحيح في تعريفات الدوال التي تستخدم الحُرّاس.
ast_node = TopSecret.to_ast("defp cat(a, b) when is_nil(a), do: nil")
TopSecret.decode_secret_message_part(ast_node, ["day"])
# => {ast_node, ["ca", "day"]}
نفّذ دالة TopSecret.decode_secret_message/1. يجب أن تأخذ سلسلة نصية تحتوي على كود Elixir وتُرجع الرسالة السرية سلسلةً نصية مفكوكة من جميع تعريفات الدوال الموجودة في الكود. تأكد من إعادة استخدام الدوال المعرّفة في الخطوات السابقة.
code = """
defmodule MyCalendar do
def busy?(date, time) do
Date.day_of_week(date) != 7 and
time.hour in 10..16
end
def yesterday?(date) do
Date.diff(Date.utc_today, date)
end
end
"""
TopSecret.decode_secret_message(code)
# => "buy"
سجّل في Exercism لتتعلّم وتتقن Elixir عبر 58 مفهومًا168 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.