Πώς να είστε * ένας μεταγλωττιστής - κάνετε έναν μεταγλωττιστή με JavaScript

*Ναί! θα πρέπει να είστε μεταγλωττιστής. Είναι φοβερό.

Αυτή η δημοσίευση δημοσιεύεται με την άδεια CC BY-NC-SA 4.0. Ενημερώστε με αν μεταφράζετε σε άλλες γλώσσες, ώστε να μπορώ να προσθέσω στη λίστα.

  • Ιαπωνική έκδοση από μένα εδώ
  • Απλοποιημένη κινεζική έκδοση με @sqrtthree εδώ
  • Κορεατική έκδοση από το @ whello64 ​​εδώ
  • Ισπανική έκδοση από @rodkings εδώ
  • Πορτογαλικά Verizon από @CarolinaPascale, @CaiqueMitsuoka, και @ felipesoares6_

Μια υπέροχη Κυριακή στο Bushwick του Μπρούκλιν. Βρήκα ένα βιβλίο "Design by Numbers" του John Maeda στο τοπικό μου βιβλιοπωλείο. Σε αυτό ήταν μια βήμα προς βήμα διδασκαλία της γλώσσας προγραμματισμού DBN - μιας γλώσσας που έγινε στα τέλη της δεκαετίας του '90 στο MIT Media Lab, σχεδιασμένο για να εισαγάγει με τον οπτικό τρόπο έννοιες προγραμματισμού υπολογιστών.

Δείγμα κώδικα DNB από τη διεύθυνση http://dbn.media.mit.edu/introduction.html

Σκεφτόμουν αμέσως ότι η δημιουργία του SVG από το DBN και η εκτέλεση του στο πρόγραμμα περιήγησης θα ήταν ένα ενδιαφέρον έργο το 2016 αντί να εγκαταστήσει το περιβάλλον Java για να εκτελέσει τον αρχικό κώδικα DBN.

Σκέφτηκα ότι θα χρειαζόταν να γράψω ένα DBN στον μεταγλωττιστή SVG, οπότε άρχισε η αναζήτηση της σύνταξης ενός μεταγλωττιστή. "Κάνοντας τον μεταγλωττιστή" ακούγεται σαν πολλή επιστήμη των υπολογιστών ... αλλά ποτέ δεν έχω διασχίσει κόμβους στην κωδικοποίηση συνέντευξης, μπορώ να κάνω έναν μεταγλωττιστή;

Ο φανταστικός μου μεταγλωττιστής, όπου ο κώδικας πηγαίνει να τιμωρηθεί. Εάν ο κώδικας είναι κακός, καταγράφεται στο μήνυμα σφάλματος για πάντα.

Ας προσπαθήσουμε πρώτα να γίνουμε μεταγλωττιστής

Ο μεταγλωττιστής είναι ένας μηχανισμός που παίρνει ένα κομμάτι κώδικα και το μετατρέπει σε κάτι άλλο. Ας καταρτίσουμε έναν απλό κώδικα DBN σε ένα φυσικό σχέδιο.

Υπάρχουν τρεις εντολές σε αυτόν τον κωδικό DBN, το "Χαρτί" ορίζει το χρώμα του χαρτιού, το "Στυλό" ορίζει το χρώμα της γραφίδας και το "Γραμμή" σχεδιάζει μια γραμμή. Το 100 σε έγχρωμη παράμετρο σημαίνει 100% μαύρο ή rgb (0%, 0%, 0%) σε CSS. Η εικόνα που παράγεται στο DBN είναι πάντα σε κλίμακα του γκρι. Στο DBN, ένα χαρτί είναι πάντα 100 × 100, το πλάτος γραμμής είναι πάντα 1 και η Γραμμή ορίζεται από τις συντεταγμένες x y του σημείου εκκίνησης και της καταμέτρησης τελικού σημείου από την κάτω αριστερή γωνία.

Ας προσπαθήσουμε να είμαστε ο ίδιος ο μεταγλωττιστής. Σταματήστε εδώ, αρπάξτε ένα χαρτί και ένα στυλό και προσπαθήστε να συντάξετε τον παρακάτω κώδικα ως σχέδιο.

Χαρτί 0
Στυλό 100
Γραμμή 0 50 100 50

Σχεδιάσατε μια μαύρη γραμμή στη μέση από την αριστερή πλευρά στην δεξιά πλευρά; Συγχαρητήρια! Μόλις έγινε μεταγλωττιστής.

Συμπληρωμένο αποτέλεσμα

Πώς λειτουργεί ο μεταγλωττιστής;

Ας δούμε τι συνέβη στο κεφάλι μας ως μεταγλωττιστής.

1. Λεξική ανάλυση (tokenization)

Το πρώτο πράγμα που κάναμε ήταν να διαχωρίσουμε κάθε λέξη-κλειδί (που ονομάζεται μάρκες) από λευκό χώρο. Ενώ χωρίζουμε λέξεις, αναθέτουμε επίσης πρωτόγονους τύπους σε κάθε μάρκες, όπως "λέξη" ή "αριθμός".

λεξική ανάλυση

2. Ανάλυση (σύνταξη)

Μόλις μια σφήνα κειμένου χωριστεί σε μάρκες, περάσαμε μέσα από κάθε ένα από αυτά και προσπαθήσαμε να βρούμε μια σχέση μεταξύ των μαρκών.
Σε αυτήν την περίπτωση, ομαδοποιούμε τους αριθμούς που σχετίζονται με τη λέξη-κλειδί εντολών. Κάνοντας αυτό, αρχίζουμε να βλέπουμε μια δομή του κώδικα.

Τεχνολογία

3. Μετασχηματισμός

Αφού αναλύσαμε τη σύνταξη με την ανάλυση, μεταμορφώσαμε τη δομή σε κάτι κατάλληλο για το τελικό αποτέλεσμα. Σε αυτή την περίπτωση, θα σχεδιάσουμε μια εικόνα, οπότε πρόκειται να την μετατρέψουμε σε βήμα προς βήμα οδηγίες για τον άνθρωπο.

Μεταμόρφωση

4. Δημιουργία Κώδικα

Τέλος, κάνουμε ένα συγκεντρωμένο αποτέλεσμα, ένα σχέδιο. Σε αυτό το σημείο, ακολουθούμε ακριβώς τις οδηγίες που κάναμε στο προηγούμενο βήμα για να σχεδιάσουμε.

Δημιουργία Κώδικα

Και αυτό κάνει ο μεταγλωττιστής!

Το σχέδιο που δημιουργήσαμε είναι το καταγεγραμμένο αποτέλεσμα (όπως το αρχείο .exe κατά την εκτύπωση του κώδικα C). Μπορούμε να περάσουμε αυτό το σχέδιο σε οποιονδήποτε ή οποιαδήποτε συσκευή (σαρωτή, φωτογραφική μηχανή κλπ.) Για να το "τρέξει" και όλοι (ή συσκευή) θα δουν μια μαύρη γραμμή στη μέση.

Ας κάνουμε έναν μεταγλωττιστή

Τώρα που ξέρουμε πώς λειτουργούν οι μεταγλωττιστές, ας το κάνουμε στο JavaScript. Αυτός ο μεταγλωττιστής παίρνει τον κωδικό DBN και τα μετατρέπει σε κώδικα SVG.

1. Λειτουργία Lexer

Ακριβώς όπως μπορούμε να χωρίσουμε την αγγλική φράση "έχω ένα στυλό" στο [I, have a, pen], ο λεξικογράφος χωρίζει μια συμβολοσειρά κώδικα σε μικρά σημαντικά κομμάτια (μάρκες). Στο DBN, κάθε διακριτικό οριοθετείται από λευκούς χώρους και ταξινομείται ως "λέξη" ή "αριθμός".

είσοδος: "Χαρτί 100"
παραγωγή:[
  {type: "λέξη", τιμή: "Χαρτί"}, {type: "αριθμός", τιμή: 100}
]

2. Λειτουργία προγραμματιστή

Ο αναλυτής περνάει από κάθε μάρκες, βρίσκει συντακτικές πληροφορίες και δημιουργεί ένα αντικείμενο που ονομάζεται AST (Δέντρο σύνταξης σύνταξης). Μπορείτε να σκεφτείτε την AST ως έναν χάρτη για τον κώδικα μας - έναν τρόπο να κατανοήσετε πώς είναι δομημένο ένα κομμάτι κώδικα.

Στον κώδικα μας, υπάρχουν 2 τύποι σύνταξης "NumberLiteral" και "CallExpression". NumberLiteral σημαίνει ότι η τιμή είναι ένας αριθμός. Χρησιμοποιείται ως παράμετρος για το CallExpression.

είσοδος: [
  {type: "λέξη", τιμή: "Χαρτί"}, {type: "αριθμός", τιμή: 100}
]
έξοδος: {
  "τύπος": "Σχέδιο",
  "σώμα": [{
    "type": "CallExpression",
    "όνομα": "Χαρτί",
    "επιχειρήματα": [{"τύπος": "ΑριθμόςLiteral", "τιμή": "100"}]
  }]
}}

3. Λειτουργία μετασχηματιστή

Το AST που δημιουργήσαμε στο προηγούμενο βήμα είναι καλό στο να περιγράψουμε τι συμβαίνει στον κώδικα, αλλά δεν είναι χρήσιμο να βγάλουμε από αυτό το αρχείο SVG.
Για παράδειγμα. Το "χαρτί" είναι μια ιδέα που υπάρχει μόνο στο πρότυπο DBN. Στο SVG, μπορούμε να χρησιμοποιήσουμε το στοιχείο για να αναπαριστούμε ένα χαρτί. Η λειτουργία του μετασχηματιστή μετατρέπει το AST σε άλλο AST που είναι φιλικό προς το SVG.

είσοδος: {
  "τύπος": "Σχέδιο",
  "σώμα": [{
    "type": "CallExpression",
    "όνομα": "Χαρτί",
    "επιχειρήματα": [{"τύπος": "ΑριθμόςLiteral", "τιμή": "100"}]
  }]
}}
έξοδος: {
  "ετικέτα": "svg",
  "attr": {
    "πλάτος": 100,
    "ύψος": 100,
    "viewBox": "0 0 100 100",
    "xmlns": "http://www.w3.org/2000/svg",
    "έκδοση": "1.1"
  },
  "σώμα": [{
    "ετικέτα": "ορθή",
    "attr": {
      "χ": 0,
      "γ": 0,
      "πλάτος": 100,
      "ύψος": 100,
      "συμπληρώστε": "rgb (0%, 0%, 0%)"
    }}
  }]
}}

4. Λειτουργία γεννήτριας

Ως τελικό βήμα αυτού του μεταγλωττιστή, η λειτουργία γεννήτριας δημιουργεί κώδικα SVG με βάση το νέο AST που κάναμε στο προηγούμενο βήμα.

είσοδος: {
  "ετικέτα": "svg",
  "attr": {
    "πλάτος": 100,
    "ύψος": 100,
    "viewBox": "0 0 100 100",
    "xmlns": "http://www.w3.org/2000/svg",
    "έκδοση": "1.1"
  },
  "σώμα": [{
    "ετικέτα": "ορθή",
    "attr": {
      "χ": 0,
      "γ": 0,
      "πλάτος": 100,
      "ύψος": 100,
      "συμπληρώστε": "rgb (0%, 0%, 0%)"
    }}
  }]
}}
παραγωγή:

  Δημιουργούμε ένα αντικείμενο sbn με μεθόδους lexer, parser, μετασχηματιστή και γεννήτρια. Στη συνέχεια, προσθέστε μια μέθοδο "compile" για να καλέσετε και τις 4 μεθόδους σε μια αλυσίδα.

Τώρα μπορούμε να περάσουμε τη συμβολοσειρά κώδικα στη μέθοδο καταγραφής και να βγούμε από το SVG.

Έχω κάνει μια διαδραστική επίδειξη που σας δείχνει τα αποτελέσματα κάθε βήματος σε αυτόν τον μεταγλωττιστή. Ο κώδικας για τον μεταγλωττιστή sbn δημοσιεύεται στο github. Προσθέτω περισσότερες δυνατότητες στον μεταγλωττιστή αυτή τη στιγμή. Αν θέλετε να ελέγξετε τον βασικό μεταγλωττιστή που κάναμε σε αυτή την ανάρτηση, παρακαλούμε να ελέγξετε απλό υποκατάστημα.

https://kosamari.github.io/sbn/

Δεν πρέπει ο μεταγλωττιστής να χρησιμοποιήσει αναδρομή και μετακίνηση κ.λπ .;

Ναι, αυτές είναι όλες οι θαυμάσιες τεχνικές για την κατασκευή ενός μεταγλωττιστή, αλλά αυτό δεν σημαίνει ότι πρέπει πρώτα να ακολουθήσετε αυτή την προσέγγιση.

Ξεκίνησα κάνοντας μεταγλωττιστή για ένα μικρό υποσύνολο της γλώσσας προγραμματισμού DBN, ένα πολύ περιορισμένο σύνολο χαρακτηριστικών. Έκτοτε, επέκτεινα το πεδίο εφαρμογής και τώρα σχεδιάζω την προσθήκη χαρακτηριστικών όπως μεταβλητή, μπλοκ κώδικα και βρόχοι σε αυτόν τον μεταγλωττιστή. Θα ήταν καλή ιδέα να χρησιμοποιήσετε αυτήν την τεχνική σε αυτό το σημείο, αλλά δεν ήταν η απαίτηση να ξεκινήσετε.

Ο γραπτικός μεταγλωττιστής είναι φοβερός

Τι μπορείτε να κάνετε κάνοντας το δικό σας compiler; Ίσως να θελήσετε να δημιουργήσετε μια νέα γλώσσα που να μοιάζει με JavaScript στην ισπανική γλώσσα ... τι γίνεται με το español script;

// ES (σενάριο español)
función () {
  si (verdadero) {
    επιστροφή «○ Hola!»
  }}
}}

Υπάρχουν άνθρωποι που έκαναν γλώσσα προγραμματισμού στο Emoji (Emojicode) και σε έγχρωμη εικόνα (γλώσσα προγραμματισμού Piet). Οι δυνατότητες είναι ατελείωτες!

Μάθημα από τη δημιουργία ενός μεταγλωττιστή

Κάνοντας ένα compiler ήταν διασκεδαστικό, αλλά το σημαντικότερο, μου έμαθε πολλά για την ανάπτυξη λογισμικού. Εδώ είναι λίγα πράγματα που έμαθα κάνοντας τον μεταγλωττιστή μου.

Πώς μπορώ να φανταστώ τον μεταγλωττιστή αφού έκανα τον εαυτό μου

1. Είναι εντάξει να έχετε άγνωστα πράγματα.

Όπως και ο λεξικογράφος μας, δεν χρειάζεται να γνωρίζετε τα πάντα από την αρχή. Αν δεν καταλαβαίνετε πραγματικά ένα κομμάτι κώδικα ή τεχνολογία, είναι εντάξει να πείτε απλά "Υπάρχει κάτι, το ξέρω πολύ" και να το μεταφέρετε στο επόμενο βήμα. Μην τονίζετε γι 'αυτό, θα φτάσετε τελικά εκεί.

2. Μην είστε τρελός με το κακό μήνυμα σφάλματος.

Ο ρόλος του Parser είναι να ακολουθήσει τον κανόνα και να ελέγξει αν τα πράγματα είναι γραμμένα σύμφωνα με αυτούς τους κανόνες. Έτσι, πολλές φορές συμβαίνει σφάλμα. Όταν συμβαίνει, προσπαθήστε να στείλετε χρήσιμα και φιλόξενα μηνύματα. Είναι εύκολο να πείτε "Δεν λειτουργεί με αυτόν τον τρόπο" (όπως το "ILLEGAL Token" ή το "undefined not function" σφάλμα στο JavaScript), αλλά αντί να προσπαθήσετε να πείτε στους χρήστες τι πρέπει να συμβεί όσο μπορείτε.

Αυτό ισχύει και για την ομαδική επικοινωνία. Όταν κάποιος έχει κολλήσει με μια ερώτηση, αντί να λέει "ναι αυτό δεν λειτουργεί", ίσως μπορείτε να αρχίσετε να λέτε "Θα ήθελα google λέξεις-κλειδιά όπως ___ και ___." Ή "Συνιστώ να διαβάσετε αυτή τη σελίδα σε τεκμηρίωση. δεν χρειάζεται να κάνετε την εργασία γι 'αυτούς, αλλά σίγουρα μπορείτε να τους βοηθήσετε να κάνουν τη δουλειά τους καλύτερα και πιο γρήγορα παρέχοντας λίγο περισσότερη βοήθεια.

Το Elm είναι μια γλώσσα προγραμματισμού που αγκαλιάζει αυτή τη μέθοδο. Έβαλαν "ίσως θέλετε να δοκιμάσετε αυτό;" στο μήνυμα λάθους τους.

3. Το πλαίσιο είναι όλα

Τέλος, ακριβώς όπως ο μετασχηματιστής μας μετασχημάτισε έναν τύπο AST σε ένα άλλο πιο κατάλληλο για το τελικό αποτέλεσμα, όλα είναι ειδικά για το περιβάλλον.

Δεν υπάρχει κανένας ένας τέλειος τρόπος να κάνουμε πράγματα. Επομένως, μην κάνετε μόνο πράγματα επειδή είναι δημοφιλής ή έχετε κάνει πριν, σκεφτείτε πρώτα το πλαίσιο. Τα πράγματα που λειτουργούν για έναν χρήστη μπορεί να είναι καταστροφή για έναν άλλο χρήστη.

Επίσης, εκτιμήστε τη δουλειά αυτών των μετασχηματιστών. Μπορεί να γνωρίζετε καλούς μετασχηματιστές στην ομάδα σας - κάποιος που είναι πραγματικά καλός στη γεφύρωση κενών. Εκείνοι που εργάζονται με μετασχηματιστές μπορεί να μην δημιουργούν άμεσα έναν κώδικα, αλλά είναι μια πολύ σημαντική δουλειά για την παραγωγή ποιοτικού προϊόντος.

Ελπίζω να απολαύσατε αυτή τη θέση και ελπίζω να σας πείσω πόσο φοβερό είναι να οικοδομήσουμε & να είστε ένας μεταγλωττιστής!

Αυτό είναι ένα απόσπασμα από τη συζήτηση που έδωσα στο JSConf Colombia 2016 στη Μεντεγίν της Κολομβίας. Αν θέλετε να μάθετε για τη συζήτηση, δείτε τις διαφάνειες εδώ.