Compile (un subconjunto de) JavaScript o AS2 a ActionScript 2 (Flash Player 8 Compatible) Bytecode Ensamblaje compilable por Flasm. Extremadamente wip.
Estoy modificando un viejo juego flash y me cansé de escribir bytecode de ActionScript a mano. Afortunadamente, ActionScript 2 es extremadamente similar a JavaScript (ya que AS2 se ajusta parcialmente a la especificación Ecmascript 4), por lo que puedo usar @babel/parser para analizar mi fuente como JavaScript en un AST, y luego emitir el bytecode del AST.
El compilador solo admite un subconjunto de características JS/AS2 que necesito, y el proyecto se reserva el derecho a la semántica del lenguaje para facilitar la implementación. El desarrollo también progresará solo cuando encuentro características que quiero implementar para facilitar mi vida.
El compilador también simula la pila para documentar la pila actual como comentarios a la derecha de cada línea para facilitar la verificación manual. Si se alcanza una rama o un código de operación Branchiftrue (un if-else o un bucle), el simulador omite el descanso de la función/contexto. Las funciones internas se simulan con sus propias pilas. Las declaraciones de código también se agregan como comentarios antes del Bytecode para dicha declaración.
@js2f/push-register-context , @js2f/pop-register-contextFuente de muestra de trabajo en progreso actual:
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 ( ) ;Emite:
// -- 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> Clonar el repositorio y ejecutar npm i . Luego, ejecute npm start , o instálelo como un módulo NPM relativo local y ejecutarlo como 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.flmSi estás en Linux, probablemente puedas
chmod u+x bin/index.js
ln -s $PWD /bin/index.js ./js-to-flasm-as2-compiler
./js-to-flasm-as2-compiler El compilador compilará los archivos de origen JS en samples/ y emitirá la salida al directorio dist/ .
El compilador se bloquea si usa funciones JS que no he implementado.
Los comentarios también son //-- y /*--[[ --]]*/ Porque los comentarios de Flasm son // y /* */ , pero uso la sintaxis de Lua para el resaltado para FLM (¡es lo suficientemente bueno!) Y los lua son -- y --[[ --]] .
var (no let ni const )if no admite operadores binarios lógicos&& o || ) ! esclass no se implementa, use la programación basada en prototipos (realice una función que sea un constructor y asigne funciones a CTOR.Prototype)int() en lugar de parseInt()var foo = parseInt(bar) do var foo = int(bar)intfoo[bar] o foo['bar'] o foo[x + y + 1 + true + 0] está bien, pero foo[bar++] o foo[selector()] no se compilará++variable funciona pero ++foo.bar o ++foo[bar] novariable++ o variable++ no se implementa El compilador busca un archivo de configuración llamado js-to-flasm.config.json en el directorio actual, o desde una ruta especificada por el primer argumento que se le pasó.
P.ej
js-to-flasm-as2-compiler ./config/js-to-flasm.config.json
Archivo de configuración de ejemplo:
{
"dist" : " dist/ " ,
"sourceRoot" : " samples/ "
} No se admiten otras claves, y ambas son necesarias. Ambas rutas de archivos pueden ser cualquier cosa que comprenda fs.readdir() de nodo. Terminar / probablemente sea opcional.
Las directivas del compilador se implementan a través de comentarios de una sola línea. No se buscan directivas para comentarios de bloque interior o comentarios de JSDOC. Una directiva es de forma:
// directive-name
o si acepta argumentos, es de forma:
// directive-with-args: arg1 arg2 arg3 argn
Las directivas actuales son:
@js2f/push-register-context , @js2f/pop-register-context Le permite informar al compilador sobre las asociaciones variables de registro <->. Por ejemplo, si está compilando un código que obtiene #include 'D dentro de una función que no controla (es decir, es un código desmontado), y desea acceder this función, puede instruir al compilador que está en el registro n.
Hazte el contexto una vez que ya no lo necesitas.
Ejemplo 1
// @js2f/push-register-context: r:1=this
enqueueSocketJob ( '{"type": "cash", "data":' + this . cash + "}" ) ;
// @js2f/pop-register-contextEjemplo 2
// @js2f/push-register-context: r:3=socket
socket . onConnect = function ( ) {
// can use local variables here
var foobar = 123 ;
} ;
// @js2f/pop-register-context MIT, ver licencia.