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
- Host: GitHub
- URL: https://github.com/purplesyringa/batchbuilder
- Owner: purplesyringa
- License: mit
- Created: 2018-02-12T19:14:56.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2018-03-15T18:36:15.000Z (over 8 years ago)
- Last Synced: 2025-04-11T18:54:26.819Z (about 1 year ago)
- Topics: abnormal-programming, batch, build, cmd, dos, transpile, windows
- Language: Batchfile
- Size: 58.6 KB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme-ru.md
- License: LICENSE
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.