
Меня зовут Степан, я C# профессионал уже более 7 лет на рынке и рассказываю об этом в Telegram каналe StepOne. Иногда мне скучно на работе, потому что перекладывать JSON это слишком просто, даже если микросервисы.
Я отучился на системного программиста-компиляторщика и столкнулся с отсутствием спроса рынка на такие навыки. Но выбрал быть счастливым и написал язык программирования hydrascript, чтобы JSON гонялся даже в докере на макбуке. Решение под катом вас точно удивит!
Дисклеймер
Интерпретатор языка программирования hydrascript создан исключительно с помощью Vanilla C#. Никаких lex, flex, yacc, bison, antlr, llvm и других компиляторных инструментов не было использовано и не планируется
Лор hydrascript
Проект стартовал, как диплом бакалавра "Расширенное Подмножество JavaScript". Я взял небольшую часть стандарта ECMA-262 и дополнил её своим авторским видением. Далее успешно закончил кафедру ИУ-9 Бауманки и решил продолжить проект под кодовым именем "HydraScript".
После починки одного бага появляется несколько других!
Четыре года разработки по вечерам пролетели незаметно. Проект усилил мои компетенции в архитектуре и платформе .NET. Я даже изобрёл новую реализацию паттерна Visitor.
Также, open source разработка в репозитории hydrascript обрела 4 цели:
Частичная реализация JavaScript с объектами и сильной структурной статической типизацией без таких ключевых слов, как:
constructor,class,interfaceПубличный реверс-инжиниринг современного статического анализа (вывод типов, forward ссылки, runtime ошибки на этапе компиляции и так далее)
Демистификация и фасилитация компиляторного домена через исходный код HydraScript
Сбор понятных решений стандартных компиляторных задач (лексер, парсер, CFG, SSA, DCE и так далее)
Установка
Теперь всё серьёзно. Это больше не просто диплом студента, а полноценный open source - CI/CD на GitHub Actions, семантическое версионирование, стандарты разработки, шаблоны PR, беклог, автоматические релизы и не только.
В рамках релиза бинарник интерпретатора собирается под три платформы в режиме Native AOT, чтобы не требовать зависимостей:
Windows (x64)
macOS (arm64 Apple Silicon)
Linux (x64)
Последний релиз доступен на GitHub по этой ссылке. В качестве альтернативы можно поставить себе HydraScript, как утилиту .NET:
dotnet tool update --global hydrascript
Ссылка на NuGet: https://www.nuget.org/packages/hydrascript
"Киллер" фичи
Детально останавливаться на основных возможностях и конструкциях языка не хочется, чтобы не раздувать статью. Тем более, актуальная документация на английском лежит в GitHub репозитории:
Так что сейчас выделю самое главное. В основном это достижения статического анализа.
Проверка доступа к переменной до инициализации
Если в TypeScript написать программу, в которой пытаются обратиться к переменной, которой ещё не присвоили значение, то получится ошибка рантайма. Во время выполнения скомпилированного JavaScript мы получим ошибку, которую HydraScript находит ещё на этапе статического анализа:
let x = f() function f() { console.log(x) return 5 }
Всегда есть значение по умолчанию
Переменные в C# требуют присваивания значения при объявлении. Если я напишу такой код, то получу ошибку Local variable ‘x’ might not be initialized before accessing
int x; Console.WriteLine(x);
Однако, в HydraScript переменной сразу будет присвоено значение по умолчанию. Для этого интерпретатор должен вывести тип. Если тип не будет выведен, то появится ошибка статического анализа Cannot define type
let x: number >>> x // 0 let xArr: number[] >>> xArr // [] let s: string >>> ~s // 0
Непересекающиеся идентификаторы символов
Символ для компилятора - это сущность в программе, которую можно использовать в исходном коде. Переменная, тип, функция, класс - и так далее. Несмотря на то, что символы могут быть разных типов, ограничения по именованию будут присутствовать. Например, такой код в C# не скомпилируется, хотя очевидно, что идентификатор x имеет разные значения:
x x = new(); x x(x x) { Console.WriteLine(x); return x; } class x { x x(x x) { Console.WriteLine(x); return x; } }
IDE покажет не одну, а сразу ТРИ ошибки:

Сейчас в HydraScript три типа символов:
VariableSymbol - переменная или объект
TypeSymbol - тип
FunctionSymbol - функция или метод
И уникальность идентификатора требуется в рамках типа символа. То есть скрипт может содержать тип, переменную и функцию под одним именем и успешно выполниться:
type x = number let x:x function x(x:x) { >>>x x = x + 1 } x(x)
Перекладываю JSON в Docker на MacBook через HydraScript и CGI интеграцию
С ростом возможностей HydraScript мне захотелось превратить его в нечто большее, чем просто очередной студенческий интерпретатор. Поскольку я backend разработчик, то и свой проект решил повернуть в сторону перекладывания JSON.
Довести до ума язык, чтобы на нём можно было создать веб-сервер? Пока что за рамками моих временных ресурсов. Сделать язык CGI-скриптинг совместимым? Проще пареной репы - нужно иметь в языке:
API для работы со строками
Вывод в stdout
Чтение переменных среды ($ENV)
Поддержку shebang комментариев, они же решётка строки (
#)
Что такое CGI-скриптинг
CGI-скриптинг — это набор команд, которые превращают обычную «неподвижную книжку» сайта в живую страницу: когда сервер получает твой запрос:
он собирает нужные данные в переменные окружения
envзапускает скрипт через интерпретатор по пути в shebang строке. Например,
#!/usr/bin/bashскрипт записывает ответ в
stdout. Например,Console.WriteLineв C#сервер подхватывает этот поток, превращает его в веб-страницу и сразу показывает тебе на экране
Это если простыми словами. Более сложно, есть официальный стандарт RFC 3875
Или вот статья на хабре, первая в гугле по запросу "habr cgi"
Всё это я добавил в рамках релиза 2.6.0. Далее надо было достать веб-сервер с поддержкой CGI, который можно развернуть в Docker за пару команд. Оказалось, что есть образ httpd, где установлен Apache 2. Осталось докинуть в образ бинарник интерпретатора, который я решил поставить как dotnet tool:
FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine as sdk-build RUN dotnet tool update -g hydrascript ENV PATH="/root/.dotnet/tools:${PATH}" FROM httpd:2.4-alpine COPY --from=sdk-build /root/.dotnet/tools/ /usr/bin RUN apk add dotnet10-runtime
Далее собрал образ через docker-compose, докинув конфиг Apache и CGI скрипты. В качестве демонстрации я решил написать программу, которая разбирает QueryString в массив объектов имя-значение. Например, для строки a=1&b=bla&c=false сервер должен отдать JSON вида:
{ "result": [ { "name": "a", "value": "1" }, { "name": "b", "value": "bla" }, { "name": "c", "value": "false" } ] }
Скрипт получился большим, поэтому листинг положу под спойлер. Полный сетап с конфигом и композ файлом можно найти на GitHub в этом репозитории:
uri_parse.cgi
#!/usr/bin/hydrascript type QueryStringParseResultItem = { name: string; value: string; } type QueryStringParseResult = { result: QueryStringParseResultItem[]; } type QueryStringParser = { input: string; } function parse(parser: QueryStringParser): QueryStringParseResult { const qsLen = ~parser.input let i = 0 let items: QueryStringParseResultItem[] let currentName: string, currentValue: string let isName = true while (i < qsLen) { const currentChar = parser.input[i] if (currentChar == "&" || i == qsLen - 1) { let addittion = i == qsLen - 1 && currentChar != "&" ? currentChar : "" items = items ++ [{name: currentName; value: currentValue + addittion;}] currentName = "" currentValue = "" isName = true } else if (currentChar == "=") { isName = false } else { if (isName) { currentName = currentName + currentChar } else { currentValue = currentValue + currentChar } } i = i + 1 } return { result: items; } } let parser: QueryStringParser = { input: $QUERY_STRING; } >>> "Content-Type: application/json\n\n" >>> parser.parse()

Итоги
Сегодня вы узнали, как бекенд-разработчик может скучать от работы по перекладыванию JSON.
Если вы тоже устали от корпоративного булшита и недостатка rocket science, то поставьте звезду репозиторию GitHub hydrascript:
Ещё я веду Telegram канал StepOne, куда выкладываю много интересного контента о программировании на C#, даю карьерные советы, рассказываю истории из личного опыта и раскрываю все тайны IT-индустрии!
