Μετάβαση στο περιεχόμενο

Αρχιτεκτονική

Το Phasis είναι ένας περιπατητής δέντρου τεσσάρων σταδίων με μια εικονική μηχανή bytecode που ενεργοποιείται ευκαιριακά. Η διαδρομή των δεδομένων είναι:

Κώδικας
JS text
  │
  ▼
┌──────────┐
│  Lexer   │  src/Lexer/
└────┬─────┘
     ▼ tokens
┌──────────┐
│  Parser  │  src/Parser/
└────┬─────┘
     ▼ AST nodes
┌──────────────┐
│ Interpreter  │  src/Runtime/
│  + Bytecode  │  src/Bytecode/
│      VM      │
└────┬─────────┘
     ▼ JsValue
PHP host

Κάθε στάδιο είναι μια κλάση PHP με μικρή διεπαφή που καταναλώνει το επόμενο στάδιο. Δεν υπάρχει βήμα σύνδεσης — η ανάλυση παράγει AST, το οποίο ο διερμηνευτής διατρέχει απευθείας.

Λεκτικός αναλυτής

Το src/Lexer/Lexer.php μετατρέπει το κείμενο πηγής σε ροή από στιγμιότυπα Token. Κάθε διακριτικό είναι μια δομή μόνο για ανάγνωση: τύπος (απαρίθμηση TokenType, ~80 περιπτώσεις), τιμή, γραμμή, στήλη.

Ο λεκτικός αναλυτής χειρίζεται:

  • Αριθμητικά literals — δεκαδικά, δεκαεξαδικά (0x), οκταδικά (0o), δυαδικά (0b), εκθετικά και αριθμητικά διαχωριστικά.
  • Συμβολοσειρές — μονά και διπλά εισαγωγικά, όλες τις ακολουθίες διαφυγής και template literals με ένθετες εκφράσεις.
  • Literals κανονικών εκφράσεων — πλήρη ανάλυση διακριτικών /pattern/flags, με επίγνωση του πότε ένα / πρέπει να ερμηνευτεί ως κανονική έκφραση ή ως διαίρεση.
  • Αναγνωριστικά, λέξεις-κλειδιά, σημεία στίξης.
  • Σχόλια (γραμμής και μπλοκ), που διατηρούνται ως trivia.
  • Πηγαίος κώδικας Unicode — τα byte UTF-8 ερμηνεύονται ως η πηγή UTF-16 του JS.

Η έξοδος είναι μια γεννήτρια που αποδίδει διακριτικά σταδιακά. Ο αναλυτής σύνταξης τα καταναλώνει με προεπισκόπηση ενός διακριτικού.

Αναλυτής

Το src/Parser/Parser.php είναι ένας χειροποίητος αναλυτής Pratt. Η προτεραιότητα των εκφράσεων ορίζεται στο src/Parser/Precedence.php. Οι εντολές χρησιμοποιούν τη δική τους αναδρομική ανάλυση καθόδου.

Παράγει αμετάβλητους κόμβους AST από το src/Ast/. Κάθε κόμβος περιλαμβάνει τη θέση του στην πηγή για τα ίχνη στοίβας και την αναφορά σφαλμάτων.

Αξιοσημείωτες μορφές:

  • Έκφραση: Literal, Identifier, BinaryExpression, UnaryExpression, AssignmentExpression, CallExpression, MemberExpression, ArrowFunction, ObjectExpression, ArrayExpression, ConditionalExpression, TemplateLiteral, SpreadElement, NewExpression, ThisExpression, SequenceExpression, TaggedTemplate, ClassExpression, YieldExpression, AwaitExpression.
  • Εντολή: BlockStatement, IfStatement, ForStatement, ForInStatement, ForOfStatement, WhileStatement, DoWhileStatement, SwitchStatement, ReturnStatement, ThrowStatement, TryStatement, BreakStatement, ContinueStatement, ExpressionStatement.
  • Δήλωση: VariableDeclaration, FunctionDeclaration, ClassDeclaration, ImportDeclaration.
  • Μοτίβο (αποδόμησης): ArrayPattern, ObjectPattern, RestElement, AssignmentPattern.

Η αυτόματη εισαγωγή ελληνικών ερωτηματικών υλοποιείται στον αναλυτή σύνταξης (όχι στον λεκτικό αναλυτή), σύμφωνα με τις προδιαγραφές.

Διερμηνευτής

Το src/Runtime/Interpreter.php διατρέχει το AST. Επισκέπτεται κάθε κόμβο και επιστρέφει ένα JsValue (ή ένα Completion για εντολές που αλλάζουν τη ροή ελέγχου).

Βασικές υποστηρικτικές κλάσεις:

  • Environment (src/Runtime/Environment.php) — αλυσίδα λεξικών πεδίων εμβέλειας. Κάθε κλήση συνάρτησης δημιουργεί ένα νέο περιβάλλον που δείχνει στον λεξικό γονέα του. Το var ανυψώνεται στο πεδίο εμβέλειας της συνάρτησης· τα let / const έχουν εμβέλεια μπλοκ και TDZ.
  • CallStack (src/Runtime/CallStack.php) — ρητή στοίβα με όριο βάθους. Οι κλήσεις συναρτήσεων και μεθόδων προσθέτουν πλαίσια· οι εξαιρέσεις τα ξετυλίγουν.
  • Completion (src/Runtime/Completion.php) — διακριτή ένωση των normal, return, throw, break, continue, μαζί με προαιρετική τιμή και ετικέτα.
  • Reference (src/Runtime/Reference.php) — ο τύπος Reference των προδιαγραφών για αξιολόγηση της αριστερής πλευράς σε αναθέσεις και delete.

Τιμές

Οι τιμές JavaScript είναι αντικείμενα PHP υπό το src/Value/. Καθένα υλοποιεί τη διεπαφή JsValue και διαθέτει τις δικές του μεθόδους μετατροπής τύπων (toNumber(), toString(), toBoolean()).

Κλάση Τύπος JS
JsUndefined undefined (singleton)
JsNull null (singleton)
JsBoolean true / false
JsNumber Διπλή ακρίβειας IEEE 754· διατηρεί NaN, ±Infinity, ±0
JsString Σημασιολογία συμβολοσειρών UTF-16
JsObject Βασικό αντικείμενο με χάρτη ιδιοτήτων και αλυσίδα πρωτοτύπων
JsArray Επεκτείνει το JsObject με παρακολούθηση μήκους
JsFunction Κλείσιμο με δεσμευμένο Environment
JsSymbol Πρωτογενής τιμή Symbol
JsBigInt Αυθαίρετη ακρίβεια μέσω bcmath
JsProxy Και οι 13 παγίδες, με δυνατότητα ανάκλησης
JsPromise Σύγχρονος εκτελεστής + ουρά μικροεργασιών
JsGenerator Διπλή λειτουργία: στιγμιότυπο πλαισίου (σώματα μεταγλωττισμένα σε bytecode· βλ. Γεννήτριες στιγμιότυπων πλαισίου VM bytecode) με εφεδρική χρήση του Fiber του PHP για yield*, try-with-yield και ασύγχρονες γεννήτριες
JsMap, JsSet, JsWeakMap, JsWeakSet, JsWeakRef, JsFinalizationRegistry Συλλογές
JsArrayBuffer, JsSharedArrayBuffer, JsDataView, JsTypedArray Δυαδικά δεδομένα

Ο χειρισμός αριθμών είναι η μεγαλύτερη ιστορικά πηγή σφαλμάτων: το NaN !== NaN πρέπει να είναι αληθές, το -0 === 0 πρέπει να είναι αληθές και οι οριακές περιπτώσεις του ToNumber απαιτούν ακριβή φροντίδα σε επίπεδο bit. Η κλάση Spec/TypeConversion υλοποιεί κατά γράμμα τους αλγορίθμους των προδιαγραφών και έχει ελεγχθεί εξαντλητικά.

Ενσωματωμένη βιβλιοθήκη

Το src/BuiltIn/ παρέχει όλα τα αντικείμενα της τυπικής βιβλιοθήκης: Array, String, Object, Math, JSON, Date, RegExp, Map, Set, Promise, Proxy, Reflect, Symbol, BigInt, TypedArray, Temporal, Intl, console, καθώς και τα πακέτα Web Platform / Fetch (URL, Headers, Request, Response, Blob, FormData, πλήρη Streams, EventTarget, AbortController/AbortSignal, DOMException, structuredClone, performance, TextEncoder/TextDecoder, atob/btoa), crypto + SubtleCrypto, WebSocket, XMLHttpRequest και τον βρόχο συμβάντων (setTimeout, setInterval, queueMicrotask, AsyncContext).

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

Γέφυρα PHP↔JS

Η μετατροπή τιμών ανάμεσα στα όρια γίνεται σε δύο σημεία:

  • Phasis\Interop\PhpToJs::convert() — τιμή PHP σε JsValue, που χρησιμοποιείται για ορίσματα setGlobal(), call() και τιμές επιστροφής συναρτήσεων κεντρικού υπολογιστή. Ένα εκτελέσιμο PHP γίνεται JsFunction, το σώμα του οποίου το καλεί.
  • PhpToJs::toPhp() — JsValue σε PHP για τα ορίσματα μιας συνάρτησης κεντρικού υπολογιστή.
  • Engine::toPhp() (ιδιωτικό) — JsValue σε PHP για τα αποτελέσματα των eval() και call().

Δεν υπάρχει γέφυρα αντικειμένων: τα αντικείμενα PHP που δεν είναι εκτελέσιμα μετατρέπονται σε undefined (issue #18). Η κλάση JsToPhp στο src/Bytecode/ δεν σχετίζεται με τη διαλειτουργικότητα· είναι ο μεταγλωττιστής μετασχηματισμού που περιγράφεται παρακάτω. Οι κανόνες μετατροπής τεκμηριώνονται στην ενότητα Μετατροπή τιμών.

Χειρισμός σφαλμάτων

Ένα μη συλληφθέν JS throw γίνεται PHP Phasis\Exceptions\JsThrowable· τα σφάλματα που προκαλεί η ίδια η μηχανή εμφανίζονται ως Phasis\Exceptions\TypeError, RangeError ή ReferenceError, όλα υποκλάσεις του Phasis\Exceptions\RuntimeError. Στο εσωτερικό της μηχανής, ένα JS try / catch συλλαμβάνει JsThrowable, κάθε RuntimeError και Phasis\Exceptions\SyntaxError. Οι υπόλοιπες εξαιρέσεις PHP, συμπεριλαμβανομένων όσων προκαλούνται από συναρτήσεις κεντρικού υπολογιστή, δεν μετατρέπονται και μεταδίδονται απευθείας στον καλούντα PHP (issue #20).

Υπάρχουν δύο όρια: το ανώτατο όριο της στοίβας κλήσεων (CallStack::maxDepth, 4,096 πλαίσια, συν όριο 8,192 πλαισίων για κλεισίματα που έχουν υποστεί μετασχηματισμό) και το maxLoopIterations, που ορίζεται μέσω του Engine::setLimit() και επιβάλλεται μόνο από τον περιπατητή δέντρου (issue #19). Και τα δύο προκαλούν Phasis\Exceptions\InternalError. Δεν υπάρχουν όρια στο μήκος συμβολοσειράς, στο μέγεθος εξόδου ή στον χρόνο εκτέλεσης.

Εικονική μηχανή bytecode

Οι συχνά εκτελούμενες συναρτήσεις μετασχηματίζονται με δύο τρόπους. Το JsToPhp (στο src/Bytecode/) εκπέμπει ένα κλείσιμο PHP για σώματα με πολλή αριθμητική ή χρήση τοπικών μεταβλητών, τα οποία το tracing JIT του PHP μπορεί να μεταγλωττίσει σε εγγενή κώδικα· η διαδικασία μετασχηματίζει αναγνώσεις new Ctor(args), obj.method(args) και this.member, αρκετές ώστε το συνηθισμένο μοτίβο μεθόδου κλάσης μέσα σε βρόχο να παρακάμπτει πλήρως την εικονική μηχανή. Όλα τα υπόλοιπα μεταγλωττίζονται σε επίπεδο bytecode που καταναλώνει το VM::execute, με προσαρμοσμένη στοίβα κλήσεων που κρατά κάθε κλήση JS μέσα σε ένα μόνο πλαίσιο PHP και προσωρινή μνήμη inline ανά PC για αναγνώσεις μεθόδων πρωτοτύπων. Τα απλά σώματα γεννητριών μεταγλωττίζονται επίσης σε bytecode, με το Op::YIELD να καταγράφει ένα στιγμιότυπο πλαισίου στη μνήμη σωρού, αντί να περνά από το Fiber του PHP.

Κάθε μορφή που δεν μετασχηματίζει ο μεταγλωττιστής (e.g. with, try με επαναδέσμευση catch, yield*, ασύγχρονες γεννήτριες) επιστρέφει στον περιπατητή δέντρου. Δείτε το Εικονική μηχανή bytecode για τον πλήρη μηχανισμό.