Arhitectură
Phasis este un arbore de sintaxă parcurs în patru etape, cu o mașină virtuală de bytecode folosită oportunist. Fluxul de date este:
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.vareste ridicat la nivelul funcției;let/constau 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 valorinormal,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 șidelete.
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 înJsValue, folosită pentrusetGlobal(), argumentelecall()și valorile returnate de funcțiile gazdă. Un callable PHP devine unJsFunctional cărui corp îl apelează.PhpToJs::toPhp()—JsValueîn PHP pentru argumentele unei funcții gazdă.Engine::toPhp()(privat) —JsValueîn PHP pentru rezultatele dineval()șicall().
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.