Toate cele scrise mai jos corespund lui Haxe 2 și sunt aproape valabile pentru Haxe 3. Dar să lucrezi fără schimbări va fi numai în cel de-al doilea. Cine va adapta toate exemplele la cel de-al treilea și va împărți cu ceilalți va primi o mulțime de experiență. În mod similar, scrisul nu pretinde adevărul în ultima instanță și poate conține erori.
Deci, ce este de fapt o macro în Haxe? Nu veți crede, dar macrocomenzile sunt funcții normale. Ei bine, nu destul de normal, dar nu este nimic neobișnuit în comparație cu codul Haxe. Principalele avantaje ale macro-uri - acest lucru este ceva ce ei, în plus față de biblioteca standard, sunt disponibile, de asemenea, toate pachetele și clasele haxe.macro Neko și că macro-uri de funcții sunt invocate la momentul compilării, mai degrabă decât la rulare și pentru a reveni cod haXe care și va fi executat în stadiul de execuție a programului. Neko oferă acces la sistemul de fișiere și, în general, la sistemul în ansamblul său, iar clasele haxe.macro permit. Da, toate permit: crearea de clase noi, schimbarea structurii celor existente, primirea de date complete despre toate tipurile, enum-ah etc. în general, accesul complet. Principalul lucru pe care trebuie să știți exact de ce aveți nevoie. Toate acestea fac macro-uri atât de interesante pentru noi. Dar să nu de mult pentru a întârzia și uita-te la un exemplu care va fi stocat într-o aplicație compilat și a afișa data construi proiectului (fără utilități terțe părți o astfel de problemă rezolvată destul de dificil și apoi vine în ajutorul unui macro):
import haxe. macro. context;
import haxe. macro. expr;
@: funcția publică macro statică getBuildDate # 40; # 41; : Expr # 123;
var d = Data. acum # 40; # 41; ;
returnează contextul. makeExpr # 40; d. toString # 40; # 41;. Context. currentPos # 40; # 41; # 41; ;
# 125;
# 125;
Funcția getBuildDate este macro-ul nostru. Imediat acordăm atenție metaetichetei @: macro la începutul funcției este un semn distinctiv care spune compilatorului că este necesar să se efectueze această funcție la momentul compilării! Funcția noastră returnează tipul Expr, despre asta mai târziu. Apoi, în metoda getBuildDate, se creează o dată normală cu data curentă, care este tradusă într-un șir și transmisă "magic" Context.makeExpr și metoda chiar mai puțin inteligibilă a Context.currentPos. Și aici merită revenirea la tipul Expr, definiția sa este ușor de găsit în codul modulului haxe.macro.Expr
typedef Expr = # 123;
var expr. ExprDef;
var pos. poziţia;
# 125;
Expr - este o structură simplă, cu două domenii: expr - orice admisibil expresie în haXe, dar nu și în forma obișnuită, ci ca una dintre valorile ExprDef enum-un (tip enumerat), de exemplu EFunction, EVAR, ENew, EConst, eCall, EContinue , EReturn, etc. Și pos este un pointer la locul în care expresia va fi localizată în fișier. De fapt, pentru a calcula pos necesară nu atât de des și să înceapă să-și amintească faptul că, în locul lui Context.currentPos de substituție (). Acesta este modul în care arată poziția:
Poziția tippedef = # 123;
var fișier. string;
var min. int;
var max. int;
# 125;
Acum că Expr a încetat să mai fie o enigmă completă, să revenim la metoda Context.makeExpr. Din titlu este clar că face ceva și aici este mai bine, așa cum se spune, o dată pentru a vedea ce face el:
Înapoi la Expr descrierea structurii și asigurați-vă că într-adevăr Expr, care conține un șir de Econst constantă (CString ()) până în prezent construi proiectului și situat în Main.hx 8 linii, și ocupă 8-20 de caractere, iar acest lucru este foarte important! Uită-te la linia a opta a primului exemplu cu un macro, se spune:
urmă # 40; getBuildDate # 40; # 41; # 41; ;
Așa că sa întâmplat: ați dezasamblat și, sper, ați înțeles, probabil, prima, dar nu ultima, macro. Deși sunt sigur că întrebarea a rămas: "Care este magia # sau Macro înainte de funcția principală?". Lucru este că funcția macro influențează foarte mult comportamentul clasei în care sunt definite, iar importurile disponibile funcțiile macro repaus adesea nepermisă a codului, și care a trebuit să se furișa astfel încât să se potrivească totul într-un singur modul. Dar sfatul meu pentru tine (și într-adevăr, este mai corect): scrie macro-uri în clasele alocate separat, fără a le amesteca cu restul codului. Apoi, voi face doar asta, dar versiunea "mixtă" pe care nu o pot ajuta decât să o arăt.
Mărturisesc că, în acest articol, am vrut să scriu o grămadă de alte macrocomenzi pentru a atrage cât mai mulți oameni, dar mi-am dat seama că cunoștințele tale sunt foarte mici și că face ceva rău este prea devreme. Dar pentru a complica prima noastră macro este timpul!
Ne-am stabilit sarcina nu cea mai simplă, dar chiar și puțin dificilă. makeExpr nu ne va ajuta, el este capabil să lucreze numai cu tipuri de bază și care sunt enumerate (Int, float, String, Bool, Array și obiecte anonime compuse din aceste tipuri), este timpul să apeleze la metoda Context.parse. Uite, la mine sa dovedit:
@: funcția publică statică macro getBuildDate2 # 40; # 41; : Expr # 123;
var d = Data. acum # 40; # 41; ;
returnează contextul. analiza # 40; "Data.fromString (" + d. ToString # 40; # 41; + ")". Context. currentPos # 40; # 41; # 41; ;
# 125;
Se vede că Context.parse a luat primul parametru ca un șir care conține codul Haxe, dar vă sfătuiesc să vă uitați la rezultatul său, doar spune-mi cum am făcut-o:
var date = getBuildDate2 # 40; # 41; ;
urmă # 40; data # 41; ;
urmă # 40; Tip. typeof # 40; data # 41; # 41; ;
Context.parse este o metodă foarte utilă pentru parsarea șirurilor care conțin codul Haxe, de exemplu, dintr-un fișier extern sau șiruri de sine-asamblate, ca în exemplul de mai sus. Totuși, există o sarcină dificilă, care poate fi rezolvată numai cu ajutorul acestei metode, și voi spune despre ea într-un fel. Dar, pentru a-ți spune adevărul, nu-mi place să analizez și să încerci să-l folosești doar dacă este absolut necesar, doar pentru că liniile transmise pot conține erori sintactice sau logice, iar parsarea extra necesită timp. În continuare, vă arăt cum putem face fără să analizăm sarcina noastră.
Pentru a "scăpa" de Context.parse și pentru a arăta puterea macro-urilor, vom scrie totul la fel, dar diferit și mai complicat :). Dar, mai întâi, să ne întoarcem la structura ExprDef, așa cum am spus, descrie orice expresie din Haxe, ceea ce înseamnă că ea poate descrie, de asemenea, Data.fromString (). "Dar cum să faceți asta", vă întrebați, și vă mărturisesc sincer, nu știu. Nu, bine, cred că unele ExpDef-uri știu, iar restul pot fi găsite în documentație, dar vă voi arăta cea mai simplă metodă de a ști cum să scrieți expresia de care aveți nevoie. Este foarte simplu, să creăm această metodă:
@: test macro funcțional static # 40; e: Expr # 41; # 123;
urmă # 40; e # 41; ;
retur e;
# 125;
și să-i dăm expresia de care avem nevoie:
@: funcția publică statică macro getBuildDate3 # 40; # 41; : Expr # 123;
var d = Data. acum # 40; # 41; ;
var p = Context. currentPos # 40; # 41; ;
întoarcere # 123; expr: ECall # 40;
# 123; expr: EField # 40;
# 123; expr: EConst # 40; CIdent # 40; „Data“ # 41; # 41;. pos: p # 125; .
"FromString" # 41; .
pos: p # 125; .
# 91; # 123; expr: EConst # 40; CString # 40; d. toString # 40; # 41; # 41; # 41;. pos: p # 125; # 93; # 41; .
pos: p # 125; ;
# 125;
Ei bine, am spus că vom face mai dificil, așa că am făcut-o. Nu am de gând să te convingă că atât de corect sau altceva, aș spune doar că următoarea va fi o altă opțiune este mult mai simplu decât atât, și dacă nu au fost încă vâna să înțeleagă că aici, și cum merge în curând pe, dar nu uita că Expr ExprDef și încă trebuie să lucreze, atunci când ajunge la macro-uri complexe și parse obișnuite și makeExpr nu va ajuta acolo. Foarte important, nu vă fie teamă să se uite înapoi de multe ori, în timp ce colectează expresie haXe (metoda de testare mai târziu), și tot ce primești. Și nu fi leneș să studiezi cu atenție întreaga linie cu un retur, deși pe acest exemplu simplu.
Amintiți-vă de unde am început: avem o linie de date și dorim să o transferăm într-o metodă care o va analiza și returna data. Ie În mod ideal, aș dori să iau și să scriu data.fromString (d.toString ()); și o vom face, sau aproape așa:
@: funcția publică statică macro getBuildDate4 # 40; # 41; : Expr # 123;
var d = Data. acum # 40; # 41; ;
var e = Context. makeExpr # 40; d. toString # 40; # 41;. Context. currentPos # 40; # 41; # 41; ;
return date macro. fromString # 40; $ e # 41; ;
# 125;
În opinia mea, sa dovedit perfect! Rezultatul getBuildDate4 este același cu getBuildDate3, iar extern getBuildDate4 arată mai bine. Uită-te la valoarea variabilei e. Aceasta este exact ceea ce a returnat prima noastră metodă getBuildDate, o expresie care conține un șir cu o dată. Toată magia în ultima linie după declarația de returnare. Mai întâi vedem operatorul macro, care îi spune compilatorului că tot ce merge mai departe trebuie translatat imediat în Expr. Ie dacă scriu
atunci acesta este același lucru cu scrierea
# 123; expr: EConst # 40; CString # 40; "Foo" # 41; # 41;. poz: Context. currentPos # 40; # 41; # 125;
Sunt de acord, bine gândit. Și astfel, în această expresie pentru a incorpora valori suplimentare din exterior, trebuie să le transferați la $ cheie în primul rând, apoi compilatorul la acest loc umple expresia variabila, atâta timp cât (variabilă) a fost, de asemenea, un tip Expr, în acest exemplu, $ un e, transmis metoda fromString . astfel oroarea de mai multe imbricată enum-uri, am înregistrat toate într-o singură linie: Date.fromString macro ($ e), 3 haXe Si poate fi chiar mai ușor să scrie.
Blogul lui Nicholas are un exemplu foarte clar de modul în care macrocomenzile simplifică codul de macrocomenzi și nu pot să-i arăt:
@: funcția statică macro se repetă # 40; e. Expr, eN. expr # 41; # 123;
retur pentru # 40; x în 0. $ eN # 41; $ e;
# 125;
Înainte de această simplificare, această macrocomandă ar putea fi scrisă cu cel puțin șapte rânduri de cod! Analiza a ceea ce face el și modul în care lucrează este lăsat la tine, sper că acum nu va fi dificil. Sugestie și opțiune de simplificare aici.
Pentru prima dată, cred că este suficient și îmi propun să se oprească la acest lucru. Rezumând, pot spune că acum ați învățat cum să scrieți macro-uri simple, să lucrați cu Expr, să învățați câteva metode utile ale clasei Context și, de fapt, sunt mult mai multe, să vă familiarizați cu macrocomenzile etc. Ceea ce nu ați învățat este cum să vă creați clasele și enumele, cum să editați clasele întregi, completându-le cu metode sau schimbând cele existente și multe altele. Sper că am destulă putere și voi scrie despre toate astea.
În cele din urmă, pentru a analiza macrocomanda din blogul lui Nicholas, vă voi sugera că vom încerca să scriem o macrocomandă care va stoca valoarea fișierului într-un șir și va funcționa după cum se arată mai jos:
var str: String = MyMacros. getFileContent # 40; "Readme.txt" # 41; ;
Mulțumiri speciale lui Alexander Kuzmenko. Alexander Khokhlov și SlavaRa pentru ajutor în scrierea articolului, revizuirea, editarea și critica utilă.