Или что бывает если заставить очень опытного разработчика заниматься не своим делом. Думаю после этой статьи термин «overqualified» заиграет для вас новыми красками.
От авторов, запихнувших веб‑сервер на визитку и локализующих корпоративные приложения на клингонский — держим марку!
Наш волшебный дикий веб
Что первым делом приходит в голову, когда говорят о «веб-разработке»? Наверное создание сайтов или веб-приложений?
Лендинги, сайты-визитки, интернет-магазины или какие-нибудь веб-порталы
в ад.
Самые продвинутые из читателей наверное вспомнят PWA или какой-нибудь React Native с Flutter — предел полета фантазии обычного разработчика.
Что плохо:
главное что отделяет человека от великих свершений это его фантазия — точно нельзя сделать только то, что невозможно вообразить.
Поэтому сейчас мы будем расширять ваше воображение — в превентивных мерах, дрелью и дыроколом подручными средствами.
Перед вами шесть проектов отборнейшей дичи — реализующих самые безумные идеи с помощью вполне обыденных инструментов современного веб-разработчика.
Пожалуйста не пытайтесь рассказывать о таком на интервью в обычных компаниях — пожалейте интервьюера и его нежную психику.
Автор когда‑то давно (когда еще ходил по собеседованиям) довел одного интервьюера до нервного срыва, просто в деталях рассказав как на самом деле работает загрузка больших файлов через форму со стороны сервера — про HTTP 100 Continue, разбор MIME и прочие радости.
Люди тогда были сильно крепче головой чем сейчас, в нынешних реалиях могут и побить прямо на интервью.
Дичь первая: HTMLang
Не смог пропустить столь жизнеутверждающее описание от автора этого замечательного проекта:
They were laughing that HTML was not a real programming language... WHO"S LAUGHING NOW!!11
Да, это именно то что вы подумали — кто-то будучи сильно не в духе взял общий синтаксис HTML и создал на его основе полноценный язык программирования.
Вот так выглядит реализация знаменитого FizzBuzz:
<!DOCTYPE html>
<html>
<head>
<title>FizzBuzz</title>
</head>
<body>
<h3>Look into the DevTools Console</h3>
<htmlang style="display: none;">
<call target="console.log"><s>Generating FizzBuzz...</s></call>
<for><v>i</v> <n>1</n> <n>100</n>
<cond>
<when>
<eq>
<mod>
<v>i</v>
<n>15</n>
</mod>
<n>0</n>
</eq>
<call target="console.log">
<s>FizzBuzz</s>
</call>
</when>
<when>
<eq>
<mod>
<v>i</v>
<n>3</n>
</mod>
<n>0</n>
</eq>
<call target="console.log">
<s>Fizz</s>
</call>
</when>
<when>
<eq>
<mod>
<v>i</v>
<n>5</n>
</mod>
<n>0</n>
</eq>
<call target="console.log">
<s>Buzz</s>
</call>
</when>
<else>
<call target="console.log"><v>i</v></call>
</else>
</cond>
</for>
</htmlang>
<script src="./HTMLang.js"></script>
</body>
</html>
Не представляю что будет если самому Джоэлу выдать его же знаменитый «FizzBuzz» в такой реализации — есть шанс что старый сишный программист впадет в рекурсию.
Кстати кто там рассказывал на лекциях про «декларативный язык разметки» и «общую неполноценность»?
HTML (от англ. HyperText Markup Language — «язык гипертекстовой разметки») — стандартизированный язык гипертекстовой разметки документов для просмотра веб-страниц в браузере.
Зря старались, автор этого проекта тем временем спокойно пишет в консоль тегами HTML:
<call target="console.log">
<s>FizzBuzz</s>
</call>
Все потому что не надо нанимать системных программистов, прошедших полноценное обучение по дисциплинам CS (вроде курса по разработке компиляторов) для работы штатным говночистом разработчиком в обычном корпоративном проекте — о чем так мечтают все рекрутеры.
Может случиться неприятность:
в самый интересный момент окажется что половина работы реализована на чем-то таком интересном.
А сотворивший это «великий гуру» внезапно пропадет, выключив телефон и переехав в поисках истины в солнечную Караганду Канаду.
Прецеденты были.
Дичь вторая: HTML-as-programming-language
Нехорошие мысли терзают очень многих опытных разработчиков — все та же идея «полноценной разработки на HTML» не дает покоя и автору данного проекта.
Но только он зашел в этом процессе несколько дальше предыдущего.
Как вам например функция на чистом HTML:
<def multiplyFunction returns=int> <!-- You can create functions -->
<param a type=int/>
<param b type=int/>
<return>a * b</return>
</def>
<def main>
<var result type=int>
<!-- Create variables -->
<multiplyFunction>
<!-- and store the result of the function in the variable -->
<param>5</param>
<param>6</param>
</multiplyFunction>
</var>
</def>
Известная библейская истина «многие знания — многие печали» — как раз про этот проект, например я бы очень хотел все это забыть и никогда о подобном не знать.
Но к сожалению уже слишком поздно, поэтому делюсь откровениями:
Да, вы все правильно поняли — это самый настоящий компилятор из HTML в нативный ELF64 бинарник.
А сейчас вам совсем поплохеет:
To write code for Adruino/AVR microcontrollers, (Arduino UNO for example) you need to put a DOCTYPE tag in your HTML file.
For example:
<!DOCTYPE avr/atmega328p>
Да, это была оригинальная задумка автора — разработка для микроконтроллеров на HTML, я ничего не додумываю.
Увидев такой вот
DOCTYPE
, один знакомый веб-разработчик навсегда бросил пить.
К слову, небольшая магия с #include <stdio.h> на скриншоте выше была необходима как раз потому, что компилятор предназначен для микроконтроллеров и не добавляет в генерируемый код на С этот стандартный для обычной ОС заголовок.
К сожалению у меня не оказалось под рукой такого девайса, так что полноценную работу и весь пайплайн проверить не смог.
To compile your AVR/Arduino code:
htmlc my-code.html -compile
To upload your code to an arduino or other AVR microcontroller:
htmlc my-code.html -upload
Но если среди читателей найдутся смелые люди, которые смогут это запустить — с радостью почитаю о впечатлениях.
А мы тем временем переходим к следующему замечательному проекту.
Дичь третья: ass-js
Таким названием и не менее характерным логотипом, авторы честно намекают на суть проекта:
Assembler implemented in JavaScript
Полагаю вы еще никогда не устанавливали компилятор ассемблера с помощью npm? Что ж, все когда‑нибудь бывает в первый раз:
npm i ass-js
А так выглядит классический «Hello, world»:
import {X64} from '../src/index';
const asm = X64();
asm.code(_ => {
_('db', 'Hello World!n');
_('mov', ['rax', 1]); // 0x48, 0xC7, 0xC0, 0x01, 0x00, 0x00, 0x00
_('mov', ['rdi', 1]);
_('lea', ['rsi', _('rip').disp(-34)]);
_('mov', ['rdx', 13]);
_('syscall');
_('ret');
});
console.log(asm.toString());
Только не показывайте любимому преподавателю курса по ассемблеру — дедушке поплохеет, сердце может не выдержать такого накала дичи.
Вот так выглядит результат работы:
Зато есть отдельный туториал по разбору примера с «Hello world», где по шагам разобрано как оно работает.
Еще нашелся замечательный вопрос к автору:
I was looking at your project and I couldn't figure out a reason as to why I would (and what, rather) implement with this.
Который как бы намекает на уровень треша и угара в этом проекте. Но едем дальше — к следующему отбитому уникальному проекту.
Дичь четвертая: ts2c
Тут все просто и очевидно:
Produces readable C89 code from JS/TS code.
Собственно все кроме смысла существования этого замечательного проекта — понятно и очевидно. Как-то так выглядит весь пайплайн:
Если вам вдруг будет нужен транспилер из Typescript в чистый Си — берите и пользуйтесь, благо проект очень даже рабочий:
npm install -g ts2c
Работает кстати и из браузера:
<script src="https://unpkg.com/typescript"></script>
<script src="ts2c.bundle.js"></script>
<script>
var cCode = ts2c.transpile("console.log('Hello world!')");
alert(cCode);
</script>
Есть даже онлайн версия:
Несмотря на то что автор честно пишет о куче недоработок:
Work in progress: it works, but only about 70% of ES3 specification is currently supported: statements and expressions - 95%, built-in objects - 17%.
Скажу что это самый работоспособный проект из серии, все остальное буквально рассыпается в руках. Рассыпается и валится как и следующий объект исследования.
Дичь пятая: nerd
Как легко и быстро понять что исследуемый проект — дикое, нерабочее и глючное говно?
По описанию, обещающему бесконечные ништяки:
Javascript's God Mode. No VM. No Bytecode. No GC. Just native binaries.
Отсылка к чему-то божественному в описании технического проекта это вообще практически 100% диагноз, можно отбраковывать только по одному этому признаку — врядли ошибетесь.
Как нетрудно догадаться, вместо нормального JavaScript тут тоже что-то свое божественное:
NerdLang is a substract of JS with some additions, focus on efficiency.
И это «свое» скажем так застряло в далеком прошлом:
Supporting EcmaScript 3 standard
На минуточку, 3я редакция стандарта вышла еще в далеком 2000м году.
А сам проект пытается в который раз «натянуть сову на глобус» и залезть туда, где последовательно обломали клыки все крупные корпрорации уровня Google:
Nerd is a JavaScript native compiler aiming to make JavaScript universal, Nerd is able to compile native apps for Windows, Mac, Linux, iOS, Android, Raspberry, STM32 and more.
Разумеется я не мог пройти мимо такого, поэтому всю эту дичь собрал и запустил, хотя и пришлось немного исправлять скрипты сборки.
Пайплайн (присутствует на титульном скриншоте) выглядит вот так:
Автор настолько суров, что запихал инстукции сборки и линковки модуля работы с сокетами в package.json:
{
..
"version": "0.0.1",
"nerd":
{
"env": ["std"],
"read_only": [],
"lib":
{
"win32":
[
"-D_WIN32_WINNT=0x0600",
"-Wno-narrowing",
"-D_GNU_SOURCE",
"-I{__EXTERN__}/libuv/include/",
"-I{__EXTERN__}/libuv/src/",
"-D_CRT_SECURE_NO_DEPRECATE",
"-D_CRT_NONSTDC_NO_DEPRECATE",
"{__EXTERN__}/libuv/src/*.h",
"{__EXTERN__}/libuv/src/*.c",
"{__EXTERN__}/libuv/src/win/*.h",
"{__EXTERN__}/libuv/src/win/*.c",
"-I {__MODULE__}/httplib/uWS/",
"-I {__MODULE__}/httplib/uSockets/",
"{__MODULE__}/httplib/uSockets/*.c",
"{__MODULE__}/httplib/uSockets/crypto/*.c",
"{__MODULE__}/httplib/uSockets/eventing/*.c",
"-DLIBUS_NO_SSL",
"-DUWS_NO_ZLIB",
"-fpermissive",
"-w",
"-lm",
"-ladvapi32",
"-liphlpapi",
"-lpsapi",
"-lshell32",
"-luser32 ",
"-luserenv",
"-lwsock32",
"-lws2_32"
]
}
}
}
Увидев вот такой package.json, знакомый веб-разработчик (другой) решил навсегда уйти из профессии и теперь пасет коз в горах Кавказа.
Ну а я всего лишь не рискнул адаптировать такое для сборки под Linux, так что вы останетесь без примера запуска HTTP-сервера на этом чудище.
Дичь шестая: lemon
Наконец последний на сегодня проект, который по сравнению с предыдущими является можно сказать нормальным и где-то даже применимым:
Lemon is a framework for building Javascript runtime software, built on the Chrome V8 Javascript Engine.
Мне он понравился своей запредельной простотой (по сравнению со всеми остальными проектами) и легкостью встраивания.
Специально показываю скрипт сборки целиком:
CXX = g++
V8 = engine/lib/v8
define INCLUDE
$(V8)/include
engine/Core.cpp
engine/Environment.cpp
engine/Lemon.cpp
engine/StaticHelpers.cpp
engine/ObjectCreator.cpp
endef
define APP
app/*.cpp
endef
define LIB
$(V8)/out/x64.release/obj/
endef
define OBJ
v8_monolith
endef
export INCLUDE
export APP
export LIB
export OBJ
build:
(CXX) -I " class="formula inline">$INCLUDE $$APP -L $$LIB -l $$OBJ -std=c++0x -pthread -o lemon
И.. это все.
Настолько простую сборку V8 вижу впервые в жизни, честно.
Оно действительно собирается одной командой:
Ниже показано как выглядит двойной «Hello, world», в котором есть как часть на JavaScript так и часть на C++ — немного подумав объединил два примера из документации в один.
App.js:
version();
console.log("Превед из JS");
helloworld();
App.hpp:
#ifndef APP
#define APP
#include "../engine/Lemon.hpp"
using v8::Context;
class App : public Lemon {
public:
void Start(int argc, char* argv[]);
void SetupEnvironment();
};
#endif
App.cpp:
#include "App.hpp"
using namespace v8;
static void Log(const FunctionCallbackInfo<Value>& args) {
HandleScope scope(args.GetIsolate());
String::Utf8Value str(args.GetIsolate(), args[0]);
const char* cstr = StaticHelpers::ToCString(str);
fprintf(stdout, "%s", cstr);
fprintf(stdout, "n");
fflush(stdout);
}
static void HelloWorld(const FunctionCallbackInfo<Value>& args) {
printf("Превед из C++ n");
}
void App::SetupEnvironment() {
this->CreateGlobalMethod("helloworld", HelloWorld);
}
void App::Start(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
// Get filename of the javascript file to run
const char* filename = argv[i];
// Create a new context for executing javascript code
Local<Context> context = this->CreateLocalContext();
// Enter the new context
Context::Scope contextscope(context);
this->CreateGlobalObject("console")
.SetPropertyMethod("log", Log)
.Register();
// Run the javascript file
this->RunJsFromFile(filename);
}
}
Чудны дела твои Господи, коль даже перебирая запредельную дичь есть шанс найти столь мощный проект.
Спросите с чего столько радости?
Потому что это самый настоящий V8, не самопал с реализацией ECMAScript «в переводе Гоблина», а именно тот самый движок, который используется в браузере Chrome — со всеми оптимизациями и наворотами.
А значит при определенных усилиях, у вас будет работать практически любой JavaScript код — в вашем нативном приложении, без всяких жирных Node.js и всех проблем с линковкой и версиями.
Словом берите на вооружение, пригодится.
Одной строкой
Конечно же интересных проектов в области творения дичи куда больше чем хватит сил описать, поэтому ниже небольшая подборка найденного и интересного, но неработающего.
Compile javascript to LLVM IR, x86 assembly and self interpreting
К сожалению оказался прибит гвоздями к определенной версии MacOS, ни нормально собрать ни прогнать тесты под Linux не удалось. Интересен тем что в одном проекте собран и интерпретатор и компилятор, причем в нативный бинарник.
JavaScript Assembler x86-16
Генерирует готовые COM-файлы времен DOS, но под эмулятором Dosbox они работать отказались.
Duktape — embeddable Javascript engine with a focus on portability and compact footprint
Более продвинутый и известный аналог Lemon, который я нашел слишком поздно и не успел посмотреть.
«clang ported to js» — можно сразу в цитаты добавлять.
Сломанная и сильно устаревшая сборка, но сам проект — очень крутой, поскольку это полноценный компилятор Clang, вытащенный в веб.
Вот тут есть онлайн версия.
LLVM compiled to JavaScript using Emscripten
Снова сломанная и устаревшая сборка, починить за разумное время не получилось.
Она же в готовом виде онлайн.
Compile JS into LLVM IR - JavaScript Static Analysis Tool
Вот тут находится статья про этот проект, но мне собрать так и не удалось.
A toy js -> c++ compiler written in coffeescript. Uses escodegen to write c++ and tern to figure out types.
Опять сильно устаревшая и сломанная сборка.
Compiled implementation of Javascript, targeting C (for fun)
Половина тестов сломана, но сама сборка проходит — не стал детально изучать.
Letter is a compiler project built in TypeScript using LLVM node bindings.
Очень интересный, но к сожалению устаревший проект — требует 13й LLVM и старую же версию Node.js.
Подружить с новыми версиями LLVM и Node не удалось.
A tiny C compiler written purely in JavaScript.
Еще один интересный, но неработающий проект — сам компилятор отработал, но ассемблерный код отказался собираться.
Эпилог
Все описанные в этой статье проекты приведены в первую очередь для расширения границ вашего воображения — просто чтобы вы знали что так тоже бывает.
Пожалуйста не пытайтесь такое внедрять и использовать в рельном проекте, если не имеете за плечами серьезного опыта и/или профильного обучения по CS.
Либо умеете быстро бегать и имеете запасное гражданство.
Куда более вольная версия данной статьи доступна в нашем блоге.
0x08 Software
Мы небольшая команда ветеранов ИТ‑индустрии, создаем и дорабатываем самое разнообразное программное обеспечение, наш софт автоматизирует бизнес‑процессы на трех континентах, в самых разных отраслях и условиях.
Оживляем давно умершее, чиним никогда не работавшее и создаем невозможное — затем рассказываем об этом в своих статьях.
Автор: Alex Chernyshev