Kompilasi (subset dari) JavaScript atau AS2 ke ActionScript 2 (flash player 8 kompatibel) Bytecode rakitan yang dapat dikompilasi oleh Flasma. Sangat wip.
Saya memodong permainan flash lama dan saya bosan menulis bytecode actioncript dengan tangan. Untungnya, ActionScript 2 sangat mirip dengan JavaScript (karena AS2 sebagian sesuai dengan spesifikasi Ecmascript 4) sehingga saya bisa menggunakan @babel/parser untuk menguraikan sumber saya sebagai JavaScript ke AST, dan kemudian memancarkan kode bytecode dari AST.
Kompiler hanya mendukung subset fitur JS/AS2 yang saya butuhkan, dan proyek berhak untuk membantai semantik bahasa untuk membuatnya lebih mudah diimplementasikan. Pengembangan juga akan berkembang hanya ketika saya menemukan fitur yang ingin saya terapkan untuk membuat hidup saya lebih mudah.
Kompiler juga mensimulasikan tumpukan untuk mendokumentasikan tumpukan saat ini sebagai komentar di sebelah kanan setiap baris untuk membuat verifikasi manual lebih mudah. Jika opcode cabang atau cabang tercapai (if-else atau loop), simulator melewatkan sisa fungsi/konteks. Fungsi dalam disimulasikan dengan tumpukan mereka sendiri. Pernyataan kode juga ditambahkan sebagai komentar sebelum bytecode untuk pernyataan tersebut.
@js2f/push-register-context , @js2f/pop-register-contextSumber sampel kerja-dalam-kemajuan saat ini:
outsideGlobalVar = globalVar2 = 123 ;
function gatherStats ( velocity ) {
var emptyLocal , emptyLocal2 , nonEmptyLocal3 ;
var localVar = 123 ;
globalVar = 5432 ;
globalVar = localVar = 1111 ;
globalVar = globalVar2 = 1111 ;
globalVar = globalVar2 = undefined ;
velocity = atv . velocity ;
globalVelocity = atv . velocity ;
atv . bar = 1 ;
this . foo = this . bar + 1 ;
atv . x = atv . velocityX - atv . x ;
localVar = "foo\nbar" ;
return '{"type":"velocity","data":' + ( velocity + 1 ) + "}" ;
}
global . enqueueStats ( gatherStats ( atvMC . velocity ) , 1 ) ;
enqueueStats ( gatherStats ( atvMC . velocity ) ) ;
emptyFunction ( ) ;
global . emptyFunction ( ) ;Memancarkan:
// -- outsideGlobalVar = globalVar2 = 123;
// -- outsideGlobalVar = globalVar2 = 123
push ' outsideGlobalVar ' // ' outsideGlobalVar '
// -- globalVar2 = 123
push ' globalVar2 ' , 123 // ' outsideGlobalVar ' | ' globalVar2 ' | 123
setVariable // ' outsideGlobalVar '
push 123 // ' outsideGlobalVar ' | 123
setVariable // -- <empty>
function2 ' gatherStats ' ( r : 2 = ' velocity ' ) ( r : 1 = ' this ' )
// -- var emptyLocal, emptyLocal2, nonEmptyLocal3;
// -- var localVar = 123;
push 123 // 123
setRegister r : 6 /* local:localVar */ // 123
pop // -- <empty>
// -- globalVar = 5432;
// -- globalVar = 5432
push ' globalVar ' , 5432 // ' globalVar ' | 5432
setVariable // -- <empty>
// -- globalVar = localVar = 1111;
// -- globalVar = localVar = 1111
push ' globalVar ' // ' globalVar '
// -- localVar = 1111
push 1111 // ' globalVar ' | 1111
setRegister r : 6 /* local:localVar */ // ' globalVar ' | 1111
setVariable // -- <empty>
// -- globalVar = globalVar2 = 1111;
// -- globalVar = globalVar2 = 1111
push ' globalVar ' // ' globalVar '
// -- globalVar2 = 1111
push ' globalVar2 ' , 1111 // ' globalVar ' | ' globalVar2 ' | 1111
setVariable // ' globalVar '
push 1111 // ' globalVar ' | 1111
setVariable // -- <empty>
// -- globalVar = globalVar2 = undefined;
// -- globalVar = globalVar2 = undefined
push ' globalVar ' // ' globalVar '
// -- globalVar2 = undefined
push ' globalVar2 ' , UNDEF // ' globalVar ' | ' globalVar2 ' | UNDEF
setVariable // ' globalVar '
push UNDEF // ' globalVar ' | UNDEF
setVariable // -- <empty>
// -- velocity = atv.velocity;
// -- velocity = atv.velocity
push ' atv ' // ' atv '
getVariable // atv
push ' velocity ' // atv | ' velocity '
getMember // atv . velocity
setRegister r : velocity // atv . velocity
pop // -- <empty>
// -- globalVelocity = atv.velocity;
// -- globalVelocity = atv.velocity
push ' globalVelocity ' , ' atv ' // ' globalVelocity ' | ' atv '
getVariable // ' globalVelocity ' | atv
push ' velocity ' // ' globalVelocity ' | atv | ' velocity '
getMember // ' globalVelocity ' | atv . velocity
setVariable // -- <empty>
// -- atv.bar = 1;
// -- atv.bar = 1
push ' atv ' // ' atv '
getVariable // atv
push ' bar ' , 1 // atv | ' bar ' | 1
setMember // -- <empty>
// -- this.foo = this.bar + 1;
// -- this.foo = this.bar + 1
push r : this , ' foo ' , r : this , ' bar ' // r : this | ' foo ' | r : this | ' bar '
getMember // r : this | ' foo ' | r : this . bar
push 1 // r : this | ' foo ' | r : this . bar | 1
add // r : this | ' foo ' | r : this . bar + 1
setMember // -- <empty>
// -- atv.x = atv.velocityX - atv.x;
// -- atv.x = atv.velocityX - atv.x
push ' atv ' // ' atv '
getVariable // atv
push ' x ' , ' atv ' // atv | ' x ' | ' atv '
getVariable // atv | ' x ' | atv
push ' velocityX ' // atv | ' x ' | atv | ' velocityX '
getMember // atv | ' x ' | atv . velocityX
push ' atv ' // atv | ' x ' | atv . velocityX | ' atv '
getVariable // atv | ' x ' | atv . velocityX | atv
push ' x ' // atv | ' x ' | atv . velocityX | atv | ' x '
getMember // atv | ' x ' | atv . velocityX | atv . x
subtract // atv | ' x ' | atv . velocityX - atv . x
setMember // -- <empty>
// -- localVar = "foo\nbar";
// -- localVar = "foo\nbar"
push ' foo n bar ' // ' foo n bar '
setRegister r : 6 /* local:localVar */ // ' foo n bar '
pop // -- <empty>
// -- return '{"type":"velocity","data":' + (velocity + 1) + "}";
push ' {"type":"velocity","data": ' , r : velocity , 1 // ' {"type":"velocity","data": ' | r : velocity | 1
add // ' {"type":"velocity","data": ' | r : velocity + 1
add // ' {"type":"velocity","data": ' + ( r : velocity + 1 )
push ' } ' // ' {"type":"velocity","data": ' + ( r : velocity + 1 ) | ' } '
add // ( ' {"type":"velocity","data": ' + ( r : velocity + 1 )) + ' } '
return // ( ' {"type":"velocity","data": ' + ( r : velocity + 1 )) + ' } '
end // of function gatherStats
// -- global.enqueueStats(gatherStats(atvMC.velocity), 1);
push 1, ' atvMC ' // 1 | ' atvMC '
getVariable // 1 | atvMC
push ' velocity ' // 1 | atvMC | ' velocity '
getMember // 1 | atvMC . velocity
push 1 , ' gatherStats ' // 1 | atvMC . velocity | 1 | ' gatherStats '
callFunction // 1 | gatherStats ( atvMC . velocity )
push 2 , ' global ' // 1 | gatherStats ( atvMC . velocity ) | 2 | ' global '
getVariable // 1 | gatherStats ( atvMC . velocity ) | 2 | global
push ' enqueueStats ' // 1 | gatherStats ( atvMC . velocity ) | 2 | global | ' enqueueStats '
callMethod // global . enqueueStats ( gatherStats ( atvMC . velocity ), 1 )
pop // -- <empty>
// -- enqueueStats(gatherStats(atvMC.velocity));
push ' atvMC ' // ' atvMC '
getVariable // atvMC
push ' velocity ' // atvMC | ' velocity '
getMember // atvMC . velocity
push 1 , ' gatherStats ' // atvMC . velocity | 1 | ' gatherStats '
callFunction // gatherStats ( atvMC . velocity )
push 1 , ' enqueueStats ' // gatherStats ( atvMC . velocity ) | 1 | ' enqueueStats '
callFunction // enqueueStats ( gatherStats ( atvMC . velocity ))
pop // -- <empty>
// -- emptyFunction();
push 0 , ' emptyFunction ' // 0 | ' emptyFunction '
callFunction // emptyFunction ()
pop // -- <empty>
// -- global.emptyFunction();
push 0 , ' global ' // 0 | ' global '
getVariable // 0 | global
push ' emptyFunction ' // 0 | global | ' emptyFunction '
callMethod // global . emptyFunction ()
pop // -- <empty> Kloning repo, dan jalankan npm i . Kemudian, jalankan npm start , atau instal sebagai modul NPM relatif lokal dan jalankan sebagai js-to-flasm-as2-compiler di dalam package.json
js-to-flasm-as2-compiler [configFilePath]
$ npm start
> [email protected] start D: P rojects j s-to-flasm-compiler
> node ./bin/index.js
samples A .js - > dist A .flm
samples B .js - > dist B .flm
empty file samples C a.js
samples C aa.js - > dist C aa.flm
samples h ooks f oobar i nitialize-socket-hook.js - > dist h ooks f oobar i nitialize-socket-hook.flmJika Anda berada di Linux, Anda mungkin bisa saja
chmod u+x bin/index.js
ln -s $PWD /bin/index.js ./js-to-flasm-as2-compiler
./js-to-flasm-as2-compiler Kompiler akan mengkompilasi file sumber JS dalam samples/ , dan memancarkan output ke dist/ Directory.
Kompiler macet jika Anda menggunakan fitur JS yang belum saya terapkan.
Komentar juga //-- dan /*--[[ --]]*/ Karena komentar Flasma adalah // dan /* */ , tetapi saya menggunakan sintaksis sintaks LUA untuk FLM (cukup baik!) Dan Lua's Are -- dan --[[ --]] .
var yang didukung (tidak let atau const )if tidak mendukung operator biner logis&& atau || ), meskipun ! adalahclass tidak diimplementasikan, gunakan pemrograman berbasis prototipe (buat fungsi yang merupakan konstruktor dan penugasan fungsi ke ctor.prototype)int() bukan parseInt()var foo = parseInt(bar) do var foo = int(bar)intfoo[bar] atau foo['bar'] atau foo[x + y + 1 + true + 0] tidak apa -apa, tetapi foo[bar++] atau foo[selector()] akan gagal mengkompilasi++variable berfungsi tetapi ++foo.bar atau ++foo[bar] tidakvariable++ atau variable++ tidak diimplementasikan Kompiler mencari file konfigurasi bernama js-to-flasm.config.json di direktori saat ini, atau dari jalur yang ditentukan oleh argumen pertama yang diteruskan ke sana.
Misalnya
js-to-flasm-as2-compiler ./config/js-to-flasm.config.json
Contoh file konfigurasi:
{
"dist" : " dist/ " ,
"sourceRoot" : " samples/ "
} Tidak ada kunci lain yang didukung, dan keduanya diperlukan. Kedua jalur file dapat menjadi apa pun yang dipahami oleh Node's fs.readdir() . Terakhir / mungkin opsional.
Arahan kompiler diimplementasikan melalui komentar satu baris. Arahan tidak dicari komentar blok di dalam atau komentar JSDOC. Arahan adalah bentuk:
// directive-name
atau jika menerima argumen, itu dari bentuk:
// directive-with-args: arg1 arg2 arg3 argn
Arahan saat ini adalah:
@js2f/push-register-context , @js2f/pop-register-context Memungkinkan Anda untuk memberi tahu kompiler tentang register <-> asosiasi variabel. Misalnya, jika Anda menyusun kode yang mendapat #include 'D di dalam fungsi yang tidak Anda kendalikan (yaitu kode yang dibongkar), dan Anda ingin mengakses this di dalam fungsi, Anda dapat menginstruksikan kompiler yang ada dalam register n.
Pop konteksnya begitu Anda tidak membutuhkannya lagi.
Contoh 1
// @js2f/push-register-context: r:1=this
enqueueSocketJob ( '{"type": "cash", "data":' + this . cash + "}" ) ;
// @js2f/pop-register-contextContoh 2
// @js2f/push-register-context: r:3=socket
socket . onConnect = function ( ) {
// can use local variables here
var foobar = 123 ;
} ;
// @js2f/pop-register-context MIT, lihat lisensi.