Το Abstract Syntax Tree (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 ως έξοδο.
Μια άλλη περίπτωση χρήσης των AST είναι η στατική ανάλυση κώδικα, όπως το εργαλείο της ίδιας της Exercism, το Analyzer, που ίσως το ξέρεις ήδη ως το μικρό bot που αφήνει σχόλια στις λύσεις σου.
Είσαι μέλος μιας ειδικής ομάδας που μάχεται ενάντια στην εταιρική κατασκοπεία. Έχεις έναν μυστικό πληροφοριοδότη στη 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 και έναν συσσωρευτή για το μυστικό μήνυμα (μια λίστα). Πρέπει να επιστρέφει μια πλειάδα με τον κόμβο AST αμετάβλητο ως πρώτο στοιχείο και τον συσσωρευτή ως δεύτερο στοιχείο.
Αν η πράξη του κόμβου 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"]}
Αυτή η συνάρτηση δεν χρειάζεται να κάνει αναδρομικές κλήσεις για να ελέγξει ολόκληρο το AST, μόνο τον δοσμένο κόμβο. Θα διασχίσουμε ολόκληρο το AST με ενσωματωμένα εργαλεία στο τελευταίο βήμα.
Επέκτεινε τη συνάρτηση TopSecret.decode_secret_message_part/2. Αν η πράξη στον κόμβο AST ορίζει μια συνάρτηση, μην επιστρέψεις ολόκληρο το όνομα της συνάρτησης. Αντ' αυτού, έλεγξε το πλήθος ορισμάτων της συνάρτησης. Μετά, επέστρεψε μόνο τους πρώτους 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. Βεβαιώσου ότι το όνομα και το πλήθος ορισμάτων της συνάρτησης εντοπίζονται σωστά για ορισμούς συναρτήσεων που χρησιμοποιούν guards.
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 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.