Treci la conținut

Arhitectură

Phasis este un arbore de sintaxă parcurs în patru etape, cu o mașină virtuală de bytecode folosită oportunist. Fluxul de date este:

Cod
JS text
  │
  ▼
┌──────────┐
│  Lexer   │  src/Lexer/
└────┬─────┘
     ▼ tokens
┌──────────┐
│  Parser  │  src/Parser/
└────┬─────┘
     ▼ AST nodes
┌──────────────┐
│ Interpreter  │  src/Runtime/
│  + Bytecode  │  src/Bytecode/
│      VM      │
└────┬─────────┘
     ▼ JsValue
PHP host

Fiecare etapă este o clasă PHP cu o interfață restrânsă, consumată de etapa următoare. Nu există etapă de legare — parsarea produce un AST, pe care interpretorul îl parcurge direct.

Lexer

src/Lexer/Lexer.php transformă textul sursă într-un flux de instanțe Token. Fiecare jeton este o structură doar pentru citire: tip (enumerare TokenType, aproximativ 80 de cazuri), valoare, linie, coloană.

Lexerul gestionează:

  • Literali numerici — zecimali, hexazecimali (0x), octali (0o), binari (0b), exponențiali și separatori numerici.
  • Literali șir — ghilimele simple și duble, toate secvențele de escape, literali șablon cu expresii imbricate.
  • Literali de expresii regulate — tokenizare completă /pattern/flags, cu detectarea situațiilor în care un / trebuie interpretat ca expresie regulată sau ca împărțire.
  • Identificatori, cuvinte cheie, semne de punctuație.
  • Comentarii (pe linie și în bloc), păstrate ca elemente auxiliare.
  • Sursă Unicode — octeți UTF-8 interpretați ca sursa UTF-16 JS.

Rezultatul este un generator care furnizează leneș jetoanele. Parserul le consumă anticipând un singur jeton.

Parser

src/Parser/Parser.php este un parser Pratt scris manual. Prioritatea expresiilor este definită în src/Parser/Precedence.php. Instrucțiunile folosesc propria analiză descendentă recursivă.

Acesta produce noduri AST imuabile din src/Ast/. Fiecare nod include locația din sursă pentru urmele stivei și raportarea erorilor.

Forme importante:

  • Expresie: Literal, Identifier, BinaryExpression, UnaryExpression, AssignmentExpression, CallExpression, MemberExpression, ArrowFunction, ObjectExpression, ArrayExpression, ConditionalExpression, TemplateLiteral, SpreadElement, NewExpression, ThisExpression, SequenceExpression, TaggedTemplate, ClassExpression, YieldExpression, AwaitExpression.
  • Instrucțiune: BlockStatement, IfStatement, ForStatement, ForInStatement, ForOfStatement, WhileStatement, DoWhileStatement, SwitchStatement, ReturnStatement, ThrowStatement, TryStatement, BreakStatement, ContinueStatement, ExpressionStatement.
  • Declarație: VariableDeclaration, FunctionDeclaration, ClassDeclaration, ImportDeclaration.
  • Tipar (destructurare): ArrayPattern, ObjectPattern, RestElement, AssignmentPattern.

Inserarea automată a punctului și virgulei este implementată în parser (nu în lexer), conform specificației.

Interpretor

src/Runtime/Interpreter.php este componenta care parcurge AST-ul. Vizitează fiecare nod și returnează o JsValue (sau o Completion pentru instrucțiunile care modifică fluxul de control).

Clase de suport importante:

  • Environment (src/Runtime/Environment.php) — lanțul domeniilor de vizibilitate lexicale. Fiecare apel de funcție creează un mediu nou, legat de părintele său lexical. var este ridicat la nivelul funcției; let / const au domeniu de bloc și o zonă de inițializare temporală (TDZ).
  • CallStack (src/Runtime/CallStack.php) — stivă explicită cu limită de adâncime. Apelurile de funcții și metode adaugă cadre; excepțiile le elimină.
  • Completion (src/Runtime/Completion.php) — reuniune discriminată de valori normal, return, throw, break, continue, plus o valoare opțională și o etichetă.
  • Reference (src/Runtime/Reference.php) — tipul Reference din specificație, pentru evaluarea membrului stâng în atribuiri și delete.

Valori

Valorile JavaScript sunt obiecte PHP în cadrul src/Value/. Fiecare implementează interfața JsValue și are propriile metode de conversie a tipului (toNumber(), toString(), toBoolean()).

Clasă Tipul JS
JsUndefined undefined (singleton)
JsNull null (singleton)
JsBoolean true / false
JsNumber IEEE 754 dublă precizie; păstrează NaN, ±Infinity, ±0
JsString Semantică de șir UTF-16
JsObject Obiect de bază cu mapă de proprietăți și lanț de prototipuri
JsArray Extinde JsObject cu urmărirea lungimii
JsFunction Închidere cu Environment capturat
JsSymbol Primitiv Symbol
JsBigInt Precizie arbitrară prin bcmath
JsProxy Toate cele 13 capcane, revocabile
JsPromise Executor sincron + coadă de microtaskuri
JsGenerator Mod dual: instantaneu al cadrului (corpuri compilate în bytecode; consultați Generatoare cu instantaneu al cadrului pentru mașina virtuală de bytecode), cu fallback la Fiber PHP pentru yield*, try-with-yield și generatoare asincrone
JsMap, JsSet, JsWeakMap, JsWeakSet, JsWeakRef, JsFinalizationRegistry colecții
JsArrayBuffer, JsSharedArrayBuffer, JsDataView, JsTypedArray date binare

Gestionarea numerelor este cea mai importantă sursă de erori istorice: NaN !== NaN trebuie să fie adevărat, -0 === 0 trebuie să fie adevărat, iar cazurile-limită ToNumber necesită atenție exactă la nivel de biți. Clasa Spec/TypeConversion implementează algoritmii specificației întocmai și este testată exhaustiv.

Biblioteca încorporată

src/BuiltIn/ oferă toate obiectele bibliotecii standard: Array, String, Object, Math, JSON, Date, RegExp, Map, Set, Promise, Proxy, Reflect, Symbol, BigInt, TypedArray, Temporal, Intl, console, plus pachetele Web Platform / Fetch (URL, Headers, Request, Response, Blob, FormData, Streams complet, EventTarget, AbortController/AbortSignal, DOMException, structuredClone, performance, TextEncoder/TextDecoder, atob/btoa), crypto + SubtleCrypto, WebSocket, XMLHttpRequest și bucla de evenimente (setTimeout, setInterval, queueMicrotask, AsyncContext).

Fiecare componentă încorporată este o metodă statică install() a unei clase. Engine le apelează în ordinea dependențelor la construire. Fiecare definește lanțul de prototipuri, instalează metodele ca instanțe JsFunction încapsulate în închideri PHP și expune constructorul ca globală.

Puntea PHP↔JS

Conversia valorilor la trecerea graniței are loc în două puncte:

  • Phasis\Interop\PhpToJs::convert() — valoare PHP în JsValue, folosită pentru setGlobal(), argumentele call() și valorile returnate de funcțiile gazdă. Un callable PHP devine un JsFunction al cărui corp îl apelează.
  • PhpToJs::toPhp() — JsValue în PHP pentru argumentele unei funcții gazdă.
  • Engine::toPhp() (privat) — JsValue în PHP pentru rezultatele din eval() și call().

Nu există o punte pentru obiecte: obiectele PHP care nu sunt apelabile se convertesc în undefined (problema #18). Clasa JsToPhp din src/Bytecode/ nu are legătură cu interoperabilitatea; este transpilerul descris mai jos. Regulile de conversie sunt documentate în Conversia valorilor.

Tratarea erorilor

Un JS throw neprins devine un PHP Phasis\Exceptions\JsThrowable; erorile generate chiar de motor sunt semnalate ca Phasis\Exceptions\TypeError, RangeError sau ReferenceError, toate subclase ale lui Phasis\Exceptions\RuntimeError. În interiorul motorului, un JS try / catch prinde JsThrowable, orice RuntimeError și Phasis\Exceptions\SyntaxError. Celelalte excepții PHP, inclusiv cele aruncate de funcțiile gazdă, nu sunt convertite și se propagă direct către apelantul PHP (problema #20).

Există două limite: plafonul stivei de apeluri (CallStack::maxDepth, 4,096 cadre, plus o protecție de 8,192 cadre pentru închiderile transpuse) și maxLoopIterations, setată prin Engine::setLimit(), aplicată doar de componenta care parcurge arborele (problema #19). Ambele generează Phasis\Exceptions\InternalError. Nu există limite pentru lungimea șirurilor, dimensiunea rezultatului sau timpul de execuție.

Mașina virtuală de bytecode

Funcțiile intens solicitate sunt reduse în două moduri. JsToPhp (în src/Bytecode/) emite o închidere PHP pentru corpuri cu multe operații numerice sau variabile locale, pe care JIT-ul de urmărire al PHP îl poate compila în cod nativ; fluxul de compilare reduce accesările new Ctor(args), obj.method(args) și this.member, ceea ce permite tiparului canonic „metodă de clasă într-o buclă” să ocolească complet mașina virtuală. Orice altceva este compilat în bytecode liniar, consumat de VM::execute, cu o stivă de apeluri personalizată care păstrează fiecare apel JS într-un singur cadru PHP și o memorie cache inline per PC pentru accesările metodelor prototipului. Corpurile simple ale generatoarelor sunt compilate și ele în bytecode, iar Op::YIELD capturează un instantaneu al cadrului din heap, fără a trece prin Fiber PHP.

Orice formă pe care compilatorul nu o poate reduce (e.g. with, try cu o captură ce reatribuie variabile, yield*, generatoare asincrone) revine la componenta care parcurge arborele. Consultați Mașina virtuală de bytecode pentru mecanismul complet.