تجميع (مجموعة فرعية من) javaScript أو AS2 إلى ActionScript 2 (Flash Player 8 متوافق) مجموعة Bytecode المدمجة بواسطة flasm. WIP للغاية.
أنا أعدل لعبة فلاش قديمة وسئمت من كتابة Actionscript bytecode باليد. لحسن الحظ ، فإن ActionScript 2 يشبه إلى حد كبير JavaScript (نظرًا لأن AS2 يتوافق جزئيًا مع ECMASCRIPT 4 SPEC) ، بحيث يمكنني فقط استخدام @babel/parser لتحليل مصدري كـ javaScript في AST ، ثم ينبعث منه رمز Bytecode من AST.
يدعم برنامج التحويل البرمجي فقط مجموعة فرعية من ميزات JS/AS2 التي أحتاجها ، ويحتفظ المشروع بالحق في الجزار في دلالات اللغة لتسهيل التنفيذ. لن يتقدم التطوير أيضًا عندما أجد ميزات أريد تنفيذها لجعل حياتي أسهل.
يحاكي المترجم أيضًا المكدس لتوثيق المكدس الحالي كتعليقات على يمين كل سطر لجعل التحقق اليدوي أسهل. إذا تم الوصول إلى رمز فرع أو فرع أو الفرع (IF-ELSE أو حلقة) ، يتخطى المحاكاة بقية الوظيفة/السياق. يتم محاكاة الوظائف الداخلية مع المداخن الخاصة بها. تتم إضافة عبارات الرمز أيضًا كتعليقات قبل رمز Bytecode للبيان المذكور.
@js2f/push-register-context ، @js2f/pop-register-contextمصدر عينة العمل الحالي في Progress:
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 ( ) ;تنبعث:
// -- 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> استنساخ repo ، وتشغيل npm i . بعد ذلك ، قم بتشغيل npm start ، أو قم بتثبيته كوحدة NPM النسبية المحلية وقم بتشغيله كـ js-to-flasm-as2-compiler داخل 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.flmإذا كنت في Linux ، فربما يمكنك فقط
chmod u+x bin/index.js
ln -s $PWD /bin/index.js ./js-to-flasm-as2-compiler
./js-to-flasm-as2-compiler سيقوم برنامج التحويل البرمجي بتجميع ملفات مصدر JS في samples/ ، وينبعث من الإخراج إلى dist/ Directory.
تحطم المترجم إذا كنت تستخدم ميزات JS التي لم أقم بتطبيقها.
التعليقات هي أيضًا //-- و /*--[[ --]]*/ لأن تعليقات Flasm هي // و /* */ ، لكنني أستخدم بناء جملة Lua لـ FLM (إنه جيد بما فيه الكفاية!) و Lua's-و-[ -- --[[ --]] .
var فقط (لا let أو const )if لم تدعم المشغلين الثنائيين المنطقيين&& أو || ) ، على الرغم من ! يكونclass ، واستخدم البرمجة القائمة على النموذج الأولي (اصنع وظيفة تعتبر مُنشئًا وتعيين وظائف لـ ctor.prototype)int() بدلاً من parseInt()var foo = parseInt(bar) do var foo = int(bar)intfoo[bar] أو foo['bar'] أو foo[x + y + 1 + true + 0] على ما يرام ، لكن foo foo[bar++] foo[selector()]++variable يعمل ولكن ++foo.bar أو ++foo[bar] لاvariable++ أو variable++ يبحث المترجم عن ملف تكوين يسمى js-to-flasm.config.json في الدليل الحالي ، أو من مسار محدد بواسطة الوسيطة الأولى التي تم تمريرها إليها.
على سبيل المثال
js-to-flasm-as2-compiler ./config/js-to-flasm.config.json
مثال ملف التكوين:
{
"dist" : " dist/ " ,
"sourceRoot" : " samples/ "
} لا يتم دعم مفاتيح أخرى ، وكلاهما مطلوب. يمكن أن يكون كل من مسارات الملف أي شيء يفهمه fs.readdir() . إنهاء / ربما اختياري.
يتم تنفيذ توجيهات المترجم عبر تعليقات الخط الواحد. لم يتم البحث في التوجيهات عن تعليقات الكتلة الداخلية أو تعليقات JSDOC. التوجيه هو الشكل:
// directive-name
أو إذا قبلت الحجج ، فهي من الشكل:
// directive-with-args: arg1 arg2 arg3 argn
التوجيهات الحالية هي:
@js2f/push-register-context ، @js2f/pop-register-context يتيح لك إخبار المحول البرمجي عن تسجيلات التسجيل <-> المتغير. على سبيل المثال ، إذا كنت تقوم بتجميع التعليمات البرمجية التي تحصل على #include 'd داخل وظيفة لا تتحكم فيها (أي رمز تفكيكها) ، وتريد الوصول إلى this داخل الوظيفة ، يمكنك توجيه المترجم الذي يكون في السجل n.
قم ببث السياق بمجرد أن لا تحتاجه بعد الآن.
مثال 1
// @js2f/push-register-context: r:1=this
enqueueSocketJob ( '{"type": "cash", "data":' + this . cash + "}" ) ;
// @js2f/pop-register-contextمثال 2
// @js2f/push-register-context: r:3=socket
socket . onConnect = function ( ) {
// can use local variables here
var foobar = 123 ;
} ;
// @js2f/pop-register-context معهد ماساتشوستس للتكنولوجيا ، انظر الترخيص.