คอมไพล์ (ชุดย่อยของ) JavaScript หรือ AS2 ถึง ActionScript 2 (Flash Player 8 เข้ากันได้) แอสเซมบลีไบต์ที่รวบรวมได้โดย flasm WIP มาก
ฉันกำลังดัดแปลงเกมแฟลชเก่าและฉันเบื่อที่จะเขียน Actionscript Bytecode ด้วยมือ โชคดีที่ ActionScript 2 นั้นคล้ายคลึงกับ JavaScript (เนื่องจาก AS2 บางส่วนสอดคล้องกับ ECMASCRIPT 4 Spec) ดังนั้นฉันจึงสามารถใช้ @babel/parser เพื่อแยกวิเคราะห์แหล่งที่มาของฉันเป็น JavaScript เป็น AST แล้วปล่อยไบต์จาก AST
คอมไพเลอร์รองรับเพียงชุดย่อยของคุณสมบัติ JS/AS2 ที่ฉันต้องการและโครงการขอสงวนสิทธิ์ในการใช้ความหมายของคนขายเนื้อเพื่อให้ง่ายต่อการใช้งาน การพัฒนาจะดำเนินการเฉพาะเมื่อฉันพบคุณสมบัติที่ฉันต้องการนำไปใช้เพื่อทำให้ชีวิตของฉันง่ายขึ้น
คอมไพเลอร์ยังจำลองสแต็กเพื่อจัดทำเอกสารสแต็กปัจจุบันเป็นความคิดเห็นทางด้านขวาของแต่ละบรรทัดเพื่อให้การตรวจสอบด้วยตนเองง่ายขึ้น หากมี opcode สาขาหรือ branchiftrue (if-else หรือ loop) ตัวจำลองจะข้ามส่วนที่เหลือของฟังก์ชั่น/บริบท ฟังก์ชั่นภายในถูกจำลองด้วยสแต็คของตัวเอง คำสั่งรหัสจะถูกเพิ่มเป็นความคิดเห็นก่อนที่รหัส bytecode สำหรับคำสั่งดังกล่าว
@js2f/push-register-context , @js2f/pop-register-contextแหล่งตัวอย่างที่กำลังดำเนินการอยู่ในปัจจุบัน:
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) ทำ var foo = int(bar)intfoo[bar] หรือ foo['bar'] หรือ foo[x + y + 1 + true + 0] ไม่เป็นไร แต่ 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 MIT ดูใบอนุญาต