compile (하위 세트) JavaScript 또는 AS2 to ActionScript 2 (Flash Player 8 호환) 바이트 코드 어셈블리 Flasm에 의해 컴파일 가능합니다. 극도로 wip.
나는 오래된 플래시 게임을 모으고 있으며 Actionscript Bytecode를 손으로 쓰는 데 지쳤습니다. 운 좋게도 Actionscript 2는 JavaScript와 매우 유사합니다 (AS2는 ECMAScript 4 사양에 부분적으로 일치하므로) @babel/parser 사용하여 내 소스를 javaScript로 AST로 구문 분석 한 다음 AST에서 바이트 코드를 방출 할 수 있습니다.
컴파일러는 필요한 JS/AS2 기능의 하위 집합 만 지원하며 프로젝트는 언어의 정육점 시맨틱을 쉽게 구현할 수 있도록 권리를 보유합니다. 개발은 또한 내 인생을 편하게하기 위해 구현하고 싶은 기능을 찾을 때만 진행될 것입니다.
또한 컴파일러는 스택을 시뮬레이션하여 수동 검증을보다 쉽게하기 위해 각 라인의 오른쪽에있는 주석으로 현재 스택을 문서화합니다. 분기 또는 분기 오피 코드에 도달하는 경우 (IF-ELSE 또는 루프) 시뮬레이터는 나머지 기능/컨텍스트를 건너 뜁니다. 내부 기능은 자체 스택으로 시뮬레이션됩니다. 코드 명령문은 또한 상기 문의 바이트 코드 전에 주석으로 추가됩니다.
@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> 레포를 복제하고 npm i . 그런 다음 npm start 실행하거나 로컬 상대 NPM 모듈로 설치하여 js-to-flasm-as2-compiler Inside 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의 구문 강조 표시를 사용하고 Lua 's는 -- --[[ --]] 입니다.
var 만 지원됩니다 (No let 또는 const )if&& 또는 || ) ! ~이다class 구현되지 않고 프로토 타입 기반 프로그래밍을 사용하지 않습니다 (생성자 인 함수를 만들고 ctor.prototype에 함수를 할당)parseInt() int() ()를 사용하십시오.var foo = parseInt(bar) 대신 var foo = int(bar) 대신int opcode로 컴파일합니다foo[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/ "
} 다른 키는 지원되지 않으며 둘 다 필요합니다. 두 파일 경로는 Node의 fs.readdir() 이해하는 것이 될 수 있습니다. 종료 / 아마도 선택 사항 일 것입니다.
컴파일러 지침은 단일 라인 주석을 통해 구현됩니다. 지시문은 내부 블록 주석 또는 JSDOC 댓글을 검색하지 않습니다. 지침은 형태입니다.
// directive-name
또는 인수를 받아들이면 형태입니다.
// directive-with-args: arg1 arg2 arg3 argn
현재 지침은 다음과 같습니다.
@js2f/push-register-context , @js2f/pop-register-context Compiler에 register <-> 변수 연관성에 대해 알릴 수 있습니다. 예를 들어, 제어하지 않는 함수 내에서 #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, 라이센스를 참조하십시오.