An open API service indexing awesome lists of open source software.

https://github.com/purplesyringa/batchbuilder

Transpiler from Batch to Batch written completely in Batch
https://github.com/purplesyringa/batchbuilder

abnormal-programming batch build cmd dos transpile windows

Last synced: 3 days ago
JSON representation

Transpiler from Batch to Batch written completely in Batch

Awesome Lists containing this project

README

          

# BatchBuilder v1.0
(c) 2018 Ivanq

**BatchBuilder - система сборки BAT/CMD файлов.**

## Пример использования

Начальные действия:
1. Копируем build.cmd и папку compiler куда-нибудь (например, в batch_test).
2. Создаем папку src и кидаем в нее файл entry.cmd. entry.cmd - ваш BAT.

После изменений:
1. Запускаем build.cmd.
2. Появляется папку dist со скриптом bootstrap.cmd, запускаем его.

## Настройка "под себя"

Для управления BatchBuilder в папке src можно создать файл build.ini. Имеются следующие опции:

1. `entry=`
Значение по умолчанию: `entry.cmd`

Скрипт, который будет запускаться при старте.

2. `delete_compiled=yes/no`
Значение по умолчанию: `yes`

Удалять ли скомпилированные BAT-файлы. Если выбрано no, скомпилированный код будет храниться в папке `compiler\compiled`.

3. `compile_if=`
Значение по умолчанию: `batch ~-4 .cmd,batch ~-4 .bat`

Определяет, какие файлы будут обработаны компилятором BatchBuilder.

Набор правил разделяется запятой. Сами же правила (`~-4 .cmd` и `~-4 .bat`) делятся на имя компилятора (`batch`), ключ (`~-4`) и значение (`.cmd` и `.bat`).

Если ключ начинается с тильды (~), а следующее за ней число - отрицательное (-4), то будут учитываться последние -n знаков (4). Если число положительное, будут учитываться все знаки, кроме первых n. Затем знаки сравниваются со значением.

Если ключ не начинается с тильды, то это правило замены. Например, ключ a=b означает "заменить все a на b и затем сравнить со значением".

Пример:
`batch ~-4 .cmd,batch ~-4 .bat`
Обрабатывать файлы, у которых последние 4 символа - .cmd или .bat (то есть, имеют расширение cmd или bat), компилятором batch.

Пример:
`batch o=a hello.bat`
Обрабатывать файлы, имя которых равно hello.bat или hella.bat, компилятором batch.

4. `packed=`
Значение по умолчанию: `yes`

Определяет, должен ли BatchBuilder упаковывать проект в один файл.

В случае, если значение - no, будет создан файл bootstrap.cmd и папка contents, содержащая откомпилированные скрипты, и при запуске папка contents будет скопирована в %TEMP%.

Если значение - local, будут также созданы файл bootstrap.cmd и папка contents, однако скрипт будет запущен из директории contents, а не %TEMP%.

Если же значение - yes, файл bootstrap.cmd будет содержать распаковщик и сам архив, и скрипт будет запущен из %TEMP%.

## Компилятор BatchBuilder

Для упрощения работы BatchBuilder представляет возможность создания модулей. Модуль - обычный BAT-файл. Пример модуля:

```
+------------------------------------------------+
| hi.bat |
+------------------------------------------------+
| export say_hello |
| echo Hello |
| end export |
+------------------------------------------------+
```

Сохраним его в папку src. Теперь напишем скрипт, использующий say_hello:

```
+------------------------------------------------+
| entry.cmd |
+------------------------------------------------+
| import say_hello |
+------------------------------------------------+
```

Компилятор BatchBuilder заменит "import say_hello" на вызов "echo Hello".

## Аргументы методов

При вызове `import say_hello` мы могли передать дополнительные данные - например, имя:

```
+------------------------------------------------+
| entry.cmd |
+------------------------------------------------+
| import say_hello Kirill |
| import say_hello Vanya |
+------------------------------------------------+
```

В таком случае первое переданное значение будет доступно как %1, второе - как %2 и так далее.

```
+------------------------------------------------+
| hi.bat |
+------------------------------------------------+
| export say_hello |
| echo Hello, %1! |
| end export |
+------------------------------------------------+
```

Также можно указать имя для некоторых параметров, тогда `%1`, `%2` и т.д. будут означать "1/2 параметры после ":

```
+------------------------------------------------+
| hi.bat |
+------------------------------------------------+
| export say_hello name |
| echo Hello, %name%! |
| end export |
+------------------------------------------------+
```

Модули могут быть полезны для выполнения рутинных операция, например, запроса на удаление папки.

```
+------------------------------------------------+
| delete.cmd |
+------------------------------------------------+
| export delete_dir dir |
| set /p agree=Do you want to delete %dir%? |
| if "%agree%" == "yes" ( |
| rmdir /S /Q %1 |
| ) |
| end export |
+------------------------------------------------+
```

```
+------------------------------------------------+
| entry.cmd |
+------------------------------------------------+
| import delete_dir Documents |
| import delete_dir Desktop |
| import delete_dir Pictures |
| import delete_dir Music |
+------------------------------------------------+
```

## Возврат значений

Компилятор "batch" BatchBuilder также поддерживает оператор `return` внутри блоков `export` для возврата значения из метода. Для получения возвращенного значения можно использовать оператор `import -> variable command`.

```
+------------------------------------------------+
| delete.cmd |
+------------------------------------------------+
| export ask q |
| set /p result=%q%? |
| return %result% |
| end export |
| |
| export delete_dir |
| import -> agree ask "Delete %1" |
| if "%agree%" == "yes" ( |
| rmdir /S /Q %1 |
| ) |
| end export |
+------------------------------------------------+
```

## Глобальные переменные

По умолчанию переменные, определенные внутри одной функции, недоступны из другой функции или вызова рекурсии этой же функции.

```
+------------------------------------------------+
| local_vars.cmd |
+------------------------------------------------+
| export a |
| echo Enter A |
| import b |
| echo Read local variable my_var from A |
| echo my_var=%my_var% |
| echo Exit A |
| end export |
| |
| export b |
| echo Enter B |
| echo Set local variable my_var |
| set my_var=hello |
| echo Read local variable my_var from B |
| echo my_var=%my_var% |
| echo Exit B |
| end export |
| |
| import a |
+------------------------------------------------+
| Enter A |
| Enter B |
| Set local variable my_var |
| Read local variable my_var from B |
| my_var=hello |
| Exit B |
| Read local variable my_var from A |
| my_var= |
| Exit A |
+------------------------------------------------+
```

Однако, иногда необходимо пробросить переменную во все функции. Для этого используется ключевое слово `global`.

```
+------------------------------------------------+
| global_vars.cmd |
+------------------------------------------------+
| export a |
| echo Enter A |
| import b |
| echo Read global variable my_var from A |
| echo my_var=%my_var% |
| echo Exit A |
| end export |
| |
| export b |
| echo Enter B |
| echo Set global variable my_var |
| global my_var=hello |
| echo Read global variable my_var from B |
| echo my_var=%my_var% |
| echo Exit B |
| end export |
| |
| import a |
+------------------------------------------------+
| Enter A |
| Enter B |
| Set global variable my_var |
| Read global variable my_var from B |
| my_var=hello |
| Exit B |
| Read global variable my_var from A |
| my_var=hello |
| Exit A |
+------------------------------------------------+
```

## Директивы экспорта

Перед командой `export` может идти команда вида `@directive `, что означает установку директивы `@something` для текущей функции. Можно устанавливать сразу несколько директив, например: `@directive `.

1. `@safe_recursion`

По умолчанию при вызове функции происходит создание нового контекста для того, чтобы локальные переменные одной функции не были видны из другой. Для ускорения используются команды `setlocal` и `endlocal`. Однако, `setlocal` имеет определенный уровень глубины вызова.

Если необходим большой уровень рекурсии, имеет смысл использовать директиву `@safe_recursion`, которая сохраняет и восстанавливает переменные через файлы, а не `setlocal` и `endlocal`. Однако, `@safe_recursion` может сильно замедлить программу.

2. `@follow_local`

Для упрощения работы с переменными все локальные переменные caller'а удаляются, а после выполнения восстанавливаются. Например:

```
+------------------------------------------------+
| non_local.cmd |
+------------------------------------------------+
| export a |
| echo Enter A |
| echo Set my_var=hello |
| set my_var=hello |
| import b |
| echo Exit A |
| end export |
| |
| export b |
| echo Enter B |
| echo Read local variable my_var |
| echo my_var=%my_var% |
| echo Exit B |
| end export |
| |
| import a |
+------------------------------------------------+
| Enter A |
| Set my_var=hello |
| Enter B |
| Read local variable my_var |
| my_var= |
| Exit B |
| Exit A |
+------------------------------------------------+
```

Однако, для ускорения вызовов рекурсии имеет смысл использовать директиву `@follow_local`, если не учитываются пустые переменные:

```
+------------------------------------------------+
| local.cmd |
+------------------------------------------------+
| export a |
| echo Enter A |
| echo Set my_var=hello |
| set my_var=hello |
| import b |
| echo Exit A |
| end export |
| |
| @directive follow_local |
| export b |
| echo Enter B |
| echo Read local variable my_var |
| echo my_var=%my_var% |
| echo Exit B |
| end export |
| |
| import a |
+------------------------------------------------+
| Enter A |
| Set my_var=hello |
| Enter B |
| Read local variable my_var |
| my_var=hello |
| Exit B |
| Exit A |
+------------------------------------------------+
```

Наибольшее ускорение можно получить, если использовать `@follow_local` и unsafe-рекурсию.

## Классы и объекты

Компилятор "batch" поддерживает систему классов. Для того, чтобы создать класс, используется пара `class`/`end class`:

```
+------------------------------------------------+
| class.cmd |
+------------------------------------------------+
| class ClassTest |
| end class |
+------------------------------------------------+
```

Методы можно создавать при помощи слова `export`:

```
+------------------------------------------------+
| class.cmd |
+------------------------------------------------+
| class ClassTest |
| export say_hello |
| echo Hello, %1! |
| end export |
| export say_bye |
| echo Bye, %1! |
| end export |
| end class |
+------------------------------------------------+
```

Для создания класса используется слово `new`, а вызов происходит через полученный инстанс:

```
+------------------------------------------------+
| class.cmd |
+------------------------------------------------+
| echo Create ClassTest and put it to my_class |
| new -> my_class ClassTest |
| |
| echo Call say_hello and then say_bye |
| import %my_class%.say_hello Ivan |
| import %my_class%.say_bye Ivan |
+------------------------------------------------+
```

Стоит отметить, что объекты создаются в куче, то есть, они не привязаны к имени инстанса. Таким образом, объекты можно передавать по ссылке.

```
+------------------------------------------------+
| reference_class.cmd |
+------------------------------------------------+
| export by_reference |
| echo Inside: %1 |
| end export |
| |
| new -> my_class ClassTest |
| echo Outside: %my_class% |
| import by_reference %my_class% |
+------------------------------------------------+
| Outside: __instance_343654654__ |
| Inside: __instance_343654654__ |
+------------------------------------------------+
```

## Поля классов

Внутри методов доступна переменная `%this%`, указывающая на текущий инстанс:

```
+------------------------------------------------+
| class.cmd |
+------------------------------------------------+
| class ClassTest |
| export save |
| set "%this%.data=%~1" |
| echo Saved %~1 |
| end export |
| |
| export load |
| call set "data=%%%this%.data%%" |
| return %data% |
| end export |
| |
| export echo |
| import -> data %this%.load |
| echo Loaded %data% |
| end export |
| end class |
| |
| new -> instance ClassTest |
| import %instance%.save One |
| import %instance%.echo |
+------------------------------------------------+
| Saved One |
| Loaded One |
+------------------------------------------------+
```

## Магические методы

Иногда возникает необходимость обрабатывать нестандартные операции, без вызова `import`. Для этого можно определить магические методы.

1. `magic_init`

Вызывается, когда создается объект при помощи оператора `new`. В метод `magic_init` можно передать параметры через оператор `new`:

```
+------------------------------------------------+
| magic.cmd |
+------------------------------------------------+
| class ClassTest |
| export magic_init |
| echo Created %~1 |
| end export |
| end class |
| |
| new -> instance ClassTest "for test" |
+------------------------------------------------+
| Created for test |
+------------------------------------------------+
```

## Рабочие примеры

С BatchBuilder поставляются примеры, названные как src-admin, src-alert и так далее. Для их запуска переименуйте папку в src, запустите build.cmd, а затем dist\bootstrap.cmd.