Az absztrakt szintaxisfa (AST), más néven quoted expression, a kód adatként való ábrázolásának egyik módja.
Az AST minden csomópontja egy háromelemű tuple.
# AST representation of:
# 2 + 3
{:+, [], [2, 3]}
Az első elem egy atom: maga a művelet. A második elem egy keyword list: a metaadatok. A harmadik elem az argumentumok listája, amely további csomópontokat tartalmaz. A literál értékeket, például az egész számokat, atomokat és stringeket az AST önmagukként ábrázolja, nem háromelemű tuple-ként.
Az Elixir-kód AST-vé alakítása és az AST-k visszaalakítása kóddá a szabványos könyvtár része. Az AST-kkal való munkához szükséges függvényeket a Code modulban találod (például hogy egy kódot tartalmazó stringet AST-vé alakíts), és a Macro modulban (például hogy bejárd az AST-t, vagy stringgé alakítsd).
Figyeld meg, hogy a szabványos könyvtár minden függvénye a „quoted” nevet használja, amikor az AST-ra utal (a quoted expression rövidítéseként).
A kód AST-vé alakítására szolgáló speciális forma neve quote. Ez egy kódblokkot fogad, és visszaadja annak AST-ját.
quote do
2 + 3 - 1
end
# => {:-, [], [
# {:+, [], [2, 3]},
# 1
# ]}
Az Elixir metaprogramozásának a lényege, hogy a kódot AST-ként ábrázolhatjuk. A makrók, amelyek segítségével olyan Elixir-kódot írhatsz, ami Elixir-kódot állít elő, úgy működnek, hogy kimenetként AST-kat adnak vissza.
Az AST-k másik felhasználási területe a statikus kódelemzés, mint például az Exercism saját eszköze, az Elemző, amelyet talán már ismersz: az a kis bot, amely megjegyzéseket hagy a megoldásaidon.
Egy vállalati kémkedés ellen harcoló munkacsoport tagja vagy. Van egy titkos informátorod a Shady Company X-nél, amelyről azt gyanítod, hogy titkokat lop a versenytársaitól.
Az informátorod, Agent Ex, Elixir-fejlesztő. Titkos üzeneteket kódol a kódjában.
A titkos üzenetei dekódolásához:
n karakterét, ahol n a függvény aritása.Valósítsd meg a TopSecret.to_ast/1 függvényt. Egy Elixir-kódot tartalmazó stringet kell fogadnia, és vissza kell adnia az AST-ját.
TopSecret.to_ast("div(4, 3)")
# => {:div, [line: 1], [4, 3]}
Valósítsd meg a TopSecret.decode_secret_message_part/2 függvényt. Egy AST-csomópontot és a titkos üzenethez tartozó akkumulátort (egy listát) kell fogadnia. Egy tuple-t kell visszaadnia, amelynek első eleme a változatlan AST-csomópont, második eleme pedig az akkumulátor.
Ha az AST-csomópont művelete függvényt definiál (def vagy defp), a függvény nevét (stringgé alakítva) fűzd az akkumulátor elé. Ha a művelet valami más, add vissza az akkumulátort változatlanul.
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"]}
Ennek a függvénynek nem kell rekurzív hívásokat végeznie a teljes AST ellenőrzéséhez, csak az adott csomópontot kell vizsgálnia. A teljes AST-t az utolsó lépésben beépített eszközökkel járjuk be.
Terjeszd ki a TopSecret.decode_secret_message_part/2 függvényt. Ha az AST-csomópontban a művelet függvényt definiál, ne a teljes függvénynevet add vissza. Ehelyett ellenőrizd a függvény aritását. Ezután csak a név első n karakterét add vissza, ahol n az aritás.
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"]}
Terjeszd ki a TopSecret.decode_secret_message_part/2 függvényt. Ügyelj arra, hogy az őrfeltételeket használó függvénydefinícióknál a függvény neve és aritása helyesen legyen felismerve.
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"]}
Valósítsd meg a TopSecret.decode_secret_message/1 függvényt. Egy Elixir-kódot tartalmazó stringet kell fogadnia, és vissza kell adnia a kódban található összes függvénydefinícióból dekódolt titkos üzenetet stringként. Ügyelj arra, hogy újrahasználd a korábbi lépésekben definiált függvényeket.
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"
Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) Elixir nyelvet 58 fogalom168 feladat segítségével, valódi emberi mentorálással, mindez ingyen.