Компиля (подмножество) JavaScript или AS2 к ActionScript 2 (Flash Player 8 Copatible) Сборка байт -кодов, подготовленную FLASM. Чрезвычайно WIP.
Я модирую старую флеш -игру, и я устал от написания Bytecode ActionScript вручную. К счастью, 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, которые я не реализовал.
Комментарии также //-- и /*--[[ --]]*/ потому что комментарии Флазма // и /* */ , но я использую синтаксис Луа для FLM (это достаточно хорошо!) И Луа-и --[[ --]] --
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[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, см. Лицензию.